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 带零扩展传送指令

格式为:MOVZX DST,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出栈指令

格式为:POP DST

执行操作:

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

 

 

 

XCHG EAX,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(store AH into flags) AH送标志寄存器

PUSHF/PUSHFD(push the flags or eflags) 标志进栈

POPF/POPFD(pop the flags or eflags) 标志出栈

 

LAHF 标志寄存器送AH指令

格式为:

LAHF

执行的操作:

(AH)<-(FLAGS的低字节)

SAHF AH送标志寄存器指令

格式:

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 字节交换指令

格式:

BSWAP r32

该指令只能针对486和后继机型,r32指32位寄存器

执行的操作:

使指令指定的32位寄存器的字节次序变反。

1、4字节互换,2、3字节互换。