Timing how long something takes

For posting library code and examples.
User avatar
Yazwho
Posts: 172
Joined: Fri Feb 19, 2021 2:59 pm
Contact:

Timing how long something takes

Post by Yazwho »

Sometimes you want to know how long something takes to complete. While emulators generally do a good job of timing methods, they're not always accurate so there are times where you just have to check on the hardware.

If the function takes less than ~65k cycles you can use the VIA1 timer to time your function. We do this using the 'timer 1' within the 65c22. This timer can be set to start from a 16bit number and counts down to zero at the same rate as the CPU clock. At zero it can optionally fire an interrupt. So if we set this to $ffff, perform our function and then read the counter, we can determine how many CPU cycles have elapsed.

First we need to setup the VIA. We do this by writing `01` to the Timer 1 Control to put it in continuous but no interrupt. (PB7 is connected to the CPU's interrupt line, and we don't want that firing randomly!) This is on `V_ACR` ($9f0b)
via.png
via.png (70.29 KiB) Viewed 11399 times
Set the timer to start at $ffff by writing $ff to `V_T1L_L` ($9f06) and `V_T1L_H` ($9f07). Now when we write $ff to `V_T1_H` ($9f05) the counter will reset to $ffff and start counting down.
(Note: I'm not sure setting the `V_T1L_H` is necessary, as I think writing to V_T1_H sets the high latch value.)

Once the method has completed, the timer can be read from `V_T1_L` ($9f04) and `V_T1_H` ($9f05). Reading from the low register latches the counter's value so we don't have to worry about the ticks between reading low and high values.

Because there is some overhead on this process, I perform this function w/o any method to get the VIA test 'latency'. This will also correct for any VIA emulator timing issues.

After sampling a method remove the latency to get the time elapsed.

Example code performing this function:

Code: Select all

    ; we don't want interrupts firing which would mess up the timing
    sei

    ; setup timer 1
    lda V_ACR
    and #$3f
    ora #$40
    sta V_ACR       ; set to continuous
    lda #$ff
    sta V_T1L_L
    sta V_T1L_H

    ; test VIA latency
    lda #$ff
    sta V_T1_H      ; resets counter

    ; invert the value and store
    clc
    lda V_T1_L
    eor #$ff
    adc #1
    sta via_latency
    lda V_T1_H
    eor #$ff
    adc #0
    sta via_latency + 1

    lda #$ff
    sta V_T1_H

    lda $a000                   ; test command, should be 4 cycles

    ; capture this sample
    clc
    lda V_T1_L
    eor #$ff
    adc #1
    sta via_elapsed
    lda V_T1_H
    eor #$ff
    adc #0
    sta via_elapsed + 1

    ; take the latency off
    sec
    lda via_elapsed
    sbc via_latency
    sta via_elapsed
    lda via_elapsed + 1
    sbc via_latency + 1
    sta via_elapsed + 1

    ; BitMagic macros to output text
    NewLine();
    Display("latency_display");
    DisplayHex("via_latency", 4);
    Display("elapsed_display");
    DisplayHex("via_elapsed", 4);

.loop:
    jmp -loop
    
    .padvar ushort via_latency
    .padvar ushort via_elapsed
    
.latency_display:
    BM.Bytes(BM.StringToPetscii("VIA LATENCY : "));
.elapsed_display:
    BM.Bytes(BM.StringToPetscii("ELAPSED     : "));
    
Running the code we can see the correct value is displayed.
via_timing.png
via_timing.png (6.64 KiB) Viewed 11399 times
Of course this only works if the method will take less than ~64k CPU cycles. For longer calls you'll need to employ other methods.
User avatar
ahenry3068
Posts: 1218
Joined: Tue Apr 04, 2023 9:57 pm

Re: Timing how long something takes

Post by ahenry3068 »

Yazwho wrote: Sun Dec 01, 2024 3:36 pm Sometimes you want to know how long something takes to complete. While emulators generally do a good job of timing methods, they're not always accurate so there are times where you just have to check on the hardware.

If the function takes less than ~65k cycles you can use the VIA1 timer to time your function. We do this using the 'timer 1' within the 65c22. This timer can be set to start from a 16bit number and counts down to zero at the same rate as the CPU clock. At zero it can optionally fire an interrupt. So if we set this to $ffff, perform our function and then read the counter, we can determine how many CPU cycles have elapsed.

First we need to setup the VIA. We do this by writing `01` to the Timer 1 Control to put it in continuous but no interrupt. (PB7 is connected to the CPU's interrupt line, and we don't want that firing randomly!) This is on `V_ACR` ($9f0b)

via.png

Set the timer to start at $ffff by writing $ff to `V_T1L_L` ($9f06) and `V_T1L_H` ($9f07). Now when we write $ff to `V_T1_H` ($9f05) the counter will reset to $ffff and start counting down.
(Note: I'm not sure setting the `V_T1L_H` is necessary, as I think writing to V_T1_H sets the high latch value.)

Once the method has completed, the timer can be read from `V_T1_L` ($9f04) and `V_T1_H` ($9f05). Reading from the low register latches the counter's value so we don't have to worry about the ticks between reading low and high values.

Because there is some overhead on this process, I perform this function w/o any method to get the VIA test 'latency'. This will also correct for any VIA emulator timing issues.

After sampling a method remove the latency to get the time elapsed.

Example code performing this function:

Code: Select all

    ; we don't want interrupts firing which would mess up the timing
    sei

    ; setup timer 1
    lda V_ACR
    and #$3f
    ora #$40
    sta V_ACR       ; set to continuous
    lda #$ff
    sta V_T1L_L
    sta V_T1L_H

    ; test VIA latency
    lda #$ff
    sta V_T1_H      ; resets counter

    ; invert the value and store
    clc
    lda V_T1_L
    eor #$ff
    adc #1
    sta via_latency
    lda V_T1_H
    eor #$ff
    adc #0
    sta via_latency + 1

    lda #$ff
    sta V_T1_H

    lda $a000                   ; test command, should be 4 cycles

    ; capture this sample
    clc
    lda V_T1_L
    eor #$ff
    adc #1
    sta via_elapsed
    lda V_T1_H
    eor #$ff
    adc #0
    sta via_elapsed + 1

    ; take the latency off
    sec
    lda via_elapsed
    sbc via_latency
    sta via_elapsed
    lda via_elapsed + 1
    sbc via_latency + 1
    sta via_elapsed + 1

    ; BitMagic macros to output text
    NewLine();
    Display("latency_display");
    DisplayHex("via_latency", 4);
    Display("elapsed_display");
    DisplayHex("via_elapsed", 4);

.loop:
    jmp -loop
    
    .padvar ushort via_latency
    .padvar ushort via_elapsed
    
.latency_display:
    BM.Bytes(BM.StringToPetscii("VIA LATENCY : "));
.elapsed_display:
    BM.Bytes(BM.StringToPetscii("ELAPSED     : "));
    
Running the code we can see the correct value is displayed.
via_timing.png

Of course this only works if the method will take less than ~64k CPU cycles. For longer calls you'll need to employ other methods.

I'll have to dig up the code. But on the Emulator actually keeps a 32 bit CPU cycle counter. It can be read by X16 software running on the Emulator from 4 psuedo hardware ports in the $9F range.
cosmicr
Posts: 50
Joined: Tue Nov 14, 2023 4:29 am

Re: Timing how long something takes

Post by cosmicr »

$9FB8 to $9FBB in X16EMU is the 32-bit counter.

I wonder if you could also extend the VIA timer by using VIA #2?
User avatar
Yazwho
Posts: 172
Joined: Fri Feb 19, 2021 2:59 pm
Contact:

Re: Timing how long something takes

Post by Yazwho »

I wonder if you could also extend the VIA timer by using VIA #2?
I think you'd be able to have a 17bit counter by checking the interrupt state of the VIA at the end.

But I don't think there is a way to increase the resolution above that without any CPU management.
User avatar
desertfish
Posts: 1126
Joined: Tue Aug 25, 2020 8:27 pm
Location: Netherlands

Re: Timing how long something takes

Post by desertfish »

On the C64 you could connect timer A to timer B in the CIA chip. The 6522 VIA has 2 timers as well doesn't it? Can't they be connected in a similar fashion to get a single 32 bit timer?
Wavicle
Posts: 290
Joined: Sun Feb 21, 2021 2:40 am

Re: Timing how long something takes

Post by Wavicle »

It seems like you could get another 1 or 2 bits using the YM2151 timers.
User avatar
ahenry3068
Posts: 1218
Joined: Tue Apr 04, 2023 9:57 pm

Re: Timing how long something takes

Post by ahenry3068 »

Wavicle wrote: Thu Dec 05, 2024 3:42 am It seems like you could get another 1 or 2 bits using the YM2151 timers.
If you did this you would need some code to make sure you were not running on a YM2164.
mortarm
Posts: 342
Joined: Tue May 16, 2023 6:21 pm

Re: Timing how long something takes

Post by mortarm »

Yazwho wrote: Sun Dec 01, 2024 3:36 pm We do this by writing `01` to the Timer 1 Control to put it in continuous but no interrupt.
This seems to conflict with the diagram as it says, "Continuous Interrupts".
(Note: I'm not sure setting the `V_T1L_H` is necessary, as I think writing to V_T1_H sets the high latch value.)
Could this have been tested?
doslogo
Posts: 32
Joined: Fri Dec 20, 2024 4:26 pm

Re: Timing how long something takes

Post by doslogo »

Yazwho wrote: Sun Dec 01, 2024 3:36 pm Once the method has completed, the timer can be read from `V_T1_L` ($9f04) and `V_T1_H` ($9f05). Reading from the low register latches the counter's value so we don't have to worry about the ticks between reading low and high values.

Code: Select all

    ; capture this sample
    clc
    lda V_T1_L
    eor #$ff
    adc #1
    sta via_elapsed
    lda V_T1_H
    eor #$ff
    adc #0
    sta via_elapsed + 1
    
I have been searching for a way to get the current timer value, and this example seems too good to be true. Reading from the low counter, then executing as many instructions you want, then reading the high counter, and trust me, the timer has ticked so many cycles they are not the correct value in the end.

I captured low byte $06 from a counting timer 1 being $FF06 (-250), but when I tried to capture the high byte, the low byte had ticked over so it was $FE. Final captured 16-bit timer value was $FE06 (-506).

Can't get more inaccurate than that.

So how do one stop the timer so the full 16-bit value can be read?

I'm using the official X16 emulator r48
cosmicr
Posts: 50
Joined: Tue Nov 14, 2023 4:29 am

Re: Timing how long something takes

Post by cosmicr »

doslogo wrote: Wed Mar 19, 2025 10:26 pm So how do one stop the timer so the full 16-bit value can be read?
Did you use SEI before getting the values? If I understand correctly, you need to use SEI before, and CLI afterwards.
Post Reply