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
CodePudding user response:
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 .)
CodePudding user response:
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