Timing how long something takes

For posting library code and examples.
Post Reply
User avatar
Yazwho
Posts: 169
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 342 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 342 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: 1145
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: 35
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: 169
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: 1098
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?
Post Reply