; Co-processor registers

C0	CN	0
C1	CN	1
C2	CN	2
C3	CN	3
C4	CN	4
C5	CN	5
C6	CN	6
C7	CN	7
C8	CN	8
C9	CN	9
C10	CN	10
C11	CN	11
C12	CN	12
C13	CN	13
C14	CN	14
C15	CN	15

; Co-processor numbers

CP0	CP	0
CP1	CP	1
CP2	CP	2
CP3	CP	3
CP4	CP	4
CP5	CP	5
CP6	CP	6
CP7	CP	7
CP8	CP	8
CP9	CP	9
CP10	CP	10
CP11	CP	11
CP12	CP	12
CP13	CP	13
CP14	CP	14
CP15	CP	15

		^	0
fcc		#	4
exclusive_time	#	8
inclusive_time	#	8
sizeof_prof_blk	*	@

		AREA	|ARM$$Code|, CODE

		EXPORT	__cyg_profile_func_enter
		EXPORT	__cyg_profile_func_exit

		; Standard C Functions
		IMPORT	atexit
		IMPORT	exit
		IMPORT	malloc
		IMPORT	memset

		; In profc.c
		IMPORT	profile_save

		; Generated by Linker
		IMPORT	|Image$$RO$$Base|
		IMPORT	|Image$$RO$$Limit|

; Function entry handler
;
; On Entry:
;		r0 -> function
;		r1 -> call site
;
; On Exit:
;		r0-r3 corrupt
__cyg_profile_func_enter
		ROUT

		STMFD	sp!,{r4-r8,lr}
		MOV	r4,r0			; preserve function pointer

		LDR	r1,initialised		; initialise, if necessary
		TEQ	r1,#0
		BLEQ	profile_init

		MRC	CP14,0,a1,C1,C0,0	; read CCNT

		ADR	r5,stack_base
		LDMIA	r5,{r5-r8}

		TEQ	r5,r6
		BEQ	%FT01			; stack empty

		LDR	r1,[r6,#-8]		; r1 -> caller's profile blk

		; update caller's exclusive time

		LDR	r2,[r1,#exclusive_time]
		LDR	r3,[r1,#exclusive_time + 4]
		SUB	r5,r0,r8
		ADDS	r2,r2,r5
		ADC	r3,r3,#0
		STR	r2,[r1,#exclusive_time]
		STR	r3,[r1,#exclusive_time + 4]

01		LDR	r1,ro_base
		LDR	r2,ro_limit
		CMP	r4,r1
		CMPGT	r2,r4
		BLE	%FT02 	   	       	; address out of bounds

		SUB	r4,r4,r1		; get offset into table
		ADD	r4,r4,r7
		LDR	r1,[r4]
		TEQ	r1,#0			; create struct, if necessary
		BLEQ	profile_create

		LDR	r2,[r1,#fcc]		; increase call count
		ADD	r2,r2,#1
		STR	r2,[r1,#fcc]

		; push onto call stack

		STR	r1,[r6],#4		; profile block pointer
		STR	r0,[r6],#4		; time called
		STR	r6,stackp

02
  		STR	r0,last_called
		LDMFD	sp!,{r4-r8,pc}


; Function exit handler
;
; On Entry:
;		r0 -> function
;		r1 -> call site
;
; On Exit:
;		r0-r3 corrupt
__cyg_profile_func_exit
      	      	ROUT

		; if (!initialised || stack empty) return;
		LDR	r1,initialised
		TEQ	r1,#0
		LDRNE	r1,stack_base
		LDRNE	r2,stackp
		TEQNE	r1,r2
		MOVEQ	pc,lr

		STMFD	sp!,{r4-r8,lr}
		MOV	r4,r0

		MRC	CP14,0,a1,C1,C0,0	; read CCNT
		LDR	r5,ro_base
		LDR	r6,ro_limit
		CMP	r4,r5
		CMPGT	r6,r4
		BLE	%FT01			; address out of bounds

		ADR	r5,stack_base
		LDMIA	r5,{r5-r8}
		LDR	r1,[r6,#-4]!		; r1 = time function entered
		LDR	r2,[r6,#-4]!		; r2 -> profile block
		STR	r6,stackp

		LDMIB	r2,{r3-r6}		; load times

		SUB	r7,r0,r8		; update exclusive
		ADDS	r3,r3,r7
		ADC	r4,r4,#0

		SUB	r7,r0,r1		; update inclusive
		ADDS	r5,r5,r7
		ADC	r6,r6,#0

		STMIB	r2,{r3-r6}		; save back

01
  		STR	r0,last_called		; update last called and exit
		LDMFD	sp!,{r4-r8,pc}


; Initialise the profiling code
; Ensures ProfileMod is loaded and allocates memory for call stack etc.
;
; On Entry:
;		-
;
; On Exit:
;		r0-r3 corrupt
profile_init	ROUT
		STMFD	sp!,{r4,r5,lr}
		MOV	r0,#18
		ADR	r1,profile_mod
		SWI	&2001e			;XOS_Module
		BVC	%FT01

		;try to load the module

		MOV	r0,#1
		ADR	r1,profile_cmd
		SWI	&2001e			;XOS_Module
		MOVVS	a1,#255
		BVS	exit

01		STR	pc,initialised		; mark as initialised

		LDR	a1,ro_base		; create table
		LDR	a2,ro_limit
		SUB	a1,a2,a1
		BL	malloc

		TEQ	a1,#0
		MOVEQ	a1,#255			; malloc failed
		BEQ	exit

		STR	a1,table_base

		LDR	a2,ro_limit
		LDR	a3,ro_base		; zero-init table
		SUB	a3,a2,a3
		MOV	a2,#0
		BL	memset

		MOV	a1,#4096		; create call stack
		BL	malloc

		TEQ	a1,#0
		MOVEQ	a1,#255
		BEQ	exit

		STR	a1,stack_base
		STR	a1,stackp

		; register exit handler
		ADR	a1,profile_save_shim
		BL	atexit

		LDMFD	sp!,{r4,r5,pc}

profile_cmd	=	"<NetSurf$Dir>."
profile_mod	=	"ProfileMod",0
		ALIGN


; On Entry:
;    	   	r4 -> table entry
; On Exit:
;    	  	r1 -> block
;		table entry filled in
profile_create
	      	ROUT
		STMFD	sp!,{r0,lr}
		MOV	a1,#sizeof_prof_blk
		BL	malloc

		TEQ	a1,#0
		MOVEQ	a1,#255	       	  	; malloc failed
		BEQ	exit

		MOV	r1,#0
		STR	r1,[a1,#fcc]
		STR	r1,[a1,#exclusive_time]
		STR	r1,[a1,#exclusive_time + 4]
		STR	r1,[a1,#inclusive_time]
		STR	r1,[a1,#inclusive_time + 4]

		MOV	r1,a1

		STR	a1,[r4]
		LDMFD	sp!,{r0,pc}


; Shim for profile_save, filling in arguments as required
;
; On Entry:
;		-
;
; On Exit:
;		r0-r3 corrupt - exits through profile_save
profile_save_shim
	     	ROUT

		LDR	a1,table_base
		LDR	a3,ro_base
		LDR	a2,ro_limit
		SUB	a2,a2,a3
		MOV	a2,a2,LSR#2
		B	profile_save


; Data

initialised	DCD	0

stack_base	DCD	0

stackp		DCD	0

table_base	DCD	0

last_called	DCD	0

ro_base		DCD	|Image$$RO$$Base|

ro_limit	DCD	|Image$$RO$$Limit|

		END
