DOS.GetSystemTime function 2Ch以小时(CH)、分钟(CL)、秒(DH)和百分之一秒(DL)的形式返回当前时间,结果显示这些“百分之一秒”更像是“二十分之一秒”。
我已经包含了一个连续询问这个DOS函数的程序,只显示唯一的时间戳。结果令人失望。我怎样才能获得真正的0.01秒读数?
ORG 256
Begin:
mov bh, -1
Main:
mov ah, 01h ; BIOS.CheckKeystroke
int 16h ; -> AX ZF
jz Work
mov ah, 00h ; BIOS.GetKeystroke
int 16h ; -> AX
Pause:
mov ah, 00h ; BIOS.GetKeystroke
int 16h ; -> AX
cmp al, 27 ; ESC
jne Work
ret ; TerminateProgram
Work:
call DOSTime ; -> CX DX
cmp bh, dl
je Main ; Hundredths didn't change
mov bh, dl
push dx ; (1)
mov bl, ':'
mov al, ch ; Hours
call PrintTrio ; -> (AX DX)
mov al, cl ; Minutes
call PrintTrio ; -> (AX DX)
pop cx ; (1)
mov bl, '.'
mov al, ch ; Seconds
call PrintTrio ; -> (AX DX)
mov bl, 13
mov al, cl ; Hundredths
call PrintTrio ; -> (AX DX)
mov dl, 10
mov ah, 02h ; DOS.PrintCharacter
int 21h
jmp Main
; ----------------------
; IN (al,bl) OUT () MOD (ax,dx)
PrintTrio:
aam
add ax, '00'
push ax ; (1)
mov dl, ah
mov ah, 02h ; DOS.PrintCharacter
int 21h
pop dx ; (1)
mov ah, 02h ; DOS.PrintCharacter
int 21h
mov dl, bl
mov ah, 02h ; DOS.PrintCharacter
int 21h
ret
; ----------------------
; IN () OUT (cx,dx)
DOSTime:
push ax
mov ah, 2Ch ; DOS.GetSystemTime
int 21h ; -> CX DX
pop ax
ret
; ----------------------
上述程序的典型输出为:
17:15:25.84
17:15:25.89
17:15:25.95
17:15:26.00
17:15:26.06
17:15:26.11
17:15:26.17
17:15:26.22
17:15:26.28
17:15:26.33
17:15:26.39
17:15:26.44
17:15:26.50
17:15:26.55
17:15:26.60
17:15:26.66
17:15:26.71
17:15:26.77
17:15:26.82
17:15:26.88
17:15:26.93
17:15:26.99
17:15:27.04
17:15:27.10
这是另一个程序员提出的问题,他希望使用DOS函数2Ch进行延迟,然后发现这一不准确性造成了问题:Delay program using int 21h with ah = 2Ch .
1条答案
按热度按时间0x6upsns1#
当DOS收到系统时间请求时,它将读取BIOS维护的计时器滴答声,并迅速将其值转换为相应的小时、分钟和秒。这个计算有一个余数,DOS将其返回为"百分之一秒"值。这永远不会像0.01秒那样精确,因为计时器滴答声每0.054925秒才改变一次。
每次Programmable Interval Timer(PIT)的通道0产生中断8(IRQ0)时,BIOS保持在0040:006C的32位定时器递增。该中断是内部计数器刚刚递减65536次的结果。一次这样的递减需要0.838095 µ sec,因为PIT的工作频率为14.31818 MHz/12 = 1.193182 MHz。
我们可以读取此内部计数器,并将其用作新的"超级计数器"的最低有效16位,如果我们将其与0040:006C处的32位计数器组合在一起。
由于我们的目标是仅获得0.01秒的精度,因此计算仅使用内部计数器的高8位和BIOS定时器变量的低24位。
代码假定PIT通道0在模式2(速率发生器)或模式3(方波发生器)下工作,频率除数为65536。由于代码使用回读命令,因此还假定PIT不是8253(已废弃),而是至少8254。
一天中的滴答数为1573041,比您预期的多1,请参见Why did some BIOSes have the timer tick wrap-around at 1800B1h instead of at 1800B0h?。
| 基本 ios |年份|总刻度|坑|#0模式|
| - ------|- ------|- ------|- ------|- ------|
| 获奖模块化BIOS v4.51 PG|一九九六年|小行星1573040|小行星8254|三个|
| Phoenix BIOS 4.00版本6.00|一九九九年|* * 1573041**|小行星8254|三个|
| 康柏系统ROM 686P9 v1.11|二○ ○二年|小行星1573040|小行星8254|三个|
| Phoenix科技有限公司v1.23|二○ ○七年|* * 1573041**|小行星8254|* * 二**|
| AMI BIOS|二○一○年|小行星1573040|小行星8254|* * 二**|
| 剂量盒0.74|二○一○年|(注 )|小行星8254|三个|
()DOSBox does not make the BIOS timer wraparound at midnight与普通BIOS不同。* MyTime * 程序对此做了规定。
避免繁琐的除法。
根据
(8639999 / 1800B0FFh)
=(x / 100000000h)
,我们得到:新的"超级计数器"的最大值为1800B0FFh,通过先将其乘以92149630(057E177Eh),然后除以4294967296(100000000h)来转换为百分之一秒,这是丢弃64位乘积的低半部分的简单问题。
这是8086汇编代码,将与FASM一起汇编: