Micro Measurement Timer

Basic and Machine Language

Moderator: Moderators

Post Reply
User avatar
MrSterlingBS
Vic 20 Enthusiast
Posts: 174
Joined: Tue Jan 31, 2023 2:56 am
Location: Germany,Braunschweig

Micro Measurement Timer

Post by MrSterlingBS »

Hi,

I was comparing the speed of different integer multiplications. A small routine for measuring the cycles came out. This uses only the first of two counters $9128. The second counter would have to be implemented for longer cycle times.

I don't know why 10 cycles are used to start.

Maybe someone can use this routine.

Code: Select all

FLPASC 	equ $DDDD
INTFLP	equ $D391		

CLRSCR	equ $E55F
WRTF	equ $E742

STROUT 	equ $CB1E
STACK	equ	$0100

* = $1001
		; BASIC program to boot the machine language code
		db $0b, $10, $0a, $00, $9e, $34, $31, $30, $39, $00, $00, $00
			
	sei				; stop interrupts
	
	jsr CLRSCR		; clear screen
; code from the book VIC-20 MACHINE LANGUAGE GUIDE Abacus Software	
	lda #0
	sta $9129	
	sta $9128		;  4 cycles
	cld				;  2 cycles
	clc				;  2 cycles
					; ---------
					; 10 cycles ??? WHY ???
					; =========
	
; ************** Start Counter with 10 Cycles ********************	
	
	lda #$05		;  2 cycles
	sta $FB			;  3 cycles
	ldx #$01		;  2 cycles
	lda $FB,x		;  4 cycles
	nop				;  2 cycles
					; ---------
					; 13 cycles
					; =========

; ************** Stop Counter ********************		
	
	lda $9128		
	sta $FB

	lda #$FF
	sbc $FB
	sbc #10			; subtract the 10 cycles from start
	tay				; low
	
	lda #$00		; high
	jsr INTFLP		; integer to float point
	
	jsr Primm
	db "CYCLES:",$00	
	
	jsr FLPASC
	ldy #>STACK
	lda #<STACK
	jsr STROUT
	
	
	jmp *
	
Primm:
	pla
	sta $03
	pla
	sta $04
X10D6:	
	inc $03
	bne	X10DC
	inc $04
X10DC:
	ldy #$00
	lda ($03),y
	beq X10E7
	jsr WRTF
	bcc X10D6
X10E7:
	lda $04
	pha
	lda $03
	pha
	rts
	
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Micro Measurement Timer

Post by Mike »

Here is a different implementation that addresses the issues I mentioned about your routine in another thread (download):

Code: Select all

10 FORT=828TO893:READA:POKET,A:NEXT
11 DATA 169,125,141,30,145,169,130,141,30,145,169,64,141,27,145,169,254,141,20,145,169
12 DATA 255,141,21,145,96,8,120,174,20,145,172,21,145,40,202,202,202,202,96,32,60,3,120
13 DATA 32,86,3,134,253,132,254,32,125,3,32,86,3,134,251,132,252,88,76,249,253,96
14 :
15 SYS868:GOSUB23:PRINTDT
16 :
17 FORT=893TO898:READA:POKET,A:NEXT
18 DATA 162,1,202,208,253,96
19 :
20 SYS868:GOSUB23:PRINTDT
21 END
22 :
23 T1=256*PEEK(252)+PEEK(251)
24 T2=256*PEEK(254)+PEEK(253)
25 DT=T2-T1-55
26 IFDT<0THENDT=DT+65536:GOTO26
27 RETURN
The routine uses Timer 1 of VIA #1 and so leaves the normal IRQ (keyboard, jiffy clock) working normally. I determined the constant 55 in line 25 empirically by executing an "empty" test routine.

This is the source:

Code: Select all

; ** Set up VIA#1 Timer 1 as free running timer with maximum period
;    (see $034B..$0352)
.033C  LDA #$7D   ; disable all NMIs
.033E  STA $911E  ; except RESTORE key
.0341  LDA #$82   ; explicitly enable
.0343  STA $911E  ; NMI from RESTORE key
.0346  LDA #$40   ; set VIA#1 ACR for T1 free running mode
.0348  STA $911B  ; PB7 disabled, etc. ...
.034B  LDA #$FE   ; load $FFFE into T1L and T1C
.034D  STA $9114  ; for maximum period ($FFFE
.0350  LDA #$FF   ; down to $0000 *and* $FFFF
.0352  STA $9115  ; then reloading $FFFE from latch).
.0355  RTS

; ** Read T1C into X/Y. Take possible underflow of T1C-L while executing
;    LDY $9115 into account. Disables IRQs during read, but otherwise retains
;    environment's interrupt enable status.
.0356  PHP 
.0357  SEI 
.0358  LDX $9114
.035B  LDY $9115
.035E  PLP 
.035F  DEX 
.0360  DEX 
.0361  DEX 
.0362  DEX 
.0363  RTS 

; ** Main routine: start timer, read timer marks before and after Routine Under
;    Test. Call RUT with JSR $037D. Re-init I/O with JMP $FDF9.
.0364  JSR $033C
.0367  SEI 
.0368  JSR $0356
.036B  STX $FD
.036D  STY $FE
.036F  JSR $037D
.0372  JSR $0356
.0375  STX $FB
.0377  STY $FC
.0379  CLI 
.037A  JMP $FDF9

; ** "Empty" Routine Under Test. Used to empirically eliminate cycle count of
;    caller framework in instructions $0368..$0372 and the RTS in $037D itself.
.037D  RTS
Lines 17 and 18 replace the empty routine by a simple delay loop. By changing "162,1" in line 18 to "162,X" the delay loop can be changed to do X iterations instead of only one (with 6 cycles then) - each extra iteration adds another 5 cycles. LDX #0 will result in 256 iterations.

Code: Select all

; ** Example Routine, timing loop: as is, it needs 6 cycles (2 for LDX #$01,
;    2 for DEX; 2 for non-executed BNE). Incrementing the immediate operand
;    of LDX adds another 5 cycles for each extra iteration.
.037D  LDX #$01
.037F  DEX 
.0380  BNE $037F 
.0382  RTS 
DT gives 65535 as maximum value and then wraps around to 0. If the tested routine takes more than 65535 cycles, you will need to anticipate/estimate beforehand the number of wraparounds the timer (difference) may take and then add a corresponding multiple of 65536 to DT to obtain the true result.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Micro Measurement Timer

Post by chysn »

Mike wrote: Wed Jul 05, 2023 12:58 pm

Code: Select all

.0368  JSR $0356
.036B  STX $FD
.036D  STY $FE
.036F  JSR $037D
.0372  JSR $0356
.0375  STX $FB
.0377  STY $FC
Is it necessary to read the timer (JSR $0356) twice? The before-values are always going to be the same, aren't they? Since you're empirically finding a subtraction offset for a zero-length routine anyway, all you need is the end time* and that empirically-determined number, right?

* I'm assuming we're testing routines of less than 65536 cycles
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Micro Measurement Timer

Post by chysn »

I was interested in this because it makes a useful wAx plug-in.

Code: Select all

   10 i = 1024
   11 .@-
   12 .,'i'  jmp @&
   13 .,*    :00 ;norm
   14 .,*    ".u addr"
   15 .,*    :00 ;eos
   16 .,* @& bcs @g
   17 .,*    jsr $cf08
   18 .,* @g sei
   19 .,*    lda #$7d
   20 .,*    sta $911e
   21 .,*    lda #$82
   22 .,*    sta $911e
   23 .,*    lda #$40
   24 .,*    sta $911b
   25 .,*    lda #$fe
   26 .,*    sta $9114
   27 .,*    lda #$ff
   28 .,*    sta $9115
   29 .,*    lda $a6
   30 .,*    sta @j+1
   31 .,*    lda $a7
   32 .,*    sta @j+2
   33 .,*    lda $030c
   34 .,*    ldx $030d
   35 .,*    ldy $030e
   36 .,* @j jsr $ffff
   37 .,*    lda $9114
   38 .,*    ldy $9115
   39 .,*    sec
   40 .,*    sbc #$04
   41 .,*    cli
   42 .,*    sta $a6
   43 .,*    sty $a7
   44 .,*    sec
   45 .,*    lda #$d1
   46 .,*    sbc $a6
   47 .,*    sta $a6
   48 .,*    lda #$ff
   49 .,*    sbc $a7
   50 .,*    sta $a7
   51 .,*    jsr $a01e
   52 .,*    lda #"."
   53 .,*    jsr $a009
   54 .,*    lda #"$"
   55 .,*    jsr $a009
   56 .,*    jsr $a021
   57 .,*    jsr $a018
   58 .,*    jmp $fdf9
   59 .p 'i'
With usage like this:
Screen Shot 2023-07-14 at 2.07.16 AM.png
Includes the ability to set registers with the Register Editor tool prior to running the subroutine:
Screen Shot 2023-07-14 at 3.24.17 AM.png
Attachments
cyc.zip
(636 Bytes) Downloaded 54 times
Last edited by chysn on Fri Jul 14, 2023 7:44 am, edited 4 times in total.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Micro Measurement Timer

Post by Mike »

chysn wrote:Is it necessary to read the timer (JSR $0356) twice? The before-values are always going to be the same, aren't they? Since you're empirically finding a subtraction offset for a zero-length routine anyway, all you need is the end time* and that empirically-determined number, right?
Taking the time mark twice, with two identical instruction sequences (a per the JSR call), is what actually justifies subtracting the empirically found offset:

Code: Select all

.0368  JSR $0356
.0356  PHP 
.0357  SEI 
.0358  LDX $9114
.035B  LDY $9115
.035E  PLP       ;  4
.035F  DEX       ;  2
.0360  DEX       ;  2
.0361  DEX       ;  2
.0362  DEX       ;  2
.0363  RTS       ;  6
.036B  STX $FD   ;  3
.036D  STY $FE   ;  3
.036F  JSR $037D ;  6
.037D  RTS       ;  6
.0372  JSR $0356 ;  6
.0356  PHP       ;  3
.0357  SEI       ;  2
.0358  LDX $9114 ;  4
.035B  LDY $9115 ;  4
.035E  PLP       ; --
.035F  DEX       ; 55
.0360  DEX 
.0361  DEX 
.0362  DEX 
.0363  RTS 
.0375  STX $FB
.0377  STY $FC
Or put in another words - my implementation makes a clear distinction between setting up the timer for a full period, and reading time marks. There is also no need to place the routine under test, with its bracketing calls to read the timer, at a fixed position after the timer initialisation. Finally, one possibly wants to test not only one, but two routines, which with that setup only needs one timer init call and three timer read calls, instead of two timer init calls and two timer read calls (I regard the timer init as more 'expensive' than the timer read).

But in principle, yes, the job can also be done with one timer init and just one timer read. You have to make sure though that IRQs are disabled for the time including timer init, routine under test and timer read.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Micro Measurement Timer

Post by chysn »

OK, Mike, I think I understand the rationale, which is that it's mostly for completeness and flexibility as the program scales. I still don't really see the case for measuring twice for a kind of static utility like this, but I get why it appeals to a certain programming aesthetic. As for me, I might put my SEI up higher, like you suggested, before the timer setup.

I also have a couple questions about the timer-read subroutine, here for reference:

Code: Select all

.0356  PHP 
.0357  SEI 
.0358  LDX $9114
.035B  LDY $9115
.035E  PLP 
.035F  DEX 
.0360  DEX 
.0361  DEX 
.0362  DEX 
.0363  RTS 
(1) Why is there an SEI in here? There's already SEI in the caller, prior to both calls of 0356. I guess it's possible that the routine you're testing does CLI, but you might as well just throw the results out anyway if that's the case.

(2) Also, why PHP/PLP? What processor status flags are worth preserving here?
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Micro Measurement Timer

Post by Mike »

(1) and (2): the two accesses to $9114 and $9115 must unconditionally be executed with interrupts off, else the whole effort of correcting the readout was worthless (suppose that IRQ triggered between the two LD% instructions). The readout routines however do not assume a given interrupt enable status on entry and thus preserve the callers status with PHP/PLP.

Again, you may attribute this to my programming aesthetic. ;)
chysn wrote:I guess it's possible that the routine you're testing does CLI [...]
So does JSR $FFD2, by the way, in its leadout at $E6C7:

Code: Select all

.F27A  PHA       ; ($0326) normally points to here
.F27B  LDA $9A
.F27D  CMP #$03
.F27F  BNE $F285 
.F281  PLA 
.F282  JMP $E742

.E742  PHA 
[...]
.E76E  JMP $E6C7

.E6C7  LDX $C7
[...]
.E6E6  PLA 
.E6E7  CLC 
.E6E8  CLI       ; <-- !!!
.E6E9  RTS 
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Micro Measurement Timer

Post by chysn »

Mike wrote: Fri Jul 14, 2023 8:03 am So does JSR $FFD2, by the way, in its leadout at $E6C7:
Yeah, I was playing around with $FFD2 as a test routine last night, and the more times $FFD2 is called, the more likely you are to get a crazy result.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Micro Measurement Timer

Post by chysn »

All right, I have another question about this. The reason for this question is that I have to fight for every byte. It begins with this:
Mike wrote: Wed Jul 05, 2023 12:58 pm

Code: Select all

.033C  LDA #$7D   ; disable all NMIs
.033E  STA $911E  ; except RESTORE key
.0341  LDA #$82   ; explicitly enable
.0343  STA $911E  ; NMI from RESTORE key
.0346  LDA #$40   ; set VIA#1 ACR for T1 free running mode
.0348  STA $911B  ; PB7 disabled, etc. ...
And ends with this:
Mike wrote: Wed Jul 05, 2023 12:58 pm

Code: Select all

.037A  JMP $FDF9
The I/O setup at $FDF9 looks like it does everything at the beginning; it disables all NMIs except the RESTORE key (albeit with LDA #$7F / STA $911E / LDA #$82 / STA $911E), and sets VIA1 timer 1 to free-running with LDA #$40 / STA $911B. So why not JSR $FDF9 at the beginning, and RTS at the end?
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Micro Measurement Timer

Post by Mike »

chysn wrote:I have to fight for every byte [...] why not JSR $FDF9 at the beginning, and RTS at the end?
I chose that minimal initialisation procedure at the beginning to show which VIA registers are directly involved.

If your application is fine with that more thorough I/O init procedure at the start (with JSR $FDF9) and leaving VIA#1 timer 1 free running with the full period at the end, then go for it. :)
Post Reply