文章目录
前言
本文是王爽老师《汇编语言》(第四版) 第十六章 实验16 编写包含多个功能子程序的中断例程 的分析及代码。
子程序功能:清屏;设置前景色;设置背景色;向上滚动一行。
(最后附完整代码)
一、实验任务
本次实验任务:
安装一个新的int 7CH 中断例程,为显示输出提供如下功能子程序。
(1)清屏;
(2)设置前景色;
(3)设置后景色;
(4)向上滚动一行。
入口参数说明如下。
(1)用ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;
(2)对于1、2号功能,用al传送颜色值,al的值∈{0,1,2,3,4,5,6,7}。
二、思路分析
下面对一些关键点进行分析。
1.程序框架
首先来设计程序框架。
(1)要有一个子程序install,功能是将int 7CH中断例程安装到内存中。这个功能以前写过,这里不再赘述。
(2)对于多个功能的子程序,根据书上讲的思路,应当用“cmp” 和“ je ”这两个指令的组合来实现查找对应子程序的功能,这样程序结构清晰,扩展性良好。这类似于java语法中的switch-case语句。
以下是程序的基本框架。
;实验16:编写包含多个功能子程序的中断例程
assume cs:code
stack segment
db 64 dup(0)
stack ends
code segment
start:
mov ax,stack ;设置栈顶
mov ss,ax
mov sp,40H
call install ;安装7CH中断例程
mov ah,0 ;设置子程序功能号
mov al,1H ;指定程序参数
int 7CH ;触发7CH中断例程
mov ax,4c00H
int 21H
install: ;功能:将7CH中断例程安装在内存0:200H处
;参数:无
;返回:无
ret
do7CH: ;7CH中断例程 功能:包含设置屏幕属性的4个子程序
;参数:ah 功能号; al 颜色参数
;返回:无
;根据功能号,跳转到对应的子程序地址中
cmp ah,0
je do1
cmp ah,1
je do2
cmp ah,2
je do3
cmp ah,3
je do4
jmp do7CH_ret ;子程序执行完毕,就返回
;这部分相当于子程序地址表
do1: call sub1
jmp short do7CH_ret
do2: call sub2
jmp short do7CH_ret
do3: call sub3
jmp short do7CH_ret
do4: call sub4
jmp short do7CH_ret
;程序返回
do7CH_ret:
iret
sub1: ;功能:清屏
ret
sub2: ;功能:设置前景色
ret
sub3: ;功能:设置背景色
ret
sub4: ;功能:向上滚动一行
ret
do7CH_end:nop ;中断例程代码结尾处
code ends
end start
2.设置前景色
这里的设置方法值得注意。and 指令的功能是将属性字节(8个2进制位)的低3位置为0,其它位保持不变;or指令的功能是将属性字节的低3位设置为指定的颜色值(即al中的低三位)。这个设置方法颇为巧妙。
其中注意,在and指令中一定要写上“byte ptr”,否则,编译时不会报错但运行时会出错,因为编译器会默认翻译为“and word ptr es:[si],0000 0000 1111 1000B ”
这部分代码如下。
mov bx,0B800H ;设置es:si指向显存首个字符属性字节
mov es,bx
mov si,1
mov cx,2000 ;修改2000个字符属性字节的前景色
s_sub2:
and byte ptr es:[si],11111000B ;将属性字节最低3位置为0
or es:[si],al ;将属性字节最低3位设置为指定值
add si,2
loop s_sub2
3.设置背景色
对于属性字节,有0-7共8位二进制位,其中012三位用来设置前景色,取值范围0-7,而456位用来设置背景色,取值范围也是0-7 。al参数,其中存放的值就是0-7,假如是7,那么al=0000 0111 B 。而要通过and 指令与 or指令来设置背景色,就要将al修改为0111 0000 B。因此需要将al执行逻辑左移4次的操作。代码如下。
mov cl,04H ;将颜色参数al的有效位从012位移到456位
shl al,cl
三、最终成果
1.完整代码
完整代码如下。
;实验16:编写包含多个功能子程序的中断例程
assume cs:code
stack segment
db 64 dup(0)
stack ends
code segment
start:
mov ax,stack ;设置栈顶
mov ss,ax
mov sp,40H
call install ;安装7CH中断例程
mov ah,2 ;设置子程序功能号
mov al,1H ;指定程序颜色参数
int 7CH ;触发7CH中断例程
mov ax,4c00H
int 21H
install: ;功能:将7CH中断例程安装在内存0:200H处
;参数:无
;返回:无
push ax
push ds
push si
push es
push di
push cx
;1.将中断例程代码复制到0:200H处
mov ax,cs ;ds:si指向复制的源地址首地址
mov ds,ax
mov si,offset do7CH
mov ax,0 ;es:di指向复制的目标地址首地址
mov es,ax
mov di,200H
mov cx,offset do7CH_end - offset do7CH ;设置数据长度
rep movsb ;复制数据
;2.将中断例程入口地址写入中断向量表
mov word ptr es:[7CH*4],200H ;偏移地址
mov word ptr es:[7CH*4+2],0 ;段地址
pop cx
pop di
pop es
pop si
pop ds
pop ax
ret
do7CH: ;7CH中断例程 功能:包含设置屏幕属性的4个子程序
;参数:ah 功能号; al 颜色参数
;返回:无
;根据功能号,跳转到对应的子程序地址中
cmp ah,0
je do1 ;清屏
cmp ah,1
je do2 ;设置前景色
cmp ah,2
je do3 ;设置背景色
cmp ah,3
je do4 ;向上滚动一行
jmp do7CH_ret ;子程序执行完毕,程序返回
;这部分相当于子程序地址表
do1: call sub1
jmp short do7CH_ret
do2: call sub2
jmp short do7CH_ret
do3: call sub3
jmp short do7CH_ret
do4: call sub4
jmp short do7CH_ret
;程序返回
do7CH_ret:
iret
sub1: ;功能:清屏
;参数:无
;返回:无
push ax
push es
push si
push cx
mov ax,0B800H ;es:si指向显存首地址
mov es,ax
mov si,0
mov al,0 ;空格的ASCII码是0
mov cx,2000 ;将屏幕中每一个字符字节都写为0
s_sub1:
mov es:[si],al
add si,2
loop s_sub1
pop cx
pop si
pop es
pop ax
ret
sub2: ;功能:设置前景色
;参数:al前景色,范围0-7
;返回:无
push bx
push es
push si
push cx
mov bx,0B800H ;设置es:si指向显存首个字符属性字节
mov es,bx
mov si,1
mov cx,2000 ;修改2000个字符属性字节的前景色
s_sub2:
and byte ptr es:[si],11111000B ;将属性字节最低3位置为0
or es:[si],al ;将属性字节最低3位设置为指定值
add si,2
loop s_sub2
pop cx
pop si
pop es
pop bx
ret
sub3: ;功能:设置背景色
;参数:al 背景色,范围0-7
;返回:无
push bx
push es
push cx
push si
push ax
mov bx,0B800H ;es:si指向显存首个字符属性字节
mov es,bx
mov si,1H
mov cx,0004H ;将颜色参数al的有效位从012位移到456位
shl al,cl
mov cx,2000 ;将屏幕中每个字符的属性字节中的背景色都进行修改
s_sub3:
and byte ptr es:[si],10001111B ;将属性字节的456位(即背景色的位置)置为0
or es:[si],al ;将属性字节的背景色位(456位)设置为指定值(即al)
add si,2
loop s_sub3
pop ax
pop si
pop cx
pop es
pop bx
ret
sub4: ;功能:向上滚动一行
;参数:无
;返回:无
;实现思路:从n=0开始,将n+1行的内容复制到第n行,最后一行为空白
push ax
push es
push ds
push si
push di
mov ax,0B800H ;es:di指向复制的目标地址
mov ds,ax ;ds:si指向复制的源地址
mov es,ax
mov si,160
mov di,0
mov cx,24 ;复制24行
s_sub4:
push cx
mov cx,160*2 ;每行复制160个字符
rep movsb
pop cx
loop s_sub4 ;继续下一行
mov cx,160 ;将最后一行设置为空白
mov al,0
s_sub4_1:
mov ds:[si],al
add si,2
loop s_sub4_1
pop di
pop si
pop ds
pop es
pop ax
ret
do7CH_end:nop ;中断例程代码结尾处
code ends
end start
2.效果图
总结
本文是王爽老师《汇编语言》(第四版) 第十六章 实验16 编写包含多个功能子程序的中断例程 的分析及代码。通过这个实验,练习了在一个中断例程(或者子程序)中如何结构清晰地设计多个功能子程序,且保持良好的可扩展性,这是非常实用的编程方法。