汇编语言的一些注意点
- 汇编语言是直接在硬件之上工作的编程语言。
- CPU(Central Processing Unit) 中央处理单元。
- 汇编语言和机器语言的关系
汇编语言是机器指令便于记忆的书写形式。 - 汇编语言的三类指令
- 汇编指令:有对应的机器码
- 伪指令:没有对应的机器码,由编译器识别执行。
- 其他符号,没有对应的机器码,如 + - * / ,由编译器识别执行。
- 磁盘和内存的区别
磁盘上的数据或者程序如果不读到内存中,就无法被 CPU 使用。 - 指令和数据的区别
指令和数据是应用上的概念,在内存或者磁盘中,指令和数据没有任何区别,都是二进制信息,关键看如何去理解,理解成数据,或者是指令。
在存储器中,数据和程序以二进制的形式存放。
详细: P4
- 存储单元、bit、byte 的关系
一个存储单元可以存储 1 byte 的数据。
1 byte= 8 bit
1 byte 可以表示0~255之间的数
1 word 可以表示0~65535之间的数 - 完成对数据的读写,需要?
- 地址信息
- 控制信息(读信号输出、写信号输出...)
- 数据信息
- 总线
在计算机中专门有连接CPU和其他芯片的导线,叫做总线。
从逻辑上分成三类,地址总线、控制总线和数据总线。(外部总线) - CPU总线的宽度
一个CPU有N根地址线,则可以说这个 CPU 的地址总线的宽度为 N。 - 数据传输时,先低八位,后高八位。(想想为什么不是反过来?)
汇编指令同机器指令一一对应
- 寻址能力和地址总线宽度的关系
假设地址总线有n位,即共有n位二进制位来表示地址,那么最多可以表示2n个地址,另外,由于计算机以一个字节为寻址单位,所以CPU的寻址能力或者说最大寻址范围为2n个字节。
参考:
地址总线、数据总线、寻址能力、字长及cpu位数等概念之间的关系
https://www.cnblogs.com/chanchan/p/7648490.html
8根数据总线一次性可以传输 1B 的数据。
内存地址空间
可以寻址到的内存单元构成了这个 CPU 的内存地址空间。
可以将系统的各类存储器看作一个逻辑存储器,通过向不同的地址操作,就是对不同的存储器进行操作。一些概念
主板(BIOS)
接口卡:
显示卡((ROM)BIOS、RAM),我们将需要显示的内容写入显存,就会显示在显示器上。
网卡(BIOS)存储器芯片的分类
从读写属性上分成两类:随机存储器(RAM)和只读存储器(ROM)
随机存储器可读可写,但是必须带电存储,关机后存储器的内容丢失。
只读存储器只能读取不能写入,关机后其中的内容不丢失。如果发出向ROM的内存单元中写入数据的操作是无效的。一个典型的 CPU 由 运算器、控制器、寄存器等器件构成,由内部总线连接。
运算器:信息处理
控制器:控制器件工作
寄存器:进行信息存储
内部总线:连接各个器件,在它们之间进行数据的传送。内部总线和外部总线之间的关系
内部总线实现 CPU 内部各个器件之间的联系,外部总线实现 CPU 和 主板上其他器件的联系。程序员通过改变各种寄存器的内容来实现对 CPU 的控制。
8086寄存器的特点
- 16个寄存器
- 都是16位的
- 通用寄存器:AX,BX,CX,DX。
段寄存器: CS,DS,SS,ES。
CX中存放的是程序的长度,单位是字节。
为了保证兼容,可以拆分成两个8位寄存器。
AH,AL
BH,BL
...
这些8位寄存器都可以独立使用,也就是说,不会相互影响。 - 16位寄存器能表示最大的数是 65535 。
- 出于对兼容性的考虑,8086 CPU 可以一次性处理以下两种尺寸的数据--字(Word)和字节(Byte),字可以分成高位字节和低位字节,也可以说是高八位寄存器和低八位寄存器。
- 数制的后缀
在十六进制数后加H,在二进制数后加B,十进制数后什么也不加。 - 注意
在写一条汇编指令或者一个寄存器的名称时,不区分大小写。
指令的两个操作对象的位数应该是一致的。 - 若运算结果超出了储存结果的寄存器的存储范围,则会舍去高位,留下低位。
- 物理地址
所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,叫做物理地址。不同的CPU形成物理地址的方式不同。 - 逻辑地址
逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干。
资料:
物理地址和逻辑地址
https://blog.csdn.net/tuxedolinux/article/details/80317419
16位机为什么叫16位机?
处理、传输、暂时存储的信息的最大长度是16位的。
处理:运算器处理16位数据
传输:寄存器和运算器之间的通路为16位
暂时存储:寄存器的最大宽度为16位。8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力。
有16位数据总线,可以一次性传送16位的数据。8086如何合成物理地址?
在内部用两个16位地址合成的方法来形成一个20位的物理地址。
一个叫做段地址,一个叫做偏移地址。
物理地址=段地址*16+偏移地址。(也就是基础地址+偏移地址=物理地址)
" *16 " 可以理解成 左移 4 位探究X进制数左移1位相当于乘以 ?
为什么段的起始地址是16的倍数
因为基础地址=段地址*16为什么一个段的长度最大是64KB?
偏移地址为16位,寻址能力为64KB。(是8086 模式的限制,并不是所有的处理器都是这样)可以用不同的段地址和偏移地址形成同一个物理地址。
段地址(SA)、偏移地址(EA)
偏移地址的英文缩写为什么叫做EA?
偏移地址(Offset Address = OA),又叫有效地址(Effective Address = EA)
资料:偏移地址的英文缩写为什么叫做EA?
https://bbs.csdn.net/topics/370157156
- 数据在 21F60H 内存单元的说法
- 数据在 2000:1F60 单元
- 数据在2000H 段中的1F60单元中
CS是代码段寄存器,IP是指令指针寄存器。
cs:ip 读取指令的先后顺序
先读取指令到指令寄存器,后IP更新,然后执行读取过的指令。
(先读取,后执行)加电启动或者复位后,cs=FFFFH,IP=0000H,所以FFFF0单元的指令是8086PC机开机后执行的第一条指令。
指令的分类
传送指令,大部分寄存器的值,都可以用 mov 指令来改变。
转移指令,能够改变 cs、ip的内容的指令被统称为转移指令。jmp的用法
jmp 段地址:偏移地址 可以同时改变cs和ip的值。
jmp 合法寄存器 可以改变ip的值。(16位寄存器)地址的读取
对于一个内存片段,以最低的地址来表示该片段。
在表示内存片段的值时,高地址数据---低地址数据。
在画图时,低地址在上,高地址在下。段地址在段寄存器中存放,当8086CPU要访问内存的时候,由段寄存器提供内存单元的段地址。
debug指令
R--查看寄存器的内容
R 寄存器--修改寄存器的值
d --查看预设地址处内存的值
d 段地址:偏移地址 --查看指定开始位置的内存单元的值
d 段地址:偏移地址 末尾地址 --查看区间内内存单元的值
e 段地址:偏移地址 数据 数据... --修改内存单元的值
e 段地址:偏移地址 回车--用提问的方式改值
t ---执行一条指令
t3 ---执行3条指令
t=0103 ---执行0103处的指令
a
a 段地址:偏移地址值得注意的是
b80100 是 mov ax,0001 的机器码,在内存中是从低地址到高地址存储的,和数据正好相反。两位是一个单元(8位)
字单元
即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。(高位字节单元和低位字节单元)
N地址字单元,N是字单元的起始地址。内存单元
内存单元是字节单元(一个单元存放一个字节),也可以是字单元。mov的一些作用
将寄存器值、数据、内存单元的值送入寄存器中。
将寄存器的值传入内存单元,寄存器、段寄存器。(三对三)
将段寄存器的值传入寄存器、内存单元(
将内存单元的值传入段寄存器、寄存器[基本上是双向的](除去数据不能与内存单元和段寄存器直接交互)
add/sub
三寄存器一内存因为8086CPU硬件设计的问题,不能将数据直接送入段寄存器,需要使用另外一个寄存器作为中转,从而设定段寄存器的值。
在使用ds寄存器的时候,一般都需要先设定其值
[0]和ds:[0]的使用范围???
mov ax,[0]
在 debug 运行正常,但是在编译的时候被翻译成mov ax,0
,如果想要原有的作用,可以使用
mov bx,0
mov ax,[bx];默认段寄存器在ds中,当然也可以显式的给出。
间接的寻址。
为了避免麻烦,也可以使用 mov ax,ds:[0]
的形式寻址。
- 栈,是一种具有特殊的访问方式的存储空间,它的特殊性在于,最后进入这个空间的数据,最先出去。LIFO(last in first out)
- 向上增长型
在入栈的时候,栈顶从高地址向低地址的方向增长。
在任意的时刻,SS:SP指向栈顶元素。
当入栈的时候,sp=sp-2,后将数据移入栈顶空间。
在出栈的时候,先将栈顶空间数据移入寄存器,后sp=sp+2 - 栈中第一个内存单元地址、栈段的最高地址、栈为空时SS:SP的指向之间的关系
SS:Sp-1=栈段的最高地址-1=栈中第一个内存单元的地址。
要自己来警惕栈顶超界的问题。
8086CPU工作不会检查栈是否超界,它只考虑当前的栈顶在何处,从而节省资源。 - push/pop 指令的形式
push/pop 寄存器、段寄存器、内存单元
需要注意的是:栈操作都是以字为单位的。
本质上是内存传送指令。 - 将AX寄存器清零的两种方式
mov ax,0(三个字节的机器码)
sub ax,ax(2个字节的机器码) - 一段内存空间,可以是代码的存储空间,数据的存储空间,也可以是栈空间,也可以什么都不是。
- 一个源程序从写出到执行的过程
源程序的文本文件(.asm)->编译(.obj)连接(.exe)->可执行文件(程序和数据,相关的描述信息)->加载(command)->内存中的程序->运行(CPU) - 伪指令
code segment
...
code ends(可以理解为end segment)
end(标志着程序的结束)
伪指令一般由编译器执行,从而控制编译器的编译工作。
- 标号
一个标号指代了一个地址,被解析成一串数值。
所以不允许mov ds,data
存在。 - 程序返回
在单任务操作系统上(DOS)基础上,程序返回的过程如下。
一个程序P2在可执行文件中,则必须有一个正在运行的程序P1,将P2从可执行文件中加载入内存,将CPU的控制权交给P2,P2才能运行,P2开始运行之后,P1暂时不运行。
当P2运行完毕后,应该讲CPU的控制权交还给使它得以运行的程序P1,此后,P1继续运行。(这个过程叫做程序返回)
mov ax,4c00h
int 21h
用这两句实现程序返回。一般用于程序的末尾处。
- 语法错误和逻辑错误
- 在编译的时候,有两种情况得不到目标文件(.obj)
- 未能找到源程序文件
- 程序中有" severe errors"
- link 的作用
- 连接多个目标文件
- 连接某个库中的子程序
- 连接单独一个文件
- DOS系统的shell(外壳),是程序command.com(命令解释器),在DOS中,command处理各种输入:命令或要执行的程序的文件名。
- PSP(程序段前缀),是一个数据区,用于DOS和被加载程序的通信。
PSP区:SA:0
程序区:SA+10H:0
(段地址不相同)
ds=SA
cs=SA+10
PSP区有256个字节的空间。
可以推断出有如下对应关系:因为“ds=0B2D,所以程序从0B3DH段开始,也就是说CS是0B3DH - 在debug模式下,用 p 指令来执行 int 21h。
当int 21h
被执行后,显示出Program terminated normally
,表明返回到debug当中.然后可以用 q 指令退出debug模式,返回到 commmand 中。 - 如何完整的描述一个内存单元?
- 内存单元的地址
- 内存单元的长度
- 描述性的符号” () ”中,可以有3种类型。
- 寄存器名
- 段寄存器名
- 内存单元的物理地址(一个20位的数据)
详细P96
描述性符号 reg 表示寄存器
sreg 表示 段寄存器
- CPU执行loop指令的时候, 要进行两步操作,
- (cx)=(cx)-1
- 判断 cx 中的值,不为零则转至标号处执行程序,如果为零则向下执行。
一般情况下,用 cx 存放循环次数,用 loop 实现循环功能。
- 减少循环次数是提高计算效率的一种方式,例如 123*236 用哪个乘数作为 cx的值?
p103
- 在汇编源程序中,数据不能以字母开头,要在前面加0
p104
- 在遇到 loop 指令的时候,使用p命令来执行,Debug可以自动重复执行循环中的指令,直到(cx)=0为止。
也可以使用 g 偏移地址 直接运行到CS:偏移地址处。
两者有着相同的效果。 - 一个概念
一个简单的操作系统,大概10万行代码 - 段前缀
诸如ds: cs: ss: es:
之类的。 - 一段安全的空间
DOS或者其他合法的程序一般都不会使用0:200~0:2ff的256个字节的空间,所以我们使用这段空间是安全的。 - dw define word,字型数据。
- 可以用来定义数据
- 可以用来开辟空间
- end的作用
- 标志程序的结束
- end 后面的标号,指明了程序的入口。
- 伪指令 assume
无需深究,只要知道需要用它将你定义的具有一定用途的段和相关的寄存器联系起来就可以了。 - and指令(有0则0),or指令(有1则1)
将al 的第0位设定为0的指令是: and al,11111110B
将al的第0位设定为1的指令是: or al,00000001B - 按一下键盘的'a',在屏幕上就会显示'a',这是什么样的过程?
按下键盘的a,送入计算机中,并对其编码,转化为61h放入内存中,文本编辑软件从内存中取出61h,将其送到显卡的显存上,工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容,61h被当成'a',驱动县市区,,将字符'a'的图画显示在屏幕上。 - 一些常识
'A' 的ASCII码是 41H
'a' 的ASCII码是 61H
'1'-30H=1 - 一个有趣的tip
'a'-'A'=20H=2^5
表现在二进制上就是:小写字母的二进制第五位(从右往左,从0开始)为1,大写字母的为0,其他位相同。
通过灵活的运用and、or指令,可以将一串大小写混合的字符串转变成统一的大写或者小写。 - [bx+idata]的一些形式
[bx+idata]
=[idata+bx]
=[bx].idata
=idata[bx]
可以将idata[bx]
类比成C语言中的a[i]
所以[bx+idata]
的形式为高级语言实现数组提供了便利机制。
- si和di是8086CPU于bx功能相近的寄存器,只是不能分成两个8位寄存器来使用。
也可以有如下形式:
[si]
[si+idata]
也可以有与bx的组合形式:
[bx+si]
[bx][si]
拓展形式:
[bx+si+idata]
[bx+di+idata]
[bx+idata+si]
200[bx+si]
[bx].200[si]
[bx][si].200
要注意的是+idata可以对应成.idata
还有bx和si/di没有主次关系,后者也可以在前者的前面,上文中bx统一在si/di的前面只是习惯问题。
- 一般来说,在需要暂存数据的时候,我们都应该使用栈。
- 数据处理的两个基本问题
- 处理的数据在什么地方?
- 要处理的数据有多长?
- bx、si、di、bp中
[bp]默认的段地址是ss。
bp和bx的地位相同
si、di的地位相同。
注意下面的指令是错误的:
mov ax,[bx+bp]
mov ax,[si+di]
- 机器指令处理的数据在什么地方?
内存、CPU内部、端口 - 对于数据的处理分为哪几种操作?
读取、写入、运算。 - 立即数
对于直接包含在机器指令中的数据(执行前在CPU的指令缓存区中),在汇编语言中称为立即数(idata). - 寻址方式
p164
直接寻址--[idata]
寄存器间接寻址--[bx]
寄存器相对寻址--[bx+idata]
基址变址寻址--[bx+si]
相对基址变址寻址--[bx+si+idata]
- 在没有寄存器存在的情况下,用操作符 X ptr 指明内存单元的长度,X在汇编指令中可以为 word或者byte。
- div 除数(reg 或者 内存单元)
被除数 ax,ax与dx中(dx为高16位,ax为低16位)
除数8位,被除数16位。除数16位,被除数32位。
结果 除数8位,al商,ah余数
除数16位,ax商,dx余数。 - dd dword(double word),双字型数据。
- dup的一些用法
dw 3 dup(0)
dd 3 dup(0,1,2)
db 3 dup('abc','ABC')
转移指令对IP的修改范围不同,可以分成短转移和近转移(统称为段内转移),远转移(段间转移)
短转移的范围 -128~128
近转移 -32768~32867nop 的机器码占一个字节。
mov也可以借助偏移地址来复制指令。(本质上都是机器码)offset 操作符,取得标号的偏移地址。
jmp short 标号 的本质 ip=ip+8位位移
8位位移=标号处的地址-jmp指令后第一个字节的地址。(在编译的时候算出)
short--8位位移对应。
8位位移用补码表示。
jmp near ptr 标号 的本质 ip=ip+16位位移。
与上类似。
jmp far ptr 标号 的本质 jmp 段地址:偏移地址
jmp word ptr(段内转移)
jmp dword ptr(段间转移) 高地址是段地址,低地址是偏移地址 (cs:ip的指向)jcxz指令,
if(cx==0) jmp short 标号;所有有条件的跳转、循环都是短转移。比如jcxz,loop等
loop 标号相当于
cx--
if(cx!=0) jmp short 标号;理解只涉及位移的重要性,而不是存储实际地址。
编译器会对转移位移超界进行检测。