(二十九)《汇编语言(王爽)》 | 实验 16:编写包含多个功能子程序的中断例程


1. 预备知识

  • 用查表的方法可以简化程序的编写。如以十六进制的形式在屏幕中间显示给定的字节型数据。

一个字节需用两个十六进制码表示,程序使用 0~F 这 16 个字符来显示。如要在屏幕显示 2Bh,需将 2 映射为 ASCII 码 32h(2 + 30h),将 B 映射为 ASCII 码 42h(11 + 37h),二者的映射方式不同。因此,在实际映射时需对给定字符进行判断,然后再确定映射方式。

为了简化程序,我们应在数字 0~15 和 0~F 之间建立新的映射关系。具体做法是建立一张表,表中依次存储 0~F,然后通过数字 0~15 直接查找对应的字符。

table db '0123456789ABCDE'

子程序如下:

;al传入待显示数据
showbyte:
	jmp short show 
	table db '0123456789ABCDE'
show:
	push bx 
	push es		;保护现场
	mov ah,al
	mov cl,4
	shr ah,cl			;右移4位得到高4位的值
	and al,00001111b	;按位与操作得到低4位的值
	mov bl,ah
	mov bh,0
	mov ah,table[bx]	;高4位值作为偏移取得对应字符
	mov bx,0b800h
	mov es,bx 
	mov es:[160*12+40*2],ah
	;显示高位字符
	mov bl,al 
	mov bh,0
	mov al,table[bx]	;低4位值作为偏移取得对应字符
	mov es:[160*12+40*2+2],al
	;显示低位字符
	pop es 
	pop bx 		;恢复现场
	ret

整体代码为:

assume cs:code 
code segment
start:
	mov al,2Bh
	call showbyte
	mov ax,4c00h
	int 21h
showbyte:
	jmp short show 
	table db '0123456789ABCDE'
show:
	push bx 
	push es		;保护现场
	mov ah,al
	mov cl,4
	shr ah,cl			;右移4位得到高4位的值
	and al,00001111b	;按位与操作得到低4位的值
	mov bl,ah
	mov bh,0
	mov ah,table[bx]	;高4位值作为偏移取得对应字符
	mov bx,0b800h
	mov es,bx 
	mov es:[160*12+40*2],ah
	;显示高位字符
	mov bl,al 
	mov bh,0
	mov al,table[bx]	;低4位值作为偏移取得对应字符
	mov es:[160*12+40*2+2],al
	;显示低位字符
	pop es 
	pop bx 		;恢复现场
	ret
code ends
end start

程序运行结果如下:

请添加图片描述

  • 在上面的子程序中,使用表是为了算法的清晰和简洁。下面将介绍查表的另一个特点,加快运算速度。如编写一个子程序,计算 sin(x),x∈{0°, 30°, 60°, 90°, 120°, 150°, 180°}。

首先查看需要计算的 sin 的结果:

sin(0)=0
sin(30)=0.5
sin(60)=0.866
sin(90)=1
sin(120)=0.866
sin(150)=0.5
sin(180)=0

我们可以将上述各角度的 sin 值计算完成后存入表中,然后对输入角度除以 30 获得偏移,并取得对应的值。

table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
;表存放各字符串对应的偏移
ag0   db '0',0
ag30  db '0.5',0
ag60  db '0.866',0
ag90  db '1',0
ag120 db '0.866',0
ag150 db '0.5',0
ag180 db '0',0

子程序如下:

showsin:
	jmp short show
	table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
	;表存放各字符串对应的偏移
	ag0   db '0',0
	ag30  db '0.5',0
	ag60  db '0.866',0
	ag90  db '1',0
	ag120 db '0.866',0
	ag150 db '0.5',0
	ag180 db '0',0
show:
	push bx 
	push es 
	push si 
	mov bx,0b800h
	mov es,bx 
	mov ah,0
	mov bl,30
	div bl		;计算除以30
	mov bl,al	;AL存储除法运算的商
	mov bh,0
	add bx,bx 	;表中的字段为双字类型
	mov bx,table[bx]
	mov si,160*12+40*2
shows:
	mov ah,cs[bx]
	cmp ah,0
	je showret	;到达字符串结尾
	mov es:[si],ah 
	inc bx 
	add si,2
	jmp short shows 
showret:
	pop si 
	pop es 
	pop bx 
	ret

整体代码为:

assume cs:code
code segment
start:
	mov ax,3Ch	;传入角度为60°
	call showsin
	mov ax,4c00h
	int 21h
showsin:
	jmp short show
	table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
	;表存放各字符串对应的偏移
	ag0   db '0',0
	ag30  db '0.5',0
	ag60  db '0.866',0
	ag90  db '1',0
	ag120 db '0.866',0
	ag150 db '0.5',0
	ag180 db '0',0
show:
	push bx 
	push es 
	push si 
	mov bx,0b800h
	mov es,bx 
	mov ah,0
	mov bl,30
	div bl		;计算除以30
	mov bl,al	;AL存储除法运算的商
	mov bh,0
	add bx,bx 	;表中的字段为双字类型
	mov bx,table[bx]
	mov si,160*12+40*2
shows:
	mov ah,cs:[bx]
	cmp ah,0
	je showret	;到达字符串结尾
	mov es:[si],ah 
	inc bx 
	add si,2
	jmp short shows 
showret:
	pop si 
	pop es 
	pop bx 
	ret
code ends
end start

程序运行结果如下:

请添加图片描述

  • 我们可以在表中存储子程序的地址,从而方便地实现不同子程序的调用。

2. 实验任务

安装一个新的 int 7ch 中断例程,为显示输出提供如下功能子程序:清屏;设置前景色;设置背景色;向上滚动一行。入口参数说明:

用 ah 传递功能号:0 表示清屏,1 表示设置前景色,2 表示设置背景色,3 表示向上滚动一行
对于 2、3 号功能,用 al 传递颜色值,(al)∈{0,1,2,3,4,5,6,7}

首先分别实现四个子程序的内容。清屏,即将显存中当前屏幕中的字符设定为空格。

cls_screen:
	push bx 
	push cx 
	push es 	;保护现场
	mov bx,0b800h
	mov es,bx
	mov bx,0
	mov cx,2000	;当前屏幕含2000个字符
help:
	mov byte ptr es:[bx],' '
	;写入空格
	add bx,2
	loop help 
	pop es 
	pop cx 
	pop bx 		;恢复现场
	ret

设置前景色,颜色值存放在 AL 中。字符属性表示如下:

[7]    [6    5    4]    [3]   [2    1    0]
闪烁      背景(RGB)      高亮     前景(RGB)

前景色对应于后三位,首先使用与运算清除字符属性区域的后三位 and 11111000b,然后将 AL 中的内容存入字符属性区域。

set_fore:
	push bx 
	push cx 
	push es 	;保护现场 
	mov bx,0b800h
	mov es,bx 
	mov bx,1
	mov cx,2000
help_set_fore:
	and byte ptr es:[bx],11111000b
	;清除后三位的值
	or es:[bx],al
	;设置前景色
	add bx,2
	loop help_set_fore 
	pop es 
	pop cx 
	pop bx		;恢复现场 
	ret 

设置背景色,颜色值存放在 AL 中。背景色为第 2~4 位,和设置前景色类似。

set_back:
	push bx 
	push cx 
	push es		;保护现场
	mov cl,4
	shl al,cl
	mov bx,0b800h 
	mov es,bx
	mov bx,1
	mov cx,2000
help_set_back:
	and byte ptr es:[bx],10001111b
	;清空对应位
	shl al,1
	shl al,1
	shl al,1
	shl al,1
	;左移4位,将AL的内容移到前4位
	or es:[bx],al
	;设置背景色
	add bx,2
	loop help_set_back 
	pop es 
	pop cx 
	pop bx 		;恢复现场
	ret

向上滚动一行,借助 rep movsb 指令每次传送一行数据,即 160 字节,共传送 24 行。

roll:
	push cx 
	push si 
	push di 
	push es 
	push ds 
	mov si,0b800h 
	mov es,si 
	mov ds,si 	;ES和DS均指向显示缓冲区
	mov si,160	;ds:si指向第n+1行
	mov di,0	;es:di指向第n行
	cld 		;设置传输方向为正
	mov cx,24	;共移动24行
help_roll:
	push cx 
	mov cx,160
	rep movsb 	;源地址为ds:si,目的地址为es:di,共传送160字节
	pop cx 
	loop help_roll
	mov cx,80	;每列共80个字符
	mov si,0	
help_help_roll:
	mov byte ptr [160*24+si],' '
	;最后一行使用空格填充
	add si,2
	loop help_help_roll
	pop ds 
	pop es 
	pop di 
	pop si 
	pop cx 
	ret

然后将各子程序的入口地址存储在表中,并且它们在表中的位置与其功能号相对应,满足功能号*2=对应子程序的入口地址

setscreen:
	jmp short set 
table:
	dw cls_screen,set_fore,set_back,roll
set:
	push bx 
	cmp ah,3	;判断功能号是否大于3
	ja setret
	mov bl,ah
	mov bh,0
	add bx,bx	;根据功能号得到对应子程序在表中的偏移
	call word ptr table[bx]
	;根据偏移调用对应的子程序
setret:
	pop bx 
	ret

用 AH 传送功能号、对于功能 2 和 3 使用 AL 传送颜色。整体代码为:

assume cs:code 
code segment
start:
	mov ah,1
	mov al,2h
	call setscreen
	mov ax,4c00h
	int 21h
	
setscreen:
	jmp short set 
table:
	dw cls_screen,set_fore,set_back,roll
set:
	push bx 
	cmp ah,3	;判断功能号是否大于3
	ja setret
	mov bl,ah
	mov bh,0
	add bx,bx	;根据功能号得到对应子程序在表中的偏移
	call word ptr table[bx]
	;根据偏移调用对应的子程序
setret:
	pop bx 
	ret
	
cls_screen:
	push bx 
	push cx 
	push es 	;保护现场
	mov bx,0b800h
	mov es,bx
	mov bx,0
	mov cx,2000	;当前屏幕含2000个字符
help_cls_screen:
	mov byte ptr es:[bx],' '
	;写入空格
	add bx,2
	loop help_cls_screen 
	pop es 
	pop cx 
	pop bx 		;恢复现场
	ret
	
set_fore:
	push bx 
	push cx 
	push es 	;保护现场 
	mov bx,0b800h
	mov es,bx 
	mov bx,1
	mov cx,2000
help_set_fore:
	and byte ptr es:[bx],11111000b
	;清除后三位的值
	or es:[bx],al
	;设置前景色
	add bx,2
	loop help_set_fore 
	pop es 
	pop cx 
	pop bx		;恢复现场 
	ret 
	
set_back:
	push bx 
	push cx 
	push es		;保护现场
	mov cl,4
	shl al,cl	;左移4位,将AL的内容移到前4位
	mov bx,0b800h 
	mov es,bx
	mov bx,1
	mov cx,2000
help_set_back:
	and byte ptr es:[bx],10001111b
	;清空对应位
	or es:[bx],al
	;设置背景色
	add bx,2
	loop help_set_back 
	pop es 
	pop cx 
	pop bx 		;恢复现场
	ret
	
roll:
	push cx 
	push si 
	push di 
	push es 
	push ds 
	mov si,0b800h 
	mov es,si 
	mov ds,si 	;ES和DS均指向显示缓冲区
	mov si,160	;ds:si指向第n+1行
	mov di,0	;es:di指向第n行
	cld 		;设置传输方向为正
	mov cx,24	;共移动24行
help_roll:
	push cx 
	mov cx,160
	rep movsb 	;源地址为ds:si,目的地址为es:di,共传送160字节
	pop cx 
	loop help_roll
	mov cx,80	;每列共80个字符
	mov si,0	
help_help_roll:
	mov byte ptr [160*24+si],' '
	;最后一行使用空格填充
	add si,2
	loop help_help_roll
	pop ds 
	pop es 
	pop di 
	pop si 
	pop cx 
	ret
code ends
end start	

0 号功能展示:

请添加图片描述
1 号功能展示:

请添加图片描述

2 号功能展示:

请添加图片描述

3 号功能展示:

请添加图片描述


3. 总结

  • 直接定址表提供了一种快速访问标号或字符的方法,通过待访问元素的索引定位其偏移地址。
  • 在包含多个子程序的程序中,直接定址表可以快速定位各子程序的偏移地址。实验展示了包含 4 个子程序的程序,通过直接定址表实现快速调用。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值