TEMPORARY RAM USAGE OF GENERATED CODE

Normally when a compiler generates assembly code from a high level expression there will be occations when the expression is so complicated that all the ALU registers available are not enough. The compiler then falls back to using RAM. Very often this RAM is managed as a stack. The PIC however cannot handle a user stack very efficently. It has a single index register which could be used as a stack pointer, but since there is only one index register and it must be used for array lookup and pointer dereferencing, it cannot be used as a stack pointer.

The XCASM compiler therefore allocates temporary RAM (for use in expression evaluation) as fixed variables. This has the advantage of producing fast efficient code but it also has the great disadvantage of producing non-re-enterant code.

Consider an expression such as:

	a = (b + c) + func(x)
this would probably be converted to:
	movf	b,w
	addwf	c,w
	movwf	xacc_0		; <-- point 1
	movf	x,w
	call	func		; <-- point 3
	addwf	xacc_0,w	; <-- point 2
	movwf	a
during evaluation of the expression, the CPU must make a detour through function 'func' at point 3. However before it does so, it has computed a partial result which it has stored in xacc_0 at point 1. If the function 'func' also uses high level expressions which are compiled useing the XCASM compiler, then there is a very high probability that it will overwrite xacc_0 before returning. So at point 2 xacc_0 may no longer contain the value stored in it at point 1. If the PIC supported stacks efficiently, then the XCASM compiler could use the stack to store the (b+c) partial result instead of xacc_0. In order to avoid such complications, calling user functions from within an expression is not allowed.

Another complication (actually the same one but in a diffrent guise) occures when the user includes high level expressions in an interrupt service routine. This is like the function calling problem above, except that it happens (seemingly) randomly during the evaluation of an expression outside the interrupt.

consider an expression such as

	a = (b + c) - (d + e)
this would probably be converted to:
	movf	b,w
	addwf	c,w
	movwf	xacc_0		; <-- point 1
	movf	d,w
	addwf	e,w
	addwf	xacc_0,w	; <-- point 2
	movwf	a
now what would happen if an interrupt occured during point 1 and point 2? This would be the same as calling the interrupt service routine (which if it contained a high level expresion would probably corrupt the value in xacc_0, just like calling function 'func' above).

For interrupt service routines, this can be easilly overcome. It is a simple mater of using variables (see .set statement) to change the RAM locations used as temporary storage during computation.

The compiler is very predictable regarding its use of temporary RAM. The compiler uses wacc_N for word variables and xacc_N for byte variables. wacc_N must overlay xacc_N this allows the compiler to optimally organise part word operations without the overhead of moving data between byte and word variables.

e.g.
xfred_0	.ds	2
xfred_1	.ds	2
xfred_2	.ds	2
	.org	$-6
wfred_0	.dw	0
wfred_1	.dw	0
wfred_2	.dw	0

; NOTE: wfred_N overlays xfred_N
;	wfred_N is defined as a 16 bit RAM variable
;	xfred_N is defined as an 8 bit RAM variable

xacc_0	.set	xfred_0
xacc_1	.set	xfred_1
xacc_2	.set	xfred_2

wacc_0	.set	wfred_0
wacc_1	.set	wfred_1
wacc_2	.set	wfred_2
BEWARE: functions called from an interrupt service routine (ISR) must use the same temporary RAM as the ISR and not those of the main line code. It is therefor very unwise to call the same routine from both the main line and the ISR without taking precausions to safegard the use of the temporary RAM. This could be done using simple and straight forward enabling and disabling of the interrupt, or saving and restoring the temporary RAM contents on entry to and exit from the ISR, or by having two versions of the same routine (each using the same temporary RAM as the function, mainline or ISR that will be calling it).