HGA MCGA
HGA+(Hercules图形卡增强型) VGA
Hercules InColor卡 SVGA
CGA卡
在SuperVGA适配卡中,要进一步识别生产厂家和芯片的类型。
下列显示器类型也能识别:
与MDA兼容的(单色)
与CGA兼容的
EGA兼容的
PS/2兼容、单色的
PS/2兼容、彩色的
最后,这些过程就探测到了两个视频硬件系统并区分哪个是活动的,哪个是非活动
的。
识别视频适配卡首先是尝试VGA或EGA特有的视频BIOS调用。如果这些调用成
功了,再继续完成下列两项任务:识别EGA或VGA BIOS没有注意的CGA或MDA卡,
识别(如果是VGA适配卡)潜在的SuperVGA适配卡。
通过探测CRTC。可以识别CGA或MDA适配卡。
MDA的CTRC状态端口通常是在I/O地址3B4h处,而CGA则在3D4h处。该程序
假定地址正确,那么就试着向光标位置低寄存器写入值。在短暂的耽搁后,如果数值能被
读回来,就可以假定CRTC状态端口已经找到并且CGA或MDA已经定位。
如果探测到了是MDA适配卡,那么可进一步识别它是不是一个普通的MDA或
HGA。这要利用MDA和HGA之间的差异:在MDA中,垂直同步位从不改变,而在HGA
88页
中则会改变。HGA能进一步划分为HAG+、Hercules InColor(内部颜色)卡,或规则的
HGA,要做到这一点可以观察状态端口的某些位。
探测并识别SVGA是一个有趣的问题。开发商们在开始仿制IBM的EGA和VGA
卡时,他们制成的适配卡确实不能与原来的IBM适配卡区分开来。但是开发商以他们自
己的方法来开发高级VGA(Super VGA)。每个适配卡都支持一个800*600视频方式(通
常这是确定一个适配卡是VGA或是SVGA的标准),但达到此目的却是以各自不同的、
独有的方式来进行的。每个适配卡还支持一些特有的分辨率或提供一些特殊功能,而别的
适配卡是做不到这些的。类似地,不同的开发商用来识别每个高级VGA适配卡的方法是
各不相同的。识别某个特定开发商的适配卡的技术将在每个适配卡的过程中进行描述。将
SVGA从VGA中分离出来需要尝试每一项SVGA识别技术;如果所有技术都未成功,那
么这就是一个简单VGA适配卡。
列表5.1是用于识别视频适配卡的程序(注意:这并非是一个唯一标准的程序)。
列表5.1
page 55,132
name video_id
; video_id.asm
;Procedure for identifying the video system(s)
;-----Equates-----
;Adapter types
UNKNOWN_ADAPTER equ 000h
CGA equ 001h
MDA equ 002h
EGA equ 003h
MCGA equ 004h
VGA equ 005h
SVGA equ 006h
VESA SVGA equ 007h
ADAPTER_SYSTEM_ MASK equ 007h
;Monitor types
UNKNOWN MONITOR equ 000h shl 3
MDA_MONITOR equ 001h SHL 3
CGA_MONITOR equ 002h SHL 3
EGA_MONITOR equ 003h SHL 3
VGA_MONO equ 004h SHL 3
VGA COLOR equ 005h SHL 3
MONITOR_MASK equ 007h SHL 3
;SVGA BIOS types
UNKNOWN_BIOS equ 000h SHL 6
AHEAD equ 001h SHL 6
ATI equ 002h SHL 6
CIRRUS equ 003h SHL 6
CTI BASED equ 004h SKL 6
GENOA equ 005h SHL 6
HEADLAND equ 006h SHL 6
EVEREX equ 007h SHL 6
PARADISE equ 008h SHL 6
TSENG BASED equ 009h SHL 6
89页
; video chip types
UNKNOWN_CHIP equ 000h SHL 10
HGC equ 001h SHL 10
HGCPLUS equ 002h SHL 10
HERCULESINCOLOR equ 003h SHL 10
AHEAD_VERSION_A equ 001h SHL 10
AHEAD_VERSION_B equ 002h SHL 10
ATI18800REV1 equ 001h SHL 10
ATI18800REV2 equ 002h SHL 10
ATI18800w18810 equ 003h SHL 10
CIRRUS_510_520 equ e01h SHL 10
CIRRUS_610_620 equ 002h SHL 10
CIRRUS_VSEVEN equ 003h SHL 10
CTI82c451 equ 001h SHL 10
CTI82c452 equ 002h SHL 10
CTI82C453 equ 003h SHL 10
TRIDENT_8800BR equ 001h SHL 10
TRIDENT_8800CS equ 002h SHL 10
; Start of simplified directives
; Change the model size to match the program in which
; these will be used
.model small
. data
PrimarySystem dw 0000 ; Primacy video system
SecondarySystem dw 0000 ; Secondary video system
adapter_table db UNKNOWN_ADAPTER OR UNKNOWN_MONITOR
db MDA OR MDA_MONITOR
db CGA OR CGA_MONITOR
db UNKNOWN_ADAPTER OR UNKNOWN_MONITOR
db EGA OR EGA_MONITOR
db EGA OR MDA_MONITOR
db UNKNOWN ADAPTER OR UNKNOWN_MONITOR
db VGA OR vga_MONO
db VGA OR VGA_COLOR
db UNKNOWN_ADAPTER OR UNKNOWN_MONITOR
db MCGA OR EGA_MONITOR
db MCGA OR VGA_MONO
db MCGA OR VGA_COLOR
db (16 - 13) dup (0)
ega_table db EGA OR CGA_MONITOR
db EGA OR EGA_MONITOR
db EGA OR MDA_MONITOR
db EGA OR CGA_MONITOR
db EGA OR EGA_MONITOR
db EGA OR MDA_MONITOR
db (8 - 6) dup (EGA OR UNKNOWN_MONITOR)
AHEAD_BIOS_Sig db "AHEAD"
ATI_BIOS_Sig db "761295520"
ATI_BIOS_Sig2 db "31"
CIRRUS_BIOS_Sig db "CL"
GENOA_BIOS_Sig db 77h , 11h , 99h , 66h
PARADISE_BIOS_Sig db"VGA="
VESA_BIOS_Sig db "VESA"
scratch_pad db 256 dup (?)
90页
;-----Program Code - - - - -
. code
; Main procedure for identifying the video systems
video_ident proc
push bx
push cx
push ds ;save caller\'s DS
mov ax , seg video_ident
mov ds,ax ,get new DS
; Initialize structures
mov PrimarySystem,0
mov SecondarySystem,0
; Test for VGA presence
mov ax,01A00h ; Read display code\'s function
int 10h ; call video BIOS
cmp al,01Ah ; Successful call?
jne no_vga_present
; we have VGA at least
or bh,bh ; Secondary system detected?
jz no_secondary
; We have a secondary system present
push bx ; Preserve bx
mov al,bh ; Put secondary type in AL
and al , 00Fh
mov bx,offset adapter_table ; Get lookup table
xlat
xor ah,ah ; Clear byte
or SecondarySystem,ax ; Set flags
pop bx ; Restore bx
; what iS the primary System?
no_secondary :
mov al,bl ; Put primary type in AL
and al , 00Fh
mov bx,offset adapter_table ; Get lookup byte
xlat
xor ah,ah ; Clear byte
or PrimacySystem,ax ; set flags
and ax , ADAPTER_SYSTEM_MASK ; What adapter?
cmp ax,VGA , Did we detect a VGA?
je PVGA_detected
mov ax,SecondarySystem ; How about the Secondary?
and ax, ADAPTER_SYSTEM_MASK
cmp ax , VGA
jne noVGA_detected
mov bx,offset SecondarySystem
jmp short detect_SVGA
pVGA_detected :
mov bx,offset PrimarySystem
; Attempt to detect SVGA Systems
detect_SVGA: call SVGA_detect
; Although enhanced video BIOS call succeeded, no VGA was found
noVGA_detected :
mov ax,SecondarySystem ; Is secondary system MDA?
and ax , ADAPTER_SYSTEM_MASK
cmp ax,MDA ; Is secondary system MDA?
je clarify_MDA ; If it iS, identify further
91页
mov ax,PrimarySystem ;How about the primary?
and ax ,ADAPTER_SYSTEM MASK
cmp ax,MDA ; Is secondary system MDA?
jne swap_systems
clarify_MDA :
call Hercules_detect ; Identify the specific MDA system
jmp short swap_systems
; The enhanced video BIOS call failed
no_vga_present :
mov bl,010h ; Attempt tO find EGA
mov ah , 012h
int 10h
cmp bl,010h ; Call failed?
je no_ega_present
mov bx,offset ega_table
mov al,cl ; Get switch settings
shr al , 1
and ax , 7
xlat
or PrimacySystem,ax ; Set values
and ax ,ADAPTER_SYSTEM_MASK ; What did we find?
cmp ax ,MDA ; MDA?
je seek_CGA ; Then look for a CGA
call MDA_detect , Else look for MDA
jmp short swap_systems
Seek_CGA:
Call CGA_detect
jmp short swap_systems
; EffOrts to find EGA failed too
no_ega_present :
Call CGA_detect ; Seek CGA
Call MDA_detect ; and MDA
; May need to swap primany/secondary systems
swap_systems :
mov ax,SecondarySystem , Get current mode
and ax , ADAPTER_SYSTEM_MASK
Cmp ax , UNKNOWN_ADAPTER
je vid_exit
cmp ax , MCGA
jae vid_exit
mov ax , Primarysystem
and ax , ADAPTER_SYSTEM_MASK
cmp ax , MCGA
jae vid_exit
mov ah,00Fh ; Get current video mode
int 10h
and al , 7
cmp al,7 , Current mode is mono?
jne current_color
mov ax , PrimarySystem
and ax , MONITOR_MASK
cmp ax , MDA_MONITOR
je vid_exit
dO_swap :
mov ax , PrimarySystem
xchg ax ,SecondarySystem
mov PrimarySystem,ax
jmp short vid_exit
current_color:
92页
mov ax,primarySystem
and ax , MONITOR_MASK
cmp ax , MDA_MONITOR
je do_swap
; Return to caller
vid_exit:
mov ax,PrimarYSystem ; Set return values
mov dx,SecondarySystem
pop ds ; Restore caller\'s DS
pop cx
pop bx
ret ; Return
video_ident endp
; Routine tO detect SVGA cards
; on entry, BX is a pointer to the VGA system word
SVGA_detect proc
push ax ; Save registers
push cx
PUSh di
push dx
push si
push es
mov ax,0c000h ; Point to ROM
miv es , ax
mov di,00025h ; Signature is at C000:0025
mov si , Offset AHEAd_BIOS_sig
mov cx,5 ; Length of Signature
cld
repe cmpsb
jne not_ahead_bios
; It\'s an AHEAd BIOS-now identify which chip version
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,SVGA OR AHEAD
mov dx,003CEh ; Get i/o address
mov al,00Fh , Get index of enable register
out dx,al
inc dX , Get I/O address Of data
mov al,020h ; Get enable value
out dx,al
jmp $+2
in al,dx ; Get value
cmp al,020h ; Version A?
je ahead_a
cmp al,021h ; Version B?
jne test_vesa_isle
or word ptr [bx] , AHEAd_VERSION_B
jmp short test_vesa_isle
ahead_a:
or word ptr [bx] ,AHEAD_VERSION_A
test_vesa_isle:
jmp test_vesa
; Wasn\'t AHEAD. Test for ATI
not+ahead_bios:
mov di,00031h ; ATI signature at C000:0031
mov si , Offset ATI_BIOS_Sig
mov CX , 9
repe cmpsb
jne not_ati_bios
93页
; So far, so good. . .
mov di,00040h ; "31" at C000:0040
mov si , off set ATI_BIOS_sig2
mov cx , 2
repe cmpsb
jne not_ati_bios
; It\'s an ATI BIOS- -now identify the chipset
and word ptr [ bx ] ,NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,SVGA OR ATI
mov al , es : 043h
cmp al, \'1\' ; Which Chipset?
je ati_1
cmp al , \'2\'
je ati_2
cmp al , \'3\'
jne test_vesa_isle
or word ptr [ bx ] ,ATI18800W18810
jmp test_vesa
ati_1 :
or word ptr [bx] ,ATI18800REV1
jmp test_vesa
ati_2:
or word ptr [bx] ,ATI18800REV2
jmp test_vesa
; Now check for Cirrus
not_ati_bios:
mov di,6
mov si , Offset CIRRUS_BIOS_Sig
mov cx , 2
repe cmpsb
jne not_cirrus_bios
; It\'s a Cirrus BIOS- -now identify the chip
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,SVGA OR CIRRUS
xor ax,ax , Find CRTC
mov es , ax
mov dx,es :00463h
push dx
mov al,00Ch , Get Start address
Out dx,al
inc dx
in al,dx ; Read register
mov ah,al ; Save what we read
mov al , 00Ch
push ax
xor al,al ; Clear register
Out dx,al
dec dx
mov al,01Fh , Get iD register
out dx,al
inc dx
in al,dx ; Read unlock password
mov ch,al ; Save it--it\'s the key to the chip ID
mov dx,003C4h , Address of sequencer .
mov al,006h , Extension control reg
out dx,al
inc dx
mov al,ch ; Get unlock password
out dx,al
in al,dx ; Read it back
cmp al, 1 ; Unlocked?
94页
jne not_cirrus_chip
mov al,ch
mov cl,4
ror al , cl
out dx,al
in al , dx
or al,al ; Locked?
jnz not_cirrus_chip
cmp ch ,0ECh ; 510/520?
jne not_cirrus_510
or word ptp [ bx ] , CIRRUS_510_520
jmp short not_circus_chip
not_cirrus_510:
cmp ch , 0CAh ; 610/ 620?
jne not_cirrus_610
or word ptr [bx] ,CIRRUS_610_620
jmp Short not_Cirrus_Chip
not_cirrus_610:
cmp ch,0EAh ; Video seven?
jne not_cirrus_chip
or word ptr [bx] ,CIRRUS_VSEVEN
not_cirrus_chip:
pop ax ; Restore CRTC
pop dx
out dx,ax
jmp test_vesa
; Check for CTI
not_cirrus_bios:
cli ; Disable interrupts
mov dx,046E8h ; Put chip in setup mode
in al , dx
or al,010h
out dx,al
mov dx,00103h ; Read extended enable register
in al , dx
or al,080h ; Turn enable on
out dx,al
inc dx ; Read global ID
in al , dx
mov ah,al , Save it
mov dx,046E8h ; Turn setup back off
in al , dx
and al , 0EFh
out dx,al
sti ; Reenable interrupts
mov dx,003D6h ; Read version
xor al,al
out dx,al
inc dx
in al , dx
cmp ah,0A5h ; Right global ID?
jne not_cti_bios
; Seems to be a CTI chip. Which version?
and al,0F0h ; Check version
cmp al , 000h
jne not_cti82C451
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
Or word ptr [bx] ,CTI82C451 OR SVGA OR CTI_BASED
jmp test_vesa
not cti82c451:
cmp al , 010h
jne not_cti82C452
and word ptr [bX] , NOT ADAPTER_SYSTEM_MASK
95页
or word ptr [bx] ,CTI82C452 OR SVGA OR CTI_BASED
jmp test_vesa
not_cti82c452 :
cmp al , 030h
jne not_cti_bios
and word ptp [ bx] ,NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,CTI82C453 OR SVGA OR CTI_BASED
jmp test_vesa
; Check for Genoa
not_cti_bios:
mov di,0037h ; Read pointer
les di,es:[di] , Dereference it
mov Si , Offset GENOA_BIOS_Sig
mov cx , 4
repe cmpsb
jne not_genoa_bios
and word ptr [ bx ] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,GENOA OR SVGA
jmp test_vesa
Try Headland
not_genoa_bios:
push bx
xor bx,bx ; Special Headland BIOS call
mov ax , 06F00h
int 10h
cmp bx,\'7\'
jne not_headland
mov ax , 06F07h
int 10h
Cmp bl,070h
jb not_headland
cmp bl,07Fh
ja not_headland
pop bx
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,HEADLAND OR SVGA
jmp Short test_vesa
not_headland:
pop bx ; Restore pointer
; Try Trident/Everex
push bx , Save pointer
mov ax,07000h , Extended BIOS call
xor bx,bx
int 10h
cmp al,070h ; OK?
jne not_everex_bios
and dx,0FFF0h ; Get board number
cmp dx,06780h
jne_not_everex_bios
pop bx
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,EVEREX OR SVGA
jmp short test_trident
not everex_bios:
pop bx
test_trident:
; Identify the chip itself
mov dx,003C4h
mov al , 00Bh
96页
Out dx,al
inc dx
in al , dx
and al , 00Fh
cmp al , 1
je found_8800BR
cmp al ,2
jne not_trident
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,TRIDENT_8800CS OR SVGA
jmp short test_vesa
found 8800BR:
and word ptr [bX] ,NOT ADAPTER_SYSTEM_MASK
Or word ptr [bx] ,TRIDENT_8800BR OR SGA
jmp short test_vesa
;Try Paradise
not_trident:
mov ax , 0C000h
mov es ,ax
mov di,0007Dh
mov si , OffSet PARADISE_BIOS_Sig
mov cx, 4
repe cmpsb
jne not_paradise
and word ptr [bx] ,NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] ,SVGA OR PARADISE
jmp Short test_vesa
; Try Tseng
not_paradise :
mov dx,003CDh
in al , dx
mov ah , al
and al , 0C0h
or al , 055h
Out dx,al
in al , dx
cmp al , 055h
jne test_vesa
mov al , 0AAh
out dx,al
in al , dx
cmp al , 0AAh
jne test_vesa
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
Or word ptr [bx] ,SVGA OR TSENG_BASED
; Test for VESA BIOS
test_vesa: mov ax,04F00h
mov di,offset scratch_pad
push ds
pop es
int 10h
Cmp ax , 0004Fh
jne svga_exit
Cld
mov Si , offset VESA_BIOS_Sig
mov cx , 4
repe cmpsb
97页
jne svga_exit
and word ptr [bx] , NOT ADAPTER_SYSTEM_MASK
or word ptr [bx] , VESA_SVGA
svga_exit :
pop es
pop si
pop dx
pop di
pop cx
pop ax
ret
SVGA_detect endp
; Routine tO detect Hercules MDA card
Hercules_detect proc
push si
push dx
push cx
push bx
push ax
mov si , offset PrimarySystem
mov ax , [ si ]
and ax , ADAPTER_SYSTEM_MASK
cmp ax , MDA
je test_Hercules
mov si,offset SecondarySystem
test_Hercules:
mov dx,003BAh ; Get status port
in al , dx
and al,080h ; Save VSYNC bit
mov bl,al ; Save it
XOP CX , CX
VSYNC :
in al,dx ; Read port
mov ah , al
and ah,080h ; Isolate VSYNC
cmp ah , bl
jne found_Hercules
LOOp VSYNC
jmp Short Herc_exit
found_Hercules:
; Now that we found it, try to identify which board
and al , 070h
cmp al,010h ; HGCPlus?
jne not_HGCPlus
or word ptr [Si] ,HGCPLUS
jmp Short Herc_exit
not_HGCPlus:
cmp al , 050h
jne is_HGC
or word ptr [si] , HERCULESINCOLOR
jmp short Herc_exit
iS_HGC:
or word ptr [si] ,HGC
Herc_exit :
pop ax
pop bx
pop cx
pop dx
pop si
ret
98页
Hercules_detect endp
; Attempt to detect MDA card
MDA_detect proc
mov dx,003B4h ; If MDA, CRTC iS 3B4h
Call CRTC_detect
jne no_MDA
mov ax,PrimarySystem ; Found MDA; primary or secondary?
and ax,ADAPTer_SYSTEM_MASK
cmp ax , UNKNOWN_ADAPTER
je MDA_Primary
or SecondarySystem ,MDA OR MDA_MONITOR
jmp short found_MDA
MDA_primary:
or PrimarySystem , MDA OR MDA_MONITOR
found_MDA:
call Hercules_detect ; See if it\'s a Hercules board
no_MDA:
ret
MDA_detect endp
; Attempt to detect CGA card
CGA_detect proc
mov dx,003D4h ; If CGA, CRTC iS 3D4h
call GRTC_deteCt
jne CGA_exit
mov ax,PrimarySystem ; Found CGA; primary or secondary?
and ax , ADAPTER_SYSTEM_MASK
cmp ax,UNKNOWN_ADAPTER
je CGA_primary
or SecondarySystem , CGA OR CGA_MONITOR
jmp short CGA_exit
CGA_Primary:
or PrimarySyStem , CGA OR CGA_MONITOR
CGA_exit :
cet
GGA_detect endp
; Attempt to detect the CRT controller
; on entry, DX is the alleged CRTC port
; If detected, r.etucns Z set; else Z ceset
CRTC_detect proc
push dx ; Save registers
push cx
push bx
push ax
mov al,00Fh ; Select (alleged) cursor low
out dx , al ; register
inc dx ; Select register
in al,dx ; Read old value
mov bl,al ; Save it in BL
mov al,066h ; Write new value
out dx,al
xor cx,cx ; Loop a while
CRTC_Delay :
nop
loop CRTC_Delay
in al,dx ; Get new value
mov bh,al ; Save it
mov al,bl ; Get old value
Out dx,al ; Write it out
Cmp bh,066h ; Is new value correct?
pop ax ; Restore registers
99页
pop bX
pop CX
pop dX
ret
CRTC detect endp
end
5.3视频功能
目前,我们已经了解了屏幕显示的工作方式,那么就可以试一试简单的功能,来看看
这些功能是怎样发挥作用的。视频功能象本书介绍的其它主题一样,它们也是在DOS和
BIOS中才有用的。但与其它的编程范畴不同的是,视频功能的优势只与BIOS相关。没有
任何DOS服务能用于屏幕控制;只有几个DOS服务能用于信息的屏幕显示。
DOS服务用起来更简单。每个DOS服务提供简单的输出机制,它能够重定向并与所
有的系统操作兼容。
BIOS服务对于不直接存取视频内存的重要编程,它们通常是被选择的功能。它们不
仅提供视频系统的广泛控制,而且要比DOS服务更快和灵活得多。 BIOS服务提供了对光
标、显示属性和其他控制的存取。
当用户盼望得到给CRT的最快的、可能的输出并且想避免使用BIOS以保持与普通
的MS-DOS系统尽可能多的兼容性时,DOS的一个未公开的功能Int 29h已被证明是很
方便的功能。 Int 29h不象未公开的DOS输出功能那样,它将字符放到屏幕上时并不每次
都检查Ctrl-C,所以它比较快。
要使用Int 29h,可将要显示的字符放进AL。寄存器,然后引入中断:
mov al,‘A’
int 29h
与其他的服务不同,Int 29h使用业已存在的任何屏幕属性并且只识别响铃(bell,
ASCII码07),cR(ASCII码13)和LF(ASCII码10)字符;所有其他的“控制”字符就会显
示在屏幕上。字符放置在最后一行的最后一列时,或者光标在最后一行的任意位置,此时
送出了LF,这两种情况下,屏幕都会自动地卷起一行。
因为Int 29h并未公开,所以在未来的DOS版本中可以取消它,但这不太可能,因为
Int 29h是DOS使用得最多的方法(从2.0版本开始)并且因为它是ANSI.SYS替换显示
驱动程序系统的内存部分。可是在使用它之前,应该估计一下所冒的风险(这就象使用别
的未公开的功能一样)。
记住:DOS和BIOS只是为产生屏幕提供了构件块。程序员的想象力和技巧是使程序
运行正常或看起来简捷的主要原因。如果用户所希望产生的显示能力不能与编程技术相
一致的话,那么他的显示器就会是徒有其表的。
5.3.1利用DOS和BIOS视频功能编程
在这一节,读者可以构造一些简单的窗口功能,以便看看BIOS和DOS功能的使用
100页
是多么地容易。本章的目的并非想建立一个完整的窗口系统—这种努力超出了本书的
范围。依据一些用来分析BIOS和DOS功能使用的简单的窗口显示功能,可以检查屏幕
操作。
testscn.c 程序是一个简单的测试,它先在屏幕上填满数据,然后清除屏幕中心的那个
窗口(见列表5.2)。可以向屏幕书写更多的数据,然后将原来的窗口数据返回并卷起来。
列表5.2
/* Testscn.c
Listing 5.2 in DOS Programmer\'s Reference*/
#include<stdio.h>
#include<dos.h>
/* Prototypes */
Void Savewin(int lr,int lc, int rr, int rc);
void clearwin( int lr,int lc, int rr,int rc);
void putwin( int lr, int lc, int rr,int rc);
void border( int lr,int lc, int rr,int rc);
void upwin( int n, int lr, int lc, int rr,int rc);
void gotoxy( int r, int c);
void cls(void);
void main()
{
int i;
/*Display a screen full of lines*/
Cls();
for(i=0;i<50; i++)
printf("DOS Programmer\'s Reference ");
/* Save the data in the rectangle(5,5) tO (12,40), and
then clear that area and put a border around it.*/
Savewin(5,5,12,40);
clearwin(5,5,12,40);
border(5,5,12,40);
/* Wait 5 seconds and then scroll the screen again.
(Note: Everything scrolls, including the window.)*/
sleep(5);
gotoxy(24,0);
for(i=0; i<50; i++)
printf("This is the Second Screen of the Demo ");
/* Wait 5 seconds and then Clear the window and fill it.*/
sleep(5);
clearwin(5,5,12,40);
putwin(5,5,12,40);
/* Scroll the inside of the window up one line every
2 seconds for 10 steps,*/
for(i=0;i<10; i++) {
sleep(2);
upwin(1,6,6,11, 39);
}
/* Finally,clear the screen again and then end the
program. */
Cls();
}
testscn.c 建立在文件windows.c、screen.c 和chario.c 所包含的3个简单功能集合体
101页
之上。window.c(见列表5.3) 操纵着testscn.c所调用的窗口功能。借助于window.c中的
函数,可以完成下列工作:
savewin() 将窗口当前数据保存起来
clearwin() 清除窗口
putwin() 把数据放进某个窗口
border() 围绕窗口设置一个边界
upwin() 将窗口往上滚动
这个小的函数集合体没有进行高级复杂的尝试。它只操纵单一的、未覆盖的窗口,如
那些可能用于显示帮助信息的窗口。因为它没有使用比BIOS功能更低的东西,所以它与
其他环境,如DESQview兼容。
列表5.3
/* Window.c
Listing 5.3 of DOS Programmer\'S Reference*/
#include<stdio.h>
#include<dos.h>
#define VIDEO 0x10
/* Basic screen-size definitions*/
#define LINES 24
#define COLS 80
/* Prototypes*/
void gotoxy(int i,int j);
void rch(char *Ch, char *attr);
void wch(char ch,char attr);
void border(int lr,int lc, int rr, int rc);
/* Structure for each Character position--character and
attribute.*/
Struct charpos{
char ch;
char att;
};
/* The screen is made up of LINES*COLS character positions*/
Struct Charpos Screen[LINES][COLS];
/* Function: Savewin()*/
void savewin(lr,lc,rr,rc)
int lr,lc,rr,rc;
{
int i,j;
for(i=lr;I<=rr;i++)
for(j=lc;j<=rc;j++){
gotoxy(i,j);
rch(&screen[i][j].ch,&screen[i][j].att);
}
}
/* Function: Clearwin()*/
void Clearwin(lr, lc, rr, rc)
int lr, lc, rr,rc;
{
union REGS regs;
102页
regs.h.ah = 0x06,
regs.h.al = 0;
regs.h.bh = 7;
regs.h.ch=lr;
regs.h.cl = lc,
regs.h.dh=rr;
regs.h.dl=rc;
int86(VIDEO, ®s, ®s);
}
/* Function: putwin() */
void putwin(lr,lc,rr,rc)
int lr, lC, rr, rc;
{
int i, j;
for(i=lr;i<=rr;i++)
for(j=lc; j<=rc; j++) {
gotoxy(i, j);
wch(screen[i][j].ch, screen[i][j].att);
}
border(lr,lc,rr,rc);
}
#define VERTLINE 186
#define UPPERRIGHT 187
#define LOWERRIGHT 188
#define LOWERLEFT 200
#define UPPERLEFT 201
#define HORIZLINE 205
/* Function: border() */
void border(lr,lc,rr,rc)
int lr,lc,rr,rc;
{
int i,j;
for(i=lr;i<=rr;i++) {
gotoxy(i,lc); wch(VERTLINE, 7);
gotoxy(i, rc); wch(VERTLINE, 7);
if(i==lr||i==rr) {
for(j=lc; j<=rc;j++) {
gotoxy(i,j);
wch(HORIZLINE, 7);
}
if(i==lr) {
gotoxy(lr, lc); wch(UPPERLEFT, 7);
gotoxy(lr, rc); wch(UPPERRIGHT, 7);
}
if(i==rr) {
gotoxy(rr,lc); wch(LOWERLEFT, 7);
gotoxy(rr,rc); wch(LOWERRIGHT, 7);
}
}
}
}
/* Function: upwin() */
void upwin(n,lr,lc,rr,rc)
int n,lr,lc,rr,rc;
{
union REGS regs;
regs.h.ah=0x06;
regs.h.al=n;
103页
regs.h.bh=7;
regs.h.Ch=lr;
regs.h.cl=lC;
regs.h.dh=rr;
regs.h.dl=rc;
int86(VIDEO,®s,®s);
}
screen.c中的功能操纵与屏幕相关的函数,如光标定位和清除屏幕(分别是gotoxy()
和cls()函数)。注意cls()只是clearwin(),它在屏幕的左上角和右下角设置数值,以对应
整个屏幕。
screen.c函数对于整个屏幕显示是全局的(见列表5.4)。它们在一个屏幕宽度的基础
上进行活动。window.c函数则在某个窗口内工作。另外的函数可以添加进来操纵一个窗
口内的工作。
列表5.4
/* screen.c
Listing 5.4 of DOS Programmer\'s Reference*/
#include<Stdio.h>
#include<dos.h>
#define VIDEO 0X10
/* Function:gotoxy()*/
void gotoxy(r,c)
int r,c;
{
union REGS regs;
regs.h.ah=0x02;
regs.h.bh=0;
regs.h.dh=r;
regs.h.dl=C;
int86(VIDEO,&regs,&regs);
}
/* Function: cls()*/
void cls()
{
union REGS regs;
regs.h.ah=0x06;
regs.h.al=0;
regs.h.bh=7;
regs.h.ch=0;
regs.h.Cl=0;
regs.h.dh=25;
regs.h.dl=80;
int86(VIDEO,&regs,&regs);
}
在程序的最低层,屏幕字符函数使用户能看看单个的屏幕位置或者使用BIOS屏幕
显示函数去改变这个位置(见列表5.5)。
104页
列表5.5
/*Chario.c
Listing 5.5 of DOS Programmer\'s Reference*/
#inClUde<Stdio.h>
#include<dos.h>
#define VIDEO 0x10
union REGs regs;
/*FunctiOn:wch()*/
void wch(Ch,attr)
char ch,attr;
{
regs.h.ah=0x09;
regs.h.bh=0;
regs.h.bl=attr;
regs.h.al=ch;
regs.x.cx=1;
int86(VIDEO,&regs,&regs);
}
/*FunctiOn:rch()*/
void rch(ch,attr)
char*ch,*attr;
{
regs.h.ah=0x08;
regs.h.bh=0;
int86(vIDEO,&regs,&regs);
*ch=regs.h.al;
*attr=regs.h.ah;
}
如果用户正在使用MicrosoftC/c++,那么他就没有版本4.0所需的sleep()函数。
该函数(见列表5.6)采用与Microsoft c/c++同样的路径。在向前推进之前,它要等待,
直到指定数目的秒数过去。
列表5.6
/* Sleep.C
Listing 5.6 of DOS programmer\'S Reference*/
#include<stdio.h>
;For use with Microsoft C/C++
void sleep(n)
int n;
{
long timeval, time();
timeval=time(NULL);
while(time(NUL)<timeval+n);
}
5.3.2使用多个显示页
因为用于显示这个简单窗口系统的函数只涉及一个显示页(0页),所以它们独立于
所使用的监视器系统的类型之一。可以加上屏幕页控制,但并非所有的函数都允许它的存
在。例如,翻卷函数的变量中没有页赋值变量。于是就不能使用这些函数来给显示内容编
页码(如果用户编写了自己的窗口函数来直接存取显示器内存,那么就不会受此限制)。而
105页
且,功能0Eh不能适用于所有BIOS ROM的所有页。所有测试的非IBM的ROM中,该功
能只在当前显示的页上工作,而不在非显示页上工作。
添加屏幕——页控制的一条途径是改变路径,使之包含一个当前屏幕页号以及设置
该页号的途径。例如,可以改变screen.c,使之包含一条setpage命令(见列表5.7)。
列表5.7
/*Screen2.c
LiSting 5.7 of DOS Programmer\'s Reference*/
#include<stdio.h>
#include<dos.h>
#define BOOL int
#define VIDEO 0x10
/* PrototypeS*/
void cls(void);
static int cpage=0; /* Current display page*/
/*Function:gotoxy()*/
void gotoxy(r,c)
int r,C;
{
union REGS regs;
regs.h.ah=0x02;
regS.h.bh=cpage;
regs.h.dh=r;
regS.h.dl=C;
int86(VIDEO,®S,®S);
}
/*Function:cls()*/
void cls()
{
union REGS regs;
regS.h.ah=0x06;
regs.h.al=0;
regs.h.bh=7;
regS.h.Ch=0;
regS.h.cl=0;
regS.h.dh=25;
regs.h.dl=80;
int86(VIDEO,®s,®s);
}
/* Function:setpage()*/
void setpage(n,clrflg)
int n;
BOOL clrflg;
{
union REGS regs;
cpage=n;
regs.h.ah=0x05;
regs.h.al=n;
int86(VIDEO,®s,®s);
if(clrflg) cls();
}
/*Function:pgprint()*/
106页
void pgprint(str)
char*str;
{
union REGS regs;
regS.h.ah=0x0e;
regs.h.bh=cpage;
while(*str){
regs.h.al=*str;
int86(vIDEO,&Pegs,&regS);
str++;
}
}
在列表5.7中,加上pgprint()函数使用户可以将线打印到当前页上。通过向屏幕功
能添加页控制,可以建立在其中使用了窗口和页功能的程序以保存显示内容,而只需要变
换显示页就能快速找到显示内容。 testpage.c样本程序能让用户尝试一下新的setpage()
和pgprint()函数(见列表5.8)。
列表5.8
/* Testpage.C
Listing 5.8 of DOS Programmer\'S Reference*/
#inClude<Stdio.h>
#inClude<dOS.h>
#define FALSE 0
#define TRUE !FalSE
/*prototypes*/
void setpage(int n, int clrflg);
void pgprint(char*str);
void main()
{
int i;
Setpage(1,TRUE);
for(i=0;i<50;i++)
pgprint("DOS Programmer\'S Reference");
Sleep(5);
setpage(0,FALSE);
}
如果只有单色监视器系统,那么什么也看不到。当执行setpage()函数时,事实上屏幕
并未改变;用户只是看到有几秒钟所有的东西都保持静态,然后就回到下一个活动。但是
如果有CGA或EGA监视器或更好的监视器,那么执行程序时,屏幕会改变井显示测试
行,然后返回到屏幕已显示的相同内容上去。
对于所有的函数,当前显示页的使用远没有未显示页的工作那样令人难忘。将显示页
包含进函数变量之中,那么就可以将函数如gotoxy()用于尚未显示的页。然后可以构造一
个完整的显示,而且当它成为当前显示时,用户看到的是瞬间的显示。