DOS程序员手册(三) (21)

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, &regs, &regs);

}

/* 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,&regs,&regs);

}

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,&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);

}

/* 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,&regs,&regs);

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()用于尚未显示的页。然后可以构造一

个完整的显示,而且当它成为当前显示时,用户看到的是瞬间的显示。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwzswx.html