/*
    TMHalAsm.S, Copyright  (c) by Christian Conrad,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public Licence.

    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Threads/mips/TMHalAsm.S,v $
 	Author(s):             Christian Conrad
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2922 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $

*/

#include "asm.h"
#include "cpu.h"
#include "MemoryLayout.h"
  
/* Offset values in the ProcContext structure (expressed in bytes) */
#define RETURNVALUE0_OFFSET        0*4
#define RETURNVALUE1_OFFSET        1*4
#define ARGUMENT0_OFFSET           2*4
#define ARGUMENT1_OFFSET           3*4
#define ARGUMENT2_OFFSET           4*4
#define ARGUMENT3_OFFSET           5*4
#define CALLERSAVED0_OFFSET        6*4
#define CALLERSAVED1_OFFSET        7*4
#define CALLERSAVED2_OFFSET        8*4
#define CALLERSAVED3_OFFSET        9*4
#define CALLERSAVED4_OFFSET        10*4
#define CALLERSAVED5_OFFSET        11*4
#define CALLERSAVED6_OFFSET        12*4
#define CALLERSAVED7_OFFSET        13*4
#define CALLERSAVED8_OFFSET        14*4
#define CALLERSAVED9_OFFSET        15*4
#define CALLEESAVED0_OFFSET        16*4
#define CALLEESAVED1_OFFSET        17*4
#define CALLEESAVED2_OFFSET        18*4
#define CALLEESAVED3_OFFSET        19*4
#define CALLEESAVED4_OFFSET        20*4
#define CALLEESAVED5_OFFSET        21*4
#define CALLEESAVED6_OFFSET        22*4
#define CALLEESAVED7_OFFSET        23*4
#define GLOBALPOINTER_OFFSET       24*4
#define STACKPOINTER_OFFSET        25*4
#define FRAMEPOINTER_OFFSET        26*4
#define RETURNADDRESS_OFFSET       27*4
#define PROGRAMCOUNTER_OFFSET      28*4
#define HI_OFFSET                  29*4
#define LO_OFFSET                  30*4
#define STATUSREGISTER_OFFSET      31*4
#define AT_OFFSET                  32*4
#define TLBHI_OFFSET               33*4

#define CONTEXTSIZE                34*4

/* Offset of the processor context in the Thread structure */
#define THREADCONTEXT_OFFSET       0*4
#define THREADID_OFFSET            1*4
#define SCHEDRUNNING_OFFSET        0*4
/*-CJ 20020131 we add EXC_TLBL or EXC_TLBS to the UTLBMISS_OFFSET
	so we get a value of 31 or 32 for a load or store operation
	respectively. So we get either the UTLB_LOAD_MISS or 
	UTLB_STORE_MISS handler. 
 */
#define UTLBMISS_OFFSET            30*4 /* + 2 = 32,  + 3 = 33 as in the table */
  
#include "SyscallMsg.h" /* to get dSYSCALL_SEND_OP */

.text
.align 2
 
/* Save context of current thread. All registers but k0, k1, AT and
	* zero are saved in the ProcContext structure given by the first
	* argument to saveContext(), thus register a0. 
	*/ 
FRAME(saveContext)

.set noat
    
    /* Setting of exceptionContextFlag (works as a counter) */
    la      k0, exceptionContextFlag
		lw      k1, 0(k0)
    addu    k1, k1, 1
    sw      k1, 0(k0)

    sw      a1, ARGUMENT1_OFFSET(a0)
    sw      a2, ARGUMENT2_OFFSET(a0)
    sw      a3, ARGUMENT3_OFFSET(a0)
    sw	    v0, RETURNVALUE0_OFFSET(a0)
    sw	    v1, RETURNVALUE1_OFFSET(a0)
    sw	    AT, AT_OFFSET(a0)
    sw      t0, CALLERSAVED0_OFFSET(a0)
    sw      t1, CALLERSAVED1_OFFSET(a0)
    sw      t2, CALLERSAVED2_OFFSET(a0)
    sw      t3, CALLERSAVED3_OFFSET(a0)
    sw      t4, CALLERSAVED4_OFFSET(a0)
    sw      t5, CALLERSAVED5_OFFSET(a0)
    sw      t6, CALLERSAVED6_OFFSET(a0)
    sw      t7, CALLERSAVED7_OFFSET(a0)
    sw      t8, CALLERSAVED8_OFFSET(a0)
    sw      t9, CALLERSAVED9_OFFSET(a0)
    sw      s0, CALLEESAVED0_OFFSET(a0)
    sw      s1, CALLEESAVED1_OFFSET(a0)
    sw      s2, CALLEESAVED2_OFFSET(a0)
    sw      s3, CALLEESAVED3_OFFSET(a0)
    sw      s4, CALLEESAVED4_OFFSET(a0)
    sw      s5, CALLEESAVED5_OFFSET(a0)
    sw      s6, CALLEESAVED6_OFFSET(a0)
    sw      s7, CALLEESAVED7_OFFSET(a0)
    sw      gp, GLOBALPOINTER_OFFSET(a0)
    sw      fp, FRAMEPOINTER_OFFSET(a0)
    mfhi    k0
    sw      k0, HI_OFFSET(a0)
    mflo    k0
    sw      k0, LO_OFFSET(a0)
    mfc0    k0, C0_STATUS
    sw      k0, STATUSREGISTER_OFFSET(a0)

    la      k0, EXCEPTIONSTACKTOP
    /* Load the Exception Context that is stored on top of the EXCEPTIONSTACK. The
		 * Exception Context is used by the general exception handler before calling
     * saveContext(). This is a minimal save that allows to call saveContext as
     * a proper procedure.
     * - exceptionContext[0] <- a0 @ EXCEPTIONSTACKTOP
     * - exceptionContext[1] <- ra
     * - exceptionContext[2] <- sp
     * - exceptionContext[3] <- pc
     * - exceptionContext[4] <- tlbhi @ EXCEPTIONSTACKTOP - 16
     */
    lw      k1, 0(k0)                     /* save user a0 */
    sw      k1, ARGUMENT0_OFFSET(a0)
    lw      k1, -4(k0)                     /* save user ra */
    sw      k1, RETURNADDRESS_OFFSET(a0)
    lw      k1, -8(k0)                     /* save user sp */
    sw      k1, STACKPOINTER_OFFSET(a0)
    lw      k1, -12(k0)                    /* save user pc */
    sw      k1, PROGRAMCOUNTER_OFFSET(a0)
    lw      k1, -16(k0)
    sw      k1, TLBHI_OFFSET(a0)

.set at

END(saveContext)


/* Restore context of new thread. All registers but k0, k1 and
 * zero are restored from the ProcContext structure in the thread
 * structure reference given by the first argument to restoreContext(),
 * thus register a0. 
 * This is the R3k version of restoreContext.
 */ 
FRAME(restoreContext_R3k)

.set noreorder
.set noat

    move    k1, a0				     
    /* reducing nesting level (using a3 as temp register) */
    la      k0, exceptionContextFlag
    lw      a3, 0(k0)
    lw      a0, ARGUMENT0_OFFSET(k1)
		addu    a3, a3, -1
    sw      a3, 0(k0)
		lw      a1, ARGUMENT1_OFFSET(k1)
    lw      a2, ARGUMENT2_OFFSET(k1)
    lw      a3, ARGUMENT3_OFFSET(k1)
    lw	    v0, RETURNVALUE0_OFFSET(k1)
    lw	    v1, RETURNVALUE1_OFFSET(k1)
    lw	    AT, AT_OFFSET(k1)
    lw      t0, CALLERSAVED0_OFFSET(k1)
    lw      t1, CALLERSAVED1_OFFSET(k1)
    lw      t2, CALLERSAVED2_OFFSET(k1)
    lw      t3, CALLERSAVED3_OFFSET(k1)
    lw      t4, CALLERSAVED4_OFFSET(k1)
    lw      t5, CALLERSAVED5_OFFSET(k1)
    lw      t6, CALLERSAVED6_OFFSET(k1)
    lw      t7, CALLERSAVED7_OFFSET(k1)
    lw      t8, CALLERSAVED8_OFFSET(k1)
    lw      t9, CALLERSAVED9_OFFSET(k1)
    lw      s0, CALLEESAVED0_OFFSET(k1)
    lw      s1, CALLEESAVED1_OFFSET(k1)
    lw      s2, CALLEESAVED2_OFFSET(k1)
    lw      s3, CALLEESAVED3_OFFSET(k1)
    lw      s4, CALLEESAVED4_OFFSET(k1)
    lw      s5, CALLEESAVED5_OFFSET(k1)
    lw      s6, CALLEESAVED6_OFFSET(k1)
    lw      s7, CALLEESAVED7_OFFSET(k1)
    lw      gp, GLOBALPOINTER_OFFSET(k1)
    lw      sp, STACKPOINTER_OFFSET(k1)
    lw      fp, FRAMEPOINTER_OFFSET(k1)
    lw      ra, RETURNADDRESS_OFFSET(k1)
					    
    lw      k0, HI_OFFSET(k1)
    nop
    mthi    k0
    lw      k0, LO_OFFSET(k1)
    nop
    mtlo    k0
    lw      k0, STATUSREGISTER_OFFSET(k1)
    nop
    mtc0    k0, C0_STATUS
					    
    /* Setting of pc */
    lw      k0, PROGRAMCOUNTER_OFFSET(k1)
		lw      k1, TLBHI_OFFSET(k1)
    nop
		mtc0    k1, c0_tlbhi
    j       k0
    rfe

.set reorder
.set at
.end restoreContext_R3k


/* And now the R4k version of restoreContext */ 
FRAME(restoreContext_R4k)

.set noreorder
.set noat

    move    k1, a0				     
    /* reducing nesting level (using a3 as temp register) */
    la      k0, exceptionContextFlag
    lw      a3, 0(k0)
    lw      a0, ARGUMENT0_OFFSET(k1)
		addu    a3, a3, -1
    sw      a3, 0(k0)
    lw      a1, ARGUMENT1_OFFSET(k1)
    lw      a2, ARGUMENT2_OFFSET(k1)
    lw      a3, ARGUMENT3_OFFSET(k1)
    lw	    v0, RETURNVALUE0_OFFSET(k1)
    lw	    v1, RETURNVALUE1_OFFSET(k1)
    lw	    AT, AT_OFFSET(k1)
    lw      t0, CALLERSAVED0_OFFSET(k1)
    lw      t1, CALLERSAVED1_OFFSET(k1)
    lw      t2, CALLERSAVED2_OFFSET(k1)
    lw      t3, CALLERSAVED3_OFFSET(k1)
    lw      t4, CALLERSAVED4_OFFSET(k1)
    lw      t5, CALLERSAVED5_OFFSET(k1)
    lw      t6, CALLERSAVED6_OFFSET(k1)
    lw      t7, CALLERSAVED7_OFFSET(k1)
    lw      t8, CALLERSAVED8_OFFSET(k1)
    lw      t9, CALLERSAVED9_OFFSET(k1)
    lw      s0, CALLEESAVED0_OFFSET(k1)
    lw      s1, CALLEESAVED1_OFFSET(k1)
    lw      s2, CALLEESAVED2_OFFSET(k1)
    lw      s3, CALLEESAVED3_OFFSET(k1)
    lw      s4, CALLEESAVED4_OFFSET(k1)
    lw      s5, CALLEESAVED5_OFFSET(k1)
    lw      s6, CALLEESAVED6_OFFSET(k1)
    lw      s7, CALLEESAVED7_OFFSET(k1)
    lw      gp, GLOBALPOINTER_OFFSET(k1)
    lw      sp, STACKPOINTER_OFFSET(k1)
    lw      fp, FRAMEPOINTER_OFFSET(k1)
    lw      ra, RETURNADDRESS_OFFSET(k1)
					    
    lw      k0, HI_OFFSET(k1)
    nop
    mthi    k0
    lw      k0, LO_OFFSET(k1)
    nop
    mtlo    k0
    lw      k0, STATUSREGISTER_OFFSET(k1)
    nop
    mtc0    k0, C0_STATUS

    /* Setting of pc */
    lw      k0, PROGRAMCOUNTER_OFFSET(k1)
		lw      k1, TLBHI_OFFSET(k1)
    nop
		mtc0    k1, c0_tlbhi
.set mips3
    eret
.set mips0

.set reorder
.set at
.end restoreContext_R4k
					     
/*
 * general exception handler used for both the general exception
 * and the utlb miss exception. For Topsy it does not matter if
 * the exception was a UTLB Miss or a TLB Miss and therefor we use
 * the same handler for both exceptions.
 */
FRAME(generalExceptionHandler)

/* At this point, only SR and PC were modified.
 * Before calling saveContext(), a0, sp, and ra have to be saved
 * in a temporary exception context.
 * NOTE: When an exception occurs, pc is saved in EPC register.
 * If not in a branch delay, epc points onto 'responsible'
 * instruction (see Branch Delay Bit in CAUSE register)
 * NOTE2: the generalExceptionHandler is copied to 0x80000000 and
 * 0x80000080 form generalExceptionHandler to endGeneralExceptionHandler
 * so jumps done in the generalExceptionHandler should be relative (branches)
 * unless the jump target is outside the generalExceptionHandler.
 */
.set noreorder
    mfc0    k1, C0_EPC                /* epc -> k1 */
    /* sp is now set on the exception stack (EXCEPTIONSTACKTOP) */
    /* befor we do that push some important register on the stack */
    la      k0, EXCEPTIONSTACKTOP     /* exceptionContext=EXCEPTIONSTACKTOP -> k0 */
    sw      k1, -12(k0)               /* user pc in exceptionContext */
		mfc0    k1, c0_tlbhi              /* get current pid field */
    sw      sp, -8(k0)                /* user sp in exceptionContext */
		sw      k1, -16(k0)
		mtc0    zero, c0_tlbhi            /* kernel has pid 0 */
    la      k1, exceptionContextFlag  /* exceptionContextFlag -> k1 */
		lw      k1, 0(k1)
    sw      ra, -4(k0)                /* user ra in exceptionContext */
    beq     k1, zero, notNested       /* if ( eCF == 0 ) goto notNested */
    sw      a0, 0(k0)                 /* user a0 in exceptionContext */
    j       nestedException           /* special handlling for nested ones */
    nop
LABEL(notNested)                      /* cosmetic for gdb */
    /* not a nested exception */
    /* now set the stack pointer */
    addu    sp, k0, -(20 + 24)           /* set sp to get a valid stack */
    la      k1, scheduler                /* scheduler -> k1 */
    sw      k1, 24(sp)                   /* k1 -> arg6 */
    lw      a0, SCHEDRUNNING_OFFSET(k1)  /* ProcContext -> a0 */
LABEL(doSaveContext)                     /* cosmetic for gdb */
    mfc0    k0, C0_CAUSE                 /* casue -> k0 */
    mfc0    k1, C0_BADVM                 /* badVM -> k1 */
    sw      k0, 16(sp)                   /* cause -> arg4 */
    lw      a0, THREADCONTEXT_OFFSET(a0) /* ThreadContext -> a0 */
    jal     saveContext
    sw      k1, 20(sp)                   /* badVM -> arg5 */
    /* using branch delay slot */
LABEL(callDispatcher)                 /* cosmetic for gdb */
    la      k1, scheduler             /* scheduler -> k1 */
    lw      a0, SCHEDRUNNING_OFFSET(k1)
    /* Arguments:
          a0: scheduler.running
          a1: MessagePtr (only tmMsgRecv/tmMsgSend)
          a2: timeout (only tmMsgRecv)
          a3: syscall operation (only for syscalls)
          a4: cause
          a5: badVA (only valid for TLB exceptions)
					a6: pointer to pseudo scheduler
     */
    jal     exceptionDispatcher
		nop
    /* exceptionDispatcher does not return */

.end generalExceptionHandler
LABEL(endGeneralExceptionHandler)
.set reorder 


LABEL(nestedException)                  /* cosmetic for gdb */
.set noreorder 
    addu    k0, sp, -CONTEXTSIZE        /* at sp -4 we store the ctxt */
    addu    sp, sp, -(36 + CONTEXTSIZE) 
    sw      k0, 32(sp)                  /* pointer to the on stack context */
    addu    a0, sp, 32 - THREADCONTEXT_OFFSET
    sw      a0, 28(sp)                  /* pseudo thread address */
    addu    k0, sp, 28 - SCHEDRUNNING_OFFSET
    j       doSaveContext
    sw      k0, 24(sp)                  /* pseudo scheduler address */
.set reorder 



/* This code performs transparently to the user a tmThreadExit() when
 * the main thread routine returns 
 */
FRAME(automaticThreadExit)

    move    a1, ra
    la      t0, endAutomaticThreadExit
    la      t1, automaticThreadExit
    subu    t1, t0, t1
    addu    a1, a1, t1                  /* Exit message address -> a1 */
    li      a3, dSYSCALL_SEND_OP				     
    syscall                             /* Sending of Exit Message */

END(automaticThreadExit)
LABEL(endAutomaticThreadExit)
