# Thread: Calculating the number of loops for 1 ms.

1. ## Calculating the number of loops for 1 ms.

I was wondering if this thread should be in the Hardware thread or here. I needed a way to calculate the length of one millisecond in the form of counting a loop. I used a method I used, more or less, on my C64 as well:

Code:
```;**  Measure how many DX:CX loops are needed for 1 ms.
; Info: one timer tick of the 8253/8254 is 54.925 ms. We count ten ticks to get
;	a better end result. More ticks would be better but will slow the start up
;	of the program

; Clear the counters
xor	cx,cx
xor	dx,dx
mov	byte [cs:bTemp+4],10

; Save the original INT 08h vector ...
mov	si,TimerINT		; ptr to timer INT 08h

mov	ax,[si]
mov	[cs:bTemp],ax
mov	ax,[si+2]
mov	[cs:bTemp+2],ax

;  ... and replace it with the count loop
cli
mov	word [si],TmpInt8A
mov	[si+2],cs
sti
.L08:
jmp	.L08			; now just wait....

; Actual count loop
TmpInt8A:
mov	al,20h			; send end_of_interrupt
out	PIC8259,al		;   to 8259 interrupt chip

mov	word [si],TmpInt8C	; set second vector
TmpInt8B:
sti
.L02:
inc	cx			; overflow?
jnz	.L02			; no, ->

inc	dx
jmp	.L02

; Restore the interrupt vector
TmpInt8C:
cli
mov	al,20h			; send end_of_interrupt
out	PIC8259,al		;   to 8259 interrupt chip

dec	byte [cs:bTemp+4]	; did we do 10 cycles?
jnz	TmpInt8B		; no, ->

mov	ax,[cs:bTemp+2]
mov	[si+2],ax
mov	ax,[cs:bTemp]
mov	[si],ax
sti

mov ax,dx
mWordToScreen
mDisplaySpace

mov	ax,cx
mWordToScreen
mDisplaySpace

mov	bx,549
div	bx

mWordToScreen
mDisplaySpace```
I ran it on PCE-emulator and the result is: DX = 0001, CX = 60F2 and CX should be 00A4h for 1 ms. 164 loops for 1 ms. means 6 use/loop and the loop is nothing more than
Code:
```.L10:
inc cx
jnz .L10```
To be honest, I'm very familiar with the timing of the 6502 but never ever had a precise look at the one for the 8088. Reading the above instructions are three cycles, I think executing "inc cx" is one cycle but I have no idea how long the branch takes. too make a long story short: I thought that the whole should have been shorter, at least 4 usecs IMHO. But please, any expert may clarify things.

Then what about the refresh? I have no idea how long one will take. One of the reasons to measure over ten ticks is to middle out the number of refreshes.

At least I think the above code is usable for fast CPUs as well. On a 4.77 GHz 8088 DX would become 055Dh. A 8088 uses 4 or 5 cycles/instruction so an on-cycle machine wouldn't even come near 2000h.
Last edited by Ruud; November 29th, 2019 at 03:26 AM.

2. Originally Posted by Ruud
On a 4.77 GHz 8088 DX would become 055Dh. A 8088 uses 4 or 5 cycles/instruction so an on-cycle machine wouldn't even come near 2000h.
Unfortunately a wrong assumption When doing a divsion, in this case DX:AX div BX, AX becomes the result and DX the remainder (and not the high-word as I thought). This means the initial DX can be 548 = 224h at the max. So this means a 2.38 GHz 8088 as max and this frequency is in the range of a lot of P4 CPUs. And the P4 needs less cycles/instruction.
It means that I have to use another method to do the needed division. So far I hardly used the DIV instruction at all which means I can learn something new

3. Originally Posted by Ruud
I needed a way to calculate the length of one millisecond in the form of counting a loop.
Why? And why can you not read the timer directly for this information? (I'm not being pedantic; I'm asking because the answers will direct the suggestions I give you.)

4. Do you know if the DRAM uses distributed or burst refresh. I'd think that with a 8088 one would have enough time between accesses to put distributed refresh.
Dwight

5. Other than for the original 5150/5160, part of the PC standard is to iimplement a 50 µsec refresh flag, regardless of the actual refresh frequency--or even if the thing uses static RAM.

6. Originally Posted by Chuck(G)
Other than for the original 5150/5160, part of the PC standard is to iimplement a 50 µsec refresh flag, regardless of the actual refresh frequency--or even if the thing uses static RAM.
I assume that meant burst. That would be too slow for distributed that would require 15us between ras refresh pulses for 128 cycle.
Dwight

7. Originally Posted by Trixter
Why? And why can you not read the timer directly for this information? (I'm not being pedantic; I'm asking because the answers will direct the suggestions I give you.)
I'm writing my own Turbo Pascal compatible version, just for fun. And it has this "Wait" function which use a 1 ms. loop.

I have indeed thought about checking the 8253 timer registers directly. It should be a matter of reading the registers, calculate what the value should be after 1 ms. and then wait for it. But IMHO the overhead of reading the I/O registers every time would cause more inaccuracy than filling CX and DX with a value and then just counting down.

8. Like everything in life, there are tradeoffs. Reading the timer I/O registers every time is overhead, but it ensures you'll exit the routine at the time you want within a margin of jitter even if interrupts are enabled, whereas counting down a value might take longer because of the interrupt. Another problem with the register-counting-down method is that, if you are targeting an unknown system (like, a 286 or something) it is difficult to derive the correct value without either reading the I/O registers anyway, or division... And in the case of division, any fractional error compounds throughout the countdown loop.

If you're willing to disable interrupts, you can find a reasonable value through calibration at program startup: Find it with interrupts off by increasing the value until it exceeds 1ms (found by checking the timer registers), then back it off by one value and you've got it. Then, when you delay, that must also be with interrupts off.

If you're curious to see what Turbo Pascal actually did for their Delay(), here it is:

Code:
```DelayCnt	DW	?

...

MOV	ES,Seg0040
MOV	DI,OFFSET Timer
MOV	BL,ES:[DI]
@@2:	CMP	BL,ES:[DI]
JE	@@2
MOV	BL,ES:[DI]
MOV	AX,-28
CWD
CALL	DelayLoop
NOT	AX
NOT	DX
MOV	CX,55
DIV	CX
MOV	DelayCnt,AX

...

; Delay specified number of milliseconds

Delay:

MOV	BX,SP
MOV	CX,SS:[BX+4]
JCXZ	@@2
MOV	ES,Seg0040
XOR	DI,DI
MOV	BL,ES:[DI]
@@1:	MOV	AX,DelayCnt
XOR	DX,DX
CALL	DelayLoop
LOOP	@@1
@@2:	RETF	2

; Delay one timer tick or by CX iterations

DelayLoop:

@@1:	SUB	AX,1
SBB	DX,0
JC	@@2
CMP	BL,ES:[DI]
JE	@@1
@@2:	RET```

9. If you're working on a 286 or better, you can also use INT 15H, AH=86h to wait a specific amount of time (granularity is microseconds). So you'd only need to handle the 808x cases explicitly. I don't know if this applies to the low-end Tandy 286 boxes, but it's easy enough to check.

There are several INT 15H similar functions.

10. Originally Posted by Trixter
Reading the timer I/O registers every time is overhead, but it ensures you'll exit the routine at the time you want within a margin of jitter even if interrupts are enabled, whereas counting down a value might take longer because of the interrupt.

... if you are targeting an unknown system (like, a 286 or something) it is difficult to derive the correct value ...
I disagree. A faster CPU will just will count faster and so my initial test will get higher values for CX and DX. TP3 only uses counting down CX but TP3 was developed in the pre-Pentium era. Knowing about the various patches needed for TP7 to be able to run on faster Pentium machines, I decided to involve DX in the counting as well. But regarding the above remark, I will drop this method anyway.

... Then, when you delay, that must also be with interrupts off.
Not when using the function. It could that the user wants to test certain interrupts during, let's say a second, using the function. If I disable interrupts during this delay, his test will never work.

If you're curious to see what Turbo Pascal actually did for their Delay(), here it is:
I just happen to have the disassembly of TP3. And that's how I know they use the count down method. That's why I decided to use it as well.

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•