文章分析一個 TSR 時鐘程式中,將 BIOS 資料區 (0040h:006Ch/006Eh) 的 32 位元定時器計數值轉換為「HH:MM:SS」 ASCII 字串的核心子程式。作者詳細追蹤暫存器變化,並對計數器最大值 (0x1800B0) 與理論值 (86400 秒) 的差異提出疑問。
HMS db 8 dup(':') ; 輸出緩衝區 "::::::::"
time proc
lea di, position ; (1) DI 指向 HMS
mov ax,0; mov ds,ax ; (2-3) DS=0 (BIOS 資料區段)
mov al,ds:[46eh] ; (4) AL = 高字 (小時計數)
call clk1 ; (5) 轉換小時 → ASCII
mov ax,ds:[46ch] ; (6) AX = 低字 (總滴答數)
mov dx,0; mov bx,444h ; (7-8) DX=0, BX=1092 (444h)
call clk0 ; (9) 轉換分鐘
mov ax,dx; mov cx,3ch ; (10-11) AX=餘數, CX=60
mul cx ; (12) AX = 餘數 * 60
clk0:
add di,3 ; (13) DI 移至下一欄位
div bx ; (14) AX/DX = AX/1092
clk1:
aam ; (15) AL→AH=商, AL=餘數 (BCD)
add ax,3030h ; (16) 轉 ASCII '0'-'9'
xchg ah,al ; (17) 交換高低位 (十位在前)
mov cs:[di],ax ; (18) 存入 HMS
ret ; (19)
time endp
AL = [0040:006E] = 高字 = 總滴答數 / 65536CLK1:AAM → BCD 碼 → ASCII → 交換位序 → 存入 HMS[0:1]AX = [0040:006C] = 低字 = 總滴答數 % 65536DIV BX (1092):1092 滴答 = 60 秒 (1092/18.2 = 60)AX = 分鐘數 (0-59),餘數 DX = 分鐘內滴答數CLK0 接 CLK1 轉換存入 HMS[3:4]AX = DX (分鐘餘數),MUL CX (60) → AX = 餘數 * 60DIV BX (1092):商 = 秒數 (0-59)(餘數/1092) * 60 = 餘數 * (60/1092) = 餘數 / 18.2 即滴答轉秒HMS[6:7]假設 [006E]=0017h, [006C]=1AA3h (總計 0x00171AA3 = 1,514,147 滴答):
0x17 = 23 → AAM → 2,3 → ASCII '2','3' → 交換 → '3','2' (註:作者追蹤中 0x17 AAM 得 2,3 似有誤,實為 1,7? 0x17=23, AAM→AH=2,AL=3 正確)0x1AA3 / 1092 = 6 餘 267 → 分鐘=6 → '0','6'267 * 60 / 1092 = 14 餘 ... → 秒=14 → '1','4'HMS = "23:06:14" (作者追蹤顯示 "23:06:14",但中間過程文字描述有出入)| 項目 | 數值 | 說明 |
|---|---|---|
| 最大計數 0x1800B0 | 1,573,040 | BIOS 實際重置值 |
| 理論值 (86400×18.1818) | 1,572,480 | 560 差異 (30.8 秒) |
| 一小時滴答 (3600×18.2) | 65,520 | 比 65535 少 15 |
| 一天滴答 (24×65520) | 1,572,480 | 同上 |
結論:BIOS 定時器頻率並非精確 18.2 Hz,而是 1,193,180 Hz / 65,536 = 18.2065 Hz (8253 計數器 16 位元計數器溢位頻率)。
一天滴答 = 86,400 × 18.2065 ≈ 1,573,041 ≈ 0x1800B1,與 0x1800B0 極接近 (差 1)。
作者使用 18.2 近似值導致計算偏差。真實硬體頻率由 1.19318 MHz 晶振除以 65536 決定,非軟體可調。
這段轉換程式碼展示了早期系統程式設計師如何巧妙利用定點運算 (DIV/MUL 定比例) 避免浮點運算,將硬體計數器值高效轉換為人類可讀時間。AAM 指令雖慢但極適合 BCD 轉 ASCII。對於計數器最大值疑問,深入硬體頻率來源 (8253/1.19318 MHz) 即可釐清。這類「硬體頻率 → 軟體計數 → 時間轉換」鏈條,是理解即時系統、嵌入式開發、效能分析的基石知識。