/*****************************************************
 
     Low_Power.c
     
     Z-World, 2000

Please Note:
This program will lose communication with Dynamic C once it begins
running. Dynamic C will display a target communication error message.
Dynamic C cannot continue debugging once the target starts running
off the 32 kHz oscillator.
     
This sample program shows how to implement a function in RAM in order
to reduce power comsumption by the Rabbit microprocessor.  There are
four features which create the lowest possible power drain by the
processor:
1) Run the CPU from the 32 KHz crystal
2) Turn off the high frequency crystal oscillator
3) Run from RAM
4) Insure that internal I/O instructions do not use CS0

This sample will show LEDs toggling on the JackRabbit (BL1800) and
CoreModule prototyping boards. If you are don't have LEDs hooked to
PA0-PA3, you can use a scope. PA0 will toggle the fastest.

*******************************************************************/


/*******************************************************************
This function must be in root in order for it to be copied to RAM.
Due to the granularity of the XPC (8K byte window), this function
cannot be larger than 4K bytes or cross a 4K byte boundary.
*******************************************************************/
#asm root
RamFunction::
			ipset	3					; disable interrupts

; These two statements cause CS0 to NOT get activated when doing
; internal I/O operations thereby further reducing power by insuring
; that the flash is never accessed. They CANNOT be executed from flash.
			ld		a, 0xC5			; use CS1 for
	ioi	ld		(MB0CR), a		;  memory bank 0

; These instructions are loops which shows that the Rabbit is executing
; the function. The first 4 bits of Port A are toggled at varying speeds.
			ld    c,10          
Loop0:
			ld 	b,15	
Loop1:
			ld    d,255

Loop2:	; time delay loop    
			dec   d					 
			jr    nz,Loop2	      

			ld    a,b
			xor   0xff
	ioi	ld		(PADR), a	; write b to port A
			djnz	Loop1		

			dec   c
			jr    nz,Loop0
			
			; done looping, now restore CSO
			ld		a, 0xC0			; use CS0 for
	ioi	ld		(MB0CR), a		;  memory bank 0

	ipres		; restore the interrupt priority level
	ret
RamFunctionEnd::
#endasm

root main()
{
	auto unsigned long physaddr;			// Physical memory address to write to
	auto unsigned long FunctionSize;		//	Number of bytes in the Low Power function
	static char XPCvalue;					// XPC value where function is copied to RAM
	static unsigned int Offset;			// Offset in XPC area where function is copied
		
	WrPortI(SPCR, NULL, 0x84);		// make port A all outputs

	// Allocate space in RAM for the RAM function and copy it to RAM
	FunctionSize = (long)RamFunctionEnd - (long)RamFunction;  

	// Using xalloc ensures that unused RAM is allocated
	physaddr = xalloc(FunctionSize);			

	// Copy RamFunction to RAM
	root2xmem(physaddr, (void *)RamFunction, (int)FunctionSize);

	// Calculate xpc stuff
	XPCvalue = (char)( (physaddr - 0xE000L) >> 12 );	// get xpc value
	Offset = (unsigned int)(physaddr & 0xFFF) + 0xE000; // and offset

	// main osc off, use 32 KHz oscillator, disable periodic interrupt
	// we now loose communications with Dynamic C - if the low power
	// function executes for any length of time
	use32kHzOsc();

	// Disable the watch dog timer
	Disable_HW_WDT();

#asm
			// call that routine in SRAM - THIS MUST BE EXECUTED FROM ROOT
			ld		hl, retadr			; put the return address
			push	hl						;	on the stack so that function can use RET
			ld		a, (XPCvalue)		;  get calculated xpc value
			ld		xpc, a				;  load it into the xpc
			ld		hl, (Offset);		;  load offset into XPC area
			jp 	(hl);					;  jump to the function
retadr:
#endasm

	// Main osc. on, use main oscillator, enable periodic interrupt
	useMainOsc();

	// This loop toggles all PA0-PA3 all at once to indicate that we
	//  have returned from the low power function.
	while (1)
	{
		costate
		{
			waitfor(DelayMs(300));
			WrPortI ( PADR, NULL, 15 );
			waitfor(DelayMs(300));
			WrPortI ( PADR, NULL, 0 );
		}
	}

}
