************************************************
* Profiler code Color Computer 3 
* copyright Chris Lomont 2006 www.lomont.org
************************************************

************************************************
* This file supports a instruction cycle profiler
* the two functions of use are StartProfiler and 
* StopProfiler. 
* Call EnableProfiler and DisableProfiler before and after
************************************************

************************************************
* EnableProfiler - enables the profiler by using 
* the FIRQ interrupt and the timer at 279 ns
* also sets fast speed poke at $FFD9
* Registers changed: none
************************************************
oldInt	fcb	0,0,0			old interrupt address (had JMP =0x7E then 16 bit addr)
EnableProfiler
	pshs	a,x
	
	clr	$FFD9			high speed poke (todo - undo at end?)
	
	lda	$FF91			get timer and other control
	ora	#%00100000	
	sta	$FF91			set 279 ns clock
	
	lda	$FEF4			store old instructions at FEF4		
	sta	oldInt			
	ldx	$FEF5			interrupt vector 
	stx	oldInt+1		3 bytes stored
	
	lda	#$7E			opcode for long jump instruction	
	sta	$FEF4
	ldx	#ProfilerInterrupt	jump to our interrupt	
	stx	$FEF5			address
		
	lda	$FF93
	ora	#%00100000		set bit 5
	sta	$FF93			enable FIRQ on timer overflow
	
	lda	$FF90
	ora	#%00010000		bit 4 is FIRQ in CoCo3 mode as we need it
	sta	$FF90
	
	andcc	#%10111111		clear bit 6, enables FIRQ
	puls	a,x
	
	rts
	
************************************************
* DisableProfiler - call this when deon to stop profiler  
* stops timer and resets FIRQ address
* Registers changed: none
************************************************
DisableProfiler
	pshs	d,x
	bsr	StopProfiler		end any action
	
	orcc	#%01000000		set bit 6, disables FIRQ

	lda	$FF90
	anda	#%11101111	 	bit 4 is FIRQ in CoCo2 as it was?!
	sta	$FF90


	lda	$FF93
	anda	#%11011110		disable timer on FIRQ
	sta	$FF93			stop FIRQ on timer overflow
	
	lda	oldInt
	sta	$FEF4
	ldx	oldInt+1	
	stx	$FEF5			restore old interrupt
	puls	d,x
	
	rts

************************************************
* StartProfiler - Starts the profiler, resets count to 0 
* Registers changed: none
************************************************
StartProfiler
	pshs	d
	ldd	#0
	std	profileCounter		reset this
	ldd	#$30    		much faster and machine hangs from too many interrupts
	bsr	SetTimerCount
	puls	d
					*todo - use sync here?
	rts

************************************************
* StopProfiler - stops the profiler from counting
* returns approx cycle count / 8 in REGD
* Registers changed: REGD
************************************************
StopProfiler
	ldd	#0			
	bsr	SetTimerCount		stops timer from counting
	ldd	profileCounter		get this for return
	bsr	ProfileCountToCycles	
	rts

************************************************
* ProfileCountToCycles - from testing this converts
* profile count in REGD to cycles / 8  (approx) in REGD
* Registers changed: REGD
************************************************
* formula is (d-5)
ProfileCountToCycles
	subd	#5
	rts



************************************************
* SetTimerCount - set the timer value
* REGD - value start at (counts down)
* Registers changed: none
************************************************
SetTimerCount
	pshs	a
	anda	#15			only bottom 4 bits allowed
	stb	$FF95			LSB of timer
	sta	$FF94			MSB, starts timer
	puls	a
	rts


************************************************
* ProfilerInterrupt - this is a FIRQ interrupt
* so store any registers needed
* Registers changed: none
************************************************
profileCounter	fcb	0,0		16 bit instruction count counter
ProfilerInterrupt
	if 1	
	inc	profileCounter+1	6 - low byte
	bne	done			3 - jump if no rollover
	inc	profileCounter		6 - if rollover, inc high byte
done	tst	$FF93			2 - cause a read
	rti				6 - 17 cycles on 255/256 of the time, else 23
	endif


	if 0
	std	profileTemp		5 save register
	lda	$FF93			4 sees what triggered FIRQ, and is REQUIRED READ
	ldd	profileCounter		5 cycles
	addd	#1			4 cycles
	std	profileCounter		5 cycles
	andcc	#%10111111		3 clear bit 6, enables FIRQ
	ldd	profileTemp		5 restore register
	rti				6 return from interrupt
					*37 cycles total
profileTemp	fcb	0,0
	endif

************************************************
* Testing space
*	if 1		todo - remove from final code

************************************************
* TestProfiler - call this and walk through in debugger to 
* see how profiler works
* Registers changed: All
************************************************
TestProfiler
	lbsr	EnableProfiler
	
	ldu	#profileData		store results here
					* counts are with removing ProfileCountToCycles	

	lbsr	StartProfiler
	lbsr	StopProfiler		 
	std	,u++			save result $05
	
	lbsr	StartProfiler
	lbsr	Delay			each call to Delay is 9 + 55 = 64 cycles
	lbsr	StopProfiler		
	std	,u++			save result $1A

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $2C

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $3D

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $52

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $64

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $75

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $8A

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $9C

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			
	lbsr	StopProfiler		
	std	,u++			save result $AD

	lbsr	StartProfiler
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	Delay			 
	lbsr	StopProfiler		
	std	,u++			save result $C2

	lbsr	DisableProfiler

	lbra	TestProfiler		

	
	* now clean up data - subtract off first value from rest, and then
	* compute differences
	ldy	#profileData+2
!	ldd	,y
	subd	profileData
	std	,y++
	cmpy	#profileData+2*10
	blt	<
	
!	ldd	,y
	subd	-2,y
	std	,y
	leay	-2,y
	cmpy	#profileData
	bgt	<
	
	lbra	TestProfiler		
	rts

profileData 	rmb	2*12		space to store results

* delay 92 cycles
Delay 					
	ldd	#6			3 cycles
!	subd	#1			4 cycles * 6
	bne	<			3 cycles * 6
	ldd	#1			3 cycles
	lda	#1			2 cycles
	rts				5 cycles, 55 cycles total
	
*	endif

************************************************

************************************************
* END OF FILE
************************************************
