在 DOS 時代編寫內存駐留程式(TSR)或需要彈出視窗的工具時,常面臨一個難題:如何保存當前圖形模式下的螢幕內容?高解析度、真彩色模式下顯存高達數 MB(如 1024×768×32bpp ≈ 3MB),直接複製到常規記憶體不現實,需用磁碟交換或 XMS,極大增加複雜度。本文介紹一種巧妙技巧:利用 INT 10h AH=00h 位元 7 置位(不清除顯存切換模式) 結合 VESA 功能,將任意圖形模式下的可見畫面「壓縮」保存到約 10KB 緩衝區,後續恢復同樣高效。
ram_buffer)。INT 10h AX=0083h 設定模式 3(80×25 彩色文字)不清除顯存(AL 位元 7=1)。此時螢幕顯示亂碼,但原圖形畫面實際仍保留在顯存其他分頁/位置。TEST_VGA PROC push 0 pop ds cmp word ptr ds:[33h*4],0 jz no_mouse or cs:flag,10000000b ; has mouse no_mouse: push cs pop ds mov ah,1bh xor bx,bx mov di,offset file_end int 10h cmp al,1bh jnz tv_no_vga mov ax,4f00h mov di,offset file_end int 10h cmp al,4fh jz tv_is_vesa ; 檢測 Trident TVGA 9000 mov dx,3c4h 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 ; 檢測 Tseng ET6000 mov dx,3cdh 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 TEST_VGA ENDP
VGA_PAGE PROC cmp vga_type,1 ; VESA jnz other_vga1 cmp ah,1 ; 1=保存 2=恢復 jz vp_vesa2 cmp ah,2 jz vp_vesa1 mov ax,4f05h 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 ; Tseng 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; ret other_vga2: cmp vga_type,2 ; Trident 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 vp_ret: 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
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; jz ss_mode3 cmp al,7; jz ss_mode7 xor ax,ax call vga_page call vga_base call save_vram mov ax,0083h; int 10h ; 設模式 3 不清顯存 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; jmp scr_ret ss_mode7: push 0b000h; pop ds; call save_vram1 mov ax,3; int 10h; call restore_vram; jmp scr_ret SAVE_SCR 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 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 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; int 10h push 0b000h; pop es; call restore_vram1; jmp rs_mode31 RESTORE_SCR ENDP
這個技巧巧妙利用「不清除顯存切換模式」這一鮮為人知的特性,將龐大的圖形顯存問題轉化為僅保存文字模式可見區域(4KB)的輕量操作,極大簡化了 TSR/彈出式工具在圖形環境下的開發難度。它體現了組合語言程式設計師對硬體特性的深度洞察與創造性利用。