《汇编语言》学习笔记

寄存器(reg): CPU中存储数据的器件

在8086CPU中,有14个寄存器,可以分为8个通用寄存器、1个指令指针寄存器、1个标志寄存器以及4个段寄存器

通用寄存器:用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果。

AH&AL = AX (accumulator):累加寄存器,主要是在进行运算的时候存放操作数,同时所有的I/O指令都使用这一寄存器与外界设备传送数据
BH&BL = BX (basic):基址寄存器,用于地址的索引
CH&CL = CX (count):计数寄存器,用于计数,在移位操作以及循环(loop)时作为隐含的计数器
DH&DL = DX(data):数据寄存器,用于数据传递。在书中以8086CPU为例,如果是要用32位的数作为被除数时,通过使用DX与AX存放数据,DX放高16位,AX放低16位
SP(Stack Pointer):堆栈指针,与SS配合使用,可指向目前的堆栈位置(也就是当前栈顶的偏移地址)
BP(Base Pointer):基址指针寄存器,可用作SS的一个相对基址位置
SI(Source Index):源变址寄存器,可用来存放相对于DS段之源变址指针,主要用于对内存单元操作时与DS一起表示DS:SI处的内存单元
DI(Destination Index):目的变址寄存器,可用来存放相对于ES 段之目的变址指针,主要用于对内存单元操作时与DS一起表示DS:DI处的内存单元

SI和DI寄存器不可以分为两个8位的寄存器使用
在没有寄存器参与的指令中,应该使用指令X ptr参与以指明要操作的内存单元的大小为byte大小或word大小,否则CPU无法指定要访问的单元是字单元还是字节单元

指针指令寄存器:用于控制程序中指令的执行顺序

IP:指向段内指令的偏移量,在8086机中,CPU将CS:IP指向的内容当作指令执行

标志寄存器

PSW:也可称作flag寄存器,16位的存放条件标志、控制标志寄存器,在0、2、4、6、7、8、9、10、11位上分别对应了CF、PF、AF、ZF、SF、TF、IF、DF、OF标志

CF(Carry Flag):记录了运算结果给的最高有效位向更高位的进位值,或从更高位的借位值
PF(Parity Flag):记录相关的指令执行后结果的所有bit位中的1的个数是否是偶数,若是偶数则对应为1
AF(Auxiliary Carry Flag):
ZF(Zero Flag):零标志,记录在相关指令执行后结果是否为0,结果为0时(ZF)=1
SF(Sign Flag):记录相关指令执行后结果是否为负,如果是负则对应为1,可以理解为它记录了结果的最高位符号位
TF(Trap Flag):
IF(Interrupt-enable Flag):
DF(Direction Flag):控制每次操作之后SI和DI的增减,(DF) = 1时两者递增,反之递减
OF(Overflow Flag):溢出标记位,相加操作移除

段寄存器(sreg):对内存的分段管理而设置

CS(Code Segment):代码段寄存器
DS(Data Segment):数据段寄存器,在mov操作仅仅指定偏移地址的情况下,默认内存单元段地址由该寄存器存储
SS(Stack Segment):堆栈段寄存器,存放栈顶的段地址
ES(Extra Segment):附加段寄存器

  • 在8086机中,不能通过mov指令对CS:IP的值进行修改,但是可以通过jmp ax指令进行类似mov IP,ax的操作;不仅是不能对CS:IP进行修改,在8086CPU中,由于硬件设计问题,mov不支持将数据直接送入段寄存器的操作。

在访问内存单元的时候,可以使用段寄存器:[偏移地址]的形式对内存单元访问

字节和字

  • 字节:记为byte,一个字节由8个bit位组成,存在8位寄存器中
  • 字:记为word,由两个字节组成

当一个字存储在16位寄存器中的时候,假设在AX寄存器中,则AH存放高8位,AL存放低8位

汇编指令

mov

mov 寄存器,数据:将数据直接送入寄存器中(在未指明段时一般是指通用寄存器)
mov 寄存器,寄存器:将一个寄存器中的数据送到另外一个寄存器
mov 寄存器,内存单元:将指定内存单元的数据送入寄存器,可以使用类似mov ax,[0]的形式,在方括号中的值或者数字是相对与DS数据段里面的偏移地址
mov 内存单元,寄存器:将寄存器里面的数据送入内存单元
mov 段寄存器,内存单元:将指定内存单元的数据送入段寄存器
mov 内存单元,段寄存器:将指定段寄存器的数据送入内存单元
mov 段寄存器,寄存器:将寄存器里面的数据送到段寄存器,前面说的是不能用mov指令将数据直接送入,但是可以通过先把数据存入寄存器,再从通用寄存器转移到段寄存器
mov 段寄存器,寄存器:将段寄存器里面的数据送到寄存器,因为8086CPU内部有寄存器到段寄存器的通路,反之也可以

addsub指令与mov一样,有两个操作对象,其形式与mov指令类似
[address]表示一个偏移地址为address的内存单元,在进行mov操作的时候段地址默认为DS寄存器中的地址
同时[address]也可以用[bx]的形式,偏移地址在bx寄存器中

add & sub

add 操作对象1,操作对象2:把操作对象1和操作对象2的和放入操作对象1中,与mov指令的形式类似
sub 操作对象1,操作对象2:操作对象1减去操作对象2的值放入操作对象1中,同上

inc & dec

inc 操作对象:将操作对象的值自加1
dec 操作对象:将操作对象的值自减1

div & mul

div指令的除数在reg或者内存单元中,被除数默认放在AX和DX中
div reg(内存单元):用在reg或者内存单元中的除数去除被除数,得到的商存放在AL中,余数放在AH中,如果除数是16位,则AX存储除法操作的商,DX存储除法的余数
mul指令要求两个相乘的数位数必须相同,如果都是8位,一个默认放在AL中,另外一个放在8位reg或者内存单元中;如果都是16位,一个放在AX中,另外一个放在16位reg或者内存单元中
mul reg(内存单元):8位乘法的结果默认放在AX中,16位乘法的结果的高位放在DX中,低位放在AX中

push & pop

pushpop主要是进行对栈的操作,在8086CPU中以字为单位进行操作
push 寄存器:首先SP = SP - 2,然后将寄存器中的数据存放到SS:SP指向的内存单元(因为栈是从高地址向低地址方向增长)
pop 寄存器:将SS:SP的数据存放到寄存器中,然后SP = SP + 2,进行pop操作后,之前SS:SP指向的内存单元数据还存在,但是现在已经不在栈中

除了对寄存器的操作,这两个指令还可以使用push 段寄存器push 内存单元pop 段寄存器pop 内存单元的指令进行对栈的操作

  • 在8086CPU中,不会保证对栈的操作不好越界,在栈满的时候进行push和栈空的时候pop都会导致栈顶超界的问题,只能在编程过程中根据要存入的数据大小来安排栈的大小;
  • 编写汇编程序的过程中,可以通过先声明对应的数据,然后让SS:SP指向数据的地址,然后使用这一部分空间当作栈
loop:循环

loop 标号:令cx寄存器中的值自减1,然后判断是否为0,为0时结束循环然后向下执行程序,否则跳转到标号的位置执行程序

loop对应章节(P104)知识点:汇编程序中,数据不能以字母开头,在字母前要加一个0

dw(define word) & db(define byte)

dw(db) data1,data2,.....:主要用于定义字型数据和字节型数据
db '(string)':定义字符串类型,字符串的存储是存对应的ASCII码
dd data:(double word)定义双字类型数据

字符的转换:
大写字母 + 20H = 小写字母
小写字母 - 20H = 大写字母

位运算

and 操作对象1,操作对象2:将操作对象1和操作对象2进行按位与运算后放入操作对象1中
or 操作对象1,操作对象2:将操作对象1和操作对象2进行按位或运算后放入操作对象1中
xor 操作对象1,操作对象2:将操作对象1和操作对象2进行按位异或运算后放入操作对象1中
not 操作对象:将操作对象按位取反
shl(shr) 操作对象:将操作对象逻辑左(右)移一位,直接在右(左)边补一个0
sal(sar) 操作对象:将操作对象算数左(右)移一位,直接在右(左)边补一个0,但是与逻辑移位操作不同的是,最高位的符号位保存不变,也就是说保留了算术中的符号

移位操作的最高位会被移入PSW寄存器的CF位中

jmp:无条件跳转

jmp reg(idata):段内转移,将偏移地址(IP)修改为idata或者reg内所存的地址
jmp address1:address2:直接修改CS为address1,IP为address2,实现段间转移
jmp short ptr 标号:(IP) = (IP) + 8位偏移量,段内短转移
jmp near ptr 标号:(IP) = (IP) + 16位偏移量,段内近转移
jmp far ptr 标号:实现段间转移,也称为远转移,同时修改段地址和偏移地址
jmp word ptr 内存单元地址:实现段内转移,修改(IP)为内存单元中存放的偏移量
jmp dword ptr 内存单元地址:实现段间转移,将(CS)修改为内存单元地址的高地址,(IP)修改为内存单元地址的低地址,即(CS) = (内存单元地址) + 2、(IP) = (内存单元地址)
在编译器将指令转换为指令码时,会通过汇编指令中的标号计算出位移量以实现跳转

offset:取得标号的偏移地址
jcxz:有条件转移指令

jcxz 标号:如果(cx) = 0,转移到标号除执行
相当于:

1
if((cx) == 0) jmp short 标号

ret & retf

主要用于实现(CS:IP)的跳转
ret:将(IP)修改为栈顶的字,相当于pop IP
retf:将(IP)修改为(SS:SP)的字、将(CS)修改为(SS:SP + 2)的字,相当于pop IP然后pop CS

call

call 标号:将当前的(IP)压入栈,然后跳转到标号位置,相当于:

1
2
push IP
jmp near ptr 标号

call far ptr 标号:实现段间跳转,先把CS、IP依次压栈,然后跳转到标号位置,相当于:

1
2
3
push CS
push IP
jmp far ptr 标号

call reg:将当前的(IP)压入栈,然后IP修改为reg中存储的偏移地址,相当于:

1
2
push IP
jmp reg

call word ptr 内存单元:与上面类似,先把(IP)压栈,然后(IP)修改为内存单元中存储的偏移地址

1
2
push IP
jmp word ptr 内存单元地址

call dword ptr 内存单元:先把CS、IP依次压栈,然后(CS)、(IP)依次修改为内存单元地址中的两个字

1
2
3
push CS
push IP
jmp dword ptr 内存单元地址

dup

db(dw) X dup (Y):将Y重复定义X次

cmp

比较指令,相当于sub
cmp 操作对象1,操作对象2:计算操作结果1 - 操作结果2,根据结果对标志寄存器进行设置
je 地址:对于cmp指令的结果,如果两数相等就转移
jne 地址:对于cmp指令的结果,如果两数不相等就转移
jb 地址:低于则转移
jnb 地址:不低于则转移
ja 地址:高于则转移
jna 地址:不高于则转移

movsb & movsw

movsb:将DS:SI指向的内存单元中的字节送入ES:DI中,根据DF位使SI和DI递增或递减
movsw:将DS:SI指向的内存单元中的字送入ES:DI中,根据DF位使SI和DI递增或递减

rep

一般是配合movsbmovsw一起使用
rep movsb(sw):根据CX的值,实现CX个字符的传送

cls & std

cls:(Clear Direct Flag)将标志寄存器的DF位置0
std:(Set Direct Flag)将标志寄存器的DF位置0

pushf & popf

pushf:将标志寄存器的数据
popf:从栈顶弹出数据送入标志寄存器

汇编程序

一个简单的汇编语言源程序:

1
2
3
4
5
6
7
8
9
10
11
12
assume cs:codesg

codesg segment
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax

mov ax,4C00h
int 21H
codesg ends
end

伪指令

汇编语言源程序中,有汇编指令伪指令两种,下面三个指令是伪指令
段名 segment:与段名 ends是成对使用的伪指令,定义一个段,同时需要有一个名称来标识
段名 ends:标识一个段的结束
end [标号]:汇编程序的结束标记,在编译器遇到end指令的时候,就会停止对程序的编译,同时还可以加一个标号在后面通知编译器程序的入口
assume 段寄存器:段名,[段寄存器:段名],...:用于使某一个段寄存器和程序终端某一个段关联

对assume伪指令的深入了解:Link

标号

在伪指令中,使用某个名称作为段的一个标号,汇编过程中编译、连接程序处理为一个段的段地址
标号仅仅是指定了某个标号对应的指令行,并没有嵌套程序的作用,如果没有跳转,程序会继续往下运行

程序返回:一个程序结束后将CPU的控制器交还给它得以运行的程序的过程
1
2
mov ax,4C00h
int 21H

这一段汇编指令所实现的功能就是程序返回,在程序结束运行后,放回到command中,CPU继续执行command

程序返回的两个指令的原理:

编译 & 连接:P83 与 P85
彩色字符显示缓冲区
  • 显示缓冲区的内存地址是B8000H - BFFFFH
  • 00 - 01 单元对应显示器的第1列,02 - 03 单元对应显示器的第2列,依次类推
  • 显示缓冲区的偶地址存放字符的ASCII码,奇地址存放字符的颜色属性