在程序中常常要暂时的保存图形显示方式屏幕上的内容,然后把自己的信息输出到屏幕上,结束后再恢复原来的屏幕内容,特别在内存驻留程序弹出一个窗口时更要用到,但是图形方式下显示缓冲区的容量巨大,在常用的 80 x 25 文本方式下,显示缓冲区仅大小仅为 80 x 25 x 2 = 4000 字节,而在模式 13H 320 x 200 x 256 色时为 320 x 200 = 64000 字节,现在常用的高彩色、真彩色下如 800 x 600 x 65535 色时为 800 x 600 x 2 = 960000 字节,涉及到如此大的数据量程序必须使用磁盘交换方法或用到 XMS 做为数据保存缓冲区,使编程复杂化。
本文用了 INT 10H 中不清除显示内存设置新显示模式的方法,使不管在什么显示方式下,用到的缓冲区大下都在 10K 左右,即使在 1024 x 768 x 16.7M 色也能正常保存。
本程序的适用范围为保存屏幕后自己的程序仅仅使用文本模式 3 的情况,如果要用到图形模式,那么还是要保存全部的显示缓冲区。在兼容性方面,由于使用 VESA 标准功能,在现在的 PCI/VESA 显示卡上都能正常运行,我发现唯一不能运行的是有一段时期生产的 TVGA 8900/9000 卡,因为此卡在 VESA 功能刚出现的时候生产,支持 VESA 的伪彩色显示模式,却又不支持很多其他的 VESA 功能。大家找到克服的方法告诉我一声。
本程序要用到的一些中断的说明如下:
INT 10H 的 00H 功能,设置显示模式:
功能 入口参数 出口参数
INT 10H 的 00H功能
设置显示模式 AH = 00H
AL = 模式(如果位 7 置位,则不清除显示缓冲区)
INT 10H 的 1BH 功能,检测是否 VGA 卡:
功能 入口参数 出口参数
INT 10H 的 1BH功能
取 VGA/MCGA 的功能、状态信息 AH = 1BH AL = 1BH 成功(说明显示卡为 VGA 以上)
ES:DI 返回状态信息
BX = 0000
AL <> 1BH 非VGA/MCGA 显示卡
ES:DI 指向 64 字节缓冲区
INT 10H 的 1CH 功能,保存/恢复视频状态:
功能 入口参数 出口参数
INT 10H 的 1C00H 功能
返回状态缓冲区容量 AX = 1C00H AL = 1CH 成功
BX = 需要的 64 字节块数目
INT 10H 的 1C01H 功能
保存视频状态 AX = 1C01H
ES:BX 指向缓冲区
INT 10H 的 1C02H 功能
恢复视频状态 AX = 1C01H
CX = 要求的状态
位 0 = 恢复视频硬件状态
位 1 = BIOS 数据区
位 2 = 彩色寄存器和 DAC 状态
ES:BX 指向缓冲区(用1C01H功能保存下来的)
INT 10H 的 4FH 功能,VESA 功能:
功能 入口参数 出口参数
INT 10H 的 4F00H功能
取显示卡 VESA 信息 AX = 4F00H AL = 4FH 说明显示卡支持VESA
AH = 00H 成功
AH = 01H 失败
ES:DI 指向缓冲区 (256 字节) AL <> 4FH 显示卡不支持VESA
INT 10H 的 4F05H功能
控制对 VESA 显示卡视频 RAM 的访问 BH = 00H 选视频内存窗口
DX = 视频内存窗口地址 AH = 00H 成功
AH = 01H 失败
BH = 01H 取视频内存窗口 AH = 00H 成功
DX = 视频内存窗口地址
AH = 01H 失败
INT 33H 的 16H/17H 功能,保存/恢复鼠标驱动程序状态:
功能 入口参数 出口参数
INT 33H 的 0015H功能
确定保存鼠标驱动程序状态所需
的空间 AX = 0015H BX = 所需大小
INT 33H 的 0016H功能
保存鼠标驱动程序状态 AX = 0016H
BX = 缓冲区大小(用 0015H 获得)
ES:DX 指向缓冲区
INT 33H 的 0017H功能
恢复鼠标驱动程序状态 AX = 0017H
BX = 缓冲区大小(用 0015H 获得)
ES:DX 指向缓冲区
源程序:
;by Luo Yun Bin
;http://asm.yeah.net
;这个子程序用来检测显示卡的类型,鼠标状态等等
;在程序初始化时执行
;文中要用到的一些缓冲区请自己定义,注意大小!
flag db ? ;标志位,位 7 置 1 表示安装了鼠标
vga_type db ? ;显示卡类型
video_mode db ? ;显示模式
vga_win1 dw ? ;视频窗口,暂存 VESA 的窗口状态
vga_win2 dw ? ;
vga_win3 dw ? ;
...
TEST_VGA PROC
push 0 ;检测是否安装鼠标驱动程序
pop ds
cmp word ptr ds:[33h*4],0
jz no_mouse
or cs:flag,10000000b ;has mouse installed
no_mouse:
push cs
pop ds
mov ah,1bh ;检测是否是 VGA 以上显示卡
xor bx,bx
mov di,offset file_end
int 10h
cmp al,1bh
jnz tv_no_vga
mov ax,4f00h ;检测是否支持 VESA 功能
mov di,offset file_end
int 10h
cmp al,4fh
jz tv_is_vesa
mov dx,3c4h ;检测是否 TVGA 9000 卡
mov al,0eh ;这一段是照抄的,找不到资料
out dx,al
inc dx
in al,dx
mov bl,al
xor al,al
out dx,al
in al,dx
xchg al,bl
out dx,al
test bl,2
jnz tv_is_tvga
mov dx,3cdh ;检测是否 ET6000 卡
in al,dx
mov ah,al
mov al,11h
out dx,al
in al,dx
xchg ah,al
out dx,al
cmp ah,11h
jz tv_is_tseng
mov vga_type,4
ret
tv_is_vesa:
mov vga_type,1
ret
tv_is_tvga:
mov vga_type,2
ret
tv_is_tseng:
mov vga_type,3
ret
tv_no_vga:
int 20h ;非 VGA 卡退出
TEST_VGA ENDP
...
;================================================================
;保存显示缓冲区内容并设置新的显示模式到 80 x 25 文本 (模式 3)
SAVE_SCR PROC
push ds
push es
test flag,10000000b ;见前面
jz ss_no_mouse
mov ax,16h ;保存鼠标状态
mov dx,offset mouse_buffer
int 33h
ss_no_mouse:
mov ax,1c01h ;保存视频状态
mov bx,offset video_buffer
mov cx,7
int 10h
mov ah,0fh ;保存原显示模式
int 10h
mov video_mode,al
cmp al,3 ;80 x 25 x 16 色
jz ss_mode3
cmp al,7 ;80 x 25 黑白
jz ss_mode7
xor ax,ax ;以下为图形方式保存显示缓冲区
call vga_page
call vga_base
call save_vram
mov ax,0083h ;设置新的显示模式,不清除显示内存
int 10h
push 0b800h
pop ds ;保存显示内存
xor si,si
mov cx,1000h
mov di,offset ram_buffer
push cs
push ds
cld
rep movsb
xor di,di ;
mov cx,80*25
mov ax,57b1h ;填充背景,不然有乱字符
cld
rep stosw
scr_ret:
pop es
pop ds
ret
ss_mode3:
call save_vram ;显示模式 3 保存显示 RAM
jmp short scr_ret
ss_mode7:
push 0b000h ;显示模式 7 保存显示 RAM
pop ds
call save_vram1
mov ax,3
int 10h
call restore_vram
jmp short scr_ret
SAVE_SCR ENDP
SAVE_VRAM PROC
push 0b800h ;把显示内存保存到自己的缓冲区
pop ds
save_vram1:
push cs
pop ds
xor si,si
mov di,offset ram_buffer
mov cx,2000h
cld
rep movsb
ret
SAVE_VRAM ENDP
RESTORE_VRAM PROC
push 0b800h ;恢复显示缓冲区内容
pop es
restore_vram1:
xor di,di
push cs
pop ds
mov si,offset ram_buffer
mov cx,2000h
cld
rep movsb
ret
RESTORE_VRAM ENDP
VGA_PAGE PROC
cmp vga_type,1
jnz other_vga1
cmp ah,1
jz vp_vesa2
cmp ah,2
jz vp_vesa1
mov ax,4f05h ;保存 VESA 显示卡状态
mov bx,0100h
int 10h
mov vga_win1,dx
mov ax,4f05h
mov bx,0101h
int 10h
mov vga_win2,dx
vp_vesa1:
mov ax,4f05h
xor bx,bx
xor dx,dx
int 10h
mov ax,4f05h
mov bx,0001h
xor dx,dx
int 10h
ret
vp_vesa2:
mov ax,4f05h
xor bx,bx
mov dx,vga_win1
int 10h
mov ax,4f05h
mov bx,0001h
mov dx,vga_win2
int 10h
ret
other_vga1:
cmp vga_type,3
jnz other_vga2
mov dx,3cdh
cmp ah,1
jz vp_tseng2
cmp ah,2
jz vp_tseng1
in al,dx
mov vga_win3,al
vp_tseng1:
xor al,al
out dx,al
ret
vp_tseng2:
mov al,vga_win3
out dx,al
vp_ret:
ret
other_vga2:
cmp vga_type,2
jnz vp_ret
mov al,0eh
mov dx,03c4h
cmp ah,1
jz vp_tvga2
out dx,al
inc dx
in al,dx
cmp ah,2
jz vp_tvga1
mov vga_win3,al
xor al,al
out dx,al
ret
vp_tvga1:
mov al,2
out dx,al
ret
vp_tvga2:
mov ah,vga_win3
out dx,ax
ret
VGA_PAGE ENDP
VGA_BASE PROC
mov dx,3c4h ;这一段是照抄的,找不到资料
mov ax,402h
out dx,ax
mov ax,704h
out dx,ax
mov dx,3ceh
mov ax,0ff08h
out dx,ax
mov ax,0c06h
out dx,ax
mov ax,204h
out dx,ax
mov ax,5
out dx,ax
ret
VGA_BASE ENDP
;====================================================
;本子程序为恢复原来的显示内容
;在自己的程序执行完后使用
RESTORE_SCR PROC
push cs
pop ds
mov al,video_mode ;根据不同的原显示模式不同处理
cmp al,3
jz rs_mode3
cmp al,7
jz rs_mode7
push 0b800h ;以下为图形方式恢复显示内容
pop es
push cs
pop ds
mov si,offset ram_buffer
xor di,di
mov cx,1000h
cld
rep movsb ;恢复显示 RAM
mov ah,2
call vga_page
call vga_base
call restore_vram
xor ah,ah ;恢复到原来的显示模式
mov al,video_mode
or al,80h
int 10h
mov ah,1
call vga_base
jmp short rs_mode31
rs_mode3:
call restore_vram
rs_mode31:
push cs
pop es
push cs
pop ds
mov ax,1c02h ;恢复视频状态
mov bx,offset video_buffer
mov cx,7
int 10h
test flag,10000000b
jz rs_no_mouse
mov ax,17h ;恢复鼠标状态
mov dx,offset mouse_buffer
int 33h
rs_no_mouse:
ret
rs_mode7:
mov ax,7 ;显示模式 7 恢复
int 10h
push 0b000h
pop es
call restore_vram1
jmp short rs_mode31
RESTORE_SCR ENDP