寻址方式
1,立即寻址
直接将操作数放在指令中,例如
mov al,5;
mov ax,3064H
2,寄存器寻址方式
将操作数放在寄存器中,对寄存器进行操作
mov bx,5; 这个是直接寻址
mov ax,bx ;这个就是寄存器寻址
在8086中,把操作数的偏移地址称为有效地址,用EA来表示
基址:存放在基址寄存器中的值(如bp,bx)
变址:和上面的同理(si,di)
位移量;8,0,16位
段跨越前缀:用段跨越前缀来改变系统所指定的默认段,例如可以不用ds段来保存数据,用es来保存数据,不过此时就要使用段跨越前缀。但有以下几种情况不能使用段跨越前缀:
1,串处理指令自能用es段
2,push,pop只能用ss段
3,指令只能放在cs段
3,直接寻址:
操作数的有效地址只包含位移量,其值放在指令操作码后,如:mov ax,[2000H],这个代表将以2000h为偏移地址,以ds为数据段,将这个地址里面的字数据赋给ax。
4,寄存器间接寻址:
将直接寻址里面的地址用寄存器来存,只能使用基址寄存器和变址寄存器(bx,bp,si,si),注意其他的寄存器不行!但使用bp时段为ss段,其余的段为ds段。
使用段跨越前缀:mov ax,es:[bx],在这个时候段不再为ds,而是为es。
5,寄存器相对寻址:
操作数的有效地址为基址寄存器或变址寄存器内容和指令中指定的位移量之和。
如:mov ax,count[si]或者[count+si],这里count自行定义。
使用段跨越前缀:mov dl,es:count[si]
6,有效地址为一个基址寄存器和一个变址寄存器。
如:mov ax,[bx][ddi
7,相对基址变址寻址:
如:mov ax,count[bx][si]
8,比例变址寻址:
如:这里寄存器只能用变址寄存器。
mov ax,count[si*4]
其余的不常用。
与转移地址相关寻址方式
1,段内直接寻址:段内进行8位或者16位的转移
jmp near ptr progia:这里表示16位的转移
jmp short quest:这里表示八位的转移
这里的quest或者progia表示的是符号地址
2,段内间接寻址:除立即数外的任何寻址方式
jmp bx:bx保存符号地址偏移量
jmp word prt[bp+table]
注:条件转移指令只能用段内直接寻址的八位位移量
3,段间直接寻址:指令中直接提供段地址和偏移地址
jmp far ptr next
这里far ptr表示段间转移操作符。
4,段间间接寻址:用存储器中两个相继的内容取代cs和ip中原始内容。这里不能用立即数寻址和寄存器寻址,因为寄存器只能存16位,没办法既给cs又给ip。
如:jmp dword ptr [in]
指令系统
mem:内存,reg:寄存器,data:立即数,segreg:指定的段寄存器,ac:累加器。
1,mov:
mov mem/reg1,mem/reg2,这里不能两个都为mem,且reg不能为段寄存器。
mov reg,data
mov ac,mem
mov mem,ac
mov mov segreg,mem/reg:这里segreg不为cs寄存器,且这条指令不响应中断,要这条的下一条才有可能响应 。
注:mov指令不影响标志位。
2,push:
push src(reg,mem,segreg),且不能用立即数寻址方式
执行时:
( sp)←(sp)-2
( (sp)+1,(sp))←(src)
且我们应该知道push指令只能Push字数据,不能Push字节数据,而且这里必须是sp先减2,再将数据压入栈中,
3,pop:
pop dst(reg,mem,sergeg),这里跟push相差了一个data,因为不存在将一个数据弹出到立即数里面这种情况
( (sp)+1,(sp))←(dst)
( sp)←(sp)+2
而且这里我们应该知道sp是指向栈顶的,所有压栈的时候sp自然要减,这样子才能压入,反过来则一定要增,而且这里也符合高位高地址,低位低地址,并且push和pop也不影响标志位。
压栈的过程
出栈同理,不多赘述
4,xchg:用来交换两个操作数之间的值,且必须有一个操作数在寄存器中
格式:xchg opr1,opr2
注意:
* 不影响标志位
* 不允许使用段寄存器
* 不允许两个字段都来自存储器
由此可得说明可以两个寄存器之间交换值或者在寄存器和存储器之间交换值
如:
XCHG BX, [ BP+SI ]
XCHG AL, BH
IN/OUT指令:(这里只能使用AL或AH),这两个指令用来让cpu和外界进行交换数据,且只有这两个指令可以实现
输入指令 IN (I/O → CPU)
输出指令 OUT (CPU → I/O)
注:8086用于寻址端口号的总线宽度为16,故其最多可以访问65536个端口号,而每个端口号最大可以对外读出或写入一个字,也就是十六位数据。而端口号一共64k个,所以不需要分段,而且这两个操作也不影响标志位。
其寻址方式有两种:
1,立即寻址:前256个端口可以直接用立即寻址给出
2,DX间接寻址:超过256个后,需要用DX来保存端口号来进行数据交换
CPU与外设进行数据交换时,读出或写入的数据存放的位置只能是:
AL(读取8位数据时)
AX(读取16位数据时)
5,输入指令 IN (I/O → CPU)
长格式(直接): IN AL, PORT (字节)
IN AX, PORT (字)
执行操作: (AL) ← ( PORT ) (字节)
(AX) ← ( PORT+1, PORT )(字)
短格式(DX间接): IN AL, DX (字节)
IN AX, DX (字)
执行操作: (AL) ← ( (DX) ) (字节)
(AX) ← ( (DX)+1, (DX) )(字)
输出指令 OUT (CPU → I/O)
长格式: OUT PORT, AL (字节)
OUT PORT, AX (字)
执行操作:( PORT ) 👈 (AL) (字节)
( PORT+1, PORT ) 👈 (AX)(字)
短格式: OUT DX, AL (字节)
OUT DX, AX (字)
执行操作:( (DX) ) 👈 (AL) (字节)
( (DX)+1, (DX) ) 👈 (AX)(字)
如:测试第27H号端口的第二位是否为1
IN AL,27H
TEST AL,00000010B
JNZ ERROR:不为0则跳到error
6,换码指令:xlat
XLAT 或 XLAT OPR
执行操作:(AL) ← ( (BX) + (AL) )
解释:xlat适用于快速查表,但在使用前要先用bx来指定表头的首地址,而al则可以当作需要查的表相对表头的偏移量,到时候就将查到的表中的值传送给al
举例:
MOV BX, OFFSET TABLE ; (BX)=0040H
MOV AL, 3
XLAT TABLE
这样会将这个以0040H为表头的表格的第四号字节元素传输给al。
而这样子就可以把原本al的值换成现在在表中的那个值,所以这个叫做换码指令。
注:
- 不影响标志位
- 字节表格(长度不超过256)
首地址 👉 (BX) - 需转换的代码位移量 👉 (AL)
7,地址传送指令:
有效地址送寄存器指令: LEA REG, SRC
执行操作: (REG) 👈 SRC
指针送寄存器和DS指令: LDS REG, SRC
执行操作: (REG) 👈(SRC)
(DS) 👈 (SRC+2)
相继二字 👉 寄存器、DS
指针送寄存器和ES指令: LES REG, SRC
执行操作: (REG) 👈 (SRC)
(ES) 👈 (SRC+2)
相继二字 👉 寄存器、ES
这里lea就是把src的偏移地址的值赋给reg,
lds则是将src的值赋给reg,并且将接下来的字数据赋值给ds。
les和lds有点相似,不过是将字数据赋值给es而不是ds。
例:LEA BX, [BX+SI+0F62H]
LDS SI, [10H]
LES DI, [BX]
8,接下来为一连串标志寄存器传送指令,了解下即可:
LAHF:标志送AH(load AH with flags),且这里是低八位送到AH
SAHF:AH送标志(save flags into AH),同理这里是送到低八位里面
PUSHF/PUSHFD: 标志进栈
POPF/POPFD :标志出栈
9,类型转换数据:
CBW AL 👉 AX
执行操作: 若(AL)的最高有效位为0,则(AH)= 00H
若(AL)的最高有效位为1,则(AH)= FFH
CWD AX 👉 (DX,AX)
执行操作:若(AX)的最高有效位为0,则(DX)= 0000H
若(AX)的最高有效位为1,则(DX)= FFFFH
注:这里一般用于扩充字长。因为负数在计算机中用补码来存储,所以其值不会改变,同理这里也不不会影响标志寄存器的值
10,加法指令:
加法指令: ADD DST, SRC
执行操作: (DST) 👈 (SRC) + (DST)
带进位加法指令: ADC DST, SRC
执行操作: (DST) 👈 (SRC) + (DST) + CF
加1指令: INC OPR
执行操作: (OPR) 👈 (OPR) + 1
注意:
- 除INC指令不影响CF标志外,均对条件标志位有影响。
一些常见标志位:
SF:结果为负则为1,否则为0
ZF:结果为0则为1,否则为0
CF: 和的最高有效位 有 向高位的进位为1,否则为0
OF:两个操作数符号相同,而结果符号与之相反则为1,否则为0
注:指令执行后, CF和OF无论如何都会按照定义进行设置,
但它们的值是否有意义, 取决于把加法看作带符号数还是无符号数加法.
11,减法指令:
减法指令: SUB DST, SRC
执行操作: (DST) 👈 (DST) - (SRC)
带借位减法指令: SBB DST, SRC
执行操作: (DST) 👈 (DST) - (SRC) - CF
减1指令: DEC OPR
执行操作: (OPR) 👈 (OPR) - 1
求补指令: NEG OPR
执行操作: (OPR)👈 - (OPR)
比较指令: CMP OPR1, OPR2
执行操作: (OPR1) - (OPR2)
注:只有dec不影响标志位
减法指令对标志位的影响:
12,乘法指令:
无符号:mul src
有符号: imul src ,这里src和另一个数是补码形式,使用时记得将其转换为原本的真值
在这里面隐藏的默认的另一个乘数保存在al或ax中,在这里src不能为立即数,在这里除了cf和of外,对其他的标志位没有定义(即其他的标志位没有任何意义,可以不用考虑)
注:
有符号乘法中将两个操作数看作原数的补码,计算完成后将结果存入相应寄存器中。
对标志位的影响:
13,除法指令:
无符号数除法指令: DIV SRC
带符号数除法指令: IDIV SRC
执行操作:
字节操作 (AL) 👈 (AX) / (SRC) 的商
(AH) 👈 (AX) / (SRC) 的余数
字操作 (AX) 👈 (DX, AX) / (SRC) 的商
(DX) 👈 (DX, AX) / (SRC) 的余数
注意: * AX (DX,AX) 为隐含的被除数寄存器。
* AL (AX) 为隐含的商寄存器。
* AH (DX) 为隐含的余数寄存器。
* SRC不能为立即数。
且除法指令对任何的标志位都没有定义,所以除法指令不用管标志位,需要自己注意是否溢出
14,十进制表示指令:
BCD码:用二进制编码的十进制数,又称二–十进制数
压缩的BCD码:用 4 位二进制数表示 1 位十进制数
例:( 59 )10 =( 0101 1001 )BCD
非压缩的BCD码:用 8 位二进制数表示 1 位十进制数
例:( 59 )10 =( 0000 0101 0000 1001 )BCD
(1)压缩的BCD码调整指令
● DAA 加法的十进制调整指令
● DAS 减法的十进制调整指令
(2)非压缩的BCD码调整指令
● AAA 加法的ASCII码调整指令
● AAS 减法的ASCII码调整指令
● AAM 乘法的ASCII码调整指令
● AAD 除法的ASCII码调整指令
例子:
压缩:
非压缩:
15,逻辑运算指令:
NOT,and,or,xor,test(都不影响标志位)
使用方法:
例:屏蔽AL的第0、1两位
AND AL, 0FCH
例:置AL的第5位为1
OR AL, 20H
例:使AL的第0、1位变反
XOR AL, 3
例:测试某些位是0是1
TEST AL, 1
JZ EVEN
16,移位指令:在这里移动位数可以用立即数寻址外的任何寻址方式,移动位数为1时可以用立即数寻址,其他不行
逻辑左/右移:将最高/低位移到CF里面。(SHL al,位数)
算术左移:和逻辑左移一样(SRL)
算数右移:最低位移入CF,最高位不变(SAR)
循环左移:将最高位移入cf并且移入最低位(rol)
循环右移:将最低位移入cf且移入最高位(ror)
带进位循环左移:把cf当作操作数的最高位一起进行循环左移(rcl)
带进位循环右移:把cf当做操作数最低位一起进行循环右移(rcr)
CNT=1,SHL OPR, 1
CNT>1,MOV CL, CNT
SHL OPR, CL ; 以SHL为例
- 条件标志位:
CF = 移入的数值
1 CNT=1时,最高有效位的值发生变化
0 CNT=1时,最高有效位的值不变
移位指令:
SF、ZF、PF 根据移位结果设置,AF无定义
循环移位指令:
不影响 SF、ZF、PF、AF
运算在乘除法里的作用总结:
SAL, SHL用于乘以2的n次方
SAR, SHR用于除以2的n次方(结果只保留商,不含余数)
17,串处理指令:这里只能用es段
设置方向标志指令
CLD、STD
串处理指令 串重复前缀
MOVSB / MOVSW REP
STOSB / STOSW REPE / REPZ
LODSB / LODSW REPNE / REPNZ
CMPSB / CMPSW
SCASB / SCASW
执行 REP MOVS 之前,应先做好:
(1) 源串首地址(末地址)→ SI
(2) 目的串首地址(末地址)→ DI
(3) 串长度 → CX
(4) 建立方向标志
( CLD 使 DF=0,STD 使 DF=1 ) ,DF=0表示正向传输, DF=-1表示反向传输
例子:
在这里cld表示将df置为0,然后cx表示要传送的字数,然后进行循环
STOS 存入串指令:
STOS DST
STOSB (字节)
STOSW (字)
执行操作:
字节操作:((DI))←(AL), (DI)←(DI)±1
字操作:((DI))←(AX), (DI)←(DI)±2
例:把 附加段 中的 10 个字节缓冲区置为 20H
lea di, mess2
mov al, 20H
mov cx, 10
cld
rep stosb
这里也可以一次性存入一个字,然后存五次也是可以的。
LODS 从串取指令:
LODS SRC
LODSB (字节)
LODSW (字)
执行操作:
字节操作:(AL)←((SI)), (SI)←(SI)±1
字操作:(AX)←((SI)), (SI)←(SI)±2
注意:
- LODS 指令一般不与 REP 联用
- 源串一般在数据段中(允许使用段跨越前缀来修改),
目的串必须在附加段中 - 不影响条件标志位
CMPS 串比较指令:
CMPS SRC, DST
CMPSB (字节)
CMPSW (字)
执行操作:
(1) ((SI)) - ((DI))
根据比较结果置条件标志位:相等 ZF=1
不等 ZF=0
(2) 字节操作:(SI)←(SI)±1, (DI)←(DI)±1
字操作: (SI)←(SI)±2, (DI)←(DI)±2
SCAS 串扫描指令:
SCAS DST
SCASB (字节)
SCASW (字)
执行操作:
字节操作:(AL) - ((DI)), (DI)←(DI)±1
字操作: (AX) - ((DI)), (DI)←(DI)±2
与 REPE / REPZ(REPNE / REPNZ)配合工作的
CMPS 和 SCAS
REPE / REPZ
REPNE / REPNZ
执行操作:
(1) 如 (CX)=0 或 ZF=0 (ZF=1) 则退出串操作,
否则转(2)
(2) (CX)←(CX) -1
(3) 执行 CMPS / SCAS
(4) 重复 (1) ~ (3)
条件转移指令:
无条件转移指令:
段内直接短转移:JMP SHORT OPR
执行操作:(IP) ← (IP) + 8位位移量
段内直接近转移:JMP NEAR PTR OPR
执行操作:(IP) ← (IP) + 16位位移量
段内间接转移: JMP WORD PTR OPR
执行操作: (IP) ← (EA)
段间直接远转移:JMP FAR PTR OPR
执行操作:(IP) ← OPR 的段内偏移地址
(CS) ← OPR 所在段的段地址
段间间接转移: JMP DWORD PTR OPR
执行操作: (IP) ← (EA)
(CS) ← (EA+2)
条件转移指令:
格式 测试条件
JZ(JE) OPR ZF = 1
JNZ(JNE) OPR ZF = 0
JS OPR SF = 1
JNS OPR SF = 0
JO OPR OF = 1
JNO OPR OF = 0
JP OPR PF = 1
JNP OPR PF = 0
JC OPR CF = 1
JNC OPR CF = 0
) 比较两个无符号数,并根据比较结果转移*
格式 测试条件
< JB (JNAE,JC) OPR CF = 1
≥ JNB (JAE,JNC) OPR CF = 0
≤ JBE (JNA) OPR CF∨ZF = 1
> JNBE (JA) OPR CF∨ZF = 0
) 比较两个带符号数,并根据比较结果转移*
格式 测试条件
< JL (JNGE) OPR SFOF = 1
≥ JNL (JGE) OPR SFOF = 0
≤ JLE (JNG) OPR (SFOF)∨ZF = 1
> JNLE (JG) OPR (SFOF)∨ZF = 0
循环指令:
循环指令:LOOP OPR
测试条件:(CX) 不为 0
为零或相等时循环指令:LOOPZ(LOOPE) OPR
测试条件:ZF=1 且 (CX) 不为 0
不为零或不相等时循环指令:LOOPNZ(LOOPNE) OPR
测试条件:ZF=0 且 (CX) 不为 0
看着用就行,一般不常用,不过稍微留个印象
子程序自行看,大差不差。
中断:
中断向量:
中断例行程序的入口地址,存放于中断向量区,其中低位为ip,高位为cs。
…