您的位置:首页 > 其它

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

2017-09-09 23:31 399 查看
第十章 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


运行结果



还可以用栈来传递参数

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


运行结果



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

    子程序等内容

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

返回

改进后的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


运行结果



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



同样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

执行结果



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

运行结果



课程设计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

运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  汇编语言