繁体   English   中英

DOS.GetSystemTime function 2Ch 不准确

[英]The DOS.GetSystemTime function 2Ch is not accurate

DOS.GetSystemTime function 2Ch以小时 (CH)、分钟 (CL)、秒 (DH) 和百分之一秒 (DL) 的形式返回当前时间。 事实证明,这些“百分之一秒”更像是“二十分之一秒”。

我已经包含了一个连续询问这个 DOS function 的程序,只显示唯一的时间戳。 结果令人失望。 如何获得真正的 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
; ----------------------

上述程序中的典型 output 是:

 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 function 2Ch 进行延迟,然后发现这个问题不准确: Delay program using int 21h with ah = 2Ch

当DOS得到系统时间请求时,它会读取BIOS维护的timer tick,并迅速将其值转换成相应的时、分、秒。 此计算有余数,DOS 将其作为“百分之一秒”值返回。 鉴于计时器滴答仅每 0.054925 秒更改一次,这永远不会像 0.01 秒一样准确。

每次可编程间隔定时器(PIT) 的通道 0 生成中断 8 (IRQ0) 时,BIOS 维护在 0040:006C 的 32 位定时器递增。 该中断是内部计数器递减 65536 次的结果。 一次这样的递减需要 0.838095 微秒,因为 PIT 以 14.31818 MHz / 12 = 1.193182 MHz 运行。
如果我们将它与我们在 0040:006C 处找到的 32 位计数器结合起来,我们可以读取这个内部计数器并将其用作新“超级计数器”的最低有效 16 位。
因为我们的目标只是获得 0.01 秒的精度,所以计算只使用内部计数器的高 8 位和 BIOS 定时器变量的低 24 位。

该代码假定 PIT 通道 0 在模式 2(速率发生器)或模式 3(方波发生器)下运行,分频器为 65536。因为该代码使用回读命令,所以还假定 PIT 不是8253(过时),但至少是 8254。

一天中的刻度数取为 1573041,比您预期的多 1。 请参阅为什么某些 BIOS 在 1800B1h 而不是 1800B0h 处具有定时器滴答循环? .

BIOS 总计 #0 模式
Award 模块化 BIOS v4.51 PG 1996年 1573040 8254 3个
凤凰 BIOS 4.00 版本 6.00 1999 1573041 8254 3个
康柏系统 ROM 686P9 v1.11 2002年 1573040 8254 3个
凤凰科技有限公司 v1.23 2007年 1573041 8254 2个
BIOS 2010 1573040 8254 2个
DOSBox 0.74 2010 (*) 8254 3个

(*) 与普通的 BIOS 不同, DOSBox 不会在午夜使 BIOS 定时器回绕 MyTime程序为此做出了规定。

1800B0FFh .. 8639999

        1 .. 8639999 / 1800B0FFh

        N .. N * (8639999 / 1800B0FFh)

避免繁琐的划分。
(8639999 / 1800B0FFh) = (x / 100000000h)我们得到:

x = 8639999 / 1800B0FFh * 100000000h
x = 92149630

最大值为 1800B0FFh 的新“超级计数器”首先将其乘以 92149630 (057E177Eh),然后除以 4294967296 (100000000h),将其转换为百分之一秒,这很简单,只需丢弃 64 的低半部分位产品。

这是要用 FASM 汇编的 8086 汇编代码:

; IN () OUT (cx,dx)
MyTime:                 ; Assumes 8254 PIT
  push ax bx si di bp
  push ds               ; (1)
  pushf                 ; (2)
  xor  ax, ax
  mov  ds, ax
  mov  si, 046Ch        ; Address of the BIOS.TimerTick
  cli
  mov  cx, [si+1]       ; CX:BH = [0,1573040]
  mov  bh, [si]
  sti
  nop
  nop
.ReDo:
  cli
  mov  al, 11_00_001_0b ; Read-back latched count word and status byte
  out  43h, al          ;  for PIT channel 0
  jmp  $+2
  in   al, 40h          ; Get status byte in AH
  mov  ah, al
  in   al, 40h          ; Get count word in DX
  mov  dl, al
  in   al, 40h
  sti
  mov  dh, al
  test dx, dx           ; Don't accept 0
  jz   .ReDo
  not  dx               ; Make upcounting
  mov  al, ah
  and  al, 00111111b
  cmp  al, 00110100b    ; Is it lowbyte/highbyte mode 2 for PIT channel 0 ?
  je   .Mode2
.Mode3:
  inc  dx               ; (-Count + ~OutputPin * 65536) / 2
  shl  ah, 1
  cmc
  rcr  dx, 1
.Mode2:
  cli
  cmp  bh, [si]
  je   .GotIt
  test dx, dx
  js   .GotIt
  mov  cx, [si+1]       ; CX:BH = [0,1573040]
  mov  bh, [si]
.GotIt:
  popf                  ; (2)
  pop  ds               ; (1)
  mov  bl, dh           ; -> CX:BX

  mov  ax, 057Eh        ; 92149630 = 057E177Eh
  mul  cx
  mov  si, ax
  mov  di, dx

  mov  ax, 057Eh
  mul  bx
  mov  bp, ax
  add  si, dx
  adc  di, 0

  mov  ax, 177Eh
  mul  cx
  add  bp, ax
  adc  si, dx
  adc  di, 0

  mov  ax, 177Eh
  mul  bx
  add  bp, dx
  adc  si, 0
  adc  di, 0

  mov  ax, si
  mov  dx, di
  mov  bx, 6000
  div  bx               ; -> AX is BigMinutes, DX is BigHundredths

  mov  bl, 60
  div  bl               ; -> AL is Hours, AH is Minutes
.DOSBox:                ; DOSBox does not reset the BIOS.TimerTick
  sub  al, 24
  jnb  .DOSBox
  add  al, 24
  mov  ch, al           ; Hours [0,23]
  mov  cl, ah           ; Minutes [0,59]

  mov  ax, dx
  mov  bl, 100
  div  bl               ; -> AL is Seconds, AH is Hundredths
  mov  dh, al           ; Seconds [0,59]
  mov  dl, ah           ; Hundredths [0,99]

  pop  bp di si bx ax
  ret
; ----------------------

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM