chapter10-chapter11

发布时间 2023-11-05 10:28:58作者: scrutiny-span

chapter10:call指令和ret指令

call指令和ret指令都是转移指令,都能够修改IP,或者同时修改IP和CS,这两个指令被用来实现子程序(高级语言中称之为函数)的设计,极其重要。

1.ret和retf

  1. ret指令用栈中的数据,修改IP的内容,从而实现近转移(段内近转移)
(IP) = ((ss) * 16 + (SP))
(SP) = (SP) + 2

相当于
pop IP
  1. retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移(段间转移)。
相当于
pop IP
pop CS

2.call指令

  1. call指令不能实现短转移,call指令实现转移的方法和jmp指令相同。CPU执行call指令时,进行两步操作:
    1. 将当前的IP或者CS和IP压入栈中
    2. 转移
1.根据位移进行转移的call指令
  1. 这种call指令对应的机器指令没有转移的目的地址,而是相对于当前IP的转移地址。
  2. 指令用法:call 标号(将当前的IP(call指令的下一条指令的地址)压入栈后,转到标号处执行指令)
  3. CPU执行此种格式的call指令时,进行如下的操作
1. (sp) = (sp) - 2
;将当前IP存入栈顶,以便函数返回时可以回到call指令的下一条指令执行
2. ((ss) * 16 + (sp)) = (IP)
;转到标号处执行指令
3. (IP) = (IP) + 16位位移 

注:16位位移 = 标号处的地址 - call指令后的第一个字节的地址

16位位移的范围为-32768到32767
16位位移由编译程序在编译时算出
  1. CPU执行call 标号时相当于进行
push IP
jmp near ptr 标号
2.转移的目的地址在指令中的call指令
  1. 指令格式:call far ptr 标号实现的是段间转移
  2. CPU执行此种格式的call指令时,进行如下的操作
1. (sp) = (sp) - 2
2. ((ss) * 16 + (sp)) = (CS)    ;将当前CS存入栈顶
3. (sp) = (sp) - 2
4. ((ss) * 16 + (sp)) = (IP)    ;将当前IP存入栈顶
5. (CS) = 标号所在段的段地址
6. (IP) = 标号在段中的偏移地址
  1. CPU执行call far ptr 标号时相当于进行
push cs
push ip
jmp far ptr 标号
3.转移地址在寄存器中的call指令
  1. 格式:call 16位寄存器
  2. 功能:
1. (sp) = (sp) - 2
2. ((ss) * 16 + (sp)) = (IP)    ;将当前IP存入栈顶
3. (IP) = (16位寄存器) ;更新IP的值
  1. CPU执行call 16位寄存器时相当于进行
push IP
jmp 16位寄存器
4.转移地址在内存中的call指令
  1. 转移地址(转移的目的地址的偏移量)在内存中的call指令有两种格式:
1.call word ptr 内存单元地址 (段内转移)
    该指令相当于
    push IP
    jmp word ptr 内存单元地址
2.call dword ptr 内存单元地址(段间转移)
    该指令相当于
    push CS
    push IP
    jmp dword ptr 内存单元地址
  1. 示例:
;下面指令执行后,(IP) =0123H ,(sp) = 0EH
mov sp,10H
mov ax,0123H
mov ds:[0],ax
;偏移地址更改为从ds:[0]开始的两个字节的数据
;将当前的IP压入栈中
call word ptr ds:[0]

;下面指令执行后,(CS) = 0,(IP) = 0123H,(SP) = 0CH
mov sp,10H
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
call dword ptr ds:[0]

3.ret指令和call指令配合实现子程序

  1. 这两条指令配合可以实现类似C语言中的函数调用。使用call指令和ret指令实现子程序的框架如下:
assume cs:code
code segment
    ;程序入口地址
    main:
        call sub1;调用子程序sub1
        mov ax,4c00h
        int 21h
    ;子程序sub1开始
    sub1:
        指令
        ret;子程序返回
code ends
end main

4.mul指令(乘法指令)

  1. 规则:
    1. 两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认存放在AL中,另一个放在8位寄存器中或者内存字节单元中。如果是16位,一个默认在AX中,另一个放在16位寄存器中或者内存字单元中。
    2. 如果是八位乘法,结果默认放在AX中,如果是16位乘法,高位默认在DX,地位在AX
  2. 格式如下:
mul 寄存器
mul 内存单元
  1. 示例
;表示(ax) = (al) *((ds) * 16 + 0)
mul byte ptr ds:[0]

chapter11:标志寄存器

CPU中的内部寄存器中,有一种特殊的寄存器,它具有以下三个作用:

  1. 用来存储相关指令的某些执行结果
  2. 用来为CPU执行相关指令提供行为依据
  3. 用来控制CPU的相关工作方式

这些特殊的寄存器在8086CPU中被称为标志寄存器。8086CPU中的标志寄存器有16位,其中存储的信息通常称为程序状态字(PSW)

8086CPU的flag寄存器的结构如下图所示:1、3、5、12、等标志位没有使用,没有含义
image.png

1.标志寄存器中的ZF标志位

flag(标志寄存器)的第六位是ZF标志位,他记录相关指令执行后,其结果是否为0.如果结果为0,那么ZF为1,否则ZF为0

;指令执行后,ZF=1
mov ax,1
and ax,0

2.PF标志位

  1. flag的第二位是PF(Parity Flag)标志位,奇偶标志位。他记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。如果1的个数为偶数,则PF为1,否则PF为0
;执行后结果有三个1,所以PF=0
mov al,1
add al,10

3.SF标志位

flag的第七位是SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,则sf等于1.否则sf为0

;指令执行后,结果为10000010B,sf = 1
;如果指令进行的是有符号运算,那么结果为负(因为sf为1)
mov al,10000001B
add al,1

注意:传送指令push,pop,mov等不影响标志寄存器的状态。

4.CF标志位

flag的第0位是CF,进位标志位。一般情况下,在进行无符号运算 的时候,它记录了运算结果的最高有效位向更高位的进位值,或者从更高位的借位值。

mov al,98H
;执行后,(AL) = 30H,CF = 1
;CF记录了最高有效位向更高位的进位值
add al,al

mov al,97H
;执行后(al) = FFH,CF = 1
;CF记录了向更高位的借位值
sub al,98H

5.of标志位

  1. 溢出:运算结果超出了机器所能表示的范围
  2. 在进行有符号数运算时 ,可能发生溢出而造成结果的错误。则CPU需要对指令执行后是否产生溢出进行记录。flag的第11位是OF这个溢出标志位,如果发生溢出,则OF等于1,否则OF等于0.

注意CF与OF的区别:CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位

6.adc指令

  1. abc是带进位加法指令,它利用了CF位上记录的进位值。
  2. 指令格式:adc 操作对象1,操作对象2
  3. 功能:操作对象1 = 操作对象1 + 操作对象2 + CF

提供adc指令的缘有:和add指令配合就可以对更大的数据进行加法运算

;低位相加
add al,bl
;高位相加再加上低位相加产生的进位值
adc ah,bh
  1. 举例:
;计算1EF000H + 201000H
mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

7.sbb指令

  1. sbb是借位减法指令,它利用了CF位上记录的借位值。
  2. 指令格式:sbb 操作对象1,操作对象2
  3. 功能:操作对象1 = 操作对象1 -操作对象2 - CF
  4. 举例
;计算003E1000H - 00202000H
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H

8.cmp指令

  1. cmp指令是比较指令,cmp指令的功能相当于减法指令,只是不保存结果。cmp指令执行后,将根据计算结果对标志寄存器进行设置。
  2. cmp指令格式:cmp 操作对象1,操作对象2
  3. 功能:计算操作对象1 - 操作对象2

在考察SF标志位(得知实际结果的正负)的同时,考察OF标志位(得知有无溢出),就可以得知逻辑上真正结果的正负,同时知道比较的结果。

9.根据比较结果的条件转移指令

根据某种条件,决定是否修改IP。

  1. jcxz就是一个条件指令,它监测cx的值,如果(cx) = 0,则修改IP。
  2. 根据cmp指令的比较结果进行转移的指令:
    1. 根据无符号数的比较结果进行转移的条件转移指令:通过检测ZF、CF的值。常用的条件指令如下所示,其中b表示below、a表示above、j表示jump。使用这些条件指令和cmp指令结合可以实现高级编程语言中的选择结构,比如说if...else
    2. 根据有符号数的比较结果进行转移的条件转移指令:通过检测sf、of、zf的值

10.DF标志和串传送指令

  1. flag的第十位是DF标志位(方向标志位)。在串处理指令中,控制si和di的增减。
df = 0,每次操作后,si和di递增
df = 1,每次操作后,si和di递减
  1. 串传送指令1:movsb(传送一个字节),功能主要是将ds:si指向的内存单元的字节送入es:di中,然后根据标志寄存器df位的值,将si和di递增1或者递减1
执行movsb指令相当于执行下列操作
((es) * 16 + (di)) = ((ds) * 16 + (si))
    如果df=0,将si,di递增
    如果df=1,将si,di递减
  1. 串传送指令2:movsw(传送一个字),功能主要是将ds:si指向的内存单元的字送入es:di中,然后根据标志寄存器df位的值,将si和di递增2或者递减2
用汇编语法描述movsw的功能如下:
mov es:[di],word ptr ds:[si]    ;只是描述,实际并不存在这样的指令
  1. rep movsb指令:功能就是根据cx寄存器的值循环实现(cx)次字符的传送
rep movsb
用汇编语法描述上述指令的功能就是:
s:movsb
    loop s
  1. 下面两条指令可以对DF位进行设置
    1. cld指令:将标志寄存器的DF位置为0
    2. std指令:将标志寄存器的DF位置为1

11.pushf和popf指令

pushf和popf为直接访问标志寄存器提供了一种方法。

  1. pushf指令将标志寄存器的值压入栈中
  2. popf指令从栈中弹出数据,送入标志寄存器中。