/*  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
 *
 *  Author:     Craig Lebakken <craigl@transition.com>
 *
 *  COPYRIGHT (c) 1996 by Transition Networks Inc.
 *
 *  To anyone who acknowledges that this file is provided "AS IS"
 *  without any express or implied warranty:
 *      permission to use, copy, modify, and distribute this file
 *      for any purpose is hereby granted without fee, provided that
 *      the above copyright notice and this notice appears in all
 *      copies, and that the name of Transition Networks not be used in
 *      advertising or publicity pertaining to distribution of the
 *      software without specific, written prior permission.
 *      Transition Networks makes no representations about the suitability
 *      of this software for any purpose.
 *
 *  Derived from c/src/exec/score/cpu/no_cpu/cpu_asm.s:
 *
 *  COPYRIGHT (c) 1989-1997.
 *  On-Line Applications Research Corporation (OAR).
 *  Copyright assigned to U.S. Government, 1994.
 *
 *  The license and distribution terms for this file may in
 *  the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 */
/* @(#)cpu_asm.S       08/20/96     1.15 */

#include "cpu_asm.h"

#include "iregdef.h"
#include "idtcpu.h"

#define FRAME(name,frm_reg,offset,ret_reg)      \
        .globl  name;                           \
        .ent    name;                           \
name:;                                          \
        .frame  frm_reg,offset,ret_reg
#define ENDFRAME(name)                          \
        .end name


#define EXCP_STACK_SIZE (NREGS*R_SZ)

#if __ghs__
#define sd sw
#define ld lw
#define dmtc0 mtc0
#define dsll sll
#define dmfc0 mfc0
#endif

#if 1  /* 32 bit unsigned32 types */
#define sint sw
#define lint lw
#define stackadd addiu
#define intadd addu
#define SZ_INT 4
#define SZ_INT_POW2 2
#else /* 64 bit unsigned32 types */
#define sint dw
#define lint dw
#define stackadd daddiu
#define intadd daddu
#define SZ_INT 8
#define SZ_INT_POW2 3
#endif

#ifdef __GNUC__
#define EXTERN(x,size) .extern x,size
#else
#define EXTERN(x,size)
#endif

/* NOTE: these constants must match the Context_Control structure in cpu.h */
#define S0_OFFSET 0
#define S1_OFFSET 1
#define S2_OFFSET 2
#define S3_OFFSET 3
#define S4_OFFSET 4
#define S5_OFFSET 5
#define S6_OFFSET 6
#define S7_OFFSET 7
#define SP_OFFSET 8
#define FP_OFFSET 9
#define RA_OFFSET 10
#define C0_SR_OFFSET 11
#define C0_EPC_OFFSET 12

/* NOTE: these constants must match the Context_Control_fp structure in cpu.h */
#define FP0_OFFSET  0 
#define FP1_OFFSET  1 
#define FP2_OFFSET  2 
#define FP3_OFFSET  3 
#define FP4_OFFSET  4 
#define FP5_OFFSET  5 
#define FP6_OFFSET  6 
#define FP7_OFFSET  7 
#define FP8_OFFSET  8 
#define FP9_OFFSET  9 
#define FP10_OFFSET 10 
#define FP11_OFFSET 11 
#define FP12_OFFSET 12 
#define FP13_OFFSET 13 
#define FP14_OFFSET 14 
#define FP15_OFFSET 15 
#define FP16_OFFSET 16 
#define FP17_OFFSET 17 
#define FP18_OFFSET 18 
#define FP19_OFFSET 19 
#define FP20_OFFSET 20 
#define FP21_OFFSET 21 
#define FP22_OFFSET 22 
#define FP23_OFFSET 23 
#define FP24_OFFSET 24 
#define FP25_OFFSET 25 
#define FP26_OFFSET 26 
#define FP27_OFFSET 27 
#define FP28_OFFSET 28 
#define FP29_OFFSET 29 
#define FP30_OFFSET 30 
#define FP31_OFFSET 31 


/*PAGE
 *
 *  _CPU_ISR_Get_level
 */

#if 0
unsigned32 _CPU_ISR_Get_level( void )
{
  /*
   *  This routine returns the current interrupt level.
   */
}
#endif
/* return the current exception level for the 4650 */
FRAME(_CPU_ISR_Get_level,sp,0,ra)
	mfc0 v0,C0_SR
	nop
	andi v0,SR_EXL 
	srl v0,1
	j ra
ENDFRAME(_CPU_ISR_Get_level)

FRAME(_CPU_ISR_Set_level,sp,0,ra)
	nop
	mfc0 a0,C0_SR
	nop
	andi a0,SR_EXL 
	beqz a0,_CPU_ISR_Set_1		/* normalize a0 */
	nop
	li a0,1         
_CPU_ISR_Set_1:
	beq v0,a0,_CPU_ISR_Set_exit	/* if (current_level != new_level ) */
	nop
	bnez a0,_CPU_ISR_Set_2
	nop
	nop
	mfc0 t0,C0_SR
	nop
	li t1,~SR_EXL
	and t0,t1
	nop
	mtc0 t0,C0_SR                   /* disable exception level */
	nop
	j ra 
	nop
_CPU_ISR_Set_2:
	nop
	mfc0 t0,C0_SR
	nop
	li t1,~SR_IE
	and t0,t1
	nop
	mtc0 t0,C0_SR                   /* first disable ie bit (recommended) */
	nop
	ori t0,SR_EXL|SR_IE		/* enable exception level  */
	nop
	mtc0 t0,C0_SR         
	nop
_CPU_ISR_Set_exit:
	j ra
	nop
ENDFRAME(_CPU_ISR_Set_level)

/*
 *  _CPU_Context_save_fp_context
 *
 *  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
 * )
 * {
 * }
 */

FRAME(_CPU_Context_save_fp,sp,0,ra)
	.set noat
	ld a1,(a0)
	swc1 $f0,FP0_OFFSET*4(a1)
	swc1 $f1,FP1_OFFSET*4(a1)
	swc1 $f2,FP2_OFFSET*4(a1)
	swc1 $f3,FP3_OFFSET*4(a1)
	swc1 $f4,FP4_OFFSET*4(a1)
	swc1 $f5,FP5_OFFSET*4(a1)
	swc1 $f6,FP6_OFFSET*4(a1)
	swc1 $f7,FP7_OFFSET*4(a1)
	swc1 $f8,FP8_OFFSET*4(a1)
	swc1 $f9,FP9_OFFSET*4(a1)
	swc1 $f10,FP10_OFFSET*4(a1)
	swc1 $f11,FP11_OFFSET*4(a1)
	swc1 $f12,FP12_OFFSET*4(a1)
	swc1 $f13,FP13_OFFSET*4(a1)
	swc1 $f14,FP14_OFFSET*4(a1)
	swc1 $f15,FP15_OFFSET*4(a1)
	swc1 $f16,FP16_OFFSET*4(a1)
	swc1 $f17,FP17_OFFSET*4(a1)
	swc1 $f18,FP18_OFFSET*4(a1)
	swc1 $f19,FP19_OFFSET*4(a1)
	swc1 $f20,FP20_OFFSET*4(a1)
	swc1 $f21,FP21_OFFSET*4(a1)
	swc1 $f22,FP22_OFFSET*4(a1)
	swc1 $f23,FP23_OFFSET*4(a1)
	swc1 $f24,FP24_OFFSET*4(a1)
	swc1 $f25,FP25_OFFSET*4(a1)
	swc1 $f26,FP26_OFFSET*4(a1)
	swc1 $f27,FP27_OFFSET*4(a1)
	swc1 $f28,FP28_OFFSET*4(a1)
	swc1 $f29,FP29_OFFSET*4(a1)
	swc1 $f30,FP30_OFFSET*4(a1)
	swc1 $f31,FP31_OFFSET*4(a1)
	j ra
	nop
	.set at
ENDFRAME(_CPU_Context_save_fp)

/*
 *  _CPU_Context_restore_fp_context
 *
 *  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
 * )
 * {
 * }
 */

FRAME(_CPU_Context_restore_fp,sp,0,ra)
	.set noat
	ld a1,(a0)
	lwc1 $f0,FP0_OFFSET*4(a1)
	lwc1 $f1,FP1_OFFSET*4(a1)
	lwc1 $f2,FP2_OFFSET*4(a1)
	lwc1 $f3,FP3_OFFSET*4(a1)
	lwc1 $f4,FP4_OFFSET*4(a1)
	lwc1 $f5,FP5_OFFSET*4(a1)
	lwc1 $f6,FP6_OFFSET*4(a1)
	lwc1 $f7,FP7_OFFSET*4(a1)
	lwc1 $f8,FP8_OFFSET*4(a1)
	lwc1 $f9,FP9_OFFSET*4(a1)
	lwc1 $f10,FP10_OFFSET*4(a1)
	lwc1 $f11,FP11_OFFSET*4(a1)
	lwc1 $f12,FP12_OFFSET*4(a1)
	lwc1 $f13,FP13_OFFSET*4(a1)
	lwc1 $f14,FP14_OFFSET*4(a1)
	lwc1 $f15,FP15_OFFSET*4(a1)
	lwc1 $f16,FP16_OFFSET*4(a1)
	lwc1 $f17,FP17_OFFSET*4(a1)
	lwc1 $f18,FP18_OFFSET*4(a1)
	lwc1 $f19,FP19_OFFSET*4(a1)
	lwc1 $f20,FP20_OFFSET*4(a1)
	lwc1 $f21,FP21_OFFSET*4(a1)
	lwc1 $f22,FP22_OFFSET*4(a1)
	lwc1 $f23,FP23_OFFSET*4(a1)
	lwc1 $f24,FP24_OFFSET*4(a1)
	lwc1 $f25,FP25_OFFSET*4(a1)
	lwc1 $f26,FP26_OFFSET*4(a1)
	lwc1 $f27,FP27_OFFSET*4(a1)
	lwc1 $f28,FP28_OFFSET*4(a1)
	lwc1 $f29,FP29_OFFSET*4(a1)
	lwc1 $f30,FP30_OFFSET*4(a1)
	lwc1 $f31,FP31_OFFSET*4(a1)
	j ra
	nop
	.set at
ENDFRAME(_CPU_Context_restore_fp)

/*  _CPU_Context_switch
 *
 *  This routine performs a normal non-FP context switch.
 */

/* void _CPU_Context_switch(
 * Context_Control  *run,
 * Context_Control  *heir
 * )
 * {
 * }
 */

FRAME(_CPU_Context_switch,sp,0,ra)

	mfc0 t0,C0_SR
	li t1,~SR_IE
	sd t0,C0_SR_OFFSET*8(a0)	/* save status register */
	and t0,t1
	mtc0 t0,C0_SR                   /* first disable ie bit (recommended) */
	ori t0,SR_EXL|SR_IE		/* enable exception level to disable interrupts */
	mtc0 t0,C0_SR

	sd ra,RA_OFFSET*8(a0)           /* save current context */
	sd sp,SP_OFFSET*8(a0)
	sd fp,FP_OFFSET*8(a0)
	sd s0,S0_OFFSET*8(a0)
	sd s1,S1_OFFSET*8(a0)
	sd s2,S2_OFFSET*8(a0)
	sd s3,S3_OFFSET*8(a0)
	sd s4,S4_OFFSET*8(a0)
	sd s5,S5_OFFSET*8(a0)
	sd s6,S6_OFFSET*8(a0)
	sd s7,S7_OFFSET*8(a0)
	dmfc0 t0,C0_EPC
	sd t0,C0_EPC_OFFSET*8(a0)

_CPU_Context_switch_restore:
	ld s0,S0_OFFSET*8(a1)		/* restore context */
	ld s1,S1_OFFSET*8(a1)
	ld s2,S2_OFFSET*8(a1)
	ld s3,S3_OFFSET*8(a1)
	ld s4,S4_OFFSET*8(a1)
	ld s5,S5_OFFSET*8(a1)
	ld s6,S6_OFFSET*8(a1)
	ld s7,S7_OFFSET*8(a1)
	ld fp,FP_OFFSET*8(a1) 
	ld sp,SP_OFFSET*8(a1)
	ld ra,RA_OFFSET*8(a1)
	ld t0,C0_EPC_OFFSET*8(a1)
	dmtc0 t0,C0_EPC
	ld t0,C0_SR_OFFSET*8(a1)
	andi t0,SR_EXL
	bnez t0,_CPU_Context_1		/* set exception level from restore context */
	li t0,~SR_EXL
	mfc0 t1,C0_SR
	nop
	and t1,t0
	mtc0 t1,C0_SR
_CPU_Context_1:
	j ra
	nop
ENDFRAME(_CPU_Context_switch)

/*
 *  _CPU_Context_restore
 *
 *  This routine is generally 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.
 */

#if 0
void _CPU_Context_restore(
  Context_Control *new_context
)
{
}
#endif

FRAME(_CPU_Context_restore,sp,0,ra)
	dadd a1,a0,zero
	j _CPU_Context_switch_restore
	nop
ENDFRAME(_CPU_Context_restore)

EXTERN(_ISR_Nest_level, SZ_INT)
EXTERN(_Thread_Dispatch_disable_level,SZ_INT)
EXTERN(_Context_Switch_necessary,SZ_INT)
EXTERN(_ISR_Signals_to_thread_executing,SZ_INT)
.extern _Thread_Dispatch
.extern _ISR_Vector_table

/*  void __ISR_Handler()
 *
 *  This routine provides the RTEMS interrupt management.
 *
 */

#if 0
void _ISR_Handler()
{
   /*
    *  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.
   *
   */
#endif
FRAME(_ISR_Handler,sp,0,ra)
.set noreorder
#if USE_IDTKIT
/* IDT/Kit incorrectly adds 4 to EPC before returning.  This compensates */
	lreg    k0, R_EPC*R_SZ(sp)
	daddiu    k0,k0,-4
	sreg    k0, R_EPC*R_SZ(sp)
	lreg    k0, R_CAUSE*R_SZ(sp)
	li	k1, ~CAUSE_BD
	and     k0, k1 
	sreg    k0, R_CAUSE*R_SZ(sp)
#endif
	
/* save registers not already saved by IDT/sim */
	stackadd sp,sp,-EXCP_STACK_SIZE /* store ra on the stack */

	sreg    ra, R_RA*R_SZ(sp)
	sreg    v0, R_V0*R_SZ(sp)
	sreg    v1, R_V1*R_SZ(sp)
	sreg    a0, R_A0*R_SZ(sp)
	sreg    a1, R_A1*R_SZ(sp)
	sreg    a2, R_A2*R_SZ(sp)
	sreg    a3, R_A3*R_SZ(sp)
	sreg    t0, R_T0*R_SZ(sp)
	sreg    t1, R_T1*R_SZ(sp)
	sreg    t2, R_T2*R_SZ(sp)
	sreg    t3, R_T3*R_SZ(sp)
	sreg    t4, R_T4*R_SZ(sp)
	sreg    t5, R_T5*R_SZ(sp)
	sreg    t6, R_T6*R_SZ(sp)
	sreg    t7, R_T7*R_SZ(sp)
	mflo    k0
	sreg    t8, R_T8*R_SZ(sp)
	sreg    k0, R_MDLO*R_SZ(sp)
	sreg    t9, R_T9*R_SZ(sp)
	mfhi    k0
	sreg    gp, R_GP*R_SZ(sp)
	sreg    fp, R_FP*R_SZ(sp)
	sreg    k0, R_MDHI*R_SZ(sp)
	.set noat
	sreg    AT, R_AT*R_SZ(sp)
	.set at

	stackadd sp,sp,-40		/* store ra on the stack */
	sd ra,32(sp)

/* determine if an interrupt generated this exception */
	mfc0 k0,C0_CAUSE
	and k1,k0,CAUSE_EXCMASK
	bnez k1,_ISR_Handler_prom_exit /* not an external interrupt, pass exception to Monitor */
	mfc0 k1,C0_SR
	and k0,k1
	and k0,CAUSE_IPMASK
	beq k0,zero,_ISR_Handler_quick_exit /* external interrupt not enabled, ignore */
	nop

  /*
   *  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
   */
#if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
	lint t0,_ISR_Nest_level
	beq t0, zero,  _ISR_Handler_1
	nop
	/* switch stacks */	
_ISR_Handler_1:
#else
	lint t0,_ISR_Nest_level
#endif
  /*
   *  _ISR_Nest_level++;
   */
	addi t0,t0,1
	sint t0,_ISR_Nest_level
  /*
   *  _Thread_Dispatch_disable_level++;
   */
	lint t1,_Thread_Dispatch_disable_level
	addi t1,t1,1
	sint t1,_Thread_Dispatch_disable_level
#if 0
	nop
	j _ISR_Handler_4
	nop
  /*
   *  while ( interrupts_pending(cause_reg) ) {
   *     vector = BITFIELD_TO_INDEX(cause_reg);
   *     (*_ISR_Vector_table[ vector ])( vector );
   *  }
   */
_ISR_Handler_2:
/* software interrupt priorities can be applied here */
	li t1,-1
/* convert bit field into interrupt index */
_ISR_Handler_3:
	andi t2,t0,1
	addi t1,1
	beql t2,zero,_ISR_Handler_3
	dsrl t0,1
	li t1,7
	dsll t1,3			/* convert index to byte offset (*8) */
	la t3,_ISR_Vector_table
	intadd t1,t3
	lint t1,(t1)
	jalr t1
	nop
	j _ISR_Handler_5
	nop
_ISR_Handler_4:
	mfc0 t0,C0_CAUSE
	andi t0,CAUSE_IPMASK
	bne t0,zero,_ISR_Handler_2
	dsrl t0,t0,8
_ISR_Handler_5:
#else
	nop
	li t1,7
	dsll t1,t1,SZ_INT_POW2
	la t3,_ISR_Vector_table
	intadd t1,t3
	lint t1,(t1)
	jalr t1
	nop
#endif
  /*
   *  --_ISR_Nest_level;
   */
        lint t2,_ISR_Nest_level
	addi t2,t2,-1
        sint t2,_ISR_Nest_level
  /*
   *  --_Thread_Dispatch_disable_level;
   */
	lint t1,_Thread_Dispatch_disable_level
	addi t1,t1,-1
	sint t1,_Thread_Dispatch_disable_level
  /*
   *  if ( _Thread_Dispatch_disable_level || _ISR_Nest_level )
   *    goto the label "exit interrupt (simple case)"
   */
	or t0,t2,t1
	bne t0,zero,_ISR_Handler_exit
	nop
  /*
   *  #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
   *    restore stack
   *  #endif
   *  
   *  if ( !_Context_Switch_necessary && !_ISR_Signals_to_thread_executing )
   *    goto the label "exit interrupt (simple case)"
   */
	lint t0,_Context_Switch_necessary
	lint t1,_ISR_Signals_to_thread_executing
	or t0,t0,t1
	beq t0,zero,_ISR_Handler_exit
	nop

  /*
   *  call _Thread_Dispatch() or prepare to return to _ISR_Dispatch
   */
	jal _Thread_Dispatch
	nop
  /*
   *  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
   */
_ISR_Handler_exit:
	ld ra,32(sp)
	stackadd sp,sp,40

/* restore interrupt context from stack */
	lreg    k0, R_MDLO*R_SZ(sp)
	mtlo    k0
	lreg    k0, R_MDHI*R_SZ(sp)
	lreg    a2, R_A2*R_SZ(sp)
	mthi    k0
	lreg    a3, R_A3*R_SZ(sp)
	lreg    t0, R_T0*R_SZ(sp)
	lreg    t1, R_T1*R_SZ(sp)
	lreg    t2, R_T2*R_SZ(sp)
	lreg    t3, R_T3*R_SZ(sp)
	lreg    t4, R_T4*R_SZ(sp)
	lreg    t5, R_T5*R_SZ(sp)
	lreg    t6, R_T6*R_SZ(sp)
	lreg    t7, R_T7*R_SZ(sp)
	lreg    t8, R_T8*R_SZ(sp)
	lreg    t9, R_T9*R_SZ(sp)
	lreg    gp, R_GP*R_SZ(sp)
	lreg    fp, R_FP*R_SZ(sp)
	lreg    ra, R_RA*R_SZ(sp)
	lreg    a0, R_A0*R_SZ(sp)
	lreg    a1, R_A1*R_SZ(sp)
	lreg    v1, R_V1*R_SZ(sp)
	lreg    v0, R_V0*R_SZ(sp)
	.set noat
	lreg    AT, R_AT*R_SZ(sp)
	.set at

	stackadd sp,sp,EXCP_STACK_SIZE /* store ra on the stack */

#if USE_IDTKIT
/* we handled exception, so return non-zero value */
	li v0,1
#endif

_ISR_Handler_quick_exit:
#ifdef USE_IDTKIT
	j ra
#else
	eret
#endif
	nop

_ISR_Handler_prom_exit:
#ifdef CPU_R3000
	la      k0, (R_VEC+((48)*8))
#endif

#ifdef CPU_R4000
	la      k0, (R_VEC+((112)*8)) /* R4000 Sim's location is different */
#endif
	j       k0
	nop

       .set    reorder

ENDFRAME(_ISR_Handler)


FRAME(mips_enable_interrupts,sp,0,ra)
	mfc0 t0,C0_SR		/* get status reg */
	nop
	or t0,t0,a0		 
	mtc0 t0,C0_SR		/* save updated status reg */
	j ra
	nop
ENDFRAME(mips_enable_interrupts)

FRAME(mips_disable_interrupts,sp,0,ra)
	mfc0 v0,C0_SR		/* get status reg */
	li t1,SR_IMASK		/* t1 = load interrupt mask word */
	not t0,t1		/* t0 = ~t1 */
	and t0,v0		/* clear imask bits */
	mtc0 t0,C0_SR		/* save status reg */
	and v0,t1		/* mask return value (only return imask bits) */
	jr ra
	nop
ENDFRAME(mips_disable_interrupts)

FRAME(mips_enable_global_interrupts,sp,0,ra)
	mfc0 t0,C0_SR		/* get status reg */
	nop
	ori t0,SR_IE
	mtc0 t0,C0_SR		/* save updated status reg */
	j ra
	nop
ENDFRAME(mips_enable_global_interrupts)

FRAME(mips_disable_global_interrupts,sp,0,ra)
	li t1,SR_IE
	mfc0 t0,C0_SR		/* get status reg */
	not t1
	and t0,t1
	mtc0 t0,C0_SR		/* save updated status reg */
	j ra
	nop
ENDFRAME(mips_disable_global_interrupts)

/* return the value of the status register in v0.  Used for debugging */
FRAME(mips_get_sr,sp,0,ra)
	mfc0 v0,C0_SR
	j ra
	nop
ENDFRAME(mips_get_sr)

FRAME(mips_break,sp,0,ra)
#if 1
	break 0x0
	j mips_break
#else
	j ra
#endif
	nop
ENDFRAME(mips_break)

/*PAGE
 *
 *  _CPU_Internal_threads_Idle_thread_body
 *
 *  NOTES:
 *
 *  1. This is the same as the regular CPU independent algorithm.
 *
 *  2. If you implement this using a "halt", "idle", or "shutdown"
 *     instruction, then don't forget to put it in an infinite loop.
 *
 *  3. Be warned. Some processors with onboard DMA have been known
 *     to stop the DMA if the CPU were put in IDLE mode.  This might
 *     also be a problem with other on-chip peripherals.  So use this
 *     hook with caution.
 */

FRAME(_CPU_Thread_Idle_body,sp,0,ra)
	wait			/* enter low power mode */
	j _CPU_Thread_Idle_body
	nop
ENDFRAME(_CPU_Thread_Idle_body)

#define VEC_CODE_LENGTH 10*4

/**************************************************************************
**
**	init_exc_vecs() - moves the exception code into the addresses
**			  reserved for exception vectors
**
**	UTLB Miss exception vector at address 0x80000000
**
**	General exception vector at address 0x80000080
**
**	RESET exception vector is at address 0xbfc00000
**
***************************************************************************/

#define INITEXCFRM ((2*4)+4)		/* ra + 2 arguments */
FRAME(init_exc_vecs,sp,0,ra)
/* This code yanked from SIM */
#if defined(CPU_R3000)
	.set	noreorder
	la	t1,exc_utlb_code
	la	t2,exc_norm_code
	li	t3,UT_VEC
	li	t4,E_VEC
	li	t5,VEC_CODE_LENGTH
1:
	lw	t6,0(t1)
	lw	t7,0(t2)
	sw	t6,0(t3)
	sw	t7,0(t4)
	addiu	t1,4
	addiu	t3,4
	addiu	t4,4
	subu	t5,4
	bne	t5,zero,1b
	addiu	t2,4
	move	t5,ra		# assumes clear_cache doesnt use t5
	li	a0,UT_VEC
	jal	clear_cache
	li	a1,VEC_CODE_LENGTH
	nop
	li	a0,E_VEC
	jal	clear_cache
	li	a1,VEC_CODE_LENGTH
	move	ra,t5		# restore ra
	j	ra
	nop
	.set	reorder
#endif
#if defined(CPU_R4000)
	.set reorder
	move	t5,ra		# assumes clear_cache doesnt use t5

	/* TLB exception vector */
	la	t1,exc_tlb_code
	li	t2,T_VEC |K1BASE
	li	t3,VEC_CODE_LENGTH
1:
	lw	t6,0(t1)
	addiu	t1,4
	subu	t3,4
	sw	t6,0(t2)
	addiu	t2,4
	bne	t3,zero,1b

	li	a0,T_VEC
	li	a1,VEC_CODE_LENGTH
	jal	clear_cache

	la	t1,exc_xtlb_code
	li	t2,X_VEC |K1BASE
	li	t3,VEC_CODE_LENGTH
1:
	lw	t6,0(t1)
	addiu	t1,4
	subu	t3,4
	sw	t6,0(t2)
	addiu	t2,4
	bne	t3,zero,1b

	/* extended TLB exception vector */
	li	a0,X_VEC
	li	a1,VEC_CODE_LENGTH
	jal	clear_cache

	/* cache error exception vector */
	la	t1,exc_cache_code
	li	t2,C_VEC |K1BASE
	li	t3,VEC_CODE_LENGTH
1:
	lw	t6,0(t1)
	addiu	t1,4
	subu	t3,4
	sw	t6,0(t2)
	addiu	t2,4
	bne	t3,zero,1b

	li	a0,C_VEC
	li	a1,VEC_CODE_LENGTH
	jal	clear_cache

	/* normal exception vector */
	la	t1,exc_norm_code
	li	t2,E_VEC |K1BASE
	li	t3,VEC_CODE_LENGTH
1:
	lw	t6,0(t1)
	addiu	t1,4
	subu	t3,4
	sw	t6,0(t2)
	addiu	t2,4
	bne	t3,zero,1b

	li	a0,E_VEC
	li	a1,VEC_CODE_LENGTH
	jal	clear_cache

	move	ra,t5		# restore ra
	j	ra
#endif
ENDFRAME(init_exc_vecs)


#if defined(CPU_R4000)
FRAME(exc_tlb_code,sp,0,ra)
#ifdef CPU_R3000
        la      k0, (R_VEC+((48)*8))
#endif

#ifdef CPU_R4000
        la      k0, (R_VEC+((112)*8)) /* R4000 Sim's location is different */
#endif
        j       k0
	nop

ENDFRAME(exc_tlb_code)


FRAME(exc_xtlb_code,sp,0,ra)
#ifdef CPU_R3000
        la      k0, (R_VEC+((48)*8))
#endif

#ifdef CPU_R4000
        la      k0, (R_VEC+((112)*8)) /* R4000 Sim's location is different */
#endif
        j       k0
	nop

ENDFRAME(exc_xtlb_code)


FRAME(exc_cache_code,sp,0,ra)
#ifdef CPU_R3000
        la      k0, (R_VEC+((48)*8))
#endif

#ifdef CPU_R4000
        la      k0, (R_VEC+((112)*8)) /* R4000 Sim's location is different */
#endif
        j       k0
	nop

ENDFRAME(exc_cache_code)


FRAME(exc_norm_code,sp,0,ra)
	la	k0, _ISR_Handler /* generic external int hndlr */
	j	k0
	nop
	subu	sp, EXCP_STACK_SIZE		/* set up local stack frame */
ENDFRAME(exc_norm_code)
#endif

/**************************************************************************
**
** enable_int(mask) - enables interrupts - mask is positioned so it only
**                      needs to be or'ed into the status reg. This
**                      also does some other things !!!! caution should
**                      be used if invoking this while in the middle
**                      of a debugging session where the client may have
**                      nested interrupts.
**
****************************************************************************/
FRAME(enable_int,sp,0,ra)
        .set    noreorder
        mfc0    t0,C0_SR
        or      a0,1
        or      t0,a0
        mtc0    t0,C0_SR
        j       ra
        nop
        .set    reorder
ENDFRAME(enable_int)


/***************************************************************************
**
**      disable_int(mask) - disable the interrupt - mask is the complement
**                          of the bits to be cleared - i.e. to clear ext int
**                          5 the mask would be - 0xffff7fff
**
****************************************************************************/
FRAME(disable_int,sp,0,ra)
        .set    noreorder
        mfc0    t0,C0_SR
        nop
        and     t0,a0
        mtc0    t0,C0_SR
        j       ra
        nop
ENDFRAME(disable_int)


