实验 13 编写、应用中断例程
一、编写并安装“int 7ch”指令所引发中断的中断例程,功能为显示一个用 0 结束的字符串,中断例程安装在内存地址 0:200 处。
参数:(dh)=行号,(di)=列号,(cl)=颜色,ds:si 指向字符串首地址。
以上中断例程安装成功后,对下面的程序进行单步跟踪,尤其注意观察 int、iret 指令执行前后 CS、IP 和栈的状态。
注意,“ds:si 指向字符串首地址”是作为参数向“int 7ch”指令所引发中断的中断例程提供的,可见该字符串并不在该中断例程程序代码之中,而是位于主调程序内。
assume cs:code
data segment
db "welcome to masm!",0
data ends
code segment
start: mov dh,10
mov dl,10
mov cl,2
mov ax,data
mov ds,ax
mov si,0
int 7ch
mov ax,4c00h
int 21h
code ends
end start
具体代码如下:
1. 将要显示的字符和字符属性信息存储在 CX 寄存器中,再传递到显示缓冲区进行显示输出。
本程序与下一个程序的主要区别在于 dis 部分所使用的寄存器不同:本程序用 CX 寄存器,而下一个程序用 AX 寄存器。
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset show
mov ax,0
mov es,ax
mov di,200h
mov cx,offset showend-offset show
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
show: mov ax,0B800h
mov es,ax
dec dh
mov al,80*2
mul dh
mov di,ax
dec dl
mov al,2
mul dl
add di,ax
mov ch,cl
dis: mov cl,[si]
cmp cl,0
je showrep
mov es:[di],cx
inc si
add di,2
jmp short dis
showrep: iret
showend: nop
code ends
end start
2. 将要显示的字符和字符属性信息存储在 AX 寄存器中,再传递到显示缓冲区进行显示输出。
本程序与上一个程序的主要区别在于 dis 部分所使用的寄存器不同:上一个程序用 CX 寄存器,而本程序
用 AX 寄存器。
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset show
mov ax,0
mov es,ax
mov di,200h
mov cx,offset showend-offset show
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
show: mov ax,0B800h
mov es,ax
dec dh
mov al,80*2
mul dh
mov di,ax
dec dl
mov al,2
mul dl
add di,ax
dis: mov al,[si]
cmp al,0
je showrep
mov ah,cl
mov es:[di],ax
inc si
add di,2
jmp short dis
showrep: iret
showend: nop
code ends
end start
3. 通过调用 BIOS 的 int 10h 指令中断例程的 2 号和 9 号子程序来显示字符串。
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset show
mov ax,0
mov es,ax
mov di,200h
mov cx,offset showend-offset show
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
show: push cx ; 由于下面要用到 CX 寄存器,所以需将 CL 寄存器存储的字符属性信息数据入栈
mov bp,sp ; 为下面调用原 CL 寄存器存储的字符属性数据而作的准备,这时 ss:[bp]=(cl)
; 设置 BIOS 的 int 10h 指令中断例程的 2 号子程序参数;注意,行、列数据已经作为参数存储在 DX 寄存器中
dis: mov ah,2 ; 设置 BIOS 的 int 10h 指令中断例程子程序为 2 号:设置光标位置
mov bh,0 ; 设置显示缓冲区的第 0 页
int 10h ; 调用 BIOS 的 int 10h 指令以引发其中断,从而调用其中断例程的 2 号子程序
; 设置 BIOS 的 int 10h 指令中断例程的 9 号子程序参数
mov al,[si] ; 将要显示的字符传送到 AL 寄存器
cmp al,0 ; 验证 AL 寄存器所存储的字符 ASCII 码是否为 0 —— 是否到达字符串的结束位置
je showrep ; 执行判断字符码
mov ah,9 ; 设置 BIOS 的 int 10h 指令中断例程的子程序为 9 号:在光标位置显示字符
mov bl,[bp] ; 读取字符属性信息到 BL 寄存器
mov cx,1 ; 字符显示的重复次数为 1 次
int 10h ; 调用 BIOS 的 int 10h 指令以引发其中断,从而其中断例程的 9 号子程序
inc si ; 将 ds:[si] 指向要显示字符串的下一个字符
inc dl ; 增加列号,以便让下一个字符显示在屏幕的下一列位置
jmp short dis
showrep: pop cx
iret
showend: nop
code ends
end start
4. 通过结合调用 BIOS 的 int 10h 指令中断例程的 2 号子程序和 DOS 的 int 21h 指令中断例程的 9 号子程序来显示字符串。
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset show
mov ax,0
mov es,ax
mov di,200h
mov cx,offset showend-offset show
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
show: cmp byte ptr [si],0 ; 将字符串结束符由 0 值改为字符 '$',以适应 DOS 的 int 21h 指令中断例程的 9 号子程序的要求
je ok
inc si
jmp short show
ok: mov byte ptr [si],'$'
; 设置 BIOS 的 int 10h 指令中断例程的 2 号子程序参数;注意,行、列数据已经作为参数存储在 DX 寄存器中
mov ah,2 ; 设置 BIOS 的 int 10h 指令中断例程子程序为 2 号:设置光标位置
mov bh,0 ; 设置显示缓冲区的第 0 页
int 10h ; 调用 BIOS 的 int 10h 指令以引发其中断,从而调用其中断例程的 2 号子程序
; 设置 DOS 的 int 21h 指令中断例程的 9 号子程序参数
mov dx,0 ; 设置 ds:dx 指向字符串首地址。注意,DS 段寄存器存储的值已经以参数形式提供
mov ah,9 ; 设置 DOS 的 int 21h 指令中断例程子程序为 9 号:在光标位置显示字符串
int 21h ; 调用 DOS 的 int 21h 指令以引发其中断,从而调用其中断例程的 9 号子程序
iret
showend: nop
code ends
end start
二、编写并安装“int 7ch”指令所引发中断的中断例程,功能为完成 loop 指令的功能。
参数:(cx)=循环次数,(bx)=位移。
以上中断例程安装成功后,对下面的程序进行单步跟踪,尤其注意观察 int、iret 指令执行前后 CS、IP 和栈的状态。
在屏幕中间显示 80 个“!”字符。
assume cs:code
code segment
start: mov ax,0B800h
mov es,ax
mov di,160*12
mov bx,offset s-offset se ; 设置从标号 se 到标号 s 的转移位移
mov cx,80
s: mov byte ptr es:[di],'!'
add di,2
int 7ch ; 如果 (cx)≠0,转移到标号 s 处
se: nop
mov ax,4c00h
int 21h
code ends
end start
具体代码如下:
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset lp
mov ax,0
mov es,ax
mov di,200h
mov cx,offset lpend-offset lp
cld
rep movsb
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
lp: push bp
mov bp,sp
dec cx
jcxz lpret
add [bp+2],bx
lpret: pop bp
iret
lpend: nop
code ends
end start
三、下面的程序,分别在屏幕的第 2、4、6、8 行显示 4 句英文诗,补全程序。
assume cs:code
code segment
s1: db 'Good,better,best,','$'
s2: db 'Never let it rest,','$'
s3: db 'Till good is better,','$'
s4: db 'And better,best.','$'
s: dw offset s1,offset s2,offset s3,offset s4
row: db 2,4,6,8
start: mov ax,cs
mov ds,ax
mov bx,offset s
mov si,offset row
mov cx,4
ok: mov bh,0
mov dh,___________
mov dl,0
mov ah,2
int 10h
mov dx,___________
mov ah,9
int 21h
______________
______________
loop ok
mov ax,4c00h
int 21h
code ends
end start
完成后编译运行,体会其中的编程思想。
补全程序后的完整代码如下(其中注释中含下划线的行即是需补全的代码行):
1. 功能:分别在屏幕的第 2、4、6、8 行显示 4 句英文诗。
2. 说明:通过结合调用 BIOS 的 int 10h 指令中断例程的 2 号子程序和 DOS 的 int 21h 指令中断例程的 9 号子程序来显示指定的字符串。
assume cs:code
code segment
s1: db 'Good,better,best,','$'
s2: db 'Never let it rest,','$'
s3: db 'Till good is better,','$'
s4: db 'And better,best.','$'
s: dw offset s1,offset s2,offset s3,offset s4
row: db 2,4,6,8
start: mov ax,cs
mov ds,ax
mov bx,offset s ; BX 寄存器存储着 s 标号的偏移地址,因此 [bx]=offset s1,[bx+2]=offset s2,[bx+3]=offset s3,[bx+4]=offset s4
mov si,offset row ; SI 寄存器存储着 row 标号的偏移地址,因此 [si]=2,[si+1]=4,[si+2]=6,[si+3]=8
mov cx,4 ; 设置 loop ok 的循环次数:因为要输出 4 个字符串,所以设置 (cx)=4
; 调用 BIOS 的 int 10h 指令中断例程的 2 号子程序,以定位光标位置
ok: mov bh,0 ; 设置光标位置位于显示缓冲区的第 0 页
mov dh,[si] ; _____设置光标位置的行号
mov dl,0 ; 设置光标位置位于第 0 列
mov ah,2 ; 设置 BIOS 的 int 10h 指令中断例程子程序号参数:调用 2 号子程序
int 10h
; 调用 DOS 的 int 21h 指令中断例程的 9 号子程序,以输出字符串
mov dx,[bx] ; _____设置 ds:dx 指向的字符串首地址
mov ah,9; 设置 DOS 的 int 21h 指令中断例程子程序号参数:调用 9 号子程序
int 21h
inc si ;______
add bx,2 ;______
loop ok
mov ax,4c00h
int 21h
code ends
end start