******************************************************************************
*
* local pointer checker -- Version 3.0
*
* Copyright (C) 1989-1990 by Apple Computer.
* Written by Eric Soldan.
*
* Developer Technical Support Apple II Sample Code
*
******************************************************************************

		case	on

rtlStkDepth	equ	50		;You may need to make this bigger.

******************************************************************************

* These functions are to help detect usage of C pointers and handles that are
* not initialized.  In main(), call initPtrCheck().  Do NOT call other
* functions in main.
* 
* In other functions, call zapLocals() as the first thing in the function.
* checkForHit() will automatically be called whenever you exit a function.
* You can call checkForHit() at any point you want, also.  If you have an idea
* of which function has a pointer problem, but you are not sure where in
* the function the problem is, you can add some calls to checkForHit().
* The functions initPtrCheck(), closePtrCheck(), zapLocals(), and
* checkForHit() can be conditionally set active or inactive with #define
* statements.  This way they can be active when developing, and turned off
* when making a production version.  The calls can be defined to nothing,
* thus making any reference to them drop out of the object code.
*
* So, a C program would use the functions as below:
*
* main()
* {
*     initPtrCheck(_ownerid);
*     /* Your code goes here */
*     closePtrCheck();
*     exit(0);
* }
*
* void           someFnCalledByMain(parm1, parm2)
* unsigned int   parm1, parm2;
* {
*     unsigned int   i, j, k;
*     unsigned long  theID;
*
*     zapLocals();
*     /* Your code goes here */
*     checkForHit();
* }

* Note that zapLocals() needs to be the first code in a function.  Also,
* you can't call zapLocals before you call initPtrCheck().  Given this
* dependency, you can't call zapLocals in main().  To fix this problem,
* you may wish to have a nearly empty main().  You may want it to just
* do the initPtrCheck() and closePtrCheck(), and the code in between
* simply calls something like main0(), which can have a zapLocals().

******************

* The return address to exit zapLocals is used to point to the code used to
* create the stack frame.  The stack frame code should look like the below.
* The values in front are the negative offsets from the return address.
* (This is why zapLocals() must be the first code in a function.)

* E		PHD  	
* D		TSC  	
* C		SEC  	
* BA9		SBC  	#$fb
* 8		TCD  	
* 765		ADC  	#$f0
* 4		TCS  	

* 321.0		JSL  	>zapLocals

******************************************************************************

		include	'm16.memory'
		include	'm16.misctool'
		include	'm16.util2'


		export	initPtrCheck
initPtrCheck	proc
		export	ptrCheckHndl, rtlStkPtr, rtlStk, zapval

		DefineStack

hndl		long			;Must be at 1,s.
ptr		long

dpage		word
retaddr		block	3

userid		word

		phd

		pha			;Make space for locals.
		pha
		pha
		pha
		tsc
		tcd

		pea	1		;Get the bank-sized handle.
		lda	#0
		sta	>rtlStkPtr	;Nothing on "stack" yet.
		pha
		pei	userid
		pea	$C010
		pha
		pha
		_NewHandle
		bcc	@gothndl

		lda	#0
		sta	>ptrCheckHndl	;Flag as no handle.
		sta	>ptrCheckHndl+2
		sta	>zapval		;"Pattern" is 0.
		sta	>$0		;Point 0-2 to the screen.
		lda	#$E100		;If there is any bad stuff
		sta	>$1		;going on, then it will show
		bra	@exit		;on the super-hi-res screen.
					;This is true only for handles.
					;Simple pointers will still
					;store into zpage.  Storing
					;into zpage will, most likely,
					;cause the system to die.
					;The best test is when the
					;system has enough memory to
					;create the 64k hit-test handle.

@gothndl		lda	hndl		;Keep the handle, and initialize it.
		sta	>ptrCheckHndl
		lda	hndl+2
		sta	>ptrCheckHndl+2

		ldy	#2		;Dereference the handle to get to
		lda	[hndl],y		;the bank of memory.
		sta	ptr+2
		lda	[hndl]
		sta	ptr
		lda	ptr+2		;Move byte 2 of bank address into
		xba			;byte 3 also.
		ora	ptr+2
		sta	>zapval

		ldy	#0		;Fill the handle with the bank value,
@loop		sta	[ptr],y		;so pointers within the handle end
		iny			;up pointing back into the handle.
		iny
		bne	@loop

		sta	>$0
		sta	>$2

@exit		pla
		pla
		pla
		pla

		pld
		rtl

ptrCheckHndl	dc.l	0
zapval		dc.w	0
rtlStkPtr	dc.w	0
rtlStk		ds.b	rtlStkDepth*3

		endp

******************

		export	zapLocals
zapLocals	proc
		export	saveRegs, getRegs
		import	zapval, rtlStkPtr, rtlStk, checkForHitz

		DefineStack

ptr		long

dpage		word
retaddr		block	3

		phd

		pha			;Make space for locals.
		pha
		tsc
		tcd

		lda	retaddr+1	;Use return address as pointer.
		sta	ptr+1
		lda	retaddr
		sec
		sbc	#$0A
		sta	ptr
		lda	[ptr]		;sbc value
		tax
		ldy	#4
		lda	[ptr],y		;adc value

		sta	ptr
		txa
		sec
		sbc	ptr
		beq	@exit		;No stack frame at all.
		tax			;Save this to fetch RTL address.
		tay			;Number of bytes to zap.
		dey
		beq	@exit		;Just in case.

		tsc
		clc
		adc	#retaddr+2	;Point to beginning of stack
		sta	ptr		;frame to clobber.
		stz	ptr+2

		shortm
		lda	>zapval		;Pattern to clobber with.
@loop		sta	[ptr],y
		dey
		bne	@loop
		longm

		inx			;Save the return address on rtlStk.
		inx
		txy			;yreg is offset to return address.
		lda	>rtlStkPtr
		cmp	#rtlStkDepth*3	;Make sure we aren't overflowing rtlStk.
		bcc	@a
		brk	#$AA

@a		tax
		lda	[ptr],y
		sta	>rtlStk,x
		iny
		inx
		lda	[ptr],y
		sta	>rtlStk,x	;Return address now on rtlStk.
		inx
		inx
		txa
		sta	>rtlStkPtr	;Save new index into rtlStk.

		lda	#rtlCheck>>8	;Put return address of checking
		sta	[ptr],y		;routine in place of regular
		dey			;return address.
		lda	#rtlCheck-1
		sta	[ptr],y

@exit		pla
		pla

		pld
		rtl

rtlCheck		equ	*		;This is where we end up when a c
					;function exits with an rtl.
		jsl	saveRegs		;Keep registers for return value for c.

		lda	>rtlStkPtr	;Get the real return address onto the stack.
		tax
		dex
		dex
		lda	>rtlStk,x
		pha			;We pushed middle and hi byte here.
		phb
		pla			;We just pulled the middle byte.
		dex
		lda	>rtlStk,x
		pha			;We pushed low and middle byte here, so
					;we have a full rtl address on stack.
		txa
		sta	>rtlStkPtr
		jml	checkForHitz

saveRegs		sta	>keepa		;Keep everything.
		txa
		sta	>keepx
		tya
		sta	>keepy
		php
		php
		pla
		sta	>keepp
		rtl

getRegs		lda	>keepp
		pha
		lda	>keepx
		tax
		lda	>keepy
		tay
		lda	>keepa
		plp
		plp
		rtl

keepa		dc.w	0
keepx		dc.w	0
keepy		dc.w	0
keepp		dc.w	0

		endp

******************

		export	checkForHit
checkForHit	proc
		export	checkForHitz
		import	zapval, saveRegs, getRegs

		jsl	saveRegs

checkForHitz	lda	>zapval
		beq	@exit		;When using zpage as hit
					;detector, there is nothing
					;we can check.  We just have
					;to wait until the system
					;dies a horrible death.  This
					;horrible death should happen
					;faster when clobbering zpage.
		tax
		and	#$FFFE		;So we can go by words.
		tay
		txa

		phb
		pha
		plb
		plb

@loop		cmp	|$0,y
		bne	@oops
		iny
		iny
		bne	@loop

		plb			;Put data bank back.
@exit		jml	getRegs

@oops		plb

		brk	#$AB		;Bank is indicated by acc.
					;Offset into bank that was hit is
					;in the yreg.
					;The program can be resumed, but unless
					;the hit is repaired, the program will
					;break again.

		jml	getRegs		;Allow option of continuing from debugger.

		endp

******************

		export	closePtrCheck
closePtrCheck	proc
		import	ptrCheckHndl, zapval

		jsl	checkForHit	;Let's make sure there were no hits.

		lda	>zapval
		beq	@exit		;No handle to dispose of.

		lda	>ptrCheckHndl+2
		pha
		lda	>ptrCheckHndl
		pha
		_DisposeHandle		;We got rid of what we created.

		lda	#0
		sta	>zapval

@exit		rtl

		endp

		END

