1

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Demo
  • 15
  • 5
  • Since you check for exact equality you might miss the time if you don't poll frequently enough. Whatever is calling this code is likely blocked sometimes. – Jester Mar 08 '22 at 14:53

2 Answers2

3

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 .)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
1

Try using dh (seconds) instead of dl (hundredths of a second):

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

Example test program. Doesn't appear to drift over the 60 seconds it runs for.

        .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
rcgldr
  • 27,407
  • 3
  • 36
  • 61
  • @rcdgldr it's a problem because the timer can be on 0.99 and than when we check later it will show 1.something... So it won't be accurate – Demo Mar 08 '22 at 17:50
  • @Demo - I added code in the init call to wait for the first change. If you're code is fast enough, it should be reasonably consistent after that initial call with the initial wait. – rcgldr Mar 08 '22 at 20:24
  • Oh gotcha but the problem is i need to do this quite often so I don’t know if the program will be fast enough, how can I fix my code to work 100%, I tried to do instead of jne a ja comparison that jumps to the [newDrawTime] and it partly works the main problem being when that instead of waiting it glitches a bit and draws a lot of brief seeing cubes before drawing the correct one – Demo Mar 08 '22 at 20:34
  • @Demo - ja won't work when hundredths of a second wraps around from say 99 to 0. You could try `sub dl,[drawtime]` | `jnb next0` | `add dl,100` | `next0:` | , but using seconds would be simpler. – rcgldr Mar 08 '22 at 20:37
  • @Demo - I added a test code example to my answer. – rcgldr Mar 08 '22 at 21:42