diff --git a/ports/avr/atomport-asm.s b/ports/avr/atomport-asm.s index a4525c4..f7a55a3 100644 --- a/ports/avr/atomport-asm.s +++ b/ports/avr/atomport-asm.s @@ -113,6 +113,28 @@ archContextSwitch: push r28 push r29 + /** + * On devices with large program space we also save RAMPZ, EIND. + * Note that GCC 4.3 and later to actually save RAMPZ when called + * via an interrupt handler, which means that we end up stacking + * RAMPZ twice. However we do need to save RAMPZ for cooperative + * context switches where we are called via a function call rather + * than an ISR (at this time GCC does not save RAMPZ before function + * calls). This has the added benefit that we continue to support + * GCC < 4.3, but with the added overhead of the double-stacking for + * newer versions of GCC. + * + * An alternative method that would work for GCC >= 4.3 only would be + * to detect whether we were called from an interrupt handler and not + * save RAMPZ under those circumstances. + */ +#ifdef __AVR_3_BYTE_PC__ + in r0,_SFR_IO_ADDR(RAMPZ) + push r0 + in r0,_SFR_IO_ADDR(EIND) + push r0 +#endif + /** * Save the final stack pointer to the TCB. The parameter pointing to * the old TCB is still untouched in R25-R24. We have saved R16/R17 @@ -145,12 +167,15 @@ archContextSwitch: * * * + * (Only certain devices) + * (Only certain devices) * * The stack frame if this was a preemptive switch looks as follows: * * // saved by ISR * // * // + * // (Only certain devices) * // * // * || // @@ -167,6 +192,8 @@ archContextSwitch: * * * + * (Only certain devices) + * (Only certain devices) * * * In addition, the thread's stack pointer (after context-save) is @@ -203,6 +230,17 @@ archContextSwitch: out _SFR_IO_ADDR(SPL),r16 /* Set our stack pointer to the new thread's */ out _SFR_IO_ADDR(SPH),r17 /* stack pointer, from its TCB. */ + /** + * On devices with large program space we also restore RAMPZ, EIND. + */ +#ifdef __AVR_3_BYTE_PC__ + pop r0 + in r0,_SFR_IO_ADDR(EIND) + pop r0 + in r0,_SFR_IO_ADDR(RAMPZ) + push r0 +#endif + /** * Restore registers R2-R17, R28-R29. */ @@ -321,6 +359,17 @@ archFirstThreadRestore: out _SFR_IO_ADDR(SPL),r16 /* Set our stack pointer to the new thread's */ out _SFR_IO_ADDR(SPH),r17 /* stack pointer, from its TCB. */ + /** + * On devices with large program space we also restore RAMPZ, EIND. + */ +#ifdef __AVR_3_BYTE_PC__ + pop r0 + in r0,_SFR_IO_ADDR(EIND) + pop r0 + in r0,_SFR_IO_ADDR(RAMPZ) + push r0 +#endif + /** * Restore registers R2-R17, R28-R29. */ diff --git a/ports/avr/atomport.c b/ports/avr/atomport.c index 86e67fa..9ae5555 100644 --- a/ports/avr/atomport.c +++ b/ports/avr/atomport.c @@ -250,6 +250,14 @@ void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_poi *stack_ptr-- = 0x00; /* R28 */ *stack_ptr-- = 0x00; /* R29 */ + /** + * On devices with large program space we also context switch RAMPZ, EIND. + */ +#ifdef __AVR_3_BYTE_PC__ + *stack_ptr-- = 0x00; /* RAMPZ */ + *stack_ptr-- = 0x00; /* EIND */ +#endif + /** * All thread context has now been initialised. Save the current * stack pointer to the thread's TCB so it knows where to start @@ -344,4 +352,4 @@ ISR (TIMER1_COMPA_vect) ISR (BADISR_vect) { /* Empty */ -} \ No newline at end of file +}