/* CPROF - Profile library and print routines for C/80.

	8/11/83 - C/80 3.0 version
	2/26/82 - C/80 2.0 version
	9/23/81 - M80 compatibility.
	1/7/81 - add CP/M distinction; sort printout by time
	9/6/80 - add percentages of runtime
	4/13/80 - compile with c -l32600 */

#define CPM 1			/* Defined for CP/M, undefined for HDOS */

/* To generate version which does not use in-memory clock, delete
	following 5 lines, and recompile by "c -l32600 cprof"
	(omit the -l for M80 compatibility).  For non-Heath/Zenith
	systems with clocks, code may be modified by changing all
	references to TICCNT. */

#ifdef CPM
#define TICCNT 11
#else
#define TICCNT 8219
#endif

int *LINKPT -1; 		/* Chain of routines */

/* Routines to handle call and return */
#asm
; CPROF - C/80 Execution Profile (8/11/83)
;  Note - for use on other than Heath/Zenith systems,
;	CPROF.ASM must be regenerated.	See C/80 documentation.
CURRTN: DW 0		;CURRENT RTN
CALLTP: DW 0		;TEMP FOR TIME
CALLED: DW 0		;HOLDS RTN CALLED
FIRSTM: DB 0		;FIRST TIME FLAG

CALL::	DS 0
#ifdef TICCNT
#ifdef CPM
	LDA FIRSTM
	XRI 1
	JZ CALL1
	STA FIRSTM
#endif
	LXI H,0
	SHLD TICCNT
CALL1:	LHLD TICCNT
	SHLD CALLTP
#endif
	POP H
	MOV A,M
	STA CALLED
	INX H
	MOV A,M
	STA CALLED+1
	INX H
	MOV A,M
	STA CURRTN
	INX H
	MOV A,M
	STA CURRTN+1
	INX H
	PUSH H		;FIX RETURN ADDRESS
	PUSH B
	PUSH D
	CALL SCORET
	LHLD CALLED
	PUSH H
	LXI H,exit	;CHECK IF QUITTING
	CALL e.
	CNZ PrinPr
	POP D
	POP B
	LHLD CALLED
	PCHL		;CALL ROUTINE

RET::	XTHL		;SAVE RETURN VALUE
	PUSH B
	PUSH D
#ifdef TICCNT
	PUSH H
	LHLD TICCNT
	SHLD CALLTP
	POP H		;GET CURRENT SUBR
#endif
	CALL h.##
	SHLD CURRTN
	CALL SCORET
	PUSH H		;INCREMENT COUNT
	CALL h.
	INX H
	CALL q.##
	LHLD CURRTN
	PUSH H
	LXI H,main	;IF RETURN FROM MAIN
	CALL e.##
	CNZ PrinPr	 ;PRINT SUMMARY
	POP D
	POP B
	POP H		;RESTORE RETURN VAL
	RET

SCORET: LHLD CURRTN
	DCX H		;PREPARE TO STORE VALUES
	DCX H
	PUSH H		;PUSH ADDRESS OF COUNT
	DCX H
	DCX H
	PUSH H		;ADDRESS OF TIME
	PUSH H
	DCX H
	DCX H
	PUSH H		;GET PTR TO LINK
	CALL h.
	MOV A,H 	;CHECK LINK=0?
	ORA L
	JNZ CALL2	;YES - ADD TO CHAIN
	LHLD LINKPT
	XCHG
	LHLD CURRTN	;IF NO LINK,
	SHLD LINKPT	;LINK IT IN
	XCHG
CALL2:	CALL q.
	POP H		;ADD TO TIME
	CALL h.
	XCHG
	LHLD CALLTP
	DAD D
	CALL q.
#ifdef TICCNT
	LXI H,0 	;ERASE TICCNT
	SHLD TICCNT
#endif
	POP H		;GET ADDR OF COUNT
	RET		;AND RETURN
#endasm
PrinPr() {
	static char *p,*besticks,*n;
	static int *q,time,count,*best;
	static unsigned totime;
#ifdef TICCNT
	for (p="ROUTINE\tCALLS\tTICKS\t% TIME (Approx.)\n"; *p; putchar(*p++));
#else
	for (p="ROUTINE\tCALLS\n"; *p; putchar(*p++));
#endif
	totime = 0;
	for (q = LINKPT; q != -1; q = q[-3]) {
		if (0100000 & (time = 100 + q[-2]))
			{ totime =+ 164; time =& 077777; }
		totime =+ time / 200; }
	for (;;) {
		for (best = besticks = 0, q = LINKPT; q != -1; q = q[-3]) {
			n = q[-2];			/* Find highest time */
			if (q[-1]) if (n >= besticks) {
				besticks = n;
				best = q;
			}	}
		if (best == 0) break;			/* Quit if all out */
		count = *--best;			/* Print it. */
		*best = 0;				/* Zero count to erase*/
		p = best =- 2;
		while (p[-1]) --p;
		while (p != best) putchar(*p++); putchar('\t');
		PrinNr(count);
#ifdef TICCNT
		putchar('\t');
		PrinNr(besticks = (unsigned)besticks >> 1);
		putchar('\t');
		PrinNr(((int)besticks + (totime >> 1)) / totime);
#endif
		putchar('\n');
	}	}
PrinNr(n) int n; {
	static char a[6],*p;
	static int nn;
	p = a;
	if ((nn = n) < 0) nn =- 20000;	/* Handle up to 52767 */
	do *p++ = '0' + nn % 10; while (nn =/ 10);
	if (n < 0) p[-1] =+ 2;
	do putchar(*--p); while p > a;
	return; exit()==main(); 	/* Dummy calls to declare externals */
	}

hile p > a;
	ret