准备知识
这个中断例程的综合性较强,说真的我琢磨了很长时间,几乎考虑到了前面所有章节的知识,最后是用debug调试程序找到了关键问题。
中断例程的空间
该中断例程的功能较多,所以占用的内存有必要考虑下,功能程序写好后进入debug可查看程序长度,大约是180字节。
内存中,一般从0000:0200至0000:02FF的 256 个字节空间是空闲可用的,可以估算出7ch中断例程的长度不可能超过256个字节,故7ch可以安装到0:200处。
标号的重要用法
数据标号
- 数据标号可直接用来访问数据
用伪指令assume将标号所在的段和一个段寄存器联系起来,例如:
assume ds:data
那么data段中的标号的段地址默认在ds中,并不是说ds存放的内容就是data段的段地址。
- 数据标号可以当作数据来定义
例如:
a db 1,2,3,4
b dw 0,0
那么
c dw a,b
c处存放的为a和b的偏移地址
c dd a,b
存放的分别为a的偏移地址和段地址、b的偏移地址和段地址
- 用法举例
mov ax,标号(字单元)
mov 标号(字节单元),ax
add 标号,10
……
地址标号
地址标号(带“:”)仅表示内存单元的地址,只能在代码段中使用;
可以和数据标号搭配使用。
编译器对标号的处理
数据标号 dw 标号1,标号2
存放标号1、标号2的偏移地址,两种标号都可行。
数据标号[reg + idata]
显式的偏移地址,隐含的段地址在标号所在段相联系的段寄存器中。
- 方括号中标号的处理
[reg + 数据标号 - 地址标号 + idata]
均作为偏移地址处理,但默认段地址在ds中,本实验就用到这种形式。
参考实验
运行环境
dosbox0.74
实验要点
实验任务是安装int 7ch中断例程,实现的功能就来源于子程序setscreen,我们很容易写出的安装程序如下图
这个程序有很大的问题,因为划线处的标号的地址是安装程序的运行地址,而非目标中断例程的运行地址。
我们所写的标号偏移地址是在编译器编译过程中就确定好的,并不会随程序的运行而改变。那么table处直接定址表存放的应该为目标程序入口地址,并且在目标程序中又需要找到直接定址表。
综上,接下来要解决的问题是目标程序如何在直接定址表中取得正确的入口地址。
上图中划线处的标号用到的都是安装程序中的偏移地址,也就是说标号的相对位置(即位移)是确定的,这里我们可以联系前面章节提到过的依据位移进行转移的指令的原理考虑这个问题。
我们的安装程序是将中断例程(目标程序)安装到目标地址空间,目标程序的首部和目标地址空间首地址都确定了。因此,在安装程序中目标程序标号位置的目标偏移地址可以这样确定:
地址位移 | ⇨⇨⇨ | 目标偏移地址 |
---|---|---|
标号地址 - 首标号地址 | + 目标首偏移地址 | (地址变量) |
实验程序
;mtf安装程序,7ch中断例程 multifunction 多功能;多功能的
assume cs:code
code segment
start: mov ax,cs
mov ds,ax
mov si,offset function
mov ax,0
mov es,ax
mov di,200h
mov cx,offset functionend - offset function
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
;功能:为显示输出提供如下功能子程序
;(1)清屏;(2)设置前景色;(3)设置背景色;(4)向上滚动一行
;参数:ah传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;对1、2号功能,用al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}
;返回值:无
;
function: jmp short set ;目标代码存放在0:200处
table dw sub1 - function + 200h,sub2 - function + 200h,sub3 - function + 200h,sub4 - function + 200h ;存放目标程序入口地址
set: push bx
cmp ah,3 ;判断传递的是否大于 3
ja sret
mov bl,ah
mov bh,0
add bx,bx ;根据ah中的功能号计算对应子程序的地址在table表中的偏移
call word ptr cs:[bx + table - function + 200h] ;调用对应的功能子程序,注意这里应加上段前缀“cs:”
sret: pop bx
iret ;中断返回
;功能子程序1:清屏,0号功能
sub1: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
sub1s: mov byte ptr es:[bx],' '
add bx,2
loop sub1s
pop es
pop cx
pop bx
ret ;sub1 ends
;功能子程序2:设置前景色,1号功能
sub2: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s: and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret ;sub2 ends
;功能子程序3:设置背景色,2号功能
sub3: push bx
push cx
push es
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s: and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub3s
pop es
pop cx
pop bx
ret ; sub3 ends
;功能子程序4:向上滚动一行,3号功能
sub4: push cx
push si
push di
push es
push ds
mov si,0b800h
mov es,si
mov ds,si
mov si,160 ;ds:si指向第n+1行
mov di,0 ;es:di指向第n行
cld
mov cx,24 ;共复制24行
sub4s: push cx
mov cx,160
rep movsb ;复制
pop cx
loop sub4s
mov cx,80
mov si,0
sub4s1: mov byte ptr es:[160*24+si],' ' ;最后一行清空
add si,2
loop sub4s1
pop ds
pop es
pop di
pop si
pop cx
ret ;sub4 ends
functionend: nop
code ends
end start
编译运行后就进行功能测试了。
测试程序
在测试之前一定要先把实验程序运行一遍,以安装中断例程,注意不要关闭dosbox,建议整个实验过程不要关闭dosbox
重要事情说三遍:
先要运行实验程序!
先要运行实验程序!
先要运行实验程序!
清屏测试
assume cs:code
code segment
start: mov ah,0 ;测试 0 号功能
int 7ch ;调用int7ch中断例程
mov ax,4c00h
int 21h
code ends
end start
运行结果
前景测试
assume cs:code
code segment
start: mov ah,1 ;测试 1 号功能
mov al,4 ;红色
int 7ch ;调用int7ch中断例程
mov ax,4c00h
int 21h
code ends
end start
运行结果
背景测试
assume cs:code
code segment
start: mov ah,2 ;测试 2 号功能
mov al,1 ;蓝色
int 7ch ;调用int7ch中断例程
mov ax,4c00h
int 21h
code ends
end start
运行
向上卷行
assume cs:code
code segment
start: mov ah,3 ;测试 3 号功能
int 7ch ;调用int7ch中断例程
mov ax,4c00h
int 21h
code ends
end start
运行
结束语
以上就是我个人提供的全部解决方案,希望能够帮助到你,如果你认为有帮助,不妨推荐给身边学习汇编的朋友!感谢阅读,加油!