实验三 中断和子程序设计
一. 实验目的
1、掌握子程序、宏和中断程序的设计方法;
2、熟悉在PC机上建立、汇编、连接、调试和运行8086/8088汇编语言程序的过程。
二. 实验内容
1. 编写一个子程序计算z=f(x,y)=x/y+x-y(x,y,z有符号数内存字数)。要求通过堆栈传送输入参数,输出参数通过AX传递。(要求输入输出实现,且有提示)。
2. 编写一个宏,求三个数的最大数,原型为:MAX3 x,y,z,max,最大值要求输出。
3. 挂接1CH中断,正计时90秒后退出。要求屏幕显示0-89的秒数。
三. 实验过程和程序
(实验全为设置手动输入参数和变量)
1、
stack segment stack
dw 512 dup(?)
stack ends
data segment
msg db 'Input x,y:','$'
msg1 db 0dh,0ah,'Then x/y + x-y = ','$'
blank db ' ','$'
x dw 10
y dw 2
z dw ?
data ends
code segment
assume cs:code,ds:data,ss:stack
start: mov ax,data
mov ds,ax
mov dx ,offset msg
mov ah,09h
int 21h
mov ah,01h
int 21h
xor ah,ah
sub al,30h
mov x,ax;输入的值赋值给x
mov dx ,offset blank;打印空格
mov ah ,09h
int 21h
xor dx,dx
mov ah,01h
int 21h
xor ah,ah
sub al,30h
mov y,ax;输入的值赋值给y
push x ;入栈参数x,y
push y
call leechoy ;调用子程序leechoy
pop x
pop y
mov bx,ax
mov z,ax
mov dx ,offset msg1
mov ah,09h
int 21h
mov dx,bx
add dl,30h
mov ah,02h
int 21h
mov ah,4ch
int 21h
leechoy proc near ;采用段间调用,只入栈IP
push bp
mov bp ,sp
push bx
push cx
push dx
mov ax,[bp+6] ;x/y
mov bx,[bp+4]
idiv bx
mov bx,ax
mov cx,dx
mov ax,[bp+6]
cwd
add ax,bx ;x/y+x
adc dx,cx
mov bx,[bp+4] ;x/y +x -y
sub ax,bx ;最终保存输出参数ax
sbb dx,0
pop dx
pop cx
pop bx
pop bp
ret 4
code ends
end start
2、
max3 macro x,y,z,max
mov ax,x
cmp ax,y
jbe compare
cmp ax,z
ja outdata
compare:
mov ax,y
cmp ax,z
jae outdata
mov ax,z
outdata:
mov max,ax
mov dx,offset msg1
mov ah,09h
int 21h
xor dx,dx
mov dx,max
mov ah,02h
int 21h
endm
stack segment stack
dw 512 dup(?)
stack ends
data segment
x dw ?
y dw ?
z dw ?
max dw ?
msg db 'Input three number:','$'
msg1 db 0dh,0ah,'The biggest one is :','$'
blank db ' ','$'
data ends
code segment
assume cs:code,ds:data,ss:stack
start: mov ax,data
mov ds,ax
mov dx,offset msg ;打印输入提示
mov ah,09h
int 21h
mov ah,01h ;输入三个数x,y,z
int 21h
xor ah,ah ;由于x定义为字,且输入的为al为字符所以把ah置零
mov x,ax ;赋值给x
mov dx,offset blank
mov ah, 09h
int 21h
mov ah,01h
int 21h
xor ah,ah
mov y,ax
mov dx,offset blank ;显示空格分开输入的y,z
mov ah,09h
int 21h
mov ah,01h
int 21h
xor ah,ah
mov z,ax
max3 x,y,z,max;调用
mov ah,4ch
int 21h
code ends
end start
3、
stack segment stack
dw 512 dup(?)
stack ends
data segment
oldisr dw ?,?
calculat db 0
count db 0
downline db 0dh,0ah,"$"
data ends
code segment
assume cs:code,ds:data,ss:stack
start: mov ax,data
mov ds,ax
mov ax,0 ;使置0,这里不能用xor
mov es,ax
mov ax,es:[1ch*4] ;保存原中断向量
mov oldisr[0],ax
mov ax,es:[1ch*4+2]
mov oldisr[2],ax
mov word ptr es:[1ch*4],offset isr
mov word ptr es:[1ch*4+2],seg isr
;-------- 系统执行新的中断处理
again:cmp calculat,89
ja exit ;不跳出则一直处于新的中断状态,执行isr程序段和保存恢复之间的这三条语句
jmp again
exit:
mov ax,oldisr[0] ;恢复原中断向量
mov es:[1ch*4],ax
mov ax,oldisr[2]
mov es:[1ch*4+2],ax
mov ah,4ch
int 21h
isr proc far;由系统进行调用
push ax;保护现场
push bx
push cx
push dx
sti ;开放可屏蔽中断
inc count ;处理中断
cmp count ,1000/55 ;一秒为1000毫秒,55毫秒发送一次中断请求即一秒内发生18.2次中断,利用count和18.2比较可以确定时间为1秒
jb outdoor
mov count ,0
mov al,calculat
xor ah,ah ;ah置0
mov bh,0ah;除10将十位和各位分开
div bh
mov dh ,ah ;输出两位数0-89
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov dl,dh
add dl,30h
mov ah,02h
int 21h
mov dx,offset downline
mov ah,09h
int 21h
inc calculat
outdoor:
pushf
call dword ptr oldisr ;调用原中断处理
cli ;关闭中断
pop dx;恢复现场
pop cx
pop bx
pop ax
iret;返回调用程序
isr endp
code ends
end start
四. 实验结果(包括必要的截图)
1、
2、
3、
五. 实验体会
(1)、加强了对子程序的认识,对子程序压栈传参数栈中的变化更加深刻,far段间压栈也系统压入 cs,ip,段内near则默认传cs,在传参取相应的栈中的数据时,要注意数据地址和sp的相对位置,压栈入栈只针对r16和mem16,压栈sp自动减2.可将bp压栈并将sp赋值给bp,利用[bp+x]获取相应的栈内的数据的值,最后程序需要保护现场,顺序压栈,逆序出栈.平衡堆栈,可以利用,子程序中ret相应的压入数据的字节数总和,或者主程序call后add,sp,相应的压入数据的字节数总和 ,对于第一题压入两个参数,x,y共计4个字节,平衡堆栈,除子程序ret 4 外 还可以子程序ret主程序中add,sp,4。
(2)、宏汇编,最开始不知道,宏的位置放哪里,错了n次,记在书上,刚好那页没看到。宏的位置其实,可以直接放整个汇编完整格式的最前面。宏和子程序的最大的区别是,宏调用n次则内存赋值n次,内存开销大,而子程序不复制。宏试用于内存开销小的程序。
(3)保存原中断向量-》设置新中断向量-》执行新中断处理-》调用原中断处理-》恢复原中断向量。中断服务子程序中,sti开始中断,cli关闭中断,和子程序不同的是,中断返回利用iret而非ret,并且中断为系统调用执行,新的中断向量写在,保存和恢复原中断向量之间即可。不恢复原中断向量,则一直处于新中断状态,运行isr程序段,实现计时器功能