·原博客请看我的博客asKylin
基础知识
·汇编语言的组成(3类指令)
1)汇编指令:有对应的机器码。
2)伪指令:没有对应的机器码,由编译器执行。
3)其他符号:如+、-、*、/等,没有对应的机器码,由编译器执行。
·CPU对存储器的读写
与芯片进行3类信息的交互:
1)存储单元地址。
2)器件选择,读或写命令。
3)读或写的数据。
·外部总线
1)地址总线:N根地址线可以选找2的N次方个内存单元(一个内存单元1byte)。
2)数据总线:宽度为8k的总线一次能传送 k Btye的数据。
3)控制总线:宽度决定CPU对外部期间的控制能力。
·存储器芯片
1)随机存储(RAM):可读可写,关机数据丢失。
2)只读存储(ROM):只读,关机数据不丢失。
寄存器
·Registers Architecture(寄存器结构)
register | Accumulator | Counter | Data | Base | Stack Pointer | Stack Base Pointer | Source | Destination |
---|---|---|---|---|---|---|---|---|
64-bit | RAX | RCX | RDX | RBX | RSP | RBP | RSI | RCI |
32-bit | EAX | ECX | EDX | EBX | ESP | EBP | ESI | EDI |
16-bit | AX | CX | DX | BX | SP | BP | SI | DI |
8-bit | AH/AL | CH/CL | DH/DL | BH/BL |
·通用寄存器
1)AX BX CX DX 这四个寄存器存放一般性数据。
2)这四个通用寄存器都可以分为两个八位寄存器使用(参见上表16-bit和8-bit)
其中,低八位构成AL,高八位构成AH。
·几条汇编指令
1)汇编指令举例
汇编指令 | 控制CPU完成的操作 |
---|---|
mov ax,18 | AX=18 |
mov ah,78 | AH=78 |
mov al,8 | AL=8 |
add ax,9 | AX+=9 |
mov ax,bx | AX=BX |
mov al,bh | AL=BH |
add ax,bx | AX+=BX |
add bh,al | BH+=AL |
2)注意事项:
16位寄存器中,若是数据值相加超过4位十六进制的数据,则只保存低四位的十六进制数据。
当16位寄存器被用作两个八位寄存器时,若寄存器数据值超过两位十六进制的数据,则只保存低二位的十六进制数据(若是AL寄存器中数据超过内存,高位不是真的被CPU丢失)
指令的两个操作对象的位数应该一致,而:
mov ax,bl
mov bh,ax
mov al,2000(超出范围)
add al,100H(超出范围)
都是错误的指令。
·8086CPU给出物理地址的方法
1)地址加法器采用物理地址 = 段地址x16 + 偏移地址的方法(地址数据用16进制表示)。
2)基础地址 = 段地址 x 16。
3)“段地址x16”实际上表示16进制数左移一位(即二进制数左移4位)。
·段地址
1)“段地址”划分来自CPU,不是内存本身分段。
2)CPU可以用不同的段地址和偏移地址形成同一个物理地址。
3)给定段地址,仅用偏移地址寻址最多可寻64KB个内存单元。
·段寄存器
1)8086CPU有四个段寄存器:CS、DS、SS、ES。
2)CS:代码段寄存器(段地址),IP:指令指针寄存器(偏移地址)。
3)
4)8086CPU工作过程:
从CS:IP之乡的内存单元读取指令,指令进入指令缓冲区;
IP = IP + 所读取的指令长度,从而指向下一条指令;
执行指令。重复以上过程。
5)CPU只认被CS:IP指向的内存单元的内容为指令。
·修改CS、IP的指令
1)同时修改CS、IP的内容:“jmp 段地址:偏移地址”;
2)只修改IP的内容:“jmp 某一合法寄存器”(用寄存器里的值修改IP,eg: jmp ax)。
寄存器(内存访问)
·DS和[address]
1)DS段寄存器通常来存放要访问的数据的段地址。
2)“[]”表示ds中的数据为内存单元的段地址,“[address]”中的“address”表示偏移地址。
3)假设读取10000H单元的内容(字节型数据的传送):
mov bx,1000H
mov ds,bx
mov al,[0](使用mov指令将一个内存单元中的8位字节数据送入一个8位寄存器中)
ds是段寄存器,不能直接传入1000H,只能用一个寄存器来进行中转。
4)字的传送:
eg:
mov bx,1000H
mov ds,bx
mov ax,[0] ;1000:0处的16位字型数据送入ax
mov [0],cx ;将cx中的16位字数据传送到1000:0处
·mov、add、sub指令
1)这三个指令都带有两个操作对象。
2)以mov为例,mov、add、sub指令可以有以下几种形式:
**mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器**
4)但是mov还存在以下四种形式:
mov 段寄存器,寄存器
mov 寄存器,段寄存器
mov 内存单元,段寄存器
mov 段寄存器,内存单元
5)add,sub指令不能对段寄存器进行操作。
·栈
1)栈是一种具有特殊访问方式的存储空间:最后进入这个空间的数据,最先出去(LIFO)。
2)入栈(push)和出栈(pop)都是以字为单位进行的。
3)
4)段寄存器SS:存放栈顶的段地址;寄存器SP:存放栈顶的偏移地址;任意时刻,SS:SP指向栈顶元素。
5)栈空,SS:SP指向栈空间最高地址单元的下一个单元。
6)一个数据出栈后,该地址单元的数据依然存在,只是不在栈中,当下次有数据入栈时,它将被覆盖。
7)8086CPU不保证对栈操作是否超界,栈顶超界将会覆盖栈外数据。
·push和pop指令
1)push指令和pop指令格式有如下形式(以push为例):
push 寄存器
push 段寄存器
push 内存单元
2)push指令执行步骤:(1)SP=SP-2;(2)向SS:SP指向的字单元送入数据。
3)pop指令执行步骤:(1)从SS:SP指向的字单元读取数据(2)SP=SP+2。
4)push和pop指令中修改的只是SP,所以栈顶的变化范围最大为:0-FFFFH。
·段的综述
1)将一段连续的内存定义为一个段,用段地址指示段,偏移地址访问段内单元,数据段、代码段、栈段都是我们自己定义的。
2)数据段:段地址存放在DS中,用mov,add,sub等访问内存单元的指令时,CPU数据段的内容当作数据访问。
3)代码段:段地址存放在CS中,段中第一条指令的偏移地址放在IP中,CPU就执行代码段中的指令。
4)栈段:段地址存放在SS中,栈顶单元的偏移地址放在SP中,CPU执行栈操作时将我们定义的栈段当作占空间来用。
5)同一段内存,同时可以是代码段、栈段和数据段,也可以什么都不是,关键在于CS、IP、SS、SP、DS的指向。
初识汇编程序
·3个伪指令
1)segment和ends伪指令:这是一对成对使用的伪指令,作用是定义一个段毒啊,其格式为:
段名 segment ;段从此处开始
:
段名 ends ;段到此处结束
2)程序是由多个段组成的,指令、数据、栈被划分到了不同的段中。
3)end:汇编程序结束标记。
4)assume:假设某一段寄存器和程序中的某一个用segment…ends定义的段相关联,例如:
assume cs: 代码段的名字 将一个代码段和CS寄存器联系起来。
·程序返回
1)指令:
mov ax,4c00H
int 21H
[BX]和loop指令
·约定两个符号
1)“( )”:表示一个寄存器或者内存单元里的内容。
2)“idata”:表示常量。
·[BX]
1)同[0]一样,[bx]也表示一个内存单元,只是它的偏移地址在bx中。
2)bx中存放的数据作为一个偏移地址EA,段地址SA默认在ds中。
·Loop指令
1)loop指令格式:loop 标号;通常用loop指令来实现循环功能,cx中存放循环次数。
2)eg:
mov ax,2
mov cx,11
s: add ax,ax
loop s
mov ax,4c00h
int 21h
3)标号代表一个地址,如上例s标识了一个地址,这个地址处有一条指令:add ax,ax.
4)CPU执行 loop s 的时候,进行两步操作:
(1)(cx)=(cx)- 1
(2)判断cx中的值,不为0则转至标号s所标识的地址处执行,如果为零则执行下一条指令。
·汇编程序中的一些小变动
1)用一个长度位1字节地内存单元向16位寄存器赋值(如把ffff:0006单元给ax赋值),则应该另令(ah)=0,(al)=(ffff6H)。
2)在汇编程序中,数据不能以字母开头,例如代码中mov ax,0ffffh,不能写成mov ax,ffffh。
3)汇编程序中,指令“mov ax,[0]”被当作“mov ax,0”处理,因此有如下两种方法实现将内存单元中的数据送入寄存器(举例说明):
mov al,ds:[0]
mov al,[bx]
mov al,ds:[bx]
·段前缀
1)出现在访问内存单元的指令中,用于显式地指明内存单元的段地址,形如“mov al,ds:[bx]”,在汇编语言中称为段前缀。
2)将一段内存单元的数据复制到另一段单元中,显式使用段前缀,可以提高程序效率。
包含多个段的程序
·在代码段中使用数据
1)end的另一作用:指明编译器程序入口,用法:end 标号
2)在代码段中使用数据可以使用如下程序框架:
assume cs:code
code segment
:
:
数据
:
:
start:
:
:
代码
:
:
code ends
end start
来指明CPU从何处开始执行程序。
·在代码段中使用栈
1)在代码段中使用栈挥着数据实质上都是开辟空间。
2)在代码段中使用栈可以使用如下程序框架:
assume cs:code
code segment
:
:
数据
栈空间
:
:
start:
:
:
代码
:
:
code ends
end start
来指明CPU从何处开始执行程序。
·将数据、代码、栈放入不同的段
1)定义多个段。eg:
assume cs:code,ds:data,ss:stack
2)对段地址的引用:段名就相当于标号,它代表了段地址。eg:
mov ax,data ;将名称为“data”的段地址送入ax。
3)
更灵活的定位内存地址的方法
·and和or指令
1)and指令:逻辑与指令,按位进行与运算。该指令可以将操作对象的相应位设为0,其他位不变。
2)or指令:逻辑或指令,按位进行或运算。该指令可以将操作对象的相应位设为1,其他未不变。
3)eg:
mov al,01100011B
and al,00111011B 执行后al=00100011B
mov al,01100011B
or al,00111011B 执行后al=01111011B
·以字符形式给出的数据
1)用’……’的方式指明数据是以字符的形式给出的,编译器将其转化为ASCII码。
2)eg:
db ‘unIX’ ;相当于“db 75H,6EH,49H,58H”
mov al,’a’ ;相当于“mov al,61H”
·大小写转换问题
1)除了大写字母=小写字母-20H外,可以用and 11011111B将小写转换为大写字母。
2)可用or 01100000B将大写转换为小写字母。
·[bx+idata]
1)[bx+idata]表示一个偏移地址为(bx)+idata的内存单元。
2)常用格式:
mov ax,[bx+idata]
mov ax,[idata+bx]
mov ax,idata[bx]
mov ax,[bx].idata
3)[bx+idata]的方式处理数组更加便利。与C语言比较:
C语言:a[i],b[i]
汇编语言:0[bx],5[bx]
·SI和DI
1)si和di是8086CPU中和bx功能相近的寄存器,si和di不能分成两个8位寄存器来使用。
2)复制字符串汇编程序举例:
·[bx+si]和[bx+di]
1)[bx+si]和[bx+di]含义相似,以[bx+si]为例,其表示一个偏移地址位(bx)+(si)的内存单元。
·[bx+si+idata]和[bx+di+idata]
1)[bx+si+idata]和[bx+di+idata]含义相似,以[bx+si+idata]为例,其表示一个偏移地址位(bx)+(si)+idata的内存单元。
2)常用格式:
mov ax,[bx+idata+si]
mov ax,[idata+bx+si]
mov ax,idata[bx][si]
mov ax,[bx].idata[si]
mov ax,[bx][si].200
数据的位置和长度
·约定两个描述性符号
1)reg:寄存器
2)sreg:段寄存器
·bx、si、di、bp
1)在8086CPU中只有这四个寄存器可以用在“[…]”中来进行内存单元的寻址。
2)在“[…]”中,这四个寄存器可以单个出现,或只能以4种组合出现:bx和si、bx和di、bp和si、bp和di。
3)在“[…]”中使用寄存器bp,且指令中没有显性地给出段地址,则段地址默认在ss中。
·寻址方式
1)
·数据的长度
1)通过寄存器名指明处理数据的尺寸。
eg:
mov ax 1 ;字操作
mox al,bl ;字节操作
2)在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以为word或byte。
eg:
mov word ptr ds:[0],1 ;指明指令访问的内存单元是一个字单元
add byte ptr [bx],2 ;指明指令访问的内存单元是一个字节单元
·div指令
1)div是除法指令。需要注意以下问题:
(1)除数:有8位和16位两种,在一个reg或内存单元中;
(2)被除数:默认放在AX或DX和AX中,除数为8位,被除数则为16位,默认在AX中存放;除数为16位,被除数则为32位,DX存放高16位,AX存放低16位。
(3)除数为8位,AL存储商,AH存储余数;除数为16位,AX存储商,DX存储余数。
2)格式:div reg或者div 内存单元。
·伪指令db、dw、dd
1)db: define btye
2)dw: define word
3)dd: define double word(双字型数据,占两个字)
·dup
1)dup操作符的作用:进行数据重复。
2)用法:
db 3 dup (0) ;定义了三个值都是0字节
db 3 dup (0,1,2) ;定义了九个字节,他们是0、1、2、0、1、2、0、1、2
转移指令的原理
·转移指令
1)可以修改IP,或同时修改CS、IP的指令统称为转移指令。即控制CPU执行内存中某处代码的指令。
2)段内转移:只修改IP,比如:jmp 1000:0。
3)段内转移分为短转移(IP的修改范围为-128~127)、近转移(IP的修改范围为-32768~32767)。
4)8086CPU的转移指令分为如下几类:
无条件指令转移(如:jmp)
条件转移指令
循环指令(如:loop)
过程
中断
·操作符offset
1)offset是由编译器处理的符号,功能是取得标号的偏移地址。
2)
·依据位移指令进行转移的jmp指令
1)jmp short 标号(段内短转移,转到标号处执行指令)实现功能是:(IP)=(IP)+8位位移。
2)CPU在执行jmp指令的时候并不需要转移目的地址,而是包含转移的位移。
3)
4)jmp near ptr 标号(段内近转移)实现功能是:(IP)=(IP)+16位位移。
·转移的目的地址在指令中的jmp指令
1)“jmp far ptr 标号”实现段间转移,far ptr指明了指令用标号的段地址和偏移地址修改CS和IP。
·转移地址在寄存器中的jmp指令
1)指令格式:jmp 16位reg,功能:(IP)=(16位reg)。eg: jmp ax。
·转移地址在内存中的jmp指令
1)jmp word ptr 内存单元地址(段内转移)
功能:从内存单元地址处开始存放一个字,是转移的目的偏移地址。eg:jmp word ptr ds:[0]
2)jmp dword ptr 内存单元地址(段间转移)
功能:从内存单元地址处开始存放两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址。
eg:jmp dword ptr ds:[0]
·jcxz指令
1)jcxz指令为有条件转移指令,所有的有条件转移指令都是短转移。
2)“jcxz 标号”的功能相当于:if((cx)==0) jmp short 标号。(判断语句)
·loop指令
1)所有的循环指令都是短转移。
2)“loop 标号”的功能相当于:(cx)–; if((cx)!=0) jmp short 标号(do while循环)
·注意
1)在之前jmp指令中,“jmp 2000:0100”的转移指令,是在Debug中使用汇编指令,汇编编译器并不认识。
CALL和RET指令
·ret和retf
1)ret指令用栈中的数据,修改IP的内容,实现近转移;ret指令实现下面两步操作:
(IP)=((ss)*16+(sp))
(sp)=(sp)+2
相当于进行:pop IP
2)retf指令用栈中的数据,修改CS和IP的内容,实现远转移;retf指令实现4步操作:
(IP)=((ss)*16+(sp))
(sp)=(sp)+2
(CS)=((ss)*16+(sp))
(sp)=(sp)+2
相当于进行:pop IP pop CS
·call指令
1)执行call指令时,先将当前IP或CS和IP压入栈中,再进行转移。
2)call指令不能实现短转移。
·根据位移进行转移的call指令
1)call 标号(将当前的IP压栈后,转到标号处执行指令)。
2)CPU执行“call 标号”时,相当于进行:
push IP
jmp near ptr 标号
·转移的目的地址在指令中的call指令
1)call far ptr 标号(实现段间转移)。
2)CPU执行“call far ptr 标号”时,相当于进行:
push CS
push IP
jmp far ptr 标号
·转移地址在寄存器中的call指令
1)call 16位reg。
2)CPU执行“call 16位reg”时,相当于进行:
push IP
jmp 16位reg
·转移地址在内存中的call指令
1)call word ptr 内存单元地址
2)CPU执行“call word ptr 内存单元地址”时,相当于进行:
push IP
jmp word ptr 内存单元地址
3)call dword ptr 内存单元地址
4)CPU执行“call dword ptr 内存单元地址”时,相当于进行:
push CS
push IP
jmp dword ptr 内存单元地址
·call和ret配合使用
1)实现子程序(函数)的机制,框架如下:
标号:
指令
ret
2)当往子程序(函数)传参时,常用的方法是用栈传递参数;传字符串时,将首地址存放在寄存器中传递给子程序。
3)在子程序(函数)中用到相同的寄存器时,一般把子程序中的寄存器中的值在子程序开始时存入栈中,在子程序返回前把值出栈给相应寄存器。
·mul指令
1)mul乘法指令,需要注意以下两点:
(1)相乘的两个数位必须一样(8位和8位相乘),如果是8位,一个默认在AL存放,另一个存放在8位reg或内存字节单元中;如果是16位,一个默认存在AX中,另一个放在16位reg或内存字单元中。
(2)结果:如果是8位乘法,结果默认放在AX中;如果是16位乘法,结果高位默认存放在DX中,低位在AX中存放。
2)格式:mul reg;mul 内存单元(内存单元可以用不同的寻址方式给出)。
eg:
mov ax,1000
mov bx,10000
mul bx
标志寄存器
·概述
1)作用:
用来存储相关指令的某些执行结果;
用来为CPU执行相关指令提供行为依据;
用来控制CPU的相关指令工作方式;
2)标志寄存器(flag寄存器)按位起作用:
3)flag中空位没有使用。
·ZF标志
1)ZF,零标志位。它记录相关指令执行后结果是否为零。结果为零,zf=1;反之,zf=0。
2)在8086CPU中一般运算指令如add、sub、and等的执行会影响标志寄存器,而传送寄存器如mov、push、pop大都对标志寄存器没有影响。
·PF标志
1)PF,奇偶标志位。它记录相关指令执行后结果所有bit位中1的个数是否为偶数。偶数pf=1;反之pf=0。
·SF标志
1)SF,符号标志位。它记录相关指令执行后记过是否为负。结果为负,sf=1;反之,sf=0。
2)当我们把数据当作有符号数来运算时,可以通过sf判断结果正负;若把数据当作无符号数来运算,sf的值没有意义,虽然相关指令影响了它的值。
·CF标志
1)CF,进位标志位。在进行无符号数运算时,它记录运算结果的最高有效位向更高位的进位值,或从更高位的借位值。进位或借位cf=1。
2)eg:
mov al,98H
add al,al ;执行后:(al)=30H,CF=1,CF记录了从最高有效位向更高位的进位值。
mov al,97H
sub al,98H ;执行后:(al)=FFH,CF=1,CF记录了向更高位的借位值。
·OF标志
1)OF,溢出标志位。记录有符号数运算结果是否发生了溢出。如果溢出,of=1;反之,of=0。
adc指令
1)adc是带进位加法指令,它利用了CF位上的进位值。
2)指令格式:adc 操作对象1,操作对象2
3)功能:操作对象1 = 操作对象1 + 操作对象2 + CF
eg:
adc ax,bx实现:(ax)=(ax)+(bx)+CF
相当于:低位相加,高位相加再加上低位相加产生的进位值。
4)adc指令执行后也可能产生进位值。
·sbb指令
1)sbb是带借位减法指令,它利用了CF位上的进位值。
2)指令格式:sbb 操作对象1,操作对象2
3)功能:操作对象1 = 操作对象1 - 操作对象2 - CF
eg:
sbb ax,bx实现:(ax)=(ax)-(bx)-CF
·cmp指令
1)cmp是比较指令,功能相当于减法指令,但是不保存结果。
2)格式:cmp 操作对象1,操作对象2
3)cmp指令可以对无符号数间和有符号数间进行比较。
4)以cmp ah,bh为例,总结CPU在执行cmp指令后,sf和of的值是如何说明比较结果的:
(1)如果sf=1,而of=0;所以(ah)<(bh)。
(2)如果sf=1,而of=1;所以(ah)>(bh)。
(3)如果sf=0,而of=1;所以(ah)<(bh)。
(4)如果sf=0,而of=0;所以(ah)>=(bh)。
·检测比较结果的条件转移指令
1)无符号数(检测zf,cf的值):
2)将cmp和je等指令配合使用,与高级语言中的if语句相似。
eg:
cmp ah,bh
je s
add ah,bh
jmp short ok
s:add ah,ah
ok:…
·DF标志和传送指令
1)DF,方向标志位。在串处理指令中,控制每次操作后si,di的增减。(df=0,每次操作后si、di递增;df=1,每次操作后si、di递减)。
2)一个串传送指令格式:
movsb(传送内存单元中的字节到es:di,然后根据标志寄存器df位的值,将si、di递增或递减)
movsw(传送内存单元中的字到es:di,然后根据标志寄存器df位的值,将si、di递增2或递减2)
3)配合rep使用(rep,根据cx值重复执行后面的串传送指令。)rep movsb可以循环实现(cx)个字符的传送。
4)8086CPU提供两个指令对df位进行修改:
cld指令:将df位置0
std指令:将df位置1
·pushf和popf
1)pushf:将标志寄存器的值压栈;popf:从栈中弹出数据,送入标志寄存器中。
·标志寄存器在Debug中的表示
1)