Image Map Image Map
Page 1 of 2 12 LastLast
Results 1 to 10 of 13

Thread: Calculating the number of loops for 1 ms.

  1. #1

    Default 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 02:26 AM.
    With kind regards / met vriendelijke groet, Ruud Baltissen

    www.baltissen.org

  2. #2

    Default

    Quote Originally Posted by Ruud View Post
    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
    With kind regards / met vriendelijke groet, Ruud Baltissen

    www.baltissen.org

  3. #3
    Join Date
    Aug 2006
    Location
    Chicagoland, Illinois, USA
    Posts
    6,353
    Blog Entries
    1

    Default

    Quote Originally Posted by Ruud View Post
    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.)
    Offering a bounty for:
    - A working Sanyo MBC-775, Olivetti M24, or Logabax 1600
    - Music Construction Set, IBM Music Feature edition (has red sticker on front stating IBM Music Feature)

  4. #4

    Default

    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. #5
    Join Date
    Jan 2007
    Location
    Pacific Northwest, USA
    Posts
    33,676
    Blog Entries
    18

    Default

    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. #6

    Default

    Quote Originally Posted by Chuck(G) View Post
    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. #7

    Default

    Quote Originally Posted by Trixter View Post
    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.
    With kind regards / met vriendelijke groet, Ruud Baltissen

    www.baltissen.org

  8. #8
    Join Date
    Aug 2006
    Location
    Chicagoland, Illinois, USA
    Posts
    6,353
    Blog Entries
    1

    Default

    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
    Offering a bounty for:
    - A working Sanyo MBC-775, Olivetti M24, or Logabax 1600
    - Music Construction Set, IBM Music Feature edition (has red sticker on front stating IBM Music Feature)

  9. #9
    Join Date
    Jan 2007
    Location
    Pacific Northwest, USA
    Posts
    33,676
    Blog Entries
    18

    Default

    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. #10

    Default

    Quote Originally Posted by Trixter View Post
    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.
    Very good remark! I had not think about the last.

    ... 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.
    With kind regards / met vriendelijke groet, Ruud Baltissen

    www.baltissen.org

Bookmarks

Posting Permissions

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