8086引导程序绘制矩形闪烁问题求助及相关教程推荐
Hey there! Let's fix that annoying pixel flickering when drawing rectangles in 320x200 256-color VGA mode. I’ve messed around with this classic issue before, so here’s a straightforward breakdown of why it happens and how to fix it step by step.
In VGA Mode 13h (320x200 256-color), the display hardware constantly reads from the video memory at segment 0xA000 to refresh the screen. If your code modifies this memory while the screen is being refreshed, the hardware will catch a half-updated frame—causing that distracting flicker effect.
The most reliable fix is to use a double buffer: draw your entire rectangle in an off-screen memory area first, then copy the finished frame to video memory all at once. This way, the screen only ever shows complete, fully rendered frames.
1. Allocate an Off-Screen Buffer
First, set aside a continuous block of memory (at least 64KB, since 320×200 = 64,000 pixels) for your off-screen work. In real mode, you can use a memory segment that’s not used by the system or your code—0x1000 is a safe bet here:
[bits 16] [org 0x7c00] start: xor ax, ax mov ds, ax mov es, ax mov sp, 0x09000 mov bp, sp ; Save the off-screen buffer segment (0x1000 * 16 = 0x10000 in physical memory) mov word [offscreen_seg], 0x1000 ; Enter VGA Mode 13h mov ax, 0x13 int 0x10
2. Draw Your Rectangle to the Off-Screen Buffer
Instead of writing directly to 0xA000 (video memory), render your rectangle to the off-screen buffer. Here’s an example of drawing a solid rectangle:
; Draw a rectangle to the off-screen buffer mov ax, [offscreen_seg] mov es, ax ; Point ES to the off-screen segment mov di, 0x0000 ; Start at offset 0 ; Rectangle parameters: (10,10) top-left, 100px wide, 50px tall, color 0xFF mov cx, 50 ; Number of rows (height) draw_row: push cx mov cx, 100 ; Number of pixels per row (width) mov al, 0xFF ; Pixel color rep stosb ; Fill the row with color add di, 320 - 100 ; Move to the start of the next row (320 pixels per screen row) pop cx loop draw_row
3. Wait for Vertical Sync (Optional but Critical for Smoothness)
To avoid "screen tearing" when copying the buffer, wait for the vertical blanking interval—this is the split second when the display finishes refreshing and starts over. Use the VGA status register at 0x3DA to detect this:
wait_vsync: in al, 0x3DA test al, 0x08 jnz wait_vsync ; Wait for the vertical sync to end wait_vsync_end: in al, 0x3DA test al, 0x08 jz wait_vsync_end ; Wait for the vertical sync to start
4. Copy the Off-Screen Buffer to Video Memory
Now transfer the fully drawn rectangle to video memory in one go. Using rep movsw (copying words instead of bytes) makes this faster:
; Copy the 64KB off-screen buffer to video memory mov ax, [offscreen_seg] mov ds, ax mov ax, 0xA000 mov es, ax mov si, 0x0000 mov di, 0x0000 mov cx, 32000 ; 64,000 bytes = 32,000 words rep movsw ; Copy the entire buffer
If you don’t have enough memory for a double buffer, you can wait for vertical sync before drawing each row or pixel. This will slow down rendering but reduce flicker:
; Call this before drawing each row/pixel wait_vsync: in al, 0x3DA test al, 0x08 jnz wait_vsync wait_vsync_end: in al, 0x3DA test al, 0x08 jz wait_vsync_end ret
Looking at your snippet (mov ebx,13h mov eax,4...), make sure you’re properly entering Mode 13h first. Then replace all direct writes to 0xA000 with writes to your off-screen buffer, and add the sync + copy steps at the end.
内容的提问来源于stack exchange,提问作者Dodo

