ITEEDU

我们所要讨论的第二个重要的内容就是出现在屏幕上的文字和显示内存中各个字节的对应关系。我们知道每个显示出来的字符都由两部分内容组成,ASCII码和属性字节。其中ASCII码位于显示内存的偶数地址处,而属性字节位于奇数地址处。请看图6-6:

图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:

图6-7 显示页起始地址与内存的对应关系

MODE1.ASM
        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