;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Microsoft Research Singularity
;;; 
;;; Copyright (c) Microsoft Corporation.  All rights reserved.
;;;
;;; This file contains x86-specific assembly code related to context save and restore.
;;; The key goal here is to keep this set of code as small as possible, in favor
;;; of portable C++ or C# code.

        CODE32

        AREA    |.text|, CODE, ARM

|defining ?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SA_NPAU1@@Z| EQU 1
|defining ?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SA_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z| EQU 1
|defining ?m_Resume@Struct_Microsoft_Singularity_Isal_SpillContext@@SAXPAU1@@Z| EQU 1
|defining ?g_ResetCurrent@Struct_Microsoft_Singularity_Isal_SpillContext@@SAXXZ| EQU 1

        include hal.inc

;;; Save takes one argument - a context record to save the context in. It saves all the
;;; nonvolatile state (it does not bother saving caller save regs.)
;;;
;;; This function returns true after saving the context. When the context is resumed, control
;;; resumption will occur at the point this function returned, but with a false
;;; return value.
;;; 
;;; Calling conventions are normal __fastcall.
    
;; __fastcall bool SaveContext(Context *context)
    
        LEAF_ENTRY ?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SA_NPAU1@@Z 
        
        ;; Save the bulk of the registers (r0-r12).
        stmia   r0, {r0-r12}

        ;; Save sp and (stale) lr.
        str     sp, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___sp]
        str     lr, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___lr]
        
        ;; Save lr as the pc.
        str     lr, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___pc]

        ;; Save cpsr
        mrs     r1, cpsr
        str     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___cpsr]

        ;; Overright r0, so the return value contains zero on context resume
        ldr     r1, =0
        str     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r0]

        ;; Save stack limit
        GET_THREAD_FIELD_ADDR r2, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit
        ldr     r2, [r2]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___stackLimit]

        ;; Set spilled flag
        ldr     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]
        orr     r2, r2, #Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]

        ;; return true for initial save
        ldr     r0, =1
        bx      lr
        
        LEAF_END

;;; Save takes two arguments - a context record to save the context in, and
;;; an interrupt frame to describe an interruption location.
;;;
;;; Calling conventions are normal __fastcall.
    
;; __fastcall bool SaveContext(Context *context, InterruptContext *interrupt)

        LEAF_ENTRY ?m_Save@Struct_Microsoft_Singularity_Isal_SpillContext@@SA_NPAU1@PAUStruct_Microsoft_Singularity_Isal_InterruptContext@@@Z

        ;; Pick up registers from the interrupt context

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___r0]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r0]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___r1]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r1]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___r2]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r2]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___r3]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r3]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___r12]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r12]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___lr]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___lr]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___sp]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___sp]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___pc]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___pc]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___cpsr]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___cpsr]

        ldr     r2, [r1, #Struct_Microsoft_Singularity_Isal_InterruptContext___instruction]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___instruction]

        ;; Save the rest of the registers from the current state
        add     r2, r0, #Struct_Microsoft_Singularity_Isal_SpillContext___r4
        stmia   r2, {r4-r11}

        ;; Save stack limit
        GET_THREAD_FIELD_ADDR r2, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit
        ldr     r2, [r2]
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___stackLimit]

        ;; Set spilled flag
        ldr     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]
        orr     r2, r2, #Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled
        str     r2, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]

        ;; We're done
        bx      lr

        LEAF_END


;;; Resume restores the processor state to the state described in the given
;;; context record.
;;; 
;;; __fastcall void ResumeContext(Struct_Microsoft_Singularity_Isal_SpillContext *context);
    
|?m_Resume@Struct_Microsoft_Singularity_Isal_SpillContext@@SAXPAU1@@Z| PROC

        ;; Switch to supervisor mode so we can get an atomic resume including CPSR.
        mrs     r1, cpsr
        bic     r1, r1, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Mask
        orr     r1, r1, #Struct_Microsoft_Singularity_Isal_Arm_ProcessorMode_Supervisor
        msr     cpsr_c, r1

        ;; Clear spilled flag.
        ldr     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]
        bic     r1, r1, #Struct_Microsoft_Singularity_Isal_SpillContext_ContentsSpilled
        str     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___spillFlags]

        ;; Restore stack limit.
        ldr     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___stackLimit]
        GET_THREAD_FIELD_ADDR r2, r12, #Struct_Microsoft_Singularity_Isal_ThreadRecord___activeStackLimit
        str     r1, [r2]

        ;; Move saved CPSR into SPSR.
        ldr     r1, [r0, #Struct_Microsoft_Singularity_Isal_SpillContext___cpsr]
        msr     spsr, r1

        ;; Restore banked pre-interrupt registers (use R3 as pointer to banked registers).
        add     r3, r0, #Struct_Microsoft_Singularity_Isal_SpillContext___sp
        ldmia   r3, {sp,lr}^

        ;; Resume unbanked pre-interrupt state.
        ldmia   r0, {r0-r12,pc}^
        
        ENDP
    
;;; ResetContext resets the current context fp & debug register state to a canonical state
;;; for interrupt handler code.  This should only be used after saving the current context.
;;;
;;; Calling conventions are normal __fastcall.
;;; 
;;; __fastcall void ResetCurrent();
    
        LEAF_ENTRY ?g_ResetCurrent@Struct_Microsoft_Singularity_Isal_SpillContext@@SAXXZ
        bx      lr
        LEAF_END

        END

