首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

組合語言繪圖教學:VGA Mode 13h 畫圓與浮點運算 (FPU) 實作

     編輯:組合語言

前言:Mode 13h — 經典 256 色繪圖模式

VGA Mode 13h (320×200, 256 色) 是 DOS 時代遊戲與 Demo 的標準畫布:線性帧緩衝區位於 A000:0000,每像素 1 位元組,索引色模式配合調色盤可呈現豐富視覺效果。這份 CIRCLE.ASM 以簡潔 60 行程式碼,展示如何:

  1. 設定 Mode 13h 並初始化灰階調色盤
  2. 使用 x87 FPU (浮點運算協處理器) 計算參數方程 x = x_rad * cos(θ), y = y_rad * sin(θ)
  3. 將浮點結果轉整數 (FISTP),偏移至螢幕中心 (160,100)
  4. 直接寫入顯存 ES:[DI] 繪製像素,色號遞增產生漸層效果
  5. 角度遞增 (INC angle) 直到繞行 65536 次 (16 位元溢位歸零)

程式流程詳細解析

1. 視訊模式與調色盤初始化

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 遞增即呈現從黑到白的漸層圓環。

2. FPU 參數方程計算核心迴圈

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 未真正分離使用,但不影響畫圓)

3. 顯存位移計算 (320×200 線性緩衝區)

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),邏輯正確但寫法易讓人誤讀。

4. 結束與恢復文字模式

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

教學價值與擴展方向

  • FPU 程式設計入門:堆疊架構 (ST(0)..ST(7))、同步指令 (FWAIT 隱含)、精度控制、整數/浮點轉換。
  • 參數方程繪圖:圓、橢圓、利薩如曲線、螺旋線皆可透過調整 x_rad, y_rad, xvel, yvel 實現。
  • 顯存操作優化:Mode 13h 線性緩衝區極適合區塊傳輸 (REP MOVSD)、雙緩衝 (頁面翻轉)、組合語言手寫繪圖基元 (線、圓、多邊形填充)。
  • Demo Scene 精神:極少程式碼產生視覺效果,追求 Size Coding (如 256B/4KB Intro) 的核心技能。

這份程式碼雖簡短,卻串聯了「硬體模式設定 → 調色盤編程 → 浮點數學 → 記憶體映射顯示 → 即時互動」完整圖形管線,是學習 DOS 繪圖、FPU 指令集、Demo Coding 的優秀切入點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved