80x86的指令系统
80x86的指令系统可以分为以下6组:
数据传输指令传处理指令
算术指令控制转移指令
逻辑指令处理机控制指令
数据传送指令
负责把数据、地址或立即数送到寄存器或存储单元中。可以仔细分为以下5种:
1 通用数据传送指令
MOV传送
MOVSX带符号扩展传送
MOVZX带零扩展传送
PUSH进栈
POP 出栈
PUSHA所有寄存器进栈
POPA所有寄存器出栈
XCHG交换
MOV 传送指令
;标准:mov dst,src
;执行操作:(dst)<-(src)
;mov指令的机器语言允许有7种模式
mov mem/reg1,mem/reg2
;操作数必须至少有一个是寄存器
mov reg,data
mov ac,mem
mov mem,ac
;意思是只能通过寄存器和累加器来进行互相交互
mov segreg,mem/reg
;segreg指定段寄存器,但是不能用CS寄存器。因为CS和IP指令只能采用jmp来调用,也就是可以用立即数和普通寄存器给段寄存器赋值。
mov mem/reg,segreg
;mem表示立即数,reg为指定寄存器
mov mem/reg,data
;采用存储器寻址方式。
;data为立即数,reg为指定寄存器,segreg为段寄存器,mem表示存储器也可以说是内存。
说明MOV指令可以在CPU内或CPU和存储器之间传送字或字节。可以从寄存器到寄存器,立即数到寄存器,立即数到存储单元,从存储单元到寄存器等。
注意事项:
立即数不能之间送到段寄存器,需要将立即数送给reg再用reg送到segreg中。
MOV指令的目的操作数(dst)不允许使用立即数,也不允许使用CS寄存器。
除开源操作数是立即数的情况外,两个操作数必须有一个是寄存器,不允许用MOV指令在两个存储单元直接传送数据,也不允许在两个段寄存器间直接传送信息。
例子:
;例1
MOV AX,DATA_SEG
MOV DS,AX
;段地址必须通过寄存器传入,例1采用的是用ax来传入
;例2
MOV AL,'E'
;将立即数E的ASCII码送到寄存器AL中
;例3
MOV BX,OFFSET TABLE
;将TABLE的偏移地址送到BX中,如果为数组就是首地址,OFFSET是一个关键字,表示把后面的符号地址传递
;例四
MOV AX.Y[BP][SI]
;将EA(有效地址)=BP+SI+Y的存储内容送给AX寄存器
;例5
MOV EAX,[EBX+ECX*4]
;把有效地址EA=EBX+ECX*4的存储单元的32位内容送给EAX
MOVSX带符号扩展传送指令
格式为:MOVSX DST,SRC
执行操作:DST<-符号扩展(SRC)
本指令可以有两种格式:
MOVSX reg1,reg2
MOVSX reg,mem
注意事项:
;指令的操作数可以是8位或16位的寄存器或存储单元的内容而目的操作数必须是16或32位寄存器,传送时把源操作数符号扩展送入目的寄存器。可以是8位扩展到16或32位,也可以是16位符号扩展到32位。
同时MOVSX也不影响标志位的内容。
例子:
;例1:
MOVSX EAX,CL
;把CL寄存器中的8位数,符号扩展为32位数送到EAX寄存器中
;例2:
MOVSX EDX,[EDI]
;把DS:EDI指定地址的内容16位数符号扩展为32位后送到EDX寄存器中
MOVZX 带零扩展传送指令
格式为:MOVZXDST,SRC
执行操作:DST<-零扩展(SRC)
指令可以有两种格式:
MOVZX reg1,reg2
MOVZX reg,mem
注意事项
DST和SRC以及对于标志位的影响都和MOVSX相同,差别只是MOVSX是带符号做符号扩展,而MOVZX的无符号整数是作零扩展
MOVZX和MOVSX的标志是:SRC的操作长度一定要小于目的操作数长度。
例子:
;例1
MOVZX EX,AL
;把AL中寄存器的八位数,零扩展为16位送到DS寄存器中。
;例2
MOVZX EAX,DATA
;把DATA单元的16进制数扩展到32位送到EAX寄存器里
PUSH入栈指令
格式为:PUSH SRC
执行操作为:
16位:SP<-SP-2
((SP)+1,(SP))<-(SRC)
;简单来说就是偏移地址往上移2个单位,然后写入一个字给对应的内存单元
32位:(ESP)<-(ESP)-4
((ESP)+3,(ESP)+2,(ESP)+1,(ESP))<-(SRC)
;同上,只是把两个字节改为了四个
POP出栈指令
格式为:POPDST
执行操作:
16位指令:(DST)<-((SP)+1,(SP))
(SP)<-(SP)+2
其实也就是将sp指向的地址和往下一个地址的两个字节的内容整合为字送给16位DST。然后SP段偏移地址再往下移动2个内存单元
32位指令:(ESP)<-(ESP)-4
((ESP)+3,(ESP)+2,(ESP)+1,(ESP))<-(SRC)
同上就是扩大了两倍。
PUSH与POP指令综合
格式:
PUSH reg
PUSH mem
PUSH data
PUSH segreg
POP reg
POP mem
POP segreg
;reg表示指定寄存器,mem为内存,data为立即数,segreg为段寄存器
进出栈的内容可以为字也可以为双字。
操作数长度 | 地址长度 | 执行的操作 |
16 | 16 | sp<-sp+_2 字出栈或者进栈 |
16 | 32 | ESP<-ESP+-2 字出栈或者进栈 |
32 | 16 | SP<-SP+-4 双字出栈或者进栈 |
32 | 32 | ESP<-ESP+-4双字出栈或者进栈 |
PUSH和POP的例子:
PUSH AX;
POP AX;
PUSHA/PUSHAD所有寄存器进栈指令
格式为:PUSHA
或PUSHAD
执行操作:
PUSHA:16位通用寄存器依次进栈,进栈次序为:AX,CX,DX,BX指令执行前的SP,BP,SI,DI。指令执行后(SP)<-(sp)-16依然指向栈顶。
PUSHAD:32位通用寄存器依次进栈,进栈顺序为:EAX,ECX,EDX,EBX以及指令执行前的ESP,EBP,ESI和EDI。指令结束后(SP)<-(SP)-32
POPA/POPAD所有寄存器出栈指令
格式为:POPA
POPAD
执行操作:
注意事项
POPA:16位通用寄存器依次出栈,出栈次序为:DI,SI,BP,SP,BX.DX.CX.AX。指令执行后(SP)<-(SP)+16 SP指向栈顶。SP的出栈只是修改了指针使其后面的BX能出栈,而堆栈中原先PUSHA指令存入的SP的原始内容被丢掉,并没有真正的送到SP寄存器中。
POPAD:32位寄存器依次出栈,次序为EDI,ESI,EBP。。。。同上。(ESP)<-(ESP)+32和POPA一样。
例子:
PUSHAD
XCHGEAX,EBX
就是EAX和EBX寄存器的内容互换
2 累加器专用传送指令
IN输入
OUT输出
XLAT换码
这一组指令只能用于累加器EAX,AX或AL来传送信息
IN指令
长格式:
IN AL,PORT(字节)
IN AX,PORT(字节)
IN EAX,PORT(字节)
短格式:
IN AL,DX
IN AX,DX
IN EAX,DX
执行的操作:
(AL)<-(DX))
(AX)<-(DX+1,DX)
(EAX)<-(DX+3,DX+2,DX+1,DX)
OUT指令
长格式:
OUT PORT,AL
OUT PORT,AX
OUT PORT,EAX
短格式:
OUT DX,AL
OUT DX,AX
OUT DX,EAX
执行的操作:
同上就是反着而已
####
综述IN指令和OUT指令
在80x86里,所有的I/O端口和CPU之间的通信都由IN和OUT来完成,其中IN完成从I/O到CPU的信息传送,而OUT则完成从CPU到I/O的信息传送。
CPU只能采用累加器(AL,AX,EAX)来接受和发送信息。
外部设备最多可有65536个I/O端口,端口号为0000-FFFFH,其中前256个端口(0-FFH)可以在指令中直接设定,也就是采用长格式的PORT,这时的机器指令用两个字节表示,第二个字节就是端口号,所以在使用长格式时可以直接在指令中设置端口号,但是只能作用与外设的前256个端口。
当端口号≥256时,只能使用短格式,此时必须先把端口号放到DX寄存器中(端口号可以从0000-FFFFH)然后再用IN或OUT指令来传送信息。这里的端口号和DX的内容都是地址,而且在短格式的时候DX内容就是端口号本身,不需要用任何段寄存器来修改它的值。
IN和OUT指令提供了双字,字和字节三种使用方式,选择那一种取决于外设端口宽度,如果端口宽度只有8位,则只能用字节指令传送信息。
例子:
;例1
IN AX,28H
MOV DATA_WORD,AX
;把端口28的内容经过ax送到存储单元DATA_WORD中
;例2
MOV DX,3FCH
IN EAX,DX
;从端口3FCH送一个双字到EAX中
;例3
OUT 5,AL
;将AL寄存器输入一个字节到端口5中
XLAT换码指令
格式
XLAT OPR
;或
XLAT
执行的操作:
16位指令:(AL)<-((BX)+(AL))
32位指令:(AL)<-((EBX)+(AL))
内容:
经常需要把一种代码转换为另一种代码。例如,把字符的扫描码转换为ASCII码。
在使用XLAT前需要建立一个字节表格,表格的首地址提前存入BX或EBX寄存器,需要转换的代码应该是相对于表格首地址的位移量也提前存放在AL寄存器,表格的内容则是需要转换的代码,执行指令后就可以在AL中得到转换后的代码。指令采用XLAT或者XLAT OPR任何一种都可以。在使用XLAT OPR的时候OPR为表格是首地址,但是OPR只是为了提高可读性,指令执行时就会使用预先存入BX或EBX中的表格 首地址。
相当于就是将首地址BX然后再加上偏移地址AL的值再加上段寄存器的值然后将内存单元的值替换给AL中
也就是MOV AL,[address]
3地址传送指令
LEA(load effective address)有效地址传送寄存器
LDS(load DS with pointer) 指针送寄存器和DS
LES(load ES with Pointer)指针送寄存器和ES
LFS(load FS with Pointer)指针送寄存器和FS
LGS(load GS with Pointer)指针送寄存器和GS
LSS(load SS whth Pointer)指针送寄存器和SS
这一套指令把地址送到指定寄存器
LEA有效地址送寄存器指令
格式:
LEA REG,SRC
执行的操作:
(REG)<-SRC也就是把源操作数的有效地址送到指定的寄存器中
;reg为指定寄存器
指令的目的操作数可以使用16/32位寄存器但是不可以使用段寄存器。src操作数可使用除立即数和寄存器外的任一种存储器寻址方式。
由于存在操作数长度和地址长度的不同,该指令执行的操作如下(该指令不影响标志位)
操作数长度 | 地址长度 | 执行的操作 |
16 | 16 | 计算的16位有效地址存入16位目的寄存器 |
16 | 32 | 计算的32有效地址,截取低16位存入16位目的寄存器 |
32 | 16 | 计算的16有效地址,零扩展后存入32位目的寄存器 |
32 | 32 | 计算的32位有效地址存入32位目的寄存器 |
例子:
;例1
;假设(BX)= 0400H,(SI)=003CH
LEA BX,[BX+SI+0F62]
;执行后BX=0400+003C+0F62=139EH
;这里的BX得到的是有效地址,而不是该存储单元的内容
MOV BX,[BX+SI+0F62]
;这个指令就是将偏移地址位139E单元的内容而不是偏移地址
;例2
LEA BX,LIST
MOV BX,OFFSET LIST
;这两条指令都是把LIST的地址送到BX中
;而MOV执行的指令会比LEA的执行速度快,
;但是OFFSET指令只能与简单的符号地址相连
;不能和诸如LIST[SI]或[SI]等复杂操作数相连
;所以虽然mov BX,OFFSET LIST比LEA快,但是使用LEA还是有必要
LDS、LES、LFS、LGS和LSS指针送寄存器和段寄存器指令
格式以LDS为例为:
LDS REG,SRC
;其它指令格式与LDS指令格式相同
执行的操作:
(REG)<-(SRC)
(SREG)<-(SRC+2) 或 (SREG)<-(SREG+4)
;SREG表示为指定的段寄存器
这套指令的SRC只能采用存储器寻址方式,根据任一种存储器寻址方式找到一个存储单元。当指令指定的是16位寄存器时,把该存储单元中存放的16位偏移地址装入该寄存器中,然后把(SRC+2)中的16位数装入指定的段寄存器中。
当指定的是32位寄存器时,把该存储单元中存放的32位偏移地址装入该寄存器,然后把(SRC+4)中的16位数装入指令指定的段寄存器中。
这套指令的目的寄存器不能使用段寄存器,LFS、LGS和LSS只能用于386和后续的。
比如LDS指定的寄存器就是DS。
例子:
;例1
LES DI,[BX]
;假设 (DS)=B000H,(BX)=080AH,(0B080AH)=05AEH,(0B080CH)=4000H
;则执行指令后(DI)=05AEH,(ES)=4000
标志寄存器传送指令
LAHF(load AH with flags)标志送AH
SAHF(storeAHinto flags)AH送标志寄存器
PUSHF/PUSHFD(push the flags or eflags)标志进栈
POPF/POPFD(pop the flags or eflags)标志出栈
LAHF 标志寄存器送AH指令
格式为:
LAHF
执行的操作:
(AH)<-(FLAGS的低字节)
SAHFAH送标志寄存器指令
格式:
SAHF
执行的操作:
(FLAGS的低字节)<-(AH)
PUSHF/PUSHFD标志进栈指令
格式:
PUSHF/PUSHFD
操作:
PUSHF:
(sp)<-(sp)-2
((sp+1),(sp))<-(flags)
PUSHFD:
(ESP) <-(ESP)-4
((ESP+4)....,(ESP))<- (EFLAGS AND 0FCFFFFH)
POPF/POPFD标志出栈指令
格式:
POPF
POPFD
操作:
POPF:
(FLAGS)<-((SP)+1,(SP))
(SP)<-(SP)+2
POPFD:
(EFLAGS)<-((ESP)+3,...,(ESP))
(ESP)<-(ESP)+4
总结:
这一套指令中的LAHF和PUSHF/PUSHFD不影响标志位,SAHF和POPF/POPFD则由装入的值来确定标志位的值,但POPFD指令不影响VM,RF,IOPL,VIF和VIP的值。
类型转换指令
CBW(convert byte to word)字节转换为字
CWD/CWDE(convert word to double word)字转换为双字
CDQ(convert double to quad)双字转换为4字
BSWAP(byte swap)字节交换
CBW字节转换为字
格式:
CBW
;就是一个单指令
执行操作:
AL的内容符号扩展到AH形成AX中的字。
如果AL中的最高有效位位0则(AH)=0;
如果(AL)的最高有效位为1,则(AH)=0FFH
;也就是加起来要让首位为1表示负数就好。
CWD/CWDE字转换为双字指令
格式:
CWD
执行的操作:
将AX的内容符号扩展到DX,形成DX:AX中的双字。
如果AX的最高有效位为0,则(DX)=0
如果AX的最高有效位为1,则(DX)=0FFFFH
格式2:
CWDE
执行的操作:
AX的内容符号扩展到EAX,形成EAX中的双字
CDQ双字转换到4字节
格式:
CDQ
执行的操作:
EAX的内容符号扩展到EDC,形成EDX:EAX中的4字。
BSWAP 字节交换指令
格式:
BSWAPr32
该指令只能针对486和后继机型,r32指32位寄存器
执行的操作:
使指令指定的32位寄存器的字节次序变反。
1、4字节互换,2、3字节互换。