Search code examples
assemblytimerdosx86-16clock

1 second timer waiting too long


I made a 1 second timer code that has 2 procedures, One is calculating the end time and the other is always checking the time in order to see if that time happened. Most of the times the 1 second timer works but sometimes it doesn't wait 1 second but 4... Any

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

Solution

  • I think there are a couple of problems with this approach.

    • I believe that the DOS time as returned by INT 21h / AH=2Ch is kept by the 18.2 Hz INT 08h timer. So although it's reported in 1/100ths of a second (centiseconds), it doesn't actually have that resolution, and so you can expect it to increment in steps of 5-6 centiseconds. In particular, since 18.2 is not an integer, it could step past your target timestamp. Since you only test for equality, you'd miss it.

    • Even without this issue, it is always possible that for any of a number of reasons, your code gets interrupted for longer than one timer tick (hardware interrupt storm, slow TSR, etc), in which case your deadline could go by without you noticing.

    • You could try to fix it by determining whether at least one second has passed, which would mean comparing both the seconds and centiseconds fields, looking for "greater than" instead of "equal to". However, you'll have to be a little careful to correctly handle the possibility that you started delaying near the end of a minute, when the seconds counter is about to wrap around. (I don't think DOS did leap seconds, but that would complicate things further.)

    But you could avoid reinventing the wheel by using a BIOS call to delay instead of your own loop, e.g. INT 15h / AH = 86h, provided you are running on a PC/AT or better. (If using an emulator, though, beware that DosBox is buggy with int 15h ah = 86h .)