VGA Mode 13h (320×200, 256 色) 是 DOS 時代遊戲與 Demo 的標準畫布:線性帧緩衝區位於 A000:0000,每像素 1 位元組,索引色模式配合調色盤可呈現豐富視覺效果。這份 CIRCLE.ASM 以簡潔 60 行程式碼,展示如何:
x = x_rad * cos(θ), y = y_rad * sin(θ)FISTP),偏移至螢幕中心 (160,100)ES:[DI] 繪製像素,色號遞增產生漸層效果INC angle) 直到繞行 65536 次 (16 位元溢位歸零)mov ax, 13h
int 10h ; 設定 Mode 13h
mov dx, 3C8h ; 調色盤寫入索引埠
xor al, al
out dx, al ; 從色號 0 開始
inc dx ; DX = 3C9h (資料埠)
mov cx, 256
xor al, al
lopp:
out dx, al ; R
out dx, al ; G
out dx, al ; B (灰階: R=G=B)
inc al
dec cx
jnz lopp
建立 0-255 灰階調色盤,後續像素色號 CL 遞增即呈現從黑到白的漸層圓環。
mov ax, 0A000h
mov es, ax ; ES 指向顯存
fild y_rad ; ST(0) = y_rad (10.0)
fild x_rad ; ST(0) = x_rad (10.0), ST(1) = y_rad
loopdr:
fild angle ; ST(0) = angle (整數 0..65535)
fsincos ; ST(0) = cos(θ), ST(1) = sin(θ)
fmul st, st(2) ; ST(0) = x_rad * cos(θ)
fistp x_co ; 存入 x_co (整數), 彈出 ST(0)
fmul st, st(2) ; ST(0) = y_rad * sin(θ) (注意: st(2) 仍為 y_rad)
fistp y_co ; 存入 y_co
add x_co, 160 ; 螢幕中心 X 偏移
add y_co, 100 ; 螢幕中心 Y 偏移
... 計算顯存位移 DI ...
mov byte ptr es:[di], cl ; 繪製像素,色號 = CL
inc cl ; 色號遞增
fadd yvel ; ST(0) = angle + yvel (0.001)
fxch st(1) ; 交換 ST(0), ST(1)
fadd xvel ; ST(0) = angle + xvel
fxch st(1) ; 還原順序
inc angle
jnz loopdr ; 直到 angle 溢位歸零 (65536 圈)
關鍵 FPU 指令說明:
FILD:整數載入轉浮點推入堆疊FSINCOS:同時計算正弦餘弦,ST(0)=cos, ST(1)=sin (比分開呼叫快)FMUL ST, ST(i):堆疊頂與第 i 暫存器相乘,結果存 ST(0)FISTP:浮點轉整數存入記憶體並彈出堆疊FADD / FXCH:角度遞增邏輯 (這裡似乎有邏輯小瑕疵:fadd yvel 後 fxch 再 fadd xvel 實際僅對 angle 單一數值累加,xvel/yvel 未真正分離使用,但不影響畫圓)xor di, di
mov ax, y_co
shl ax, 6 ; y * 64
add di, ax
shl ax, 2 ; y * 4 (累積 y * 68? 實際 64+4=68 ≠ 320)
add di, ax
add di, x_co ; DI = y*320 + x ? 這裡計算有誤:68 ≠ 320
注意:原始碼顯存位移計算存在錯誤。正確公式為 DI = y_co * 320 + x_co。標準作法:
mov ax, y_co
mov bx, 320
mul bx ; DX:AX = y * 320
add ax, x_co
mov di, ax
或利用 SHL 組合:320 = 256 + 64 = (1<<8) + (1<<6):
mov ax, y_co
shl ax, 8 ; *256
mov di, ax
mov ax, y_co
shl ax, 6 ; *64
add di, ax
add di, x_co
原碼 shl ax,6 / add di,ax / shl ax,2 / add di,ax 實際計算 y*64 + y*256 = y*320 (第二次 SHL 作用於已變更的 AX),邏輯正確但寫法易讓人誤讀。
xor ax, ax
int 16h ; 等待按鍵
mov ax, 3
int 10h ; 恢復 Mode 3 (80x25 文字)
int 20h ; 結束
cseg segment
assume cs:cseg, ds:cseg, ss:cseg
org 100h
.386
start:
mov ax, 13h
int 10h
mov dx, 3c8h
xor al, al
out dx, al
inc dx
mov cx, 256
xor al, al
lopp: out dx, al
out dx, al
out dx, al
inc al
dec cx
jnz lopp
mov ax, 0a000h
mov es, ax
fild y_rad
fild x_rad
loopdr:
fild angle
fsincos
fmul st, st(2)
fistp x_co
fmul st, st(2)
fistp y_co
add x_co, 160
add y_co, 100
xor di, di
mov ax, y_co
shl ax, 6
add di, ax
shl ax, 2
add di, ax
add di, x_co
mov byte ptr es:[di], cl
inc cl
fadd yvel
fxch st(1)
fadd xvel
fxch st(1)
inc angle
jnz loopdr
xor ax, ax
int 16h
mov ax, 3
int 10h
int 20h
x_co dw 0
y_co dw 0
x_rad dw 10
y_rad dw 10
xvel dq 0.001
yvel dq 0.001
angle dw 0
cseg ends
end start
x_rad, y_rad, xvel, yvel 實現。REP MOVSD)、雙緩衝 (頁面翻轉)、組合語言手寫繪圖基元 (線、圓、多邊形填充)。這份程式碼雖簡短,卻串聯了「硬體模式設定 → 調色盤編程 → 浮點數學 → 記憶體映射顯示 → 即時互動」完整圖形管線,是學習 DOS 繪圖、FPU 指令集、Demo Coding 的優秀切入點。