学汇编,记笔记(九)《汇编语言》王爽著——call和ret_mul乘法

156 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

学习教材:《汇编语言(第4版)》王爽著 此笔记是书中内容+自我总结,方便查阅和复习 请支持原著

call和ret是两个转移指令,只修改IP或同时修改CS和IP @[toc]

章末实验10

一、call和ret的理解——模块化

在主函数内调用子函数,就要用到call和ret,这样就可以实现模块化

call会转移到标号(位移量)/详细偏移地址处运行,当运行结束后使用ret返回到call的下一行继续执行

code segment
	...
	
	child:add ax,bx ;子函数
	ret ;转移到call的下一行代码
	
	main:mov ax,0 ;主函数
	mov bx,1
	call child ;转移到标号处
	...
		
	mov ax,4c00h
	int 21h	
code ends
end main

二、ret和retf

  • ret将出栈的数据传送给IP,实现近转移
  • retf将出栈的数据传送给IP和CS,实现远转移

Ⅰ、ret的具体操作

  1. IP=ss*16+sp
  2. sp+=2

相当于执行pop IP

Ⅱ、retf的具体操作

  1. IP=ss*16+sp
  2. sp+=2
  3. CS=ss*16+sp
  4. sp+=2

相当于执行pop IP,再执行pop CS,后进先出为IP的值

三、call

与ret相反:call将当前的IP或CS和IP先后入栈,再转移到指定的标号(位移量)/地址处

注意:读取call后,IP会指向下一行代码,才会进行call的操作,因此此时入栈的IP指向call的下一行

Ⅰ、转移到标号处——call 标号

call 标号

这是16位位移,操作和注意事项同jmp near ptr,属于段内近转移

  1. sp-=2
  2. ss*16+sp=IP
  3. IP+=16位位移

相当于执行push IP,再执行jmp near ptr 标号

Ⅱ、转移地址在寄存器中——call reg

call 16位reg
  1. sp-=2
  2. ss*16+sp=IP
  3. IP+=reg

相当于执行push IP,再执行jmp 16位reg

转移地址在内存中——call X ptr

分为两种word和dword型,其中:

  • call word ptr 内存单元地址相当于执行push IP,再执行jmp word ptr
  • call dword ptr 内存单元地址相当于执行push CSpush IP,再执行jmp dword ptr,后进先出为IP的值

检测点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
	mov ax,0
	call word ptr ds:[0eh]
	inc ax
	inc ax
	inc ax
	mov ax,4c00h
	int 21h
code ends

end start

答案:ax=3

  1. 构造16B空栈,地址[0-0FH],ds指向栈段,ax置零
  2. 第一次到字型call时,IP指向第一个inc入栈。转移到栈空间[0EH-0FH](栈底字单元)所指位置,由于是空栈,即转移到第一行mov ax,stack
  3. 第二次到字型call时,IP指向第一个inc入栈。转移到栈空间[0EH-0FH](栈底字单元)所指位置,由于栈底有第一个inc的地址,转移到第一个inc
  4. 依次执行三个inc后程序结束

(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
    mov word ptr ss:[0],offset s
    mov ss:[2],cs
    call dword ptr ss:[0]
    nop
    
    s: mov ax,offset s
    sub ax,ss:[0ch]
    mov bx,cs
    sub bx,ss:[0eh]
    
    mov ax,4c00h
    int 21h
code ends

end start

答案:ax=1,bx=0

  1. 构造16B空栈,地址[0-0FH]
  2. 栈顶[0-1]存入s的偏移地址(严格来说不是一个栈结构)
  3. 栈空间[2-3]存入CS
  4. 到双字型call时,CS入栈底[0EH-0FH],IP指向nop入栈[0CH-0DH]。转移到栈空间[0-4]所指位置,此处为远转移,转移到CS:offset s(即标号s)处
  5. ax=offset s
  6. 对于第一个sub :由于[0CH-0DH]存nop的偏移地址,nop是s的下一行,因此有offset nop=offset s+1,此处即为sub ax,offset s+1,即ax=offset s-(offset s+1)=1
  7. bx=cs
  8. 对于第二个sub :由于[0EH-0FH]存CS,此处即为sub bx,cs,即bx=CS-CS=0

四、mul乘法

Ⅰ、乘法的二要素

类比div除法

  • 两个相乘的数:要么全是8位要么全是16位。8位乘法存在al和8位寄存器或内存中;16位乘法存在ax和16位寄存器或内存中
  • 积:8位乘法存在ax,16位乘法高位在dx低位在ax

积位数是乘数位数的2倍

Ⅱ、mul计算

  • mul reg
  • mul SA:EA

例:计算100*10和100*10000

前者结果小于255,后者结果大于255,前者8位乘法后者16位乘法 实现代码分别如下:

mov al,100
mov bl,10
mul bl
mov ax,100
mov bx,10000
mul bx

运算后查看ax或ax和dx以获得积

五、模块化参数传递

当模块化时,不可避免地会复用寄存器,因此避免寄存器内的值混乱,在进入子程序前应先将寄存器的内容全都入栈,在结束子程序之前将内容出栈,以保存值

后记

  • call和ret可以设计子程序
  • mul支持两种位数
  • 运行子程序之前先将寄存器的值全部保存起来
  • 书上关于多个单词转大写的程序(P204)是子程序的典型例子,应多理解其过程