用内联汇编替换movemem(),用于VGA图形处理
我在Turbo C中写一个简单的动画(用于VGA,在NuXT 2上,一个IBM PC/XT的克隆机上)。我把图形写入一个缓冲区,然后每一帧清空缓冲区、写入更新后的对象,然后把缓冲区拷贝到VGA内存。效果不错,但需要优化。目前我把缓冲区拷贝到VGA时使用的是 movedata(SEG_buffer, OFF_buffer, SEG_vga, OFF_vga, SIZE);。
当我尝试把对 movedata() 的调用替换成内联汇编时,混乱就会随之而来,通常会让电脑卡死。下面是我正在尝试的代码。 (出于无奈我把所有寄存器的push/pop都加上了,但没有用);请注意 SIZE2 = 32000、SIZE = 64000。
asm {
push ds
push si
push di
push cx
push es
mov ax, SEG_buffer
mov ds, ax
mov si, OFF_buffer
mov ax, SEG_vga
mov es, ax
mov di, OFF_vga
mov cx, SIZE2
cld
rep movsw
pop es
pop cx
pop di
pop si
pop ds
}
解决方案
是 SEG_buffer、OFF_buffer、SEG_vga、OFF_vga 和 SIZE 这些变量,还是它们只是立即数?
如果这些是基于内存的变量,那么由于 汇编操作的顺序,你将陷入混乱。一旦 mov ds, ax 完成其工作,剩余的取数将来自错误的段。只需最后加载DS。 (在 movedata(SEG_buffer, OFF_buffer, SEG_vga, OFF_vga, SIZE); 中,是Turbo C来完成解引用。) 如果这些标识符确实只是立即数,那么TASM就必须拒绝 mov ds, SEG_buffer,但如果它们是变量,那么直接加载段寄存器一直都是可能的!
这显然是VGA的老式13h模式,分辨率为320x200、256色。还能在哪儿一次性写入64000字节呢?于是问题就来了,为什么这个操作要这样那么多参数?显存入口固定在ES:DI=A000:0000,长度固定在CX=64000,你的64000字节私有缓冲区将占据它自己的内存段,因此可以预期SI=0的偏移量。
push ds
push es
push cx
push si
push di
mov cx, 0A000h
mov es, cx
xor di, di
mov ds, SEG_buffer ; (*)
xor si, si
mov cx, 64000/2
rep movsw ; (**)
pop di
pop si
pop cx
pop es
pop ds
(*) 将其设为变量才最有意义。通常,你并不知道应用程序被加载到哪里,因此你会计算出私有缓冲区的位置,并把结果存储在基于内存的变量中。 (**) 你可以相信Turbo C已经会把方向标志位清除了。