1 秒计时器等待时间过长

1 second timer waiting too long

我制作了一个 1 秒计时器代码,它有 2 个程序,一个是计算结束时间,另一个总是检查时间以查看该时间是否发生。大多数情况下,1 秒计时器会起作用,但有时它不会等待 1 秒,而是等待 4 秒...任何

proc calculateNewCubeTime
    pusha
    mov ah, 2ch
    int 21h
    mov [drawTime], dl
    cmp [drawTime], 0
    je add99
    dec [drawTime]
    jmp endCalculateDrawTime
add99:
    add [drawTime], 99
endCalculateDrawTime:
    popa
    ret
endp calculateNewCubeTime
proc checkIfTimeToDrawNewCube
    pusha 
    mov ah, 2ch
    int 21h
    cmp dl, [drawTime]
    jne endCheckDrawTime
    mov [newDrawCube], 1
endCheckDrawTime:
    popa
    ret
endp checkIfTimeToDrawNewCube

尝试使用 dh(秒)而不是 dl(百分之一秒):

proc initNewCubeTime            ; only called one time
        pusha
        mov     ah, 2ch
        int     21h
        mov     [drawTime], dh
waitForChange:
        mov     ah, 2ch
        int     21h
        cmp     dh, [drawTime]
        je      waitForChange
        mov     [drawTime], dh
        popa
        ret
endp initNewCubeTime
proc checkIfTimeToDrawNewCube
        pusha 
        mov     ah, 2ch
        int     21h
        cmp     dh, [drawTime]
        je      endCheckDrawTime
        mov     [drawTime], dh
        mov     [newDrawCube], 1
endCheckDrawTime:
        popa
        ret
endp checkIfTimeToDrawNewCube

示例测试程序。在它运行的 60 秒内似乎没有漂移。

        .286
        .model  tiny,c
        .code
        org     0100h
main    proc    far
        call    init
main0:  call    chk               ;wait for change (1 second)
        mov     dh,[flg]
        cmp     dh,0
        je      main0
        xor     dx,dx
        mov     [flg],dh
        lea     dx,msg0
        mov     ax,0900h
        int     21h
        mov     dh,[loop0]
        inc     dh
        mov     [loop0],dh
        cmp     dh,60             ;run for 60 seconds
        jb      main0
        mov     ax,04c00h         ;exit back to dos
        int     21h
msg0    db      '.','$'
secb    db      0
flg     db      0
loop0   db      0
main    endp

init    proc
        pusha
        mov     ah, 2ch
        int     21h
        mov     [secb], dh
wait0:  mov     ah, 2ch
        int     21h
        cmp     dh, [secb]
        je      wait0
        mov     [secb], dh
        popa
        ret
init    endp

chk     proc
        pusha 
        mov     ah, 2ch
        int     21h
        cmp     dh, [secb]
        je      skip0
        mov     [secb], dh
        mov     [flg], 1
skip0:  popa
        ret
chk     endp
        end     main

我认为这种方法有几个问题。

  • 我相信 INT 21h / AH=2Ch 返回的 DOS 时间由 18.2 Hz INT 08h 定时器保存。因此,虽然它以 1/100 秒(厘秒)为单位进行报告,但实际上并没有该分辨率,因此您可以预期它会以 5-6 厘秒的步长递增。特别是,由于 18.2 不是整数,它可能会超过您的目标时间戳。由于您只测试相等性,因此您会错过它。

  • 即使没有这个问题,由于多种原因中的任何一个,您的代码总是有可能被中断超过一个计时器滴答(硬件中断风暴、慢 TSR 等),在在这种情况下,您的截止日期可能会在您不注意的情况下过去。

  • 您可以尝试通过确定是否至少一秒已经过去来修复它,这意味着比较秒和厘秒字段,寻找“大于”而不是“等于”。但是,您必须小心谨慎,以正确处理在秒数即将结束时开始延迟的可能性。 (我认为 DOS 没有闰秒,但这会使事情变得更加复杂。)

但是您可以通过使用 BIOS 调用来延迟而不是您自己的循环来避免重新发明轮子,例如INT 15h / AH = 86h, provided you are running on a PC/AT or better. (If using an emulator, though, beware that .)