先说说原理...
楼主的显示模式是04h,320x200,4色,CGA模式,视讯显示地址:B800h~BFFFh,大小32K
'显示地址'和'屏幕'是直接对应的,也就是,假如将一个字节资料写入B800:0000,屏幕会马立显示该资料。但4色CGA模式是采用扫瞄线的方式映射,亦即屏幕上连续2个图点,
在显示内存地是并不连续,而是
[图点X]:
|
[显示地址Y的bit?]....[显示地址Y+N的bit?]....(的组合)
[图点X+1]:
|
[显示地址Y的bit?+1]....[显示地址Y+N的bit?+1]....(的组合)
像这样就不适合正常搬移,所以模式04h不适用.
我们换一个简单的模式13H, 320x200,256色,VGA模式,视讯显示地址:A000h~AFFFh,64K,这也是一般很老旧的D0S游戏模式, 原因是1字节=0FFH, 刚好是256, 显示方式是直接映射而不是扫瞄线.
上面方法3讲的也是直接映射.
上面提到调整(缩减)内存再申请当然可以用,但有点烦.
简单方法是:通常A000段用作绘图显示地址,B000段用作文字显示地址,但绘图时的文字都会用绘图内存,文字显示地址(B000)基本不用,于是可以拿来作缓冲,不用白不用,不必另外申请.
好了,以尽量少更动为原则.修改如下:
1.
print子程序下的这3条删除
mov ah,0;把屏幕分为320*200像素,四色
mov al,04h
int 10h;取光笔
改为
call Clear_page ;清除缓缓区
2.
call init ;这一条之下
加3条
mov ah,0 ;更变模式
mov al,13h ;模式13H,把屏幕分为320*200像素,256色
int 10h
3.
所有调用bios绘图函式int10h,ah=0ch,共6处,即
mov ah,0ch
int 10h
改为
mov ah,0ch
call Draw_dot ;调用绘点子程序
4.
call sleep 这条之上加一条
改为
call move_page ;搬动图像覆盖之前图像的子程序
call sleep
5.
原理:
Clear_page 清除缓冲
Draw_dot 把原来的int10h绘点,改为放到对应地址
move_page 完成所有绘点后,一次搬动图像覆盖之前图像
这样无须清屏,就不会闪屏
在 CODES ENDS 这一条之上,加3个子程序,
;-------------------------------------------------------
.286;启用286指令,pusha,popa用
;模拟ah=0Ch绘点至缓缓区
Draw_dot:
pusha;保存所有寄存器
push es ;保存ES
cld;清除方向, stosb,movsw等指示方向
mov di,ax ;保存al中的颜色
cmp ah,0Ch ;是否0ch函式
jnz SimX ;不是,离开
mov ax,0B000h ;缓冲段
mov es,ax ;es=ax
mov ax,320 ;一行长度(像素)
mov bx,dx ;
mul bx ; x320
add ax,cx ; 加列 (像素)
xchg di,ax ;交换al取回颜色, di=图点在缓冲段的相对位置
stosb ;es:[di] = al ; 存图点
SimX:
pop es ;取回es
popa ;取回所有寄存器
ret
;------------------------------------------------------------------
Clear_page:
;清除缓冲区资料子程序
pusha ;保存所有寄存器
push es ;保存ES
cld ;清除方向, stosb,movsw等指示方向
mov ax,0B000H ;缓冲区地址
mov es,ax
mov cx,320*200 / 2 ;长度
mov ax,0000h
mov di,0
rep stosw ;重覆CX次,es:[di]=ax,每次di+2
pop es ;取回es
popa ;取回所有暂存器
ret
;------------------------------------------------------------------
move_page:
一次搬移缓冲区(B000)图点资料至显示区(A000)
pusha ;保存所有寄存器
push ds ;保存DS
push es ;保存ES
cld ;清除方向, stosb,movsw等指示方向
mov ax,0B000H ;来源
mov ds,ax
mov ax,0A000H ;目的
mov es,ax
mov cx,320*200 / 2 ;长度
xor si,si ;起始地址
xor di,di ;起始地址
rep movsw ;由ds:si 搬移 CX 次到 es:di , 每次si+2, di+2
pop es ;取回es
pop ds ;取回ds
popa
ret
;------------------------------------------------------------------
6.
原程式开始时的
push ds
sub ax,ax
push ax
和结束的
ret
main endp
这是十分老式的写法,用意是结束时返回地址psp:0, 而通常那里会放一条指令int 20h (老式dos的结束函式)
但有些dos虚拟机并不一定支援int20h, 迂回的离开方式可能会是'离不开'.
所以可以改为正式的dos离开函式.
ret
main endp
改为
xor ax,ax ;ax=0
int 16h ;调用键盘函式,ah=0,等待键入
mov ax,3 ;文字模式3
int 10h ;调用bios,清屏返回文字模式
mov ax,4c00h ;离开函式
int 21h ;调用dos
main endp
.....
更改完毕.
![](http://tiebapic.baidu.com/forum/w%3D580/sign=e5690de07b6d55fbc5c6762e5d234f40/e33cf536afc37931c9abe717adc4b74542a9111d.jpg?tbpicau=2024-07-10-05_d1a05ab4f4622636a03eb8c22f151509)