图形显示方式屏幕的保存和恢复


概述:

     在程序中常常要暂时的保存图形显示方式屏幕上的内容,然后把自己的信息输出到屏幕上,结束后再恢复原来的屏幕内容,特别在内存驻留程序弹出一个窗口时更要用到,但是图形方式下显示缓冲区的容量巨大,在常用的 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




(C) Copyright by LuoYunBin's Win32 ASM Page,http://asm.yeah.net