【问题标题】:The DOS.GetSystemTime function 2Ch is not accurateDOS.GetSystemTime 函数 2Ch 不准确
【发布时间】:2023-01-16 01:03:39
【问题描述】:

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

【问题讨论】:

    标签: assembly time x86 x86-16 dos


    【解决方案1】:

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

    每次 Programmable Interval Timer (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。参见Why did some BIOSes have the timer tick wrap-around at 1800B1h instead of at 1800B0h?

    BIOS Year TotalTicks PIT #0 Mode
    Award Modular BIOS v4.51 PG 1996 1573040 8254 3
    Phoenix BIOS 4.00 Release 6.00 1999 1573041 8254 3
    Compaq System ROM 686P9 v1.11 2002 1573040 8254 3
    Phoenix Technologies LTD v1.23 2007 1573041 8254 2
    AMI BIOS 2010 1573040 8254 2
    DOSBox 0.74 2010 (*) 8254 3

    (*) 不同于普通的 BIOS,DOSBox does not make the BIOS timer wraparound at midnight。这我的时间程序对此作出了规定。

    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
    ; ----------------------
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-28
      • 1970-01-01
      • 2011-12-13
      • 1970-01-01
      相关资源
      最近更新 更多