汇编是各种CPU提供的机器指令的助记符的集合,人们可以用汇编语言直接控制硬件系统进行工作。
汇编指令由三类指令组成:
汇编指令(核心):有对应机器码
伪指令:没有对应机器码,由编译器执行
其他符号:如+-*/等,由编译器识别,没有对应机器码
一个典型的CPU由运算器,寄存器,控制器组成。
寄存器16位(8086CPU)
通用寄存器
AX、BX、CX、DX
存放一般性的数据
可通过mov指令修改
段寄存器
CS、DS、SS、ES
存放段地址
(CS存放代码段寄存器,cpu只认被CS:IP指向的内存为指令)
(DS通常用来存放数据的段地址。)
CS和IP不可通过mov指令修改,可通过转移指令修改,如jmp。
“jmp短地址:偏移地址”同时修改CS和IP。
“jmp某一合法寄存器”仅修改IP。
DS可以用mov修改(只能把数据先mov到一般寄存器,然后从一般寄存器mov到DS)。
“moval,[0]”数据段地址默认存在DS中,执行指令时,cpu自动从DS取段地址。
指令指针寄存器
IP
SI、DI
SI和DI是8086cpu中和bx功能相近的寄存器。
si和di不能分成两个8位寄存器来使用。
标志寄存器
FLAG
第0位:CF,进位标志位,在进行无符号数运算的时候,记录了运算结果的最高有效位向更高有效位的进位,或从更高位的借位。
OF,在进行有符号运算的时候,如果超过机器所能表示范围称为溢出。
DF,方向标志位,在串处理指令中,控制m每次操作后si和di的增减。df等于0,每次操作后si和di递增。df等于1,每次操作后si和di递减。
一个8位寄存器所能存储的数据的最大值为多少?
为了区分不同的进制,十六进制数据后加H,二进制后加B,十进制什么都不加
在写一条汇编指令或者寄存器名称时不区分大小写。
8086CPU给出物理地址的方法:
8080cpu有20位地址总线,8086CPU又时16位结构,一次处理/传输/暂存的地址为16位。
8086cpu采用2个16位地址(段地址和偏移地址)合成的方法形成20位的物理地址。
物理地址=段地址*16+偏移地址
内存中字的存储:
CPU中,用16位寄存器存储一个字。高8位存放高字节,低8位存放低位字节。
在内存中,字的低位字节存放在低地址单元,高位字节存放在高地址单元。
栈:
栈中字的存储方式是低位低地址(靠近栈顶),高位高地址(靠近栈底)。
任意时刻SS:SP指向栈顶元素。
8086CPU不保证我们对栈的操作不会越界。
栈操作以字为单位
(PUSH或者POP内存偏移地址时,自动获取DS)
MOV
SUB
ADD
POP
PUSH
LOOP
OFFEST
JZ
LEA
TEST
JLE
意思是小于等于,则跳转,转移条件寄存器描述是ZF=1ORSF≠OF
JNE
不等于则跳转
JE
JG
AND
OR
同位同为0时才为0否则为1
div
div是除法指令,使用div做除法运算时,
Jmp
无条件转移,可只修改ip,也可同时修改CS和IP.
Jmp指令要给出的两种信息。
jcxz
有条件转移,所有有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围都为:-128-127
指令格式“jcxz标号”:如果(cx)=0转移到标号处执行。操作:(IP)=(IP)+8位位移。
(八位位移计算方法,范围,表示方式同JMP指令。
Loop
循环指令,所有的循环指令都是短转移。在对应机器码中包含转移的位移。
指令格式“loop标号”:(cx)=(cx)-1:如果(cx)不等于0,转移到标号处执行。
位移的计算,范围,表示同jcxz。
RET
指令用栈数据,修改IP的内容,实现近转移。
相当于执行popIP
RET后加一个数字相当于在RET后ESP加上操作数
RETF
指令用栈中的数据,修改CS和IP,实现远转移。
(1)(IP)=((SS)*16+(SP))
(2)(SP)=(SP)+2
(3)(CS)=((SS)*16+(SP))
(4)(SP)=(SP)+2
popCS
CALL
CPU执行call指令时,进行两步操作:
Call指令不能实现短转移,除此之外,call指令实现转移方法和jmp指令原理相同。
ADC
带进位加法指令,利用了CF位上记录的值。方便对任意大的数据进行加法运算。
SBB
带借位减法指令。利用了CF位上记录的借位值。
CMP
cmp比较指令,相当于减法指令,只是不保存结果,但对标志寄存器产生影响。
等于则转移
JB
低于则转移
JNB
JA
高于则转移
JNA
MOVSB
串传送指令,执行movsb,相当于进行下面的操作。
(di)=(di)+1
如果df=1,则:(si)=(si)-1
(di)=(di)-1
MOVSW
一次传送一个字
REP
REP的作用是根据CX的值,重复执行后面的串传送指令。repmovsb可以实现(cx)个字符的传送。
CLD
将标志寄存器df置0
STD
将标志寄存器df置1
PUSHF
将标志寄存器的值压栈
POPF
从栈中弹出数据,送入标志寄存器中。
[BX]:
[BX]指的是偏移地址在BX中,段地址在DS中的那个内存单元。
要完整描述一个内存单元,需要内存单元地址和内存单元长度(类型),单元长度可以从其他操作对象(比如寄存器)指出。
在工具书中(地址)用来描述对应地址的内容,地址只能是一个20位数据。
Loop指令:
loop指令格式是“loop标号”。
CPU执行loop指令时进行两个操作:(CX)=(CX)-1,判断CX的值,不为零则转至标号处执行程序,为零则向下执行。
例子:
s:addax,ax
loops
CX和LOOP配合实现循环的三个要点:CX存放循环次数。Loop指令中标号所标识的地址要在前面。循环体在标号和LOOP指令中间。
在出现访问内存单元的指令中,用于显示指明内存单元段地址的“ds”“ss”“cs”“es”,在汇编语言中称为段前缀
一段安全的空间:
我们需要直接向一段内存中写入内容时,这段内存空间不应存放系统或者其他程序的数据或者代码,否则写入操作可能引发错误,DOS方式下,一般情况,0:200~0:2ff空间中没有系统或者其他程序的数据或者代码
在汇编程序中,用’......’的形式指明数据以字符的形式给出,编译器将他们转换为相应的ASCII码。
[BX+idata]:
[BX+idata]指明一个内存单元,他的偏移地址为(bx)+idata,内存单元地址为((ds)*16+(bx)+idata)。这种形式便于数组的处理。
一般来说,需要暂存数据的时候,应该使用栈。
数据处理的两个基本问题:
在工具书中,使用reg表示寄存器,sreg表示段寄存器。
机器指令处理的数据在什么地方:
三个地方:CPU内部、内存、端口。如“movbx,1”和“movbx,cx”是在cpu内部,“movbx,[0]”是在内存。
汇编语言中数据位置的表达:
寻址方式:
寻址方式
名称
含义
[idata]
直接寻址
SA=(ds)
[bx]
寄存器间接寻址
[si]
[di]
[bp]
SA=(ss)
[bx+idata]
寄存器相对寻址
[si+idata]
[di+idata]
[bp+idata]
[bx+si]
基址变址寻址
[bx+di]
[bp+si]
[bp+di]
[bx+si+idata]
相对基址变址寻址
[bx+di+idata]
[bp+si+idata]
[bp+di+idata]
指令要处理的数据有多长:
8086CPU的指令可以处理两种尺寸的数据,byte和word。所以在机器指令中要指明,指令进行的是字操作还是字节操作。
伪指令dd:
db和dw分别定义字节和字型数据。dd定义dword型数据。
dup:
dup是操作符,同db、dw、dd、一样,由编译器识别处理的符号,用来进行数据的重复。例:
db2dup(0)
db2dup(0,1,2)
db2dup(‘abc’,’ABC’)
可见dup的使用格式如下:
db重复的次数dup(重复的字节型数据)
db重复的次数dup(重复的字型数据)
db重复的次数dup(重复的双字型数据)
定义200字节的栈段:
法1.
stacksegment
dw0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stackends
法2.
db200dup(0)
转移指令的原理:
可以修改Ip,或者同时修改CS和IP的指令统称为转移指令。
只修改IP称为段内转移,比如jmpax.
同时修改CS和IP称为段间转移,比如jmp1000:0.
段内转移又分为短转移和近转移。短转移的修改范围是-128~127。近转移IP的修改范围为-32768~32767。
8086CPU的转移指令分为以下几类:
无条件转移
条件转移
循环指令
过程
中断
操作符offset:
操作符offest在汇编语言中是编译器处理的符号,它的功能是取得标号的偏移地址。
依据位移进行转移的jmp指令:
Jmpshort标号(转到标号处执行指令):段内短转移。指令对应的机器码中,并不包含转移的目的地址,而包含的是转移的位移,位移是根据汇编指令中的标号计算出来的。
Jmpshort标号:功能(IP)=(IP)+8位位移:
8位位移=标号处地址-jmp指令后第一个字节的地址。
8位位移用补码表示
位移由编译器在编译程序时算出
Jmpnearptr标号:功能为:(IP)=(IP)+16位位移。
16位位移的计算和表示同上。
转移目的地址在指令中的jmp指令:
“jmpfarptr标号”实现的是段间转移,又称远转移。farptr指明了指令用标号的段地址和偏移地址同时修改CS:和IP
转移地址在寄存器中的jmp指令:
格式:jmp16位reg
转移地址在内存中的jmp指令:
CALL和RET:
call和ret指令都是转移指令,都修改IP,或者同时修改CS和IP.它们经常被共同用来实现子程序的设计。
依据位移进行转移的call指令:
Call标号(将当前IP压入栈后,转到标号处执行指令)。
具体操作:
((ss)*16+(sp))=(IP)
相当于:pushIP
Jmpnearptr标号
关于位移参考jmp指令。
转移的目的地址在指令中的call指令:
callfarptr标号实现的时段间转移
Cpu执行此种格式的call指令时,进行如下操作。
1.(sp)=(sp)-2
((ss)*16+(sp))=(cs)
(sp)=(sp)-2
((ss)*16+(sp))=(ip)
2(cs)=标号所在段的段地址
(ip)=标号在段中的偏移地址
相当于:
Pushcs
Puship
Jmpfarptr标号
指令中这么存放地址?
转移地址在寄存器中的call指令:(近转移)
指令格式:call16位reg
(ip)=(16位reg)
转移地址在内存中的call指令:
转移地址在内存中的call指令有两种格式。
相当于puship
Jmpwordptr内存单元地址
相当于pushcs
Jmpfarptr内存单元地址。
call与ret的配合使用:
在需要的时候,用call指令转去执行子程序,在执行完子程序后,在子程序后面使用ret指令,用栈中的数据设置IP值,从而转到call指令后面的代码处执行。
标志寄存器:
作用:
在8086cpu中,标志寄存器有16位,存储的信息被称为程序状态字(PSW)。
在8086cpu指令集中,有的指令是影响标志寄存器的,比如,add、sub、mul、div、
、inc、or、and等,他们大多都是运算指令(逻辑或者算术运算)。有的指令的执行对标志寄存器没有影响。比如movpushpop等,他们大多都是传送指令。
计算机通常用补码来表示有符号数据,计算机中的一个数据可看作是有符号数,也可看做是无符号数。对于同一个数据,计算机可把它当作有符号数运算,也可把它当作无符号数运算,不管我们如何看待,如add指令执行的时候就包含了两种含义,也将得到用同一种x信息来记录两种结果。关键在于我们程序需要哪一种结果。
CF是对无符号数运算有效的标志位,而OF是对有符号数运算有意义的标志位。他们之间没有任何关系。
内中断产生:
8086CPU当有下面情况产生的时候,将会产生相应的中断信息:
上述的四种中断源,在8086CPU中的中断类型码(字节型数据,可表示256种中断源)如下:
中断处理程序:
根据中断信息中的中断类型码定位中断处理程序(得到段地址和偏移地址):
CPU用中断类型码,查找中断向量表,就可以得到中断c处理程序的入口地址。
中断向量表在内存呢中存放。
对于8086PC机,中断向量表指定放在内存地址0处,从0000:0000到0000:03ff的1024个内存单元存放着中断向量表。
一个表项两个字,高地址字存放段地址。低地址存放偏移地址。
中断过程:
CPU在收到中断信息之后,如果处理该中断信息,就完成一个由硬件自动执行的中断过程(程序员无法改变这个过程中所要做的工作)。
中断过程的简洁秒速:
在最后一步完成后,cpu开始执行由c程序员编写的中断处理程序。
描述了单元长度的标号:
上图中箭头处都是标号,近表示内存单元的地址。
上图中标号(称为数据标号)后面不带:,不仅表示内存地址,还表示内存单元的长度。
在其他段中使用数据标号:
(在后面加有:的地址标号,只能在代码段中使用,不能再其他段中使用)
如果想在代码段中直接使用数据标号访问数据,则需要伪指令assume将标号所在的段和一个段寄存器联系起来。否则编译器在编译的时候,无法确定标号的段地址在那个寄存器中。当然,这种联系是编译器所需要的,但这不是说,因为编译器工作需要,用assume将段寄存器和某个段相联系,段寄存器就真的会存放改段地址,在程序中还要使用指令对段寄存器进行设置。
程序入口地址的直接地址表:
可以在直接地址表中存储子程序的地址,方便实现不同子程序的调用。