(十五)《汇编语言(王爽)》 | 检测点 10.1、10.2、10.3、10.4、10.5


1. 预备知识

  • ret 指令用栈中的数据修改 IP 的内容,从而实现近转移,执行 ret 指令时进行下面两步操作:

    • (IP)=((ss)*16+(sp)),将栈顶元素值赋值给寄存器 IP
    • (sp)=(sp)+2,栈顶指针增大,相当于出栈,即 pop IP
  • retf 指令用栈中的数据修改 IP 的内容,从而实现远转移,执行 retf 指令时进行下面四步操作:

    • (IP)=((ss)*16+(sp)),将栈顶元素值赋值给寄存器 IP
    • (sp)=(sp)+2,栈顶指针增大,相当于出栈,即 pop IP
    • (CS)=((ss)*16+(sp)),将栈顶元素值赋值给寄存器 SP
    • (sp)=(sp)+2,栈顶指针增大,相当于出栈,即 pop CS
  • call 指令格式为 call 标号,执行该指令时进行如下操作:

    • (sp)=(sp)-2,((ss)*16+(sp))=(IP),相当于入栈,即 push IP
    • (IP)=(IP)+16位位移,位移通过标号处的地址减去指令后的第一个字节地址得到
  • 指令 call far ptr 标号实现段间转移,执行该指令时进行如下操作:

    • (sp)=(sp)-2,((ss)*16+(sp))=(CS),相当于入栈,即 push CS;(sp)=(sp)-2,((ss)*16+(sp))=(IP),相当于入栈,即 push IP
    • (CS)=标号所在段的段地址,(IP)标号所在段的偏移地址
  • 指令 call 16 位寄存器,执行该指令时进行如下操作:

    • (sp)=(sp)-2,((ss)*16+(sp))=(IP),相当于入栈,即 push IP
    • (IP)=(16位寄存器)
  • 指令 call word ptr 内存单元地址相当于:

    • push IP
    • jmp word ptr 内存地址单元,段内转移,目的偏移地址由内存单元给定
  • 指令 call dword ptr 内存单元地址相当于:

    • push CS
    • push IP
    • jmp dword ptr 内存单元地址,段间转移,目的段地址由内存单元高地址给出、偏移地址由内存单元低地址给出

2. 检测点 10.1

补全程序,实现从内存 1000:0000 处开始执行指令。

assume cs:code

stack segment
	db 16 dup (0)
stack ends 

code segment
start:
	mov ax,stack 
	mov ss,ax 
	mov sp,16		;初始化一个空栈
	
	mov ax,____
	push ax			;入栈,即CS的值
	mov ax,____
	push ax 		;入栈,即IP的值
	
	retf			;修改CS和IP的值,IP为第1次出栈的值,CS为第2次出栈的值
code ends
  • 最后一条 retf 指令修改 CS 和 IP 的值,根据预备知识,首先出栈的值将赋值给寄存器 IP,随后出栈的值将赋值给寄存器 CS。
  • 所以,第 1 次入栈的值为 CS,根据题意为 1000H;第 2 次入栈的值为 IP,根据题意为 0。代码段部分为:
code segment
start:
	mov ax,stack 
	mov ss,ax 
	mov sp,16		;初始化一个空栈
	
	mov ax,1000h
	push ax			;入栈,即CS的值
	mov ax,0
	push ax 		;入栈,即IP的值
	
	retf			;修改CS和IP的值,IP为第1次出栈的值,CS为第2次出栈的值

3. 检测点 10.2

下面的程序执行后,ax 中的值为多少?

内存地址    机器码    汇编指令
1000:0    b8 00 00  mov ax,0
1000:3    e8 01 00  call s
1000:6    40        inc ax
1000:7    58        s:pop ax
  • 根据预备知识,执行 call s 指令时首先执行类似于 push IP 的操作,再进行转移。所以,执行完 call s 指令后,栈顶存放着寄存器 IP 的值。由于 CPU 读取指令后,寄存器 IP 的值已经发生变化,为下一条指令的偏移地址,即 6。
  • 标号 s 处指令的含义是将栈顶元素赋值给 ax,所以寄存器 ax 中的值为 6

4. 检测点 10.3

下面的程序执行后,ax 中的值为多少?

内存地址    机器码         汇编指令
1000:0    b8 00 00       mov ax,0
1000:3    9A 09 00 00 10 call far ptr s
1000:8    40             inc ax
1000:9    58             s:pop ax
                           add ax,ax
                           pop bx
                           add ax,bx
  • call for ptr s 相当于 push CS,push IP,所以此时栈顶元素为 IP 的值,即 8;第二栈顶元素为 CS 的值,即 1000H。
  • 标号处 s 的指令,首先栈顶元素赋值给 ax,再执行 add 指令后,ax 的值为 10H;再出栈和执行 add 指令后,寄存器 ax 的值为 1010H

5. 检测点 10.4

下面的程序执行后,ax 中的数值为多少?

内存地址    机器码    汇编指令
1000:0    b8 00 00  mov ax,6
1000:2    ff d0     call ax
1000:5    40        inc ax
1000:6              mov bp,sp
                    add ax,[bp]
  • call ax 首先执行 push IP 的功能,即将 5 入栈,然后将寄存器 ax 的内容赋值给 IP 实现段内转移,此时 (IP)=6。
  • CPU 开始执行 CS:6 处的指令,首先将寄存器 SP 的内容赋值给 BP,后面 add 指令相当于 add ax,[bp]。
  • 寄存器 ax 的原值为 6,[bp] 默认使用段地址为寄存器 SS 的内容,即 [bp] 取的是栈顶的元素 5。最终,寄存器 ax 的值为 0BH

6. 检测点 10.5

(1)下面的程序执行后,ax 的数值为多少?

assume cs:code

stack segment 
	dw 8 dup (0)
stack ends

code segment
start:
	mov ax,stack 
	mov ss,ax
	mov sp,16				;空栈
	mov ds,ax				;寄存器DS指向栈的段地址
	mov ax,0
	call word ptr ds:[0EH]	
	;首先执行push IP(下一条inc的偏移地址),此时SP=0EH;再执行jmp word ptr ds:[0EH],目的偏移地址由ds:[0EH]给出
	;因为DS和SS指向同一片内存,且(SP)=0EH,所以ds:[0EH]相当于栈顶元素IP的值,即(IP)=(IP),程序顺序执行
	inc ax 	;ax=1
	inc ax 	;ax=2
	inc ax 	;ax=3
code ends 
  • 代码段的前四条语句声明一个空栈,并且使寄存器 DS 指向栈。
  • call word ptr ds:[0EH] 指令首先执行 push IP,将寄存器 IP 的内容入栈,并且 (SP)=(SP)-2=0EH。
  • 然后执行 jmp word ptr ds:[0EH] 实现段内转移,目的地址的偏移地址由 ds:[0EH] 给出。由于 DS 也指向栈段,并且 SP=0EH,所以 ds:[0EH] 实际上指的是栈顶元素,即 IP 的值。即 (IP)=(IP),程序没有受到影响,顺序执行。最后,寄存器 ax 的值为 3

(2)下面的程序执行后,ax 和 bx 中的数值为多少?

assume cs:code 

data segment
	dw 8 dup (0)
data ends

code segment
start:
	mov ax,data 
	mov ss,ax 
	mov sp,16	;空栈,大小为16字节
	
	mov word ptr ss:[0], offset s 
	;将标号s处的偏移地址赋值到ss:[0]中,即栈的第8个字
	mov ss:[2],cs 
	;将寄存器CS的内容(标号s处的段地址)赋值到ss:[2]中,即栈的第7个字
	call dword ptr ss:[0]
	;首先执行push CS和push IP(后一指令nop的CS和IP),此时SP=0CH
	;再执行jmp dword ptr ss:[0],目的地址由ss:[0]高地址(标号s处指令的段地址)给出、偏移地址由ss:[0]低地址(标号s处指令的偏移地址)给出
	;所以,此时的jmp dword ptr ss:[0]的功能是跳转到标号s处执行
	nop
s:
	mov ax,offset s 	;将标号s处的偏移地址赋值到AX
	sub ax,ss:[0cH]		
	;ax=ax-ss:[0cH],ss:[0cH]的值为栈顶元素,即nop指令的偏移地址,即s.IP-nop.IP为nop指令长度,即为1
	mov bx,cs 	
	sub bx,ss:[0eH]
	;bx=bx-ss:[0eH],ss:[0eH]的值为第二栈顶元素,即nop指令的段地址,即cs-nop.CS,即为0
code ends 
end start 
  • 代码段的前三条语句定义一个空栈,此时 (SP)=16。
  • mov word ptr ss:[0],offset s,将标号 s 处指令的偏移地址赋值到栈中 ss:[0]~ss:[1] 两个字节单元处
  • mov ss:[2],cs,将当前指令的段地址赋值到栈中 ss:[2]~ss[3] 两个字节单元处
  • call dword ptr ss:[0],首先执行 push CS 和 push IP,即将后一指令 nop 的段地址和偏移地址入栈,此时 (SP)=0cH;再执行 jmp dword ptr ss:[0],转移后的目的地址由 ss:[0] 单元的高地址决定、偏移地址由 ss:[0] 单元的低地址决定。根据上两条,高地址为 CS 的内容、低地址为 nop.IP 的内容。所以,执行完当前 call 指令后,CS:IP 指向 nop 指令
  • mov ax,offset s,将标号 s 处指令的偏移地址赋值给寄存器 AX;sub ax,ss:[0cH] 相当于 ax=ax-ss:[0cH]=s.IP-nop.IP。由于 nop 指令长度为 1,所以寄存器 ax 的数值为 1
  • 同理,sub bx,ss:[0eH] 相当于 bx=bx-ss:[0eH]=cs-nop.CS,由于段地址始终没有变化,所以寄存器 bx 的数值为 0

7. 总结

  • ret/retf 指令和 call 指令的操作对象都是栈数据。
  • call 在 jmp 指令的基础上,首先有一个入栈的过程。如果是段内转移则将 IP 的值入栈,如果是段间转移则将 CS 和 IP 的值入栈。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值