【汇编语言】寻址方式和指令系统详解(周荷琴第六版)

目录

寻址方式

1.立即寻址

2.直接寻址

3.寄存器间接寻址

4.寄存器相对寻址

5.基址变址寻址

6.相对基址变址寻址

7.端口直接寻址

8.端口间接寻址

常见的指令

算术逻辑指令

逻辑运算指令

逻辑移位指令

循环移位指令

字符串操作指令

程序控制类指令

中断指令

处理器控制指令



1.寻址方式就是指令中说明操作数所在地址的方法,是针对操作数的,也就是说寻址方式说的是操作数的寻址方式,而不是指令的寻址方式,比如指令MOV AL,12H中,源操作数采用的是立即寻址方式,目标操作数采用的是寄存器寻址

2.

寻址方式

1.立即寻址

1.MOV AL,12H

2.常用来给寄存器赋初值

3.立即数只能做源操作数,不能做目的操作数

4.不允许把立即数直接送到段寄存器去

5.字母开头应该+0

6.源操作数长度与目标操作数一致

  1. 寄存器寻址(跟立即寻址差不多,只不过立即寻址直接给了个立即数,寄存器寻址也是给一个数,但是这个数是通过寄存器给出来的)

1.MOV DX,AX

2.源操作数长度应该与目标操作数一致

3.不允许在两个段寄存器之间直接传送数据

4.CS不能作为目的操作数

2.直接寻址

([]内地址由立即数或者标号给出)

1.MOV AX,[2000H]/MOV AX,TABLE

2.[]内是立即数或者标号,如果是标号的话常与伪指令EQU或者DB等一同使用

3.默认操作数存放在DS

4.允许使用段超越前缀进行段重设

3.寄存器间接寻址

([]内地址通过寄存器给出)

1.MOV AL,[BL]

2.[]内是寄存器,寄存器里面的内容被解释为地址,[]内只能是BX,BP,SI,DI

3.[]内是BX,SI,DI,默认操作数在DS,[]内是BP,默认操作数在SS

4.允许段重设

4.寄存器相对寻址

([]内地址通过寄存器加一个数给出)

1.MOV BX,COUNT[SI]/MOV BX,5[SI]

2.不允许BX,BP同时出现在一个[]内,不允许SI,DI同时出现在一个[]内

5.基址变址寻址

([]内地址通过两个寄存器内容之和给出)

1.MOV AX,[BX][SI]

2.[]内是寄存器,寄存器中的内容被解释为地址

6.相对基址变址寻址

([]内地址由两个寄存器加一个数给出)

1.MOV AX,COUNT[BX][SI]

2.[]内同时出现两个寄存器和一个立即数或者标号

7.端口直接寻址

IN AL,63H 要求端口地址小于255

8.端口间接寻址

MOV DX,端口地址

OUT DX,AL

推荐直接用端口间接寻址,这种方式不管端口地址大于还是小于255都是对的

这是因为IN指令和OUT指令的格式决定的

常见的指令

算术逻辑指令

1.XLAT TABLE 表示把TABLE这个地址指向内存单元中的内容送到AL里面

2.取地址之后一般是放到BX里面,比如LEA BX,[SI]或者

MOV BX,OFFSET TABLE这两条指令是等价的,不过LEA是机器指令而OFFSET是运算符

3.LDS SI,[地址],功能是从源操作数指定的存储单元中取出一个双字指针,即四个字节地址指针,把低位字放在SI中,高位字放到DS中,LDS的源操作数必须是存储器操作数,目的操作数是16位寄存器,通常用SI

4.LESDI,[地址],功能是从源操作数指定的存储单元中取出一个双字指针,即四个字节地址指针,把低位字放在DI中,高位字放到ES中,LES的源操作数必须是存储器操作数,目的操作数是16位寄存器,通常用DI

5.AAA指令:课本78页,针对非压缩BCD数

压缩BCD数就是四位二进制表示一个十进制数,非压缩BCD数就是8位二进制数表示一个十进制数,BCD码是给程序员看的,它本质上是一个十进制数

若AL低四位大于9或者AF=1,则

(1)AL+6送到AL

(2)用与操作把AL高四位清零

(3)AF置1,CF置1,AH+1送到AH里面

用ASCII码表示的十进制数高半字节均为3,在运算中没用,先用AND指令把他们清零后再运算

6.DAA指令:针对压缩BCD数 加法调整指令

调整方式:AL低四位大于9或者AF=1就+6H,如果加完之后高四位大于9,就再+60H并把CF置1

7.AAS 针对非压缩BCD码的减法调整指令

若AL低四位大于9或者AF=1,则

(1)AL-6送到AL

(2)用与操作把AL高四位清零

(3)AH-1送到AH里面,CF置1

8.DAS 针对压缩BCD码的减法调整指令

AL低四位大于9或者AF=1就-6H,如果减完之后高四位大于9,就再-60H并把CF置1

9.MUL 无符号数乘法指令

格式:MUL 源

源操作数可以是存储单元或者寄存器,但不能是立即数

单操作数,目的操作数是AL或者AX,被隐藏了。如果源操作数是8位的,那么他与AL相乘之后是一个16位的数,高八位送到AH,低八位送到AL,如果源操作数是一个16位数,那么他就自动与AX相乘得到一个32位的数,低16位放在AX里面,高16位放在DX里面

MUL对于符号位的影响,如果结果的高半部分不是零,则OF和CF均为1,其余标志位由结果决定

10.IMUL 带符号数乘法指令

把源操作数和累加器中的数都作为带符号数进行相乘

如果乘完之后高半部分不是全0或者全1,则CF=1,OF=1,如果高半部分为全0或者全1,则CF=0,OF=0,其余标志位由结果决定。

至于IMUL是如何进行结果修正的,是计算机自动完成的,我们不用管,只需要知道如果两个带符号数相乘,用IMUL算出来就是正确的结果。

11.AAM 针对非压缩BCD数的乘法调整指令

格式:AAM

调整过程:用AL寄存器内容除10,商放在AH中,余数放在AL中。

影响ZF,SF,PF,不影响AF,CF,OF

12.DIV 对两个无符号数进行除法操作

格式:DIV 源

源操作数可以是存储器或者存储单元,但不能是立即数。被除数一定要是除数的双倍字长。如果源操作数是8位,被除数是16位,则16位被除数必须放在AX中,相除之后8位商在AL中,余数在AH中。如果被除数是8位,必须放在AL中,并把AH清零,然后相除。相当于把八位被除数扩展了四个16进制零变成了16位。

如果源操作数是16位,被除数是32位,则高16位放在DX中,低16位放在AX中,相除之后16位商放在放在AX里,余数放在DX里。如果被除数是16位,必须把被除数放在AX里面,并把DX清零,再相除。

13.IDIV 对两个带符号数进行除法操作

商和余数也是带符号数,规定余数的符号和被除数符号一致。

IDIV对所有标志位均无影响

如果被除数太大,源操作数(除数)过小,会造成AL或AX放不下商,造成除法错中断。实际上如果被除数的高位比除数的绝对值要大,就会产生除法错中断。

对于带符号数的除法,要求被除数必须是除数的双倍字长,如果不满足,需要对被除数进行位数扩展(用符号扩展指令)

14.CBW 把字节指令扩展为字指令

格式:CBW

功能:把寄存器AL中字节的符号位扩充到AH的所有位

15.CWD 把字指令转换成双字指令

格式:CWD

功能:把AX中的符号位扩充到DX的所有位

16.AAD 除法的ASCII码调整指令

格式:AAD

功能:在做除法之前把BCD码转换成二进制数

前面的AAA,AAS,AAM指令都是跟在两个非压缩BCD码之后,而AAD是在两个非压缩BCD码做除法之前。

调整过程:把AH×10再+AL送到AL里面,然后把00送到AH里面

8086没有提供压缩十进制数的乘除调整指令,如果要进行压缩十进制数的乘除运算,应该先把操作数转换成非压缩十进制数,再按非压缩十进制数的标准进行计算

逻辑运算指令

NOT 取反指令

格式:NOT 目的

功能:把目的操作数按位取反,结果送回目的操作数

目的操作数可以是8位或者16位寄存器或者存储器,对于存储器操作数要说明是字节还是字

NOT指令对标志位无影响

AND:常用来把操作数中的某些位保留(与1)或者把某些位清零(与0),任何数按位与自己,不会改变数本身的值,但是会影响标志位,这一点常用于结合条件转移指令来使用。

OR :按位或上0可以把操作数的位保留,按位或1可以使操作数的位变成1,任何数按位或自己,不会改变数的大小,但是会影响标志位,这一点常用于结合条件转移指令来使用。

由于AND自己和OR自己都不会改变数本身,而只是影响标志位,因此在结合条件转移指令的时候选择AND自己和OR自己都可以。比如要结合JS使用,那就OR x,x 这时候x本身不变,但是标志位发生了变化,如果原来x是负数,那么执行完OR x,x之后SF=1,就可以通过JS指令跳转到某个地址去。

XOR(异或操作指令):异或0,原来是0的地方还是0,原来是1的地方还是1,从而保留目的操作数的位。

异或1:原来是0的地方变1,原来是1的地方变0。从而可以实现目的操作数原来的位取反。

任何数异或自己本身,结果为零。

TEST:对两个操作数进行按位与操作,并修改标志位,但是不会把结果送回目的操作数,即执行TEST指令后,两个操作数都不会改变。

总结:逻辑运算指令包括:NOT,AND,OR,XOR,TEST。除了NOT指令以外,其余指令均影响标志位,因为逻辑操作不可能产生溢出,因此执行完这些指令之后CF,OF均为0,AF理应是0,但是由于逻辑运算指令中AF并没有定义,因此不需要讨论AF,而ZF,SF,PF由操作结果反映。

逻辑移位指令

1.SAL (算数左移指令):SAL 目的,计数值

SHL(逻辑左移指令):SHL 目的,计数值

这两条指令的功能完全相同,注意如果位移次数是1,可以直接写 SAL AL,1,如果位移次数大于1,应该先把计数值放到CL里面,再SAL AL,CL

对标志位的影响,如果移位结束之后最高位的符号改变,则OF=1,CF的值等于最后一次移位被移出去的那个值,SF,ZF,AF由结果决定,PF只有目的操作数在AL中的时候才有效。

2.SHR(逻辑右移):SHR 目的,计数值 针对无符号数

功能:对目的操作数的各位进行右移操作,每右移一位相当于除2,但是余数会被丢掉。最低位进入CF,最高位补0。

SAR(算术右移指令):针对有符号数。对目的操作数的各位进行右移操作,最高位补符号位。例如-128/8可以利用SAR来计算

MOV AL,10000000B

MOV CL,03H

SAR AL,CL

这两条指令计数值大于一的时候也要先送给CL

循环移位指令

ROL(循环左移指令):ROL 目的,计数值

把最高位移进CF的同时移动到最低位,效果上类似于右旋

ROR(循环右移指令):ROR 目的,计数值

把最低位移动到CF的同时移动到最高位,效果上类似于左旋

RCL(带进位位循环左移):把最高位移动到CF里面,同时把CF里原来的值移动到最低位

RCR(带进位位的循环右移):把最低位移动到CF里面,同时把CF里面原来的值移动到最高位

对标志位的影响:CF由最后一次移位的结果决定,OF只有在移位次数是1的时候才有效,如果移位次数大于1则OF是随机值,跟结果无关。其余标志位课本上没讲,不用管。

字符串操作指令

字符串操作指令的说明:

1.是针对数据块或者字符串的操作,因此操作数字节大小会比较大,故一般是存储器操作数。

2.可以实现存储器到存储器的数据传送,这与我们前面学过的双操作数指令不同,前面学过的不管是数据传送类指令,还是逻辑运算指令或者算术运算指令里面的双操作数指令,都不允许两个操作数同时为储器操作数。

3.指令的执行需要确定源串和目的串所在的区域,串的首地址,串长度,串的操作方向。

4.源串一般放在数据段,偏移地址由SI指定,即源串的地址一般是DS:SI,但允许段重设,可以设置成ES,SS但不能设置成CS。目的串必须在附加段,由ES寻址,DI做指针,即目的操作数的地址是ES:DI

5.串长度由CX指定,可以通过在串操作指令前面加重复前缀来实现对CX的自动修改,也就说如果看见一个指令是重复前缀+串操作指令,那这条指令本身就是一个循环。

6.串的操作方向由DF标志位确定,DF=0表示增地址方向,DF=1表示减地址方向,DF标志位可以由指令进行设置,CLD可以把DF置0,STD可以把DF的值置1

7.修改地址指针是由串操作指令完成的,修改CX中的值是由重复前缀完成的。

8.由于受到数据总线宽度的限制,串操作指令必须分多次,一次一个字节或者一个字来完成任务

9.因为目标操作数必须在附加段,因此需要定义附加段

重复前缀:

无条件重复:REP

相等/结果为零则重复:REPE/REPZ,当且仅当CX不等于0,且ZF等于1,才重复

不相等/结果非零则重复:REPNE/REPNZ,当且仅当CX不等于0,且ZF=0才重复

由图可知,再检查是否已经完成之前就已经修改了地址指针(SI+1,DI+1)和CX的值(CX-1)。

串传送指令:

第一种写法一般只在进行了段重设的情况下使用,因为一般对于串操作指令来讲指针和逻辑段的位置已经被规定死了,可以根据需要用MOVSB和MOVSW

虽然结果不送回,但是CMPS会影响全部六个标志位

应用:CMPS指令可以用来测试MOVS传送的比如200个数据是否正确,如果不正确,找出存放不等数据的地址以及不等数据。显然逻辑应该是不相等则停下来,因此重复前缀不能用不相等则重复,而应该用相等则重复,继续找不同,直到CX=0或者ZF=0(找到了不相等的)

LEA SI,MEM1 取源串地址

LEA,DI,MEM2 取目标串地址

MOV CX,200 设置串长度

CLD DF清零,表示按照地址增的方向开始

REPE CMPSB 按字节比较,相等则重复

JZ STOP 执行完上一条指令后ZF=1则跳转,因为ZF=0说明源串和目标串对应位置不同

DEC SI 找到存放不等数据的地址

MOV AL,[SI] 把不等数据放到AL里

MOV BX,SI 把不等数据的地址放到BX里

STOP:HLT

如果是默认源串地址是DS:SI,可以直接写成SCASB和SCASW

原理就是拿AL或者AX里面的值去和源串对应位置的值逐个相减,结果不会保留,但是会影响标志位。

逻辑应该是找到了A就停下来也即相等则停下,因此重复前缀不能用相等则重复,而应该用不等则重复

MOV DI,2000H 源串地址

MOV CX,10 源串长度

MOV AL,'A' 要找的字符

MOV BX,DI 后面找搜索次数的时候会用到

CLD DF清零,表示从低地址到高地址

REPNE SCASB 按字搜索,不相等则重复

JZ FOUND ZF=1则跳转

MOV DI,0 ZF=0,说明没找到

JMP DONE 无条件跳转

FOUND:DEC DI找到了,但此时DI已经+1了要找到源串种放'A'的内存单元,应该先把DI-1

MOV DATA2,DI 源串中存放'A'的地址

INC DI 为了正确计算出搜索次数,应该把DI+1

SUB DI,BX 搜索次数

MOV DATA2,DI

DONE:MOV DATA1,0没找到则DATA是0

HLT

把源串首地址单元中的内容送给AL或者AX

为了防止装入AL或者AX中的内容被覆盖,LODS指令一般不加重复前缀

STOSB:按字节把AL中的内容送到目标串首地址所指向内存单元

STOSW:按字把AX中的内容送到目标串首地址所指向内存单元

常与无条件重复前缀一起使用来把某一区域全部内容设置成同样的值

程序控制类指令

包括无条件转移指令,条件转移指令,过程调用指令,条件循环控制指令,中断指令

说明:1.这一类指令是为了控制程序走向而设计的,也就是可以修改CS和IP,除了程序控制类指令外,其余指令都不能修改CS和IP,即不能把CS和IP当做目标操作数

2.修改CS表示从一个代码段跳到另一个代码段执行,相当于改变了楼层,修改IP表示跳到这个代码段的另一个地方去执行,相当于在同一层楼上换了房间

3.对于这类指令应该关注他们是如何修改CS和IP的

目标地址可以直接由标号给出,如果标号不写FAR,代表就是段内转移。也可以由寄存器或者存储器操作数间接给出,假设是段内转移,如果目标地址是由寄存器给出,必须是16位寄存器,因为JMP后面跟的永远都是偏移地址,在段内转移时是16位,如果是由存储器操作数给出,则应该在存储器操作数前面协商WORD PTR说明这是一个十六位的操作数。如果是段间转移的话,JMP后面的目标地址就是32位的,低位表示IP,高位表示CS,此时如果用标号给出目标地址,应该在标号前面加一个FAR,如果由存储器操作数给出,那么存储器操作数指向单元的低位是IP,高位是CS,由于8086没有32位的寄存器,因此段间转移无法由寄存器间接给出目标地址。

这与无条件转移不同,条件转移指令只能在段内转移,甚至只能在段内一小块区域进行转移,一般条件转移指令都是根据标志位的状态来决定是否执行的。常见的条件转移指令如下。

JC表示CF=1就转移,JNC表示CF=0就转移

JZ表示ZF=1就转移,JNZ表示ZF=0就转移

我们可以看到不带N的就是标志位=1转移,带N的就是标志位=0转移

有几个比较重要的标志位需要记住:JA是大于则转移,JAE是如果大于等于(CF=0)则转移JB是小于(CF=1)则转移,JBE是小于等于则转移这4个用于无符号数的比较测试,JG是大于则转移,JGE是大于等于则转移,JL是小于则转移,JLE是小于等于则转移,这四个用于有符号数的比较测试。跟在CMP后面 无符号数比较JA,JB 带符号数比较 JG,JL

只有一个条件转移指令不是基于状态位来决定是否执行的,他就是JCXZ,这个条件转移指令是基于CX的值来决定是否执行的,当CX=0,执行,CX不为0,不执行。

因为一共就一百个数,所以正负0三种性质的数的个数均不可能超过100,用8位寄存器或者1个存储单元来统计个数就已经够了,假设PLUS,MINUS,ZERO就是三个存储单元的地址。统计各性质的数的个数的程序如下。

START:XOR AL,AL 把AL清零

MOV PLUS,AL

MOV MINUS,AL

MOV ZERO,AL 上面三条指令把用来统计个数的三块内存单元清零

LEA SI,TABLE 取TABLE的地址

MVO CL 100 串长度

CLD DF=0,表示按照增地址方向进行串操作

CHECK:LODSB 把源串第一个字符放到AL里面,同时SI+1

OR AL,AL 不改变AL的值,但影响标志位

JS X1 如果是负数,跳转到X1

JZ X2 是0跳转到X2

INC PLUS 既不是0也不是负数,那就是正数

JMP NEXT

X1:INC MINUS 负数个数+1

JMP NEXT

X2:INC ZERO 0个数+1

NEXT:LOOP CHECK 串长度为0了吗?如果串长度不为零表示没有统计完,那么应该跳转到CHECK开始装入第二个字符查看

HLT

START:LEA SI,DATA 取这串数据的首元素地址

MOV CX,200 串长度

CLD DF置0,表示串操作从低地址到高地址

LODSW 把字符串的第一个字装入AX

MOV MAX,AX 把最大值假设为AX

MOV MIN,AX 把最小值假设为AX

DEC CX 装入了第一个字符,CX-1

NEXT:LODSW 装入第二个字符到AX

CMP AX,MAX 比MAX大吗?

JG LARGE 如果比MAX大就跳到LARGE

CMP AX,MIN 比MIN小吗?

JL SMALL 如果比MIN小就跳到SMALL

JMP GOON 与AX值相等,比较下一个

LARGE:MOV MAX,AX 把MAX更新成AX的值

JMP GOON 继续比较

SMALL:MOV MIN,AX 把MIN的值更新成AX

GOON:LOOP NEXT 继续比较

HLT

注:LOOP指令每次循环会自动把CX-1

过程调用指令与无条件转移指令的区别在于过程调用指令在调用完子程序之后需要回到断点处接着原来程序执行,而无条件转移指令是直接跳过去接着往下执行。

除了子程序的功能实现以及参数的保存之外其余工作均由硬件自动完成,恢复断点的时候我们也可以观察到SP的变化,但这并不是由我们人为用PUSH或者POP指令完成的,而是系统自动完成的。

CALL指令可以段内调用,也可以段间调用,具体是什么要看子程序入口地址给的是16位还是32位,如果给32位就是段间调用,CS在高地址,IP在低地址。

CALL指令 一定和RET指令配套出现的,子程序以语句PROC开头,ENDP结束,再ENDP之前一定要放RET,他与CALL指令相呼应,确保能返回主程序

近调用:SP-2

远调用:SP-4

中断指令

INT n 里面的n不是一个普通的常数,而是一个中断类型码,系统拿到这个n之后会自动做×4的运算得到另一个常数,而新得到的这个常数会被解释为一个地址,这个地址指向内存单元中被称为中断向量表的区域中的某个内存单元,然后从这个地址开始数的四个字节单元(或者说两个字单元)高字单元中的内容被解释为CS的值,低字单元中内容被解释为IP的值。

中断向量表位于数据段的起始位置,一共1K个字节单元,因此地址是从00000H~003FFH

中断子程序和过程调用执行的子程序都是子程序,只要是子程序那肯定是程序员自己编的,对于过程调用来说,程序员是确定的知道什么时候要去执行子程序的,但是对于执行中断子程序来说,是由于异常引起的,程序员也不知道什么时候去执行这个中断子程序,当发生中断的时候,由于我们希望看看为什么会发生中断,因此除了保护断点地址以保证能够返回主程序之外,还要保护硬件现场(即FLAGS的内容)就像警察破案需要保护犯罪现场一样

中断指令均为远调用,因此要同时保护CS和IP,CS位于高地址,IP位于低地址,即先送CS,后送IP(压入堆栈)

执行一次INT n指令SP至少要-6,这个变化是由硬件系统自动完成的,不是我们用POP PUSH完成的,但是我们确实可以观察到堆栈发生的变化

处理器控制指令

包含标志位操作指令,外部同步指令,停机指令和空操作指令

了解几个标志位操作指令即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值