中断程序设计
1. 课程说明
本课程基于《汇编语言(第 2 版)》郑晓薇 编著,机械工业出版社,可以配合该教材使用。本课程由郑晓薇授权制作,提取教材中的实例以及实验内容,可以在实验楼环境中完成所有实例及实验。实验课程制作符合教材原版实例驱动教学以及实验训练贯穿始终的特点。
本教材可在 当当网 购买(点击链接即可进入购买页面)。
本课程实验列表中的最后模块为配套教材的习题参考答案,可供读者学习参考。
2. 实验环境
DOS 环境:
实验环境中安装有 dosemu
可以模拟 DOS 环境,并提供 DEBUG
、MASM
、LINK
等汇编语言开发程序。
3. 定制自己的中断
本例子为教材的示例 9-1。
**关注点
**:自制中断与系统中断
本节通过一个例子我们一起经历中断子程序的编写,中断的设置,中断触发、处理和返回的过程。
在中断类型中选取系统没有占用的 60H 号作为我们自己的中断类型,编写中断处理子程序,希望计算机在执行 INT 60H
指令时,能够调用我们编写的中断程序。
设计目标
设计一个笑脸中断 INT 60H。在应用程序中执行中断指令 INT 60H
时触发该中断。
设计思路
(1)如果触发该中断,在屏幕上显示一串笑脸;
(2)选择 60H 号中断类型作为笑脸中断类型;
(3)编写中断子程序 SMILE_FACE,显示一串笑脸;
(4)将该中断子程序的入口地址写入中断向量表中;
(5)编写应用程序,触发 60H 号中断;
(6)中断结束后,返回应用程序继续执行;
(7)把中断子程序驻留在内存中。
3.1 软件中断子程序的编写
编写软件中断处理子程序与编写子程序有相同的地方,也有的不同之处,要多加注意。
步骤如下:
- (1)保护现场;
- (2)STI 开中断指令;如允许中断嵌套,则开中断
- (3)处理中断;
- (4)CLI 关中断指令;
- (5)恢复现场;
- (6)IRET 指令,返回被中断的程序。
由于从应用程序进入中断子程序时,IF 和 TF 标志都已经被清除,CPU 在中断子程序执行中就不再响应其它外设发出的硬件中断请求。但是如果这个中断子程序的优先级不高,或者在执行时允许被打断;那么在设计中断处理子程序时,就要先开放中断标志,让 CPU 能够响应其它的硬件中断请求,之后再回到本程序继续处理,这样可使系统的执行效率更高。开中断指令可放在程序的任意位置,当然越早开放越好。
在恢复现场和返回之前,要关中断。以免恰在此时有中断发生就会破坏现场,造成无可挽回的灾难。
中断返回指令 IRET 和子程序返回指令 RET 的作用都是返回原调用程序,但是在弹出断点地址之后,IRET 还要弹出保存的标志寄存器的值。
3.2 中断的设置
由于我们选择的中断类型是 60H,那么就应该在中断向量表中把 60H 号的中断向量放入。
如果想要保留此中断,还要把中断子程序驻留在内存。有几个要考虑的问题:中断向量,中断子程序长度,存放的位置。这些用 DOS 中断 INT 21H
的功能调用解决。
设置中断向量
将 DS:DX
中的中断向量写入中断向量表中。
格式:
AH=25H
AL=中断类型号
DS:DX=中断向量
INT 21H
先来看看如何获得中断向量。所谓中断向量就是中断子程序第 1 条指令的地址,该地址可以由标号指出,也可以用子程序名表示。
将中断子程序名的段地址和偏移地址分别保存到 DS 和 DX 中,再用 DOS 中断 INT 21H
的 25H 号功能,把中断向量写入中断向量表中。
取中断向量
从中断向量表中取出中断向量放入 ES:BX
中。
格式:
AH=35H
AL=中断类型号
INT 21H
在写入中断向量之前,应该考虑到所采用的中断类型是否已经被系统或其它程序占用?如果与其有冲突,应将原有类型号的中断向量取出保存起来,等中断调用结束之后再把它恢复。这个功能可以用 INT 21H
的 35H
号功能方便地实现。
中断驻留
中断驻留是一种特殊的退出程序功能,它在退出前保留程序占用的内存,使这些内存单元不被其它程序覆盖或占用。利用这个功能,可以将自定义的中断程序驻留内存,让其它程序也可以用 INT n
指令调用我们自定义的中断。
格式:
AH=31H
AL=0
DX=驻留程序长度
INT 21H
AL=0 表示返回码。
驻留程序长度如何获取呢?在中断子程序设计中可以在开始处加上标号,在程序最后用一个有标号的 NOP(无操作)指令作为结尾,两个标号相减即可得到整个程序的长度(字节数)。将此长度再加上 16 作为驻留程序长度送入 DX,用 INT 21H
的 31H 号就可把中断子程序驻留到内存当中。至于程序驻留在哪段存储区中是由操作系统决定的。使用中断驻留方式退出程序,就不必再用 INT 21H
的 4CH 号中断功能退出程序、返回 DOS 了。
3.3 软件中断的触发与处理
软件中断设置好之后,就可以在应用程序中用 INT n
指令调用了。当 CPU 执行到 INT n 指令时,立即触发中断;并根据中断类型号 n 在中断向量表中取出中断子程序的入口地址,然后进入中断处理子程序执行。中断子程序处理结束后,用 IRET 指令返回到应用程序的断点处继续执行。
上述几个步骤都可以在主程序中编写,中断子程序 SMILE_FACE
可以跟在主程序之后。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-1.asm 笑脸中断程序INT 60H,采用驻留。
.model small
.stack
.code
mess1 db 0ah,0dh,'enter interrupt!',0ah,0dh,'$'
mess2 db 'exit interrupt!$'
mess3 db 0ah,0dh,'Continue or Quit(c/q)?$'
;主程序
main proc far
start:
mov ax,@code
mov ds,ax
;设置新的中断向量
mov dx,offset smile_face ;获得中断子程序偏移地址
mov ax,seg smile_face ;获得中断子程序段地址
mov ds,ax
mov al,60h ;将现在的60H号
mov ah,25h ;中断向量放入中断向量表中
int 21h
;应用部分
conti:
mov dx,offset mess1 ;显示提示1
mov ah,9
int 21h
int 60h ;触发60H号中断,进入中断子程序执行
mov dx,offset mess2 ;显示提示2
mov ah,9
int 21h
mov dx,offset mess3 ;显示提示3
mov ah,9
int 21h
mov ah,1 ;键入选择(c/q)
int 21h
cmp al,'c'
je conti
;选择q,将中断子程序驻留内存后退出
mov al,0
mov ah,31h ;驻留功能
mov dx,smiend-smigin+16 ;中断子程序长度
int 21h
main endp ;主程序结束
;中断子程序
smile_face proc far
smigin:
sti ;开中断
mov cx,10
leng:
mov dl,01h ;笑脸符号
mov ah,2
int 21h
loop leng
mov dl,0dh ;回车换行
int 21h
mov dl,0ah
int 21h
cli ;关中断
iret ;中断返回
smiend:nop
smile_face endp
end start
(2)在 dos 子目录下保存为 9-1.asm,经过汇编 masm 9-1.asm
,连接 link 9-1.obj
,生成 9-1.exe。
D:\dos〉masm 9-1.asm
D:\dos〉link 9-1.obj
D:\dos〉9-1.exe
(3)运行结果:
**分析
**:中断子程序 smile_face 虽然写在主程序的后面,但是只有在触发时才被执行。如果应用程序中没有相应的中断指令,则它永远不会被执行。9-1.exe 这个程序运行之后,如果其它程序中也有 INT 60H
中断调用指令,执行后就可以看到一串笑脸。这说明笑脸中断已经驻留在内存中,并且在中断向量表中占有一席之地了。
例如在上一章示例 8-1 中加入 INT 60H
指令,会看到如下笑脸:
4. 对除 0 中断的修改
本例子为教材的示例 9-2。
在系统提供的内中断中,0 号中断是当运算过程中发生了除 0 错误或者商超出了允许的范围时触发的中断。该中断处理程序的功能为显示 “Divide overflow” 提示信息然后结束程序返回 DOS。0 号中断除了在除法出错时触发之外,还可以用 INT 0
指令触发。对这种仅做简单处理的系统中断,我们可以编写一个中断处理子程序替代它。在替代之前,将原中断向量保存起来,应用程序结束时,必须恢复原有的中断向量,以确保系统不受破坏。要知道对系统中断的修改稍不小心就可能对内存中重要的系统数据或程序造成破坏,产生不可预知的后果。
系统提供的中断处理程序在开机启动时已经调入内存了,如果让系统放弃它转而采用我们编写的中断子程序,就要慎重考虑各种情况。上一节介绍了用 DOS 中断的功能调用设置中断向量、驻留中断子程序,我们不必考虑中断子程序在哪儿存放,是如何存放的,这些都是操作系统完成的。这种方法简单、安全。
为了深入了解系统工作原理,本节我们尝试绕过操作系统、直接对中断向量表和内存进行读写,以体验对硬件编程的感受。
编写一个 0 号中断子程序 SHOWERR,如果 0 号中断触发则在屏幕的 22 行 24 列上显示彩色的提示信息“Attention! error…”,然后返回主程序。它的实现需要如下步骤:
(1)在主程序中要先设定中断子程序 SHOWERR 的中断向量,此处我们不用 INT 21H 的 25H 号设置中断向量,而是采用直接将中断子程序 SHOWERR 的入口地址写入中断向量表的方法,实现对系统内存的改写。
(2)为了安全起见,先用 INT 21H 的 35H 号功能将原来的 0 号中断向量保存起来,在主程序结束时再将其恢复到中断向量表中。
(3)这个例子仅仅作为一种简单的体验,因此也不把中断子程序 SHOWERR 驻留在内存中,让它只在本应用程序有效,退回 DOS 后又恢复为系统原来的 0 号中断处理程序。如果以后想做更多的处理,可在这个程序的基础上修改,加入更多功能,并将其驻留在内存中且不要恢复原有的中断向量。只要不关机,这个新的中断子程序就会一直有效。
(4)如果在中断子程序中要显示字符串,那么数据定义伪指令 DB 要写在子程序中,便于中断子程序驻留内存时将字符串一起驻留。
- **
关注点
**:直接对系统的中断向量表进行读写 - **
关注点
**:中断驻留与中断触发
设计目标
编写和设置 0 号中断子程序。触发 0 号中断后,在屏幕的 22 行 24 列上显示彩色的提示信息 “Attention! error…”。
设计思路
(1)主程序中用 INT 21H 的 35H 号功能调用将原来的 0 号中断向量取出并保存;
(2)用程序自定义设置新的 0 号中断向量;
(3)应用程序显示提示信息后,先用 INT 0 指令触发中断,再用除法指令除 0 出错时触发中断;
(4)在主程序结束之前,用 INT 21H 的 25H 号功能调用恢复原来的 0 号中断向量;
(5)中断子程序 showerr 采用直接写显存方法显示彩色字符串。字符串在中断子程序代码段中定义。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-2.asm 自定义设置中断向量(25h),0号中断程序INT 0,在屏幕上显示彩色提示。
.model small
.stack
.code
mess1 db 0ah,0dh,'enter interrupt!',0ah,0dh,'$'
mess2 db 0ah,0dh,'exit interrupt!$'
mess3 db 0ah,0dh,'Continue or Quit(c/q)?$'
;主程序
main proc far
start:
mov ax,@code (@和c之间不要有空格!书上印刷有误)
mov ds,ax
;中断设置
;取出原中断向量
mov al,0
mov ah,35h ;取出原来的0号中断向量
int 21h ;放在ES:BX中
push es ;入栈保存ES:BX中的向量
push bx
push ds ;将当前ds入栈保存
;设置新的中断向量 ;相当于25h号功能
mov dx,offset showerr ;获得中断子程序偏移地址
mov ax,seg showerr ;获得中断子程序段地址
mov ds,ax ;改变ds值
mov ax,0
mov es,ax ;0段,中断向量表
mov bx,0 ;现在的0号
mov cl,2
shl bx,cl ;×4
mov word ptr es:[bx],dx ;中断向量放入中断向量表中
mov word ptr es:[bx+2],ds
;应用部分
conti:
mov dx,offset mess1 ;显示提示1
mov ah,9
int 21h
int 0 ;触发0号中断
mov dx,offset mess2 ;显示提示2
mov ah,9
int 21h
mov ax,15
mov bl,0
idiv bl ;除0,触发0号中断
mov dx,offset mess3 ;显示提示3
mov ah,9
int 21h
mov ah,1 ;输入选择
int 21h
cmp al,'c'
je conti
quit:
pop ds ;弹出保存的数据
pop bx
pop es
mov al,0 ;恢复原来的0号
mov ah,25h ;中断向量放入中断向量表中
int 21h
mov ax,4c00h ;返回DOS
int 21h
main endp
;中断子程序
showerr proc near
showbegin:jmp short show_str ;跳过数据定义
a1 db 'Attention! error...' ;显示信息与子程序放在一起,
a2 db 0 ;便于以后驻留
show_str:
mov ax,@code ;数据段与代码段同段
mov ds,ax
sti ;开中断
mov dh,22 ;行
mov dl,24 ;列
mov bl,0b1h ;属性,浅青底蓝字
mov si,offset a1
mov ax,0b800h ;显存首址
mov es,ax
mov ax,160
mul dh ;行号*160
mov di,ax ;起始行位置
sal dl,1 ;列号*2
mov dh,0
add di,dx ;行+列号
mov cx,a2-a1 ;字符串长度
let1: ;循环写字符和属性到显存
mov al,[si]
mov es:[di],al
mov byte ptr es:[di+1],bl
inc si
inc bl ;改变属性
add di,2
loop let1 ;写完即显示完
mov ah,2
mov dl,0dh ;回车换行
int 21h
mov dl,0ah
int 21h
cli ;关中断
iret ;中断返回
showend:nop
showerr endp
end start
(2)在 dos 子目录下保存为 9-2.asm,经过汇编 masm 9-2.asm
,连接 link 9-2.obj
,生成 9-2.exe。
D:\dos〉masm 9-2.asm
D:\dos〉link 9-2.obj
D:\dos〉9-2.exe
(3)运行结果:
5. BIOS 中断
BIOS 是基本输入输出系统(Basic Input Output System)的缩写。BIOS 程序在 8086 系统的内存空间 FE000H 开始的 8KB 只读存储器 ROM 中,是机器出厂时就带有的。
使用 BIOS 中断功能调用,程序员不必了解硬件的操作过程,使用方便。常用的 I/O 输入输出 BIOS 中断是对键盘、光标、屏幕显示、时钟、打印机等的控制。
本例子为教材的示例 9-3。
**关注点
**:键盘、光标及屏幕的控制
设计目标
在屏幕的窗口中显示字符串,并照样输入该字符串。
设计思路
(1)将清屏和开窗口定义为宏,并放入宏库中;
(2)用 DOS 中断的 9 号和 10 号功能分别显示字符串和输入字符串;
(3)用 BIOS 中断的置光标功能分别设置字符串的显示位置和输入位置;
(4)在屏幕的 8 行 30 列到 15 行 60 列开窗口,窗口内为绿底灰字显示,每输入一次屏幕上卷一行。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-3.asm 在屏幕的窗口中显示字符串并输入该字符串。
include 9-3.mac ;宏库
.model small
.data
letter db 'Every success in your study.',0ah,0dh,'$'
mess db 29,32 dup(?)
cont db ?
.code
start:
mov ax,@data
mov ds,ax
clearsc ;清屏
clearsw ;开窗口
reptt:
;置显示光标
mov ah,2
mov dh,8 ;在8行30列显示
mov dl,30
mov bh,0
int 10h
;显示串
mov ah,9
mov dx,offset letter
int 21h
;置输入光标
mov ah,2
mov dh,15 ;在15行30列输入
mov dl,30
mov bh,0
int 10h
;输入串
mov al,0
mov ah,10
mov dx,offset mess
int 21h
;窗口内上卷
mov ah,6
mov al,1 ;上卷1行
mov ch,8 ;从8行30列到15行60列
mov cl,30
mov dh,15
mov dl,60
mov bh,27h ;绿底灰白字
int 10h
inc cont ;可输入3次
cmp cont,3
jne reptt
out1:
mov ah,4ch
int 21h
end start
宏库9-3.MAC:
;9-3.mac BIOS宏库
;清屏clearsc
clearsc macro
mov ah,06h
mov al,0
mov bh,0F0h ;白底黑字
mov ch,0
mov cl,0
mov dh,23
mov dl,79
int 10h
mov dx,0 ; 光标在屏幕左上角
mov ah,2
int 10h
endm
;开窗口
clearsw macro
mov ah,06h
mov al,0
mov bh,27h ;绿底灰白字
mov ch,8 ;从8行30列到15行60列
mov cl,30
mov dh,15
mov dl,60
int 10h
endm
(2)在 dos 子目录下保存为 9-3.asm,经过汇编 masm 9-3.asm
,连接 link 9-3.obj
,生成 9-3.exe。
D:\dos〉masm 9-3.asm
D:\dos〉link 9-3.obj
D:\dos〉9-3.exe
(3)运行结果:
6. 中断程序示例
本小节中将进行四个与中断相关的综合性实验。点击右下角的下一步,进入第一个实验。
6.1 时间与计数
本例子为教材的示例 9-4。
用 BIOS 和 DOS 提供的中断调用功能,可以实现对屏幕显示、光标设置和键盘输入等各种操作。还可以对系统时钟进行访问,获得日期和时间信息。利用时间调用功能和时钟中断,还可编写随机数发生程序。有了随机数就可以做更多工作,编写特殊的功能程序。
设计目标
在屏幕右上角显示系统当前的日期和时间。
设计思路
(1)建立宏库,分别用宏实现对屏幕和光标设置;
(2)用 INT 21H 的 2CH 号功能调用读取系统日期和时间;
(3)调用二进制—十进制显示子程序 DISP_2_10,分别显示日期和时间。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-4.asm 在屏幕的右上角显示日期和时间。
include 9-4.mac ;调入宏库
.model small
.data
letter1 db 'Date and week= ','$'
letter2 db 'Time= ','$'
nears dw ?
month db ?
day db ?
week db ?
hour db ?
minutes db ?
seconds db ?
persec db ?
.code
start:
mov ax,@data
mov ds,ax
clearsc ;清屏
cursor 2,50 ;置光标
mov ah,9
mov dx,offset letter1 ;显示字符串1
int 21h
;读日期并显示
mov ah,2ah ;CX=年,DH=月,DL=日,AL=星期
int 21h
mov nears,cx
mov month,dh
mov day,dl
mov week,al
;调用2-10显示子程序
mov bx,nears
call disp_2_10 ;年
displ '/' ;显示/
mov bx,0
mov bl,month
call disp_2_10 ;月
displ '/' ;显示/
mov bx,0
mov bl,day
call disp_2_10 ;日
displ '/' ;显示/
mov bx,0
mov bl,week
call disp_2_10 ;星期
enter ;回车换行
cursor 3,50 ;置光标
mov ah,9
mov dx,offset letter2 ;显示字符串2
int 21h
;读时间
mov ah,2ch ;CH=小时,CL=分,DH=秒,DL=百分秒
int 21h
mov hour,ch
mov minutes,cl
mov seconds,dh
mov persec,dl
;调用disp_2_10显示子程序
mov bx,0
mov bl,hour
call disp_2_10 ;小时
displ ':' ;显示:
mov bx,0
mov bl,minutes
call disp_2_10 ;分
displ ':' ;显示:
mov bx,0
mov bl,seconds
call disp_2_10 ;秒
displ ':' ;显示:
mov bx,0
mov bl,persec
call disp_2_10 ;百分秒
out1:
mov ah,4ch
int 21h
;子程序。二进制-十进制数显示。对bx值,显示十进制结果。
disp_2_10 proc
store_ss ;保护现场
mov ax,bx ;bx传参
mov cx,0
mov bx,10 ;将ax变为十进制数
let1:
mov dx,0
inc cx ;统计余数个数
idiv bx ;除以10,商在AX,余数在DX
push dx ;保存余数
cmp ax,0
jnz let1
let2: ;显示结果
pop ax ;将余数弹入ax
add ax,0030h ;调整为ASCII码
mov dl,al
mov ah,2 ;显示余数
int 21h
loop let2
restore_ss ;恢复现场
ret
disp_2_10 endp
end start
宏库9-4.mac:
;9-4.mac 宏库
;清屏clearsc
clearsc macro
mov ah,06h
mov al,0
mov bh,0F0h ;白底黑字
mov ch,0
mov cl,0
mov dh,23
mov dl,79
int 10h
mov dx,0 ;光标在左上角
mov ah,2
int 10h
endm
;开窗口
clearsw macro row1,rank1,row2,rank2,color
mov ah,06h
mov al,0
mov ch,row1 ;从row1行rank1列到row2行rank2列
mov cl,rank1
mov dh,row2
mov dl,rank2
mov bh,color
int 10h
endm
;窗口内上卷
windows macro row1,rank1,row2,rank2,color
mov ah,6
mov al,1 ;上卷1行
mov ch,row1 ;从row1行rank1列到row2行rank2列
mov cl,rank1
mov dh,row2
mov dl,rank2
mov bh,color ;绿底灰白字27h
int 10h
endm
;置光标
cursor macro row,rank
mov ah,2
mov dh,row ;在row行rank列输入
mov dl,rank
mov bh,0
int 10h
endm
;2号功能显示
displ macro opr
mov ah,2
mov dl,opr
int 21h
endm
;回车换行
enter macro
mov ah,2
mov dl,0dh
int 21h
mov dl,0ah
int 21h
endm
;现场保护宏
store_ss macro
push ax
push bx
push cx
push dx
endm
;恢复现场宏
restore_ss macro
pop dx
pop cx
pop bx
pop ax
endm
(2)在 dos 子目录下保存为 9-4.asm,经过汇编 masm 9-4.asm
,连接 link 9-4.obj
,生成 9-4.exe。
D:\dos〉masm 9-4.asm
D:\dos〉link 9-4.obj
D:\dos〉9-4.exe
(3)运行结果:
6.2 计算时钟计数器差值
本例子为教材的示例 9-5。
设计目标
在屏幕右上角开窗口显示两次读取的时钟计数器当前值和计数差值。
设计思路
(1)利用 9-4 题的宏库与子程序;
(2)用 INT 1AH 读取时钟计数器,两次读取时间间隔 2 秒;
(3)在屏幕 2 行 50 列到 6 行 78 列开窗口,棕底白字 6FH。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-5.asm 开窗口显示时钟计数器当前值和计数差值。
include 9-4.mac
.model small
.data
letter1 db 'count1= ','$'
letter2 db 'count2= ','$'
letter3 db 'total= ','$'
higher1 dw ?
lower1 dw ?
higher2 dw ?
lower2 dw ?
total dw ?
.code
start:
mov ax,@data
mov ds,ax
clearsc ;清屏
clearsw 2,50,6,78,6fh ;开窗口,棕底白字
cursor 3,51 ;置光标
mov ah,9
mov dx,offset letter1 ;显示字符串1
int 21h
;读取时钟计数器当前值并显示
mov ah,00h ;CX=计数值高字,DX=计数值低字
int 1ah
mov higher1,cx
mov lower1,dx
;再次读取
reptt:
mov ah,00h ;CX=计数值高字,DX=计数值低字
int 1ah
mov higher2,cx
mov lower2,dx
mov ax,lower2
sub ax,lower1 ;差值
cmp ax,36 ;2秒间隔
jl reptt
mov total,ax
;调用disp_2_10显示子程序
mov bx,higher1
call disp_2_10 ;高位
displ '/' ;显示/
mov bx,lower1
call disp_2_10 ;低位
;显示第二次计数值
cursor 4,51 ;置光标
mov ah,9
mov dx,offset letter2 ;显示字符串2
int 21h
mov bx,higher2
call disp_2_10 ;高位
displ '/' ;显示/
mov bx,lower2
call disp_2_10 ;低位
;显示差值总数
cursor 5,51 ;置光标
mov ah,9
mov dx,offset letter3 ;显示字符串3
int 21h
mov bx,total
call disp_2_10 ;计数差值
out1:
mov ah,4ch
int 21h
end start
(2)在 dos 子目录下保存为 9-5.asm,经过汇编 masm 9-5.asm
,连接 link 9-5.obj
,生成 9-5.exe。
D:\dos〉masm 9-5.asm
D:\dos〉link 9-5.obj
D:\dos〉9-5.exe
(3)运行结果:
6.3 时钟中断和 DOS 中断的用法
本例子为教材的示例 9-6。
**关注点
**:开窗口、屏幕上卷、彩色字、响铃
设计目标
开窗口,紫底黄字,屏幕上卷三次。每隔 5 秒显示读取的时钟计数器当前值,响铃一次。
设计思路
(1)每隔 5 秒即计数值为 91 次;
(2)用 INT 1AH
的 1 号功能将计数器清 0;用 INT 1AH
的 0 号功能获得计数器计数值;
(3)调用子程序 disp_2_10 显示当前计数值;
(4)DOS 中断 INT 21H
的 2 号功能为显示一个字符;如果将 07H 放入 DL,则响铃。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-6.asm 开窗口。每隔5秒显示计数器当前值,响铃一次。
include 9-4.mac
.model small
.data
letter1 db 'current= ','$'
higher1 dw ?
lower1 dw ?
n db 6 ;从第6行开始显示
.code
start:
mov ax,@data
mov ds,ax
clearsc ;清屏
clearsw 2,30,10,50,5eh ;开窗口 紫底黄字
mov bx,0
reptt1:
cursor n,31 ;置光标,6行31列
;清时钟计数器
mov ah,01h
mov cx,0
mov dx,0 ;计数器清0
int 1ah
;显示字符串1
mov ah,9
mov dx,offset letter1
int 21h
;读取时钟计数器当前值
reptt2:
mov ah,00h ;CX=计数值高字,DX=计数值低字
int 1ah
mov higher1,cx
mov lower1,dx
cmp dx,91 ;5秒?
jl reptt2
;调用2-10显示子程序
mov bx,higher1
call disp_2_10 ;高位
displ '/' ;显示/
mov bx,lower1
call disp_2_10 ;低位
mov ah,2
mov dl,07h ;响铃
int 21h
windows 2,30,10,50,05eh ;上卷一行
inc n
cmp n,9 ;显示3次,最后显示在第9行
jl reptt1
out1:
mov ah,4ch
int 21h
end start
(2)在 dos 子目录下保存为 9-6.asm,经过汇编 masm 9-6.asm
,连接 link 9-6.obj
,生成 9-6.exe。
D:\dos〉masm 9-6.asm
D:\dos〉link 9-6.obj
D:\dos〉9-6.exe
(3)运行结果:
**思考
**:该程序每次显示同一个计数值。如果计数值每次不同,程序应该如何修改?
6.4 定时器中断及 INT 1CH 中断指令的用法
本例子为教材的示例 9-7。
**关注点
**:1 秒定时 ,中断触发
设计目标
改写 INT 1CH 中断,每隔 1 秒显示一串字符 “Time out !!”,响铃 1 次。
设计思路
(1)每 1 秒产生 18.2 次定时器中断,约为 18 次中断 1 秒。在中断子程序中判断是否 18 次,是则显示 “Time out !!” 并响铃。
(2)主程序将原 1CH 中断向量保存,并设置新的 1CH 中断向量。
(3)主程序的应用部分为在 10 秒钟内等待 10 次定时中断的发生。
(4)主程序的结束部分恢复原 1CH 中断向量。
(5)采用简化程序结构,数据段和代码段在一起。
实验步骤
(1)双击桌面上的记事本 gedit
,录入下列程序:
;9-7.asm 改写INT 1CH中断,每隔1秒显示一串字符“Time out !!”,响铃1次。
include 9-4.mac
.model small
.stack
.code
n dw 18
m db 4
mess db 0ah,0dh,'Time out !!','$'
old1ch dw ?,?
;主程序
main proc far
start:
mov ax,@code
mov ds,ax
;中断设置
mov al,1ch
mov ah,35h ;取出原来的1ch号中断向量
int 21h ;放在ES:BX中
mov old1ch,bx ;保存到old1ch
mov old1ch+2,es
push ds ;保存数据段
;设置新的中断向量
mov dx,offset win_time ;获得中断子程序偏移地址
mov ax,seg win_time ;获得中断子程序段地址
mov ds,ax
mov al,1ch ;将现在的1CH号
mov ah,25h ;中断向量放入中断向量表中
int 21h
pop ds ;弹出数据段
clearsc ;清屏宏
;应用部分
mov cx,5000 ;延时程序
delay3:mov bx,7500 ;等待定时中断触发(共计10秒)
delay2:mov si,400
delay1:dec si
jnz delay1
dec bx
jnz delay2
dec cx
jnz delay3
quit:
mov dx,old1ch ;恢复保存的数据
mov ds,old1ch+2
mov al,1ch ;恢复原来的1ch号
mov ah,25h ;中断向量放入中断向量表中
int 21h
mov ax,4c00h ;返回DOS
int 21h
main endp ;主程序结束
;中断子程序
win_time proc
mov ax,@code ;数据段与代码段同段
mov ds,ax
dec n
jnz exit ;18次?
mov n,18 ;恢复n。1秒18.2次
mov dx,offset mess ;显示提示
mov ah,9
int 21h
mov ah,2
mov dl,07h ;响铃
int 21h
exit:
iret ;中断返回
win_time endp
end start
(2)在 dos 子目录下保存为 9-7.asm,经过汇编 masm 9-7.asm
,连接 link 9-7.obj
,生成 9-7.exe。
D:\dos〉masm 9-7.asm
D:\dos〉link 9-7.obj
D:\dos〉9-7.exe
(3)运行结果:
7. 中断程序应用
本节实验取自教材中第九章的《实例九 中断程序应用》
实验目的
中断程序设计分为对系统中断及功能调用的使用和自己设计中断程序两个层次,要求掌握常用的系统中断及功能调用的用法以及中断程序的设计思路和技巧。并尝试编写一个自己设计的中断程序。
实验内容
参考示例 9-6、示例 9-7,完成下列实验内容:
- 修改示例 9-6,每次显示不同的计数值。
- 在示例 9-7 基础上,中断子程序显示出倒计数值。可参考例 7-7 的子程序 SUBR3。(代码参见网站 9-7b.ASM)
- 定义一个 INT 80H 中断。中断发生时,在屏幕上 0 页 7 行 13 列显示 5 个蓝底黄字的小写字母 a。要求:主程序中调用 3 次 INT 80H 中断,并显示提示信息。(代码参见 9-8.ASM)
实验要求
- 在 2、3 题中选作一个。
- 实验内容用截图形式记录实验结果。
- 写出实验结果分析。
实验拓展
- 设计一个小闹钟。每隔 10 秒钟响铃一次。
- 利用 INT 1AH 读取时钟计数值,产生一个随机数,并显示出来。