/* cpu_asm.s * * This file contains the basic algorithms for all assembly code used * in an specific CPU port of RTEMS. These algorithms must be implemented * in assembly language * * $Id$ */ #include #include /* * _CPU_Context_save_fp * * This routine is responsible for saving the FP context * at *fp_context_ptr. If the point to load the FP context * from is changed then the pointer is modified by this routine. * * Sometimes a macro implementation of this is in cpu.h which dereferences * the ** and a similarly named routine in this file is passed something * like a (Context_Control_fp *). The general rule on making this decision * is to avoid writing assembly language. * * void _CPU_Context_save_fp( * void **fp_context_ptr * ) * { * } */ .align 4 PUBLIC(_CPU_Context_save_fp) SYM(_CPU_Context_save_fp): save %sp,-104,%sp ld [%i0],%l0 std %f0,[%l0+FO_F1_OFFSET] std %f2,[%l0+F2_F3_OFFSET] std %f4,[%l0+F4_F5_OFFSET] std %f6,[%l0+F6_F7_OFFSET] std %f8,[%l0+F8_F9_OFFSET] std %f10,[%l0+F1O_F11_OFFSET] std %f12,[%l0+F12_F13_OFFSET] std %f14,[%l0+F14_F15_OFFSET] std %f16,[%l0+F16_F17_OFFSET] std %f18,[%l0+F18_F19_OFFSET] std %f20,[%l0+F2O_F21_OFFSET] std %f22,[%l0+F22_F23_OFFSET] std %f24,[%l0+F24_F25_OFFSET] std %f26,[%l0+F26_F27_OFFSET] std %f28,[%l0+F28_F29_OFFSET] std %f30,[%l0+F3O_F31_OFFSET] st %fsr,[%l0+FSR_OFFSET] ret restore /* * _CPU_Context_restore_fp * * This routine is responsible for restoring the FP context * at *fp_context_ptr. If the point to load the FP context * from is changed then the pointer is modified by this routine. * * Sometimes a macro implementation of this is in cpu.h which dereferences * the ** and a similarly named routine in this file is passed something * like a (Context_Control_fp *). The general rule on making this decision * is to avoid writing assembly language. * * void _CPU_Context_restore_fp( * void **fp_context_ptr * ) * { * } */ .align 4 PUBLIC(_CPU_Context_restore_fp) SYM(_CPU_Context_restore_fp): save %sp,-104,%sp ld [%o0],%l0 ldd [%l0+FO_F1_OFFSET],%f0 ldd [%l0+F2_F3_OFFSET],%f2 ldd [%l0+F4_F5_OFFSET],%f4 ldd [%l0+F6_F7_OFFSET],%f6 ldd [%l0+F8_F9_OFFSET],%f8 ldd [%l0+F1O_F11_OFFSET],%f10 ldd [%l0+F12_F13_OFFSET],%f12 ldd [%l0+F14_F15_OFFSET],%f14 ldd [%l0+F16_F17_OFFSET],%f16 ldd [%l0+F18_F19_OFFSET],%f18 ldd [%l0+F2O_F21_OFFSET],%f20 ldd [%l0+F22_F23_OFFSET],%f22 ldd [%l0+F24_F25_OFFSET],%f24 ldd [%l0+F26_F27_OFFSET],%f26 ldd [%l0+F28_F29_OFFSET],%f28 ldd [%l0+F3O_F31_OFFSET],%f30 ld [%l0+FSR_OFFSET],%fsr ret restore /* _CPU_Context_switch * * This routine performs a normal non-FP context switch. * * void _CPU_Context_switch( * Context_Control *run, * Context_Control *heir * ) * { * } */ /* from gcc-2.7.0/config/sparc/sparc.h on register usage */ /* 1 for registers that have pervasive standard uses and are not available for the register allocator. g0 is used for the condition code and not to represent %g0, which is hardwired to 0, so reg 0 is *not* fixed. On non-v9 systems: g1 is free to use as temporary. g2-g4 are reserved for applications. Gcc normally uses them as temporaries, but this can be disabled via the -mno-app-regs option. g5 through g7 are reserved for the operating system. On v9 systems: g1 and g5 are free to use as temporaries. g2-g4 are reserved for applications (the compiler will not normally use them, but they can be used as temporaries with -mapp-regs). g6-g7 are reserved for the operating system. ??? Register 1 is used as a temporary by the 64 bit sethi pattern, so must currently be a fixed register until this pattern is rewritten. Register 1 is also used when restoring call-preserved registers in large stack frames. */ .align 4 PUBLIC(_CPU_Context_switch) SYM(_CPU_Context_switch): ta 0x03 /* flush registers */ /* skip g0 */ st %g1,[%o0+G1_OFFSET] /* globals */ st %g2,[%o0+G2_OFFSET] st %g3,[%o0+G3_OFFSET] st %g4,[%o0+G4_OFFSET] st %g5,[%o0+G5_OFFSET] st %g6,[%o0+G6_OFFSET] st %g7,[%o0+G7_OFFSET] st %l0,[%o0+L0_OFFSET] st %l1,[%o0+L1_OFFSET] st %l2,[%o0+L2_OFFSET] st %l3,[%o0+L3_OFFSET] st %l4,[%o0+L4_OFFSET] st %l5,[%o0+L5_OFFSET] st %l6,[%o0+L6_OFFSET] st %l7,[%o0+L7_OFFSET] st %i0,[%o0+I0_OFFSET] st %i1,[%o0+I1_OFFSET] st %i2,[%o0+I2_OFFSET] st %i3,[%o0+I3_OFFSET] st %i4,[%o0+I4_OFFSET] st %i5,[%o0+I5_OFFSET] st %i6,[%o0+I6_OFFSET] st %i7,[%o0+I7_OFFSET] st %o0,[%o0+O0_OFFSET] st %o1,[%o0+O1_OFFSET] st %o2,[%o0+O2_OFFSET] st %o3,[%o0+O3_OFFSET] st %o4,[%o0+O4_OFFSET] st %o5,[%o0+O5_OFFSET] st %o6,[%o0+O6_OFFSET] st %o7,[%o0+O7_OFFSET] rd %psr,%o2 st %o2,[%o0+PSR_OFFSET] /* save status register */ /* enter here with o1 = context to restore */ /* o2 = psr */ restore: ld [%o1+PSR_OFFSET],%o0 and %o2,31,%o2 /* g1 = cwp */ and %o0,-32,%o0 /* o0 = psr w/o cwp */ or %o0,%o2,%o2 /* o2 = new psr */ wr %o2,0,%psr /* restore status register */ /* skip g0 */ ld [%o1+G1_OFFSET],%g1 ld [%o1+G2_OFFSET],%g2 ld [%o1+G3_OFFSET],%g3 ld [%o1+G4_OFFSET],%g4 ld [%o1+G5_OFFSET],%g5 ld [%o1+G6_OFFSET],%g6 ld [%o1+G7_OFFSET],%g7 ld [%o1+L0_OFFSET],%l0 ld [%o1+L1_OFFSET],%l1 ld [%o1+L2_OFFSET],%l2 ld [%o1+L3_OFFSET],%l3 ld [%o1+L4_OFFSET],%l4 ld [%o1+L5_OFFSET],%l5 ld [%o1+L6_OFFSET],%l6 ld [%o1+L7_OFFSET],%l7 ld [%o1+I0_OFFSET],%i0 ld [%o1+I1_OFFSET],%i1 ld [%o1+I2_OFFSET],%i2 ld [%o1+I3_OFFSET],%i3 ld [%o1+I4_OFFSET],%i4 ld [%o1+I5_OFFSET],%i5 ld [%o1+I6_OFFSET],%i6 ld [%o1+I7_OFFSET],%i7 ld [%o1+O0_OFFSET],%o0 /* do o1 last to avoid destroying heir context pointer */ ld [%o1+O2_OFFSET],%o2 ld [%o1+O3_OFFSET],%o3 ld [%o1+O4_OFFSET],%o4 ld [%o1+O5_OFFSET],%o5 ld [%o1+O6_OFFSET],%o6 ld [%o1+O7_OFFSET],%o7 ld [%o1+O1_OFFSET],%o1 /* overwrite heir pointer */ jmp %o7 + 8 /* return */ nop /* delay slot */ /* * _CPU_Context_restore * * This routine is generallu used only to restart self in an * efficient manner. It may simply be a label in _CPU_Context_switch. * * NOTE: May be unnecessary to reload some registers. * * void _CPU_Context_restore( * Context_Control *new_context * ) * { * } */ .align 4 PUBLIC(_CPU_Context_restore) SYM(_CPU_Context_restore): save %sp, -104, %sp /* save a stack frame */ ta 0x03 /* flush registers */ rd %psr,%o2 ba restore mov %i0,%o1 /* in the delay slot */ /* void _ISR_Handler() * * This routine provides the RTEMS interrupt management. * * void _ISR_Handler() * { * } */ .align 4 PUBLIC(_ISR_Handler) SYM(_ISR_Handler): ret /* * This discussion ignores a lot of the ugly details in a real * implementation such as saving enough registers/state to be * able to do something real. Keep in mind that the goal is * to invoke a user's ISR handler which is written in C and * uses a certain set of registers. * * Also note that the exact order is to a large extent flexible. * Hardware will dictate a sequence for a certain subset of * _ISR_Handler while requirements for setting */ /* * At entry to "common" _ISR_Handler, the vector number must be * available. On some CPUs the hardware puts either the vector * number or the offset into the vector table for this ISR in a * known place. If the hardware does not give us this information, * then the assembly portion of RTEMS for this port will contain * a set of distinct interrupt entry points which somehow place * the vector number in a known place (which is safe if another * interrupt nests this one) and branches to _ISR_Handler. * * save some or all context on stack * may need to save some special interrupt information for exit * * #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE ) * if ( _ISR_Nest_level == 0 ) * switch to software interrupt stack * #endif * * _ISR_Nest_level++; * * _Thread_Dispatch_disable_level++; * * (*_ISR_Vector_table[ vector ])( vector ); * * --_ISR_Nest_level; * * if ( _ISR_Nest_level ) * goto the label "exit interrupt (simple case)" * * #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE ) * restore stack * #endif * * if ( !_Context_Switch_necessary ) * goto the label "exit interrupt (simple case)" * * if ( !_ISR_Signals_to_thread_executing ) * goto the label "exit interrupt (simple case)" * * call _Thread_Dispatch() or prepare to return to _ISR_Dispatch * * prepare to get out of interrupt * return from interrupt (maybe to _ISR_Dispatch) * * LABEL "exit interrupt (simple case): * prepare to get out of interrupt * return from interrupt */