本节内容:屏幕显示的实现——显示I/O中断处理程序。显示器通过显示适配卡与系统相连,显示适配卡是显示输出的接口。早期显示适配卡是CGA和EGA等,目前常见的适配卡是VGA、TVGA、DVI、DP、Mini HDMI和Micro HDMI接口等。它们都支持两类显示方式:文本显示方式和图形显示方式,每一类显示方式含有多种显示模式。
■文本显示方式:通常0~3号显示模式为文本显示方式,它们之间的区别是每屏可以显示的字符数和可使用的颜色数目不同。用的最普遍的是3号显示模式,本节以3号显示模式为代表做介绍。
■显示属性:显示属性表示字符的文本颜色。8086计算机中,使用一个字显示字符,前一个字节为字符ASCII码,后一个字节为8位颜色属性。
■显示存储区:8086计算机中,显示存储器为系统存储器的一部分,显存分为RAM和ROM两个部分,通常显示存储器安排的存储地址空间的段值为B800H或B000H。把将要输出的字符写入显存的RAM区域就可以在屏幕显示了。
■显示I/O程序的功能和调用方法:参见表16-3。
■举例:显示I/O程序的5号功能、0FH号功能、9号功能、6号功能。
示例代码:t16-2.asm~ t16-4.asm。
16.2.1 文本显示方式
文本显示方式是指以字符为单位显示的方式。字符通常是指字母、数字、普通符号(如运算符号)和一些特殊符号(如菱形块和矩形块)。通常0~3号显示模式为文本显示方式,它们之间的区别是每屏可以显示的字符数和可使用的颜色数目不同。用的最普遍的是3号显示模式,我们就以3号显示模式为代表做介绍。
■3号文本显示模式
显示器的屏幕被划分为80列25行,随意每一屏最多显示2000(80*25)个字符。我们用行号和列号组成的坐标来定位屏幕上的每个可显示位置,左上角坐标规定为(0,0),向右增加列号,向下增加行号,右下角的坐标便是(79,24)。如图16-4所示。
图16-4 显示存储区与显示位置的对应关系
16.2.2 显示属性
屏幕上显示的字符取决于字符代码及字符属性。这里的属性指显示的颜色属性,规定了显示时的特性。
如图:16-5给出了显示时属性字节各位的定义。
在属性字节中,RGB分别表示红绿蓝,I表示亮度,BL表示闪烁。位0~3组合16种前景色,位4~6组合8种背景色。亮度和闪烁只能用于前景。I为1时表示高亮度,I为0时表示普通亮度。BL=1表示闪烁,BL=0不闪烁。
图16-5 属性字节各位定义
表16-3列出了几种典型的属性值。当前景和背景相同时,字符就看不出了。
效果 | BL R G B I R G B | 16进制值 |
黑底蓝字 | 0 0 0 0 0 0 0 1 | 01 |
黑底红字 | 0 0 0 0 0 1 0 0 | 04 |
黑底白字 | 0 0 0 0 0 1 1 1 | 07 |
黑底黄字 | 0 0 0 0 1 1 1 0 | 0E |
黑底亮白字 | 0 0 0 0 1 1 1 1 | 0F |
白底黑字 | 0 1 1 1 0 0 0 0 | 70 |
白底红字 | 0 1 1 1 0 1 0 0 | 74 |
黑底灰白闪烁字 | 1 0 0 0 0 1 1 1 | 87 |
白底红闪烁字 | 1 1 1 1 0 1 0 0 | F4 |
表16-3 属性字的典型值
16.2.3 显示存储区
显示适配卡带有显示存储器RAM,用于存放屏幕上显示文本的ASCII码及属性。显示存储器RAM作为系统存储器的一部分,访问显示存储器与访问普通内存的方法一致。通常,显示存储器安排的存储地址空间的段值为B800H或B000H,对应的内存区域就称为显示存储区。我们假设段值是B800H。在3号文本显示模式下,屏幕上的每一个显示位置依次对应显示存储区中的两个字节单元,这种对应关系如图16-4所示。为了在屏幕上某个位置显示字符,只需要把显示字符的ASCII码及其属性填到显示存储区中对应存储单元即可。
举例
在屏幕的左上角以白底红字显示字符“B”:
……
MOV AX,B800H
MOV DS,AX
MOV BX,0
MOV AL,'B'
MOV AH,74H ;白底红字
MOV [BX],AX
……
如果想了解屏幕上某个显示位置所显示的字符是什么,或显示的颜色是什么,只要从显示储存区中对应存储单元中取出字符的ASCII码和颜色属性即可。
举例
获取屏幕右下角所显示字符的ASCII码和颜色属性:
……
MOV AX,B800H
MOV DS,AX
MOV BX,(80*24+79)*2 ; .386模式下才可以这样写,8086需要分步计算表达式的值
MOV AX,[BX]
……
这种直接存取显示存储器进行显示的方法称为直接写屏。
■例2:直接写屏
动手实验98:采用直接写屏法在屏幕上用多种颜色属性显示字符串“HELLO”。
在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。
算法分析:
第一步:先用一种属性在屏幕上显示指定信息;
第二步:在用户按键后,再换一种属性显示;
第三步:如果按ESC键,结束;
流程图:如图16-6所示。
图16-6 直接写屏并指定颜色
示例代码73:
;例2:采用直接写屏法在屏幕上用多种颜色属性显示字符串“WELOCME”
;先用一种属性在屏幕上显示指定信息,然后在用户按键后,再换一种属性显示,如果按ESC键,结束
;显示子程序ECHO采用了直接写屏方法实现。
;程序名:t16-2.asm
;=============================================================
assume cs:code,ds:data
;常量定义
row=5 ;显示信息的行号
colum=10 ;列号
esckey=1bh ;ESC键的ASCII码值
;数据段
data segment
mess db ' WELOCME ' ;显示信息
mess_len=$-offset mess ;显示信息的长度
colorb db 07h,17h,0fh,70h,74h ;颜色
;colore与colorb存储地址不相同。如果前置则是别名,地址相同
colore label byte
data ends
;代码段
code segment
start:
mov ax,data
mov ds,ax
;取颜色属性首地址
mov di,offset colorb -1
nextc:
inc di ;调整指针
cmp di,offset colore
jnz nexte
mov di,offset colorb
nexte:
mov bl,[di] ;取颜色
;参数
mov si,offset mess
mov cx,mess_len
mov dh,row
mov dl,colum
;显示字符串
call echo
;接收键盘输入
mov ah,0
int 16h
cmp al,esckey
jnz nextc
mov ax,4c00h
int 21h
;------------------------------------------------------
;子程序名:echo
;功能:直接写屏显示字符串
;入口参数:DS:SI=字符串首地址,
;CX=字符串长度,BL=属性,
;DH显示开始行号,DL=显示开始列号
;出口参数:无
echo proc
mov ax,0b800h
mov es,ax
;设置显示开始位置的偏移,每屏80列25行
mov al,80
mul dh ;偏移=(行号×80+列号)×2
xor dh,dh
add ax,dx ;ax+列号
add ax,ax ;ax*2
xchg ax,bx ;偏移送入bx,属性送入ax
mov ah,al ;属性保存AH
jcxz echo2 ;显示信息长度是否为0
echo1:
mov al,[si] ;取一个要显示字符代码
inc si
mov es:[bx],ax ;送显示存储区,即显示
inc bx
inc bx
loop echo1 ;循环显示
echo2:
ret
echo endp
code ends
16.2.4 显示I/O程序的功能和调用方法
BIOS提供的显示I/O程序为10H中断处理程序。显示I/O程序的主要功能如表16-3所示。
功能 | 入口参数 | 出口参数 | 说明 |
AH=0 | AL=显示模式代号 | 可设置的显示模式与适配卡相关 | |
AH=1 | CH低4位=光标开始线 | 文本模式下光标大小;当第4位为1时光标 不显示 | |
AH=2 | BH=显示页号 | 左上角坐标是(0,0) | |
AH=3 | BH=显示页号 | CH=光标开始行 | CH和CL含光标类型左上角坐标是(0,0) |
AH=5 | AL=新页号 | ||
AH=6 | AL=上滚行数 | 1.当滚动行数为0时表示清除整个窗口 | |
AH=7 | AL=下滚行数 | 同AH=6 | |
AH=8 | BH=显示页号 | AH=属性 | |
AH=9 | BH=显示页号 | 光标不移动 | |
AH=0AH | BH=显示页号 | 1.光标不移动 | |
AH=0EH | BH=显示页号 | 光标处显示字符并后移光标; 解释回车、换行、退格和响铃等控制符 | |
AH=0FH | AL=显示模式号 | ||
AH=13H | AL=写模式 | 1.解释回车等控制符 1表示串中只含有字符代码 |
表16-3 10H号中断处理程序的基本功能
16.2.5 举例
举例
1.调用10H号中断的5号功能,选择第0页作为当前显示页:
……
MOV AL,0
MOV AH,5
INT 10H
……
2.调用10H号中断的0FH号功能,获取当前显示页号、当前显示模式和该模式下的最大显示列数:
……
MOV AH,0FH
INT 10H
……
3.调用10H号中断的09H号功能,在当前光标位置处显示指定属性的字符,但不移动光标:
……
MOV BH,0 ;第0页
MOV BL,47H ;红底白字
MOV CX,1 ;1个
MOV AL,'A' ;字符为A
MOV AH,9
INT 10H
……
4.窗口滚屏时,如果滚屏行数为0,表示清除整个窗口。如果把整个屏幕作为窗口,那么就可以实现清屏。调用10H号中断的6H号功能,清除屏幕,然后把光标定到左上角:
……
MOV CH,0 ;置左上角坐标
MOV CL,0
MOV DH,24 ;置右下角坐标
MOV DL,79
MOV BH,07H ;清除区的填充属性-黑底白字
MOV AL,0 ;清除行数为0,清整个窗口
MOV AH,6
INT 10H
MOV BH,0 ;设第0页
MOV DH,0 ;置光标定位坐标
MOV DL,0
MOV AH,2
INT 10H ;定位光标
……
例3:屏幕中间位置窗口显示
动手实验99:写一个程序,在屏幕中间部位开出一个窗口,从窗口的最底行开始接收并显示用户按键,当窗口底行显示满时,窗口内容向上滚动一行后,继续显示。
在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。
算法分析:
第一步:选择显示页;
第二步:清屏;
第三步:定位光标左下角;
第四步:接收键盘输入,并判断是否结束;
第五步:显示字符,不带属性;
第六步:判断是否越界,如越界重置光标,上滚一行;
流程图:如图16-7所示。
图16-7 屏幕中间位置窗口显示
示例代码74:
;例3:写一个程序,在屏幕中间部位开出一个窗口,从窗口的最底行开始接收并显示用
;户按键,当窗口底行显示满时,窗口内容向上滚动一行后,继续显示
;用户按CTRL+C键时,结束运行
;程序名:t16-3.asm
;-----------------------------------------
assume cs:code
;常量定义
winwidth=40 ;窗口宽度
wintop=8 ;窗口左上角行号
winleft=20 ;窗口左上角列号
winbottom=17 ;窗口右下角行号
winright=winleft+winwidth-1 ;窗口右下角列号
color=74h ;属性值,白底红字
pagen=0 ;显示页号
ctrl_c=03h ;结束符ASCII码
;代码段
code segment
start:
;选择显示页
mov al,pagen
mov ah,5
int 10h
;清屏
mov ch,wintop
mov cl,winleft
mov dh,winbottom
mov dl,winright
mov bh,color
mov al,0
mov ah,6
int 10h
;定位光标左下角
mov bh,pagen
mov dh,winbottom
mov dl,winleft
mov ah,2
int 10h
;接收键盘输入
next:
mov ah,0
int 16h
;判断是否结束
cmp al,ctrl_c
jz exit
;显示字符
mov bh,pagen
mov cx,1
mov ah,0ah ;不带颜色属性
int 10h
;
inc dl
;判断是否越界
cmp dl,winright+1
jnz setcur
;上滚一行
mov ch,wintop
mov cl,winleft
mov dh,winbottom
mov dl,winright
mov bh,color
mov al,1
mov ah,6
int 10h
;重置光标
mov dl,winleft
setcur:
mov bh,pagen
mov ah,2
int 10h
jmp next
exit:
mov ax,4c00h
int 21h
code ends
■例4:改写示例代码69中的子程序
动手实验100:在屏幕上用多种属性显示字符串“WELCOME”。
在理解下面示例程序的基础上,自己独立编写源程序。编译完成后,在debug调试器中单步跟踪调试,以验证程序的正确性。
示例代码75:
;方法一:调用I/O程序中的13H号功能直接显示字符串。
;子程序名:echoa
;功能:调用I/O程序中的13H号功能直接显示字符串。
;入口参数:DS:SI=字符串首地址
;CX=字符串长度,BL=属性
;DH=显示开始行号,DL=显示开始列号
;出口参数:无
echoa proc
push es
push bp
push ds
pop es
mov bp,si ;满足13H功能入口参数要求es:bp为要写串首地址
mov bh,0 ;指定显示页
mov al,0 ;采用0号显示方式(不移动光标)
mov ah,13h ;字符串中不含属性,主程序BL已经有属性值
int 10h
pop bp
pop es
ret
echoa endp
该子程序与ECHO稍有不同:第13H功能解释回车和换行等控制码,所以如果要显示的字符串中含有这样的控制码,那么显示效果就会有差异,第二是当显示超出最后一行的最后一列时,13H功能就要引起滚屏。
;方法二:先调用显示I/O程序的2号功能把光标定到指定位置,然后利用显示I/O程序的TTY显示功能逐个
;显示字符串中的字符,但是由于TTY方式显示不含属性,所以先调用9号功能把指定属性写到显示字符串的
;位置处
;子程序名:echob
;---------------------------
echob proc
jcxz echob2
mov bh,0 ;显示页号
mov ah,2 ;置光标位置
int 10h
mov al,20h ;用指定属性写一串空格,CX长度是重复字符串个数
mov ah,9
int 10h
mov ah,0eh ;tty显示功能
echo1:
mov al,[si]
inc si
int 10h ;逐个显示字符
loop echo1
echob2:
ret
echob endp
除了这两个方法之外,是否还存在其他方法呢?请读者独立思考。