欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

王爽 《汇编语言》 读书笔记 十 CALL和RET指令

程序员文章站 2024-03-23 22:10:34
...

第十章 CALL和RET指令


call和ret都是转移指令,它们都修改IP,或同时修改CS和IP,常用于子程序的设计。


10.1 ret 和 retf

ret用栈中的数据,修改IP的内容,从而实现近转移

retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移


ret指令

1) (IP) = ((ss)*16 + (sp))

2) (sp) = (sp) + 2

相当于cpu执行来pop IP

retf指令

1) (ip) = ((ss)*16 + (sp))

2) (sp) = (sp) + 2

3) (cs) = ((ss) * 16 + (sp))

4) (sp) = (sp) + 2

相当于cpu执行来pop IP ,  pop CS

以下代码执行以后将跳转到代码段第一条指令

assume cs:code
stack segment
	db 16 dup (0)
stack ends

code segment
		mov ax, 4c00h
		int 21h
		
start:	mov ax, stack
		mov ss, ax
		mov sp, 10h
		mov ax, 0
		push ax
		mov bx, 0
		ret
code ends

end start


监测点10.1

将代码跳转至1000:0000H处

assume cs:code
stack segment
	db 16 dup (0)
stack ends

code segment		
start:	mov ax, stack
		mov ss, ax
		mov sp, 10h
		mov ax, 1000h
		push ax
		mov ax, 0
		push ax
		retf		; equal pop ip, pop cs
code ends

end start


10.2 call指令

call执行

1)将当前的IP或cs和IP压入栈

2)转移

call不能实现短转移,转移方法和jmp指令的原理相同。


10.3  根据位移进行转移的call指令

call 标号(将当前的IP压栈后,转移到标号处执行指令

1) (sp) = (sp) - 2

      ((ss)* 16 + (sp)) = (ip)

2)  (ip) = (ip) + 16位位移

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

16位位移的范围为-32768~32767,用补码表示

16位位移由编译时算出


push IP

jmp near ptr 标号


10.4 转移的目的地址在指令中的call指令

call far ptr 标号  实现段间转移

1) (sp) = (sp) -2 

((ss)*16 + (sp)) = (cs)

      (sp) = (sp) - 2

      ((ss)*16 + (sp)) = (ip)

2)  (cs) = 标号所在的段地址

    (IP) = 标号所在的偏移地址

相当于

push cs

push ip

jmp far ptr 标号


10.5 转移地址在寄存器中的call指令

call 16位 reg

(sp) = (sp) - 2

((ss)*16 + (sp)) = (ip)

(ip) = (16位reg)


push IP

jum 16位reg


10.6 转移地址在内存中的call指令

两种格式

1) call word ptr 内存单元地址

push IP

jmp word ptr 内存单元地址


2) call dword ptr 内存单元地址

push CS

push IP

jmp dword ptr 内存单元地址


监测点10.5

1)

assume cs:code
stack segment
	dw 8 dup (0)
stack ends

code segment		
start:	mov ax, stack
		mov ss, ax
		mov sp, 10h
		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
该代码执行到  call word ptr ds:[0eh]

的时候

首先  

a. 执行push ip   (此时的ip是指向下一条inc ax的地址这里称为A)  sp = 10h -2  = 0eh   ((ss)*16 + (sp)) = A

b. jmp word ptr ds:[0eh]     这里的[0eh]就是ss:sp指向的地址A  因此直接跳转至A处执行call语句后面的inc ax

然后inc 3次 ax

最后ax = 3

使用debug单步调试的时候debug程序也会使用相同的stack因此会产生奇怪的问题。


2)ax = 1, bx = 0

注意call dword ptr 会产生的几个push pop 会改变栈的值和sp的值


10.7  call 和 ret 的配合使用

利用call 和 ret 实现的子程序框架如下

assume cs:code
stack segment
	dw 8 dup (0)
stack ends

code segment		
main:	;
		;
		; call sub1
		;
		;
		mov ax, 4c00h
		int 21h

sub1:	;
		;
		; call sub2
		;
		ret

sub2:	;
		;
		ret
code ends

end main


10.8 mul指令

1) 两个相乘的数,要么都是8位。要么都是16位。 如果是8位一个默认在AL 另一个存放在8位reg 或内存字节单元中

如果是16位,一个默认中AX中,另一个放在16位reg或者内存字单元中

2)结果,如果是8位乘法,结果默认放在AX中; 如果是16位乘法,结果高位默认在DX,低位在AX中存放

mul reg

mul 内存单元


内存单元可以用不同的寻址方式

mul byte ptr ds:[0]

含义: (ax) = (al) * ((ds)*16 + 0)

mul word ptr [bx + si + 8]

(ax) = (ax) * ((ds)*16 + (bx) + (si) + 8)  结果的低16位

(dx) = (ax) * ((ds)*16 + (bx) + (si) + 8)  结果的高16位


计算100*10

mov al, 100

mov bl, 10

mul bl


计算 100*10000

mov ax, 100

mov bx, 10000

mul bx


10.9 模块化程序设计

10.10 参数和结果传递的问题

应如何存储子程序需要的参数和产生的返回值

1)将参数N存储在什么地方?

2)计算得到的值,存储在什么地方?


计算3次方例子 参数存放于BX  返回值存放在dx 和 ax

cube: mov ax, bx
	mul bx
	mul bx
	ret

编程计算 data段组中第一组数据的三次方,结果报错中后面一组的dword单元中

assume cs:code
data segment
	dw	1, 2, 3, 4, 5, 6, 7, 8
	dd	0, 0, 0, 0, 0, 0, 0, 0
data ends

code segment		
start:	mov ax, data
		mov ds, ax
		mov si, 0		; ds:si point to first line
		mov di, 10h		; ds:di point to the second line
		
		mov cx, 8
	s:	mov bx, [si]
		call cube
		mov [di], ax
		mov [di].2, dx
		add si, 2		; ds:si point to the next data 
		add di, 4		; ds:di point to the next data
		loop s
		
		mov ax, 4c00h
		int 21h
	
cube:	mov ax, bx
		mul bx
		mul bx
		ret
code ends

end start


10.11 批量数据的传递

将批量数据的首地址放在寄存器中,

返回值也同样。


例子 将一个全是字母的字符串转换为大写

capital: and byte ptr [si], 11011111b

inc si

loop capital

ret


编程,将data段中的字符串转换为大写

assume cs:code
data segment
	db	'conversation'	;12 characters
data ends

code segment
start:	mov ax, data
		mov ds, ax
		mov si, 0
		mov cx, 12
		call capital
		
		mov ax, 4c00h
		int 21h
		
capital: and byte ptr [si], 11011111b
		 inc si
		 loop capital
		 ret
code ends
end start

运行结果

王爽 《汇编语言》 读书笔记 十 CALL和RET指令


还可以用栈来传递参数

10.12 寄存器冲突的问题

转换一个以0结尾的字符串 ,采用jcxz


capital: mov cl, [si]

    mov ch, 0

    jcxz ok

      and byte ptr [si], 11011111b

    inc si

    jmp short capital

ok: ret

将data段中的字符串转换为大写


assume cs:code
data segment
	db 'word', 0
	db 'unix', 0
	db 'wind', 0
	db 'good', 0
data ends

stack segment
	db 16 dup (0)
stack ends

code segment
start:		mov ax, data
			mov ds, ax
			mov bx, 0
			
			mov ax, stack
			mov ss, ax
			mov sp, 10h
			
			mov cx, 4
		s:	push cx	
			mov si, bx
			call capital
			add bx, 5
			pop cx
			loop s
			
			mov ax, 4c00h
			int 21h

capital: 	mov cl, [si]
			mov ch, 0
			jcxz ok
			and byte ptr [si], 11011111b
			inc si
			jmp short capital
		ok: ret
code ends

end start

运行结果

王爽 《汇编语言》 读书笔记 十 CALL和RET指令

子程序等开始: 子程序中使用的寄存器入栈

    子程序等内容

     子程序中使用的寄存器出栈

返回



改进后的capital

capital:	push cx
			push si


	change:	mov cl, [si]
			mov ch, 0
			jcxz ok
			and byte ptr [si], 11011111b
			inc si
			jmp short change
			
		ok:	pop si
			pop cx
			ret

实验10 编写子程序

1. 显示字符串

子程序描述

名称:show_str

功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串

参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79)

(cl)=颜色, ds:si指向字符串的首地址

返回:无

代码如下

assume cs:code
data segment
	db 'Welcome to masm!', 0
data ends

stack segment
	db 32 dup (0)
stack ends

code segment
start:		mov ax, stack
			mov ss, ax
			mov sp, 20h
			
			mov dh, 8
			mov dl, 3
			mov cl, 2
			mov ax, data
			mov ds, ax
			mov si, 0
			call show_str
			
			mov ax, 4c00h
			int 21h
			
show_str:	push ax
			push bx
			push cx
			push dx
			push bp
			push si
			push di
			push es
			
			mov ax, 0b800h	; load the first address to vram
			mov es, ax
			
			mov bp, 0		; reset bp to 0
			mov ax, 0		; reset al ah
			mov al, dh
			mov bl, 0a0h
			mul bl			; dh x 160 = line offset
			add bp, ax		; add the line offset
			
			mov ax, 0		; reset al ah
			mov al, dl
			mov bl, 2		
			mul bl			; dl x 2 = column offset
			add bp, ax		; add the column offset
			
			mov di, 0
			mov ah, cl		; store the color info
	change:	mov cl, [si]
			mov ch, 0
			jcxz ok			; check whether pointer to '\0'
			mov al, cl		; store the char
			mov es:[bp][di], ax ;
			inc si
			add di, 2
			jmp short change
					
		ok:	pop es
			pop di
			pop si
			pop bp
			pop dx
			pop cx
			pop bx
			pop ax
			ret
code ends

end start

运行结果

王爽 《汇编语言》 读书笔记 十 CALL和RET指令

2 。 解决除法溢出的问题

如果执行8位除法的结果大于8位则会除法溢出

例子

assume cs:code
code segment
start:	mov bh, 1
		mov ax, 10000
		div bh
		
		mov ax, 4c00h
		int 21h
code ends
end start

在debug中执行则提示div overflow

王爽 《汇编语言》 读书笔记 十 CALL和RET指令

同样16位除法也可能存在此问题。

mov ax, 1000h

mov dx, 1

mov bx, 1

div bx

设计子程序解决除法溢出的问题divdw

子程序描述

名称:divdw

功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。

参数:

(ax) = dword型数据的低16位

(bx) = dword型数据的高16位

(cx) = 除数

返回: (dx) = 结果的高16位,(ax)=结果的低16位 (cx) = 余数


代码

assume cs:code
stack segment
	db 16 dup (0)
stack ends
code segment
start:	mov ax, stack
		mov ss, ax
		mov sp, 10h
		
		mov ax, 4240h
		mov dx, 000fh
		mov cx, 0ah
		call divdw
		
		mov ax, 4c00h
		int 21h
		
divdw:	push bx

		push ax		; push the low 16bit
		mov ax, dx	; move the high 16bit to ax
		mov dx, 0	; set the high 16bit to zero
		div cx		; calc ax / cx
					; dx is mod, ax is result
		mov bx, ax	; store the result to bx
		pop ax		; pop up the low 16bit
		div cx		; calc the low 16bit div
		
		mov cx, dx	; store the mod
		mov dx, bx	; store the high 16bit result
		
		pop bx
		ret

code ends
end start


执行结果


王爽 《汇编语言》 读书笔记 十 CALL和RET指令

3.数值显示

将十进制数据在屏幕上显示出来

子程序名称:dtoc

功能:将word型数据转变为十进制数的字符串,字符串以0为结尾符。

参数:(ax)=word型数据

ds:si指向字符串的首地址

返回:无

编程将数据12666以十进制的形式显示在屏幕的第八行3列,用绿色显示出来。需要调用实验一的子程序 show_str

assume cs:code

data segment
	db 10 dup (0)
data ends

stack segment
	db 64 dup (0)
stack ends;

code segment
;main procedure
start:		mov ax, stack
			mov ss, ax
			mov sp, 20h

			mov ax, 12666
			mov bx, data
			mov ds, bx
			mov si, 0
			call dtoc
		
			mov dh, 8
			mov dl, 3
			mov cl, 2
			call show_str
		
			mov ax, 4c00h
			int 21h
			
;sub procedure dtoc	
dtoc:		push si
			push di
			push ax
			push bx
			push cx
			push dx

			mov di, si		; store the start of the string.
			mov bx, 10
	dtloop:	mov dx, 0
			div bx
			add dx, 30h		; conver to ascii
			push dx
			inc si
			mov cx, ax		; if result = 0 exit the loop
			jcxz dtocOK
			jmp short dtloop
		
	dtocOK:	mov cx, si
			mov si, 0
	
	revert: pop ax
			mov ds:[si], al
			inc si
			loop revert
			
			pop dx
			pop cx
			pop bx
			pop ax
			pop di
			pop si
			ret
			
;sub procedure show_str			
show_str:	push ax
			push bx
			push cx
			push dx
			push bp
			push si
			push di
			push es
			
			mov ax, 0b800h	; load the first address to vram
			mov es, ax
			
			mov bp, 0		; reset bp to 0
			mov ax, 0		; reset al ah
			mov al, dh
			mov bl, 0a0h
			mul bl			; dh x 160 = line offset
			add bp, ax		; add the line offset
			
			mov ax, 0		; reset al ah
			mov al, dl
			mov bl, 2		
			mul bl			; dl x 2 = column offset
			add bp, ax		; add the column offset
			
			mov di, 0
			mov ah, cl		; store the color info
	change:	mov cl, [si]
			mov ch, 0
			jcxz ok			; check whether pointer to '\0'
			mov al, cl		; store the char
			mov es:[bp][di], ax ;
			inc si
			add di, 2
			jmp short change
					
		ok:	pop es
			pop di
			pop si
			pop bp
			pop dx
			pop cx
			pop bx
			pop ax
			ret
code ends
end start

运行结果


王爽 《汇编语言》 读书笔记 十 CALL和RET指令

课程设计1

将Power iDea公司的数据按照 10.2 所示的格式在屏幕上显示出来


assume cs:code, ds:data, ss:stack
table segment
	;offset A = 0000H
	db '1975', '1976', '1977', '1978', '1979', '1980', '1981', '1982', '1983'
	db '1984', '1985', '1986', '1987', '1988', '1989', '1990', '1991', '1992'
	db '1993', '1994', '1995'
	; the above data present the 21 x4 years
	
	;offset A + 21x4(54H)
	dd	16, 22, 382, 1356, 2390, 8000, 16000, 24486, 50065, 97479, 140417, 197514
	dd  345980, 590827, 803530, 1183000, 1843000, 2759000, 3753000, 4649000, 5937000
	; the above data present the 21 dword for the income
	
	;offset A + (54H) + (54H) = A8H
	dw  3, 7, 9, 13, 28, 38, 130, 220, 476, 778, 1001, 1441, 2258, 2793, 4037, 5635, 8226
	dw  11542, 14430, 15257, 17800
	; the above data present the number of employeer for the 21years
	;offset A + A8H + 2A = D2
table ends
 

data segment
	;     01234567890         01234567890
	;  '  year      123456789 X         xxx  
	db '  year      123456789 X         xxxxx', 0		; buff of the string 37chars with 1 '\0'
data ends

stack segment
	db 128 dup (0)
stack ends

code segment
;*****************************************************************************
; main procedure start
;*****************************************************************************	
start:	mov ax, data
		mov ds, ax		; set ds to data
		
		mov ax, table
		mov es, ax		; set es to table
		
		mov ax, stack
		mov ss, ax
		mov sp, 80h		; set ss and sp 
		
		mov bx, 0		; offset for data
		mov di, 0		; the word offset of data segment
		mov bp, 3		; point to the line.
		mov cx, 21	
	
		call cls		; clean the screen
		
	s:	push cx			; main loop for 21times
	
		mov cx, 37		; clean up the buffer 
		mov si, 0
		mov al, ' '
		call memset8
	
		mov si, 2		; copy the year data to buffer
		mov ax, es:0[bx]
		mov ds:0[si], ax
		mov ax, es:2[bx]
		mov ds:2[si], ax
		
		mov ax, es:54h[bx]	; convert the incoming data to ascii
		mov dx, es:56h[bx]
		mov si, 0ch			; si point to incoming buffer
		call dtoc			; convert digit to char
		
		mov ax, es:0a8h[di] ; mov the number of employeer
		mov dx, 0
		mov si, 16h			; si point to the No. of employeer buffer
		call dtoc
		

		mov ax, es:54h[bx]	; mov the low 16bit of incoming
		mov dx, es:56h[bx]	; mov the high 16bit of incoming
		div word ptr es:0a8h[di]	;div the number of employeer
		mov dx, 0
		mov si, 20h
		call dtoc
	
		mov dx, bp			; the bp is 16bit but we only use 8bit data.
		mov dh, dl			; so just copy bp to dx and copy dl to dh.
		mov dl, 0			; point the column 2
		mov cl, 7			; 0 000 0 111B = 7 set the bkcolor(black) color(white)
		mov si, 0			; point to the start of the string
		call show_str
	
		inc bp
		add bx, 4
		add di, 2
		pop cx
		loop s
		
		mov ax, 4C00H
		int 21H
;*****************************************************************************
; sub procedure dtoc
;*****************************************************************************	
;sub procedure dtoc	
dtoc:		push si
			push di
			push ax
			push cx
			push dx

			mov di, si		; store the start of the string.
	dtloop:	mov cx, 10
			call divdw
			add cx, 30h		; convert mod to ascii
			push cx
			inc si
			mov cx, ax		; if result(ax == 0 && dx == 0) exit the loop
			or cx, dx
			jcxz dtocOK
			jmp short dtloop

	dtocOK:	mov cx, si
			sub cx, di		; calc the loop count = si - di
			mov si, di		; point to the start of the string buff
	
	revert: pop ax
			mov ds:[si], al
			inc si
			loop revert
			
			pop dx
			pop cx
			pop ax
			pop di
			pop si
			ret
			
;*****************************************************************************
; sub procedure divdw
; effect ax, dx, cx
;*****************************************************************************	
divdw:	push bx

		push ax		; push the low 16bit
		mov ax, dx	; move the high 16bit to ax
		mov dx, 0	; set the high 16bit to zero
		div cx		; calc ax / cx
					; dx is mod, ax is result
		mov bx, ax	; store the result to bx
		pop ax		; pop up the low 16bit
		div cx		; calc the low 16bit div
		
		mov cx, dx	; store the mod
		mov dx, bx	; store the high 16bit result
		
		pop bx
		ret
;*****************************************************************************
; sub memset8
; di:si point to the start of the memory
; cx is the length of the memory
; al is the value of the memory
;*****************************************************************************	
memset8:			push si
				push ax
				push cx
				
				jcxz memret8		; if the cx is equal to zero just return
	setvalue8:	mov ds:[si], al
				inc si
				loop setvalue8
		
	memret8:	pop cx
				pop ax
				pop si
				ret
				
;*****************************************************************************
; sub memset16
; di:si point to the start of the memory
; cx is the length of the memory
; ax is the value of the memory
;*****************************************************************************	
memset16:		push si
				push ax
				push cx
				
				jcxz memret16		; if the cx is equal to zero just return
	setvalue16:	mov ds:[si], ax
				add si, 2
				loop setvalue16
		
	memret16:	pop cx
				pop ax
				pop si
				ret

;*****************************************************************************
; sub show_str
;*****************************************************************************	
show_str:	push ax
			push bx
			push cx
			push dx
			push bp
			push si
			push di
			push es
			
			mov ax, 0b800h	; load the first address to vram
			mov es, ax
			
			mov bp, 0		; reset bp to 0
			mov ax, 0		; reset al ah
			mov al, dh
			mov bl, 0a0h
			mul bl			; dh x 160 = line offset
			add bp, ax		; add the line offset
			
			mov ax, 0		; reset al ah
			mov al, dl
			mov bl, 2		
			mul bl			; dl x 2 = column offset
			add bp, ax		; add the column offset
			
			mov di, 0
			mov ah, cl		; store the color info
	change:	mov cl, [si]
			mov ch, 0
			jcxz ok			; check whether pointer to '\0'
			mov al, cl		; store the char
			mov es:[bp][di], ax ;
			inc si
			add di, 2
			jmp short change
					
		ok:	pop es
			pop di
			pop si
			pop bp
			pop dx
			pop cx
			pop bx
			pop ax
			ret

;*****************************************************************************
; sub cls.
; clean the screen
;*****************************************************************************				
cls:	push ds				; clean the screen
		push ax
		push cx
		push si
		
		mov cx, 690h
		mov ax, 0b800h
		mov ds, ax
		mov al, ' '
		mov ah, 7
		mov si, 0
		call memset16
		
		pop si
		pop cx
		pop ax
		pop ds
		ret
code ends

end start

运行结果:

王爽 《汇编语言》 读书笔记 十 CALL和RET指令