我们所要讨论的第二个重要的内容就是出现在屏幕上的文字和显示内存中各个字节的对应关系。我们知道每个显示出来的字符都由两部分内容组成,ASCII码和属性字节。其中ASCII码位于显示内存的偶数地址处,而属性字节位于奇数地址处。请看图6-6:
利用DEBUG可以验证以上的结论:
C:\ASM\>DEBUG[Enter] -eb800:0[Enter] 属性 B800:0000 53.41 07.01 45.42 07.41 54.43 07.81 55.44 07.94 B800:0008 50. 字符的ASCII码
输入数据时请注意屏幕左上角是否出现了相应的字符和色彩。最后一个字母D的属性我们没有看到,被滚上去的内容盖掉了。不过这足以说明屏幕上每个文字和显示内存中各个字节的对应关系了。我们现在再来看看每个字符行在显示内存中的起始位置:
C:\ASM\>DEBUG[Enter] -eb800:00a0[Enter] 属性 B800:00A0 57.41 07.01 50.42 07.41 53.43 07.94 20.44 07.82 B800:00A8 20. 字符的ASCII码
可以看到第二个字符行起始于0B800:00A0处,第一行恰好占据了0A0H个字节。由此我们可以推导出屏幕上一个特定位置所对应的显示缓冲区内的偏移量为:
偏移量=行号×160+列号×2(模式2、3) 偏移量=行号×80+列号×2(模式0、1)
如果程序使用了40列25行的模式(模式号0、1),那么每个字符行将占据40×2=80字节显示缓存,每个显示页将占据80×25=2000字节内存。因此,对于模式0、1而言,显示页起始地址与每个字符对应的内存单元如图6-7(a)所示,注意每个显示页起始于2KB内存边界:
如果选择了80列25行的模式(模式号2、3),那么每行将占用160字节内存,每个显示页将占用4KB内存。这时显示页起始地址与每个字符对应的内存单元如图6-7(b)所示:
注意这个偏移量指得是在一个显示页之内,并非是在整个显示缓存中的偏移地址。如果将这个偏移量换算成显示缓存中的偏移量,则还要加上"显示页号×每页占据的字节数"。程序MODE1.ASM演示了如何操纵显示缓存,为了让大家对40列25行的模式有所认识,这个程序特地使用了CGA模式1:
data segment assume ds:data msg db'Enter into the paradise.',0 ;定义一个字符串 mode db ? ;保存显示模式号 attr db 1 ;字符属性 data ends code segment assume cs:code main proc far mov ax,data ;初始化DS寄存器 mov ds,ax mov ah,0fh ;利用10H中断的0FH功能 int 10h ;获得当前的显示模式 mov mode,al ;保存当前的显示模式 mov ax,0001h ;将显示模式设为1号 int 10h mov ax,0b800h ;显示缓冲区段地址置入ES寄存器 mov es,ax mov di,0 ;DI指向缓冲区首 mov si,offset msg ;SI指向字符串MSG dispmsg: lodsb ;取得一个字符 or al,al ;已经到了字符串结尾了吗? jz exit ;已到结尾,转EXIT退出 mov ah,attr ;属性字节送入AH寄存器 stosw ;将ASCII码与属性一同写入显示缓存中 add di,80 ;DI寄存器后移80个字节 inc attr ;属性字节加1 jmp dispmsg ;转DISPMSG继续显示下一个字符 exit: mov ah,0 ;等待键盘输入 int 16h mov ah,0 ;恢复原来的显示模式 mov al,mode int 10h mov ah,4ch ;结束进程 int 21h main endp code ends end main