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

     Samples\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. To prevent this, run with no polling (Alt-F9).

	This sample program shows how to implement a function in RAM in order
	to reduce power consumption 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 blink the fastest.  After
	switching to low-power, all the LEDs blink together.

*******************************************************************/
#class auto

// local function prototypes
void delay_ms(unsigned long delay_mS);

/*******************************************************************
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

   //necessary delay for packet transfer from target before calling
   //use32kHzOsc function.
   delay_ms(2);
	// 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 );
		}
	}

}

void delay_ms(unsigned long delay_mS)
{
	auto unsigned long delay_end;

	delay_end = MS_TIMER + delay_mS;
	// OK to return early if MS_TIMER + delay_mS wraps
	while ((MS_TIMER < delay_end) && (delay_end >= delay_mS));
}