;This is the port code for the PXA270 processor.
;It is written in the ARM assembly language, and follows the 
;example listed in application note AN-1011 and AN-1014
;
;NOTE:
;  1) I am not sure whether or not I need any of the following lines:
;      LDR   R4,   [SP], #4            ;Get the new task SPSR
;      MSR   SPSR_cxsf, R4             ;Put the value of SPSR back
;  In fact, I get a warning when I include them, but I think it is good to save the 
;  SPSR on the stack.  However, I should not pop this after an interrupt or FIQ.
;  Also, if I choose to disable this, I need to remove the SPSR line from OSTaskStkInit
;  in os_cpu_c.c.  I DO NOT NEED THESE LINES!!!!! THEY DON"T ALLOW OSCTXSWT TO DO ITS JOB!!!!
;
;
;This file contains definitions for the following functions:
;
;-----------------------------------------------------------------------------------

  ;Externally defined C functions, with addresses needed here in assembly
  EXTERN OSTaskSwHook
  EXTERN OSRunning
  EXTERN OSTCBHighRdy
  EXTERN OSTCBCur
  EXTERN OSPrioCur
  EXTERN OSPrioHighRdy
  EXTERN OSIntExit
  EXTERN OSIntNesting
  EXTERN OS_CPU_IRQ_ISR_Handler
  EXTERN OS_CPU_FIQ_ISR_Handler
  EXTERN MMU_SectionTable;
  
  ;Nine public functions, written in assembly, available to C code
  PUBLIC OS_CPU_SR_Save
  PUBLIC OS_CPU_SR_Restore
  PUBLIC OS_CPU_IRQ_ISR
  PUBLIC OS_CPU_FIQ_ISR
  PUBLIC OSStartHighRdy
  PUBLIC OSCtxSw
  PUBLIC OSIntCtxSw
  PUBLIC DISABLE_CACHE
  PUBLIC Return_MMU_Table_ADDR
  PUBLIC Update_MMU_Table
  
;----------------------------------------------------------------------------
;
;1) OS_CPU_SR_Save
;   This function disables interrupts and returns the value of the 
;   Core Program Status Register back to R0
;  
;   Note: we may have to enable Supervisor mode in order to set these bits
;        to disable interrupts.  This would be done with the following lines
;  	 MRS	R0, CPSR
;  	 BIC	R0, R0, #0x1F
;  	 ORR	R0, R0, #0x13
;  	 MSR	CPSR_c, R0
;
;        Also note that the examples in the ARM architecture manual show that 
;        we only have to write 0x80 to the CPSR, thus setting the I(7) (disables
;        IRQ interrupts when it is set) bit.  However, if we want to disable 
;        all interrupts, we should set the F(6) (disables FIQ interrupts 
;        when it is set) bit as well.  See p. 43 of manual (A2-9)
;----------------------------------------------------------------------------
;
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OS_CPU_SR_Save:
  MRS	R0, CPSR	  ;Move CPSR to the general purpose register R0 (p.160)
  ORR	R1, R0, #0xC0	  ;Set the interrupt disable bit with an ORR
  MSR	CPSR_c, R1	  ;Update the control bits in the CPSR, (IRQ now disabled)
  MRS	R1, CPSR	  ;Confirm that CPSR contains the proper IRQ disable flags
  AND	R1, R1, #0xC0	  ;AND R1 with 0x80 and leave it in R1
  CMP	R1, #0xC0	  ;Only 0xC0 should be set
  BNE	OS_CPU_SR_Save    ;Not disabled, so try again
  MOV	PC, LR		  ;Disabled properly so return with the original CPSR in R0

;----------------------------------------------------------------------------
;
;2) OS_CPU_SR_Restore()
;   This function enables interrupts, or more properly, it restores the value of 
;   the CSPR register to what it was before interrupts were disabled
;
;----------------------------------------------------------------------------- 


  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OS_CPU_SR_Restore:
  MSR	CPSR_c, R0        ;Return the control bits of CPSR (uncommenting this allows 
  MOV	PC, LR            ;the code to work without servicing interrupts

;----------------------------------------------------------------------------
;
;3) OSStartHighRdy
;  OSStartHighRdy() is called by OSStart() to start the highest priority 
;  task ready to run.  It only does half a context switch since we are not 
;  saving the registers of any previous task
;  
;  In this function, we must:
;    1) Call user definable OSTaskSwHook().  At this point OSRunning=FALSE
;    2) Set OSRunning = TRUE
;    3) Get the stack pointer of the task to resume (Stack pointer = OSTCBHighRdy->OSTCBStkPtr;0
;    4) Restore all the processor registers from the new task stack
;    5) Execute a return from interrupt instructions, i.e. load the PC with the new task address
;
;    The order of registers can be seen easily from the comments on the right to be 
;    SPSR, CPSR, R0, R1, R2, ..... R14 (LR), R13 (PC)
;
;  NOTE: This function is basically the reverse order of OSTaskStkInit located in os_cpu_c.c
;        Also, the author states that #1 and #2 above should actually be done in OSStart(), but 
;        he made a mistake and it is too late to change it.  Boohoo.
;       
;----------------------------------------------------------------------------

  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OSStartHighRdy:

  MSR   CPSR_cxsf, #0xDF     ;Switch to SYS momde with IRQ & FIQ disabled
  
  BL    OSTaskSwHook         ;Branch with a link to the user defined task switch hook.  
                             ;BL causes a branch to the target address and stores a return 
                             ;address in the link register (LR = R14)
  
  LDR   R4, ??OSRunning      ;Need to set OSRunning true, but we cannot handle 32 bit address (see AN-1011)
  MOV   R5, #1               ;Put a boolean to set it true
  STRB  R5, [R4]             ;Store Register Byte R5 (=0x01) into variable OSRunning
  
  LDR   R4, ??OSTCBHighRdy   ;Load the highest priority task TCB address into R4 (again, this is a 32 bit address)
  LDR   R4, [R4]             ;Put the value pointed to into R4
  LDR   SP, [R4]             ;Put the value of R4 into the Stack Pointer
 
  LDR   R4,  [SP], #4        ;Get the new task CPSR
  MSR   CPSR_cxsf, R4        ;Put the value of CPSR back
  
  LDR   R0,  [SP], #4        ;Put R0 back (this was pdata)
  LDR   R1,  [SP], #4        ;Put R1 back
  LDR   R2,  [SP], #4        ;Put R2 back
  LDR   R3,  [SP], #4        ;Put R3 back
  LDR   R4,  [SP], #4        ;Put R4 back
  LDR   R5,  [SP], #4        ;Put R5 back
  LDR   R6,  [SP], #4        ;Put R6 back
  LDR   R7,  [SP], #4        ;Put R7 back
  LDR   R8,  [SP], #4        ;Put R8 back
  LDR   R9,  [SP], #4        ;Put R9 back
  LDR   R10, [SP], #4        ;Put R10 back
  LDR   R11, [SP], #4        ;Put R11 back
  LDR   R12, [SP], #4        ;Put R12 back
  LDR   LR,  [SP], #4        ;Put R14=LR back
  LDR   PC,  [SP], #4        ;Put R13=PC back, this will start the instructions running again
  
;----------------------------------------------------------------------------
;
;4) OSCtxSw
;  OsCtxSw() is called when a task-level context switch is issued.  This can be from 
;  a software-interrupt or a TRAP instruction.
;
;  To accomplish the context switch, the following must be done
;    1) Save the current processor registers on the current task stack
;    2) Save the current task stack pointer into the current task OS_TCB
;    3) Jump to OSTaskSwHook in case the port needs to extend functionality of the context switch
;    4) Make the pointer of the current OSTCB point to the OSTCB of the task being resumed 
;       (the new task becomes the current task)
;    5) Copy the new task priority into the current task prioirty (Note in AN1101 4 and 5 are switched)
;    6) Get the stack pointer of the task to resume (retrieved from the new task OSTCB)
;    7) Restore the value of the CPU registers for the task that is being resumed
;    8) 
;----------------------------------------------------------------------------

  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OSCtxSw:
  STR   LR,   [SP, #-4]!      ;Put the return address on the stack, ! updates the value of SP
  STR   LR,   [SP, #-4]!      ;and decrements it.  Both R14 and R13 have the value of LR
  STR   R12,  [SP, #-4]!      ;Store R12 on the stack
  STR   R11,  [SP, #-4]!      ;Store R11 on the stack
  STR   R10,  [SP, #-4]!      ;Store R10 on the stack
  STR   R9,   [SP, #-4]!      ;Store R9 on the stack
  STR   R8,   [SP, #-4]!      ;Store R8 on the stack
  STR   R7,   [SP, #-4]!      ;Store R7 on the stack
  STR   R6,   [SP, #-4]!      ;Store R6 on the stack
  STR   R5,   [SP, #-4]!      ;Store R5 on the stack
  STR   R4,   [SP, #-4]!      ;Store R4 on the stack
  STR   R3,   [SP, #-4]!      ;Store R3 on the stack
  STR   R2,   [SP, #-4]!      ;Store R2 on the stack
  STR   R1,   [SP, #-4]!      ;Store R1 on the stack
  STR   R0,   [SP, #-4]!      ;Store R0 on the stack
  MRS   R4,   CPSR            ;Push the current CPSR
  STR   R4,   [SP, #-4]!

  LDR   R4,   ??OSTCBCur      ;Get the current task OSTCB address
  LDR   R5,   [R4]            ;Put the value it points in R5
  STR   SP,   [R5]            ;Store the SP in the OSTCB of the current task
  
  BL    OSTaskSwHook          ;Branch with a Link to OSTaskWwHook
    
  LDR   R4,   ??OSPrioCur     ;Load R4 with the current task priority  
  LDR   R5,   ??OSPrioHighRdy ;Load R5 with the high priority task
  LDRB  R6,   [R5]            ;Put the high priority in R6 (only need a byte since priority < 64)
  STRB  R6,   [R4]            ;Store R6 into the current priority (OSPrioCur=OSPrioHighRdy)

  LDR   R4,   ??OSTCBCur      ;Load R4 with the current task TCB
  LDR   R6,   ??OSTCBHighRdy  ;Load R6 with the high priority task TCB
  LDR   R6,   [R6]            ;Put the high priority task in R6
  STR   R6,   [R4]            ;Store R6 into the current priority TCB (OSTCBCur=OSTCBHighRdy)

  LDR   SP,   [R6]            ;Load the new task stack pointer (SP)
  
  LDR   R4,   [SP], #4        ;Get the new task CPSR
  MSR   CPSR_cxsf, R4         ;Put the value of CPSR back
  
  LDR   R0,   [SP], #4        ;Put R0 back (this was pdata)
  LDR   R1,   [SP], #4        ;Put R1 back
  LDR   R2,   [SP], #4        ;Put R2 back
  LDR   R3,   [SP], #4        ;Put R3 back
  LDR   R4,   [SP], #4        ;Put R4 back
  LDR   R5,   [SP], #4        ;Put R5 back
  LDR   R6,   [SP], #4        ;Put R6 back
  LDR   R7,   [SP], #4        ;Put R7 back
  LDR   R8,   [SP], #4        ;Put R8 back
  LDR   R9,   [SP], #4        ;Put R9 back
  LDR   R10,  [SP], #4        ;Put R10 back
  LDR   R11,  [SP], #4        ;Put R11 back
  LDR   R12,  [SP], #4        ;Put R12 back
  LDR   LR,   [SP], #4        ;Put R14=LR back
  LDR   PC,   [SP], #4        ;Put R13=PC back, this will start the instructions running again


;----------------------------------------------------------------------------
;
;5) OSIntCtxSw
;  OsIntCtxSw() is called by OSIntExit() to perform a context switch from an ISR.
;  Because it is called from an ISR, we assume that all the processor registers 
;  are properly saved onto the interrupted tasks stack (see OSTickISR)
; 
;  The code is the exact same as the second half of OSCtxSw()
;
;----------------------------------------------------------------------------

  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OSIntCtxSw:
  BL    OSTaskSwHook          ;Branch with a Link to OSTaskWwHook
    
  LDR   R4,   ??OSPrioCur     ;Load R4 with the current task priority  
  LDR   R5,   ??OSPrioHighRdy ;Load R5 with the high priority task
  LDRB  R6,   [R5]            ;Put the high priority in R6 (only need a byte since priority < 64)
  STRB  R6,   [R4]            ;Store R6 into the current priority (OSPrioCur=OSPrioHighRdy)

  LDR   R4,   ??OSTCBCur      ;Load R4 with the current task TCB
  LDR   R6,   ??OSTCBHighRdy  ;Load R6 with the high priority task TCB
  LDR   R6,   [R6]            ;Put the high priority task in R6
  STR   R6,   [R4]            ;Store R6 into the current priority TCB (OSTCBCur=OSTCBHighRdy)

  LDR   SP,   [R6]            ;Load the new task stack pointer (SP)
  
  LDR   R4,   [SP], #4        ;Get the new task CPSR
  MSR   CPSR_cxsf, R4         ;Put the value of CPSR back
  
  LDR   R0,   [SP], #4        ;Put R0 back (this was pdata)
  LDR   R1,   [SP], #4        ;Put R1 back
  LDR   R2,   [SP], #4        ;Put R2 back
  LDR   R3,   [SP], #4        ;Put R3 back
  LDR   R4,   [SP], #4        ;Put R4 back
  LDR   R5,   [SP], #4        ;Put R5 back
  LDR   R6,   [SP], #4        ;Put R6 back
  LDR   R7,   [SP], #4        ;Put R7 back
  LDR   R8,   [SP], #4        ;Put R8 back
  LDR   R9,   [SP], #4        ;Put R9 back
  LDR   R10,  [SP], #4        ;Put R10 back
  LDR   R11,  [SP], #4        ;Put R11 back
  LDR   R12,  [SP], #4        ;Put R12 back
  LDR   LR,   [SP], #4        ;Put R14=LR back
  LDR   PC,   [SP], #4        ;Put R13=PC back, this will start the instructions running again


;----------------------------------------------------------------------------
;
;6) OS_CPU_IRQ_ISR
;   This is the interrupt service routine written in assembly code. It is well described in 
;   AN-1011-PPT.pdf on pages 15-30, AN-1011.pdf on page 29, and the text MicroC-OSII on page 104-105
;
;   It does the following:
;     1) Pushes the working registers (R1-R3) onto the ISR stack because they will be used by the ISR
;     2) Adjusts them to account for offsets
;     3) Save the interrupted tasks registers to its stack
;     4) Increment OSNesting to tell mC/OS-II that we are starting an ISR
;        Note: the text says we can do this by either 
;              a) entering OSIntEnter()
;              b) incrementing OSIntNesting (it turns out this is faster than a)
;     5) Check to see if this leve is the first interrupt level
;        If it is: immediately save the stack pointer into the current task OS_TC
;     6) Clear the interrupt source so as not to re-enter the ISR
;     7) Switch back to system mode and return to task level code
;     8) Call the uC/OS-II schedule to determine whether this or any other nested interrupt made 
;        a higher priority task ready to run
;     9) Restore all the registers
;
;   NOTE:  This command actually could replace a whole bunch of store operations:
;                 STMFD   SP!,{R4-R12,LR}               ;    Push task's LR,R12-R4
;
;----------------------------------------------------------------------------
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OS_CPU_IRQ_ISR:
  STR   R3,     [SP, #-4]!        ;Put R3 on the IRQ stack
  STR   R2,     [SP, #-4]!        ;Put R2 on the IRQ stack
  STR   R1,     [SP, #-4]!        ;Put R1 on the IRQ stack
 
  MOV   R1,     SP                ;Save SP in R1 for future use
  ADD   SP,     SP, #12           ;Adjust IRQ stack pointer to remove the stacked data
  
  SUB   R2,     LR, #4            ;Adjust return address because CPU saves PC+4 into LR during interrupt
  
  MRS   R3,     SPSR              ;Copy CPSR of interrupted task into R3 for future use
  
  MSR   CPSR_c, #0xDF             ;Switch to SYS mode with IRQ & FIQ disabled
 
  STR   R2,     [SP, #-4]!        ;Put the return address onto the stack in the place of the return PC
  STR   LR,     [SP, #-4]!        ;Put the R14_sys LR into R14
  STR   R12,    [SP, #-4]!        ;Put R12 on the stack
  STR   R11,    [SP, #-4]!        ;Put R11 on the stack
  STR   R10,    [SP, #-4]!        ;Put R10 on the stack
  STR   R9,     [SP, #-4]!        ;Put R9 on the stack
  STR   R8,     [SP, #-4]!        ;Put R8 on the stack
  STR   R7,     [SP, #-4]!        ;Put R7 on the stack
  STR   R6,     [SP, #-4]!        ;Put R6 on the stack
  STR   R5,     [SP, #-4]!        ;Put R5 on the stack
  STR   R4,     [SP, #-4]!        ;Put R4 on the stack
 
  LDR   R4,     [R1], #4          ;Get R1 of interrupted task (SP was pointing at it) into R4
  LDR   R5,     [R1], #4          ;Get R2 of interrupted task into R5
  LDR   R6,     [R1], #4          ;Get R3 of interrupted task into R6
  STR   R6,     [SP, #-4]!        ;Put R3 on the stack
  STR   R5,     [SP, #-4]!        ;Put R2 on the stack
  STR   R4,     [SP, #-4]!        ;Put R1 on the stack
  
  STR   R0,     [SP, #-4]!        ;Put R0 on the stack
  STR   R3,     [SP, #-4]!        ;Put SPSR of interrupted task into CPSR of stack
  
  LDR   R0,     ??OSIntNesting    ;Put OSIntNesting address into R0
  LDRB  R1,     [R0]              ;Put value of OSIntNesting into R1
  ADD   R1,     R1, #1            ;Increment OSIntNesting by 1
  STRB  R1,     [R0]              ;Put it back in the global location
  
  CMP   R1,     #1                ;Check if OSIntNesting==1
  BNE   OS_CPU_IRQ_ISR_1          ;Jump to OS_CPU_IRQ_ISR_1 if it is not
  
  LDR   R4,     ??OSTCBCur        ;Get the current OS_TCBCur address in R4
  LDR   R5,     [R4]              ;Get the value of OS_TCBCur in R5
  STR   SP,     [R5]              ;Store the SP of the current task into the OS_TCB of the ISR
  
OS_CPU_IRQ_ISR_1:
  
  MSR   CPSR_c, #0xD1             ;Switch to IRQ mode with interrupts disabled
  BL    OS_CPU_IRQ_ISR_Handler    ;Call the code that will handle the ISR (written in C)
  
  MSR   CPSR_c, #0x1F             ;Switch to SYS mode with interrupts enabled
  BL    OSIntExit                 ;Change this to 0xDF for disabled interrupts
  
  LDR   R4,   [SP], #4            ;Get the new task CPSR
  MSR   CPSR_cxsf, R4             ;Put the value of CPSR back
  
  LDR   R0,   [SP], #4            ;Put R0 back (this was pdata)
  LDR   R1,   [SP], #4            ;Put R1 back
  LDR   R2,   [SP], #4            ;Put R2 back
  LDR   R3,   [SP], #4            ;Put R3 back
  LDR   R4,   [SP], #4            ;Put R4 back
  LDR   R5,   [SP], #4            ;Put R5 back
  LDR   R6,   [SP], #4            ;Put R6 back
  LDR   R7,   [SP], #4            ;Put R7 back
  LDR   R8,   [SP], #4            ;Put R8 back
  LDR   R9,   [SP], #4            ;Put R9 back
  LDR   R10,  [SP], #4            ;Put R10 back
  LDR   R11,  [SP], #4            ;Put R11 back
  LDR   R12,  [SP], #4            ;Put R12 back
  LDR   LR,   [SP], #4            ;Put R14=LR back
  LDR   PC,   [SP], #4            ;Put R13=PC back, this will start the instructions running again

;----------------------------------------------------------------------------
;
;6) OS_CPU_FIQ_ISR
;   NOTE: Right now it does exactly what OS_CPU_IRQ_ISR does.  This will have to change
;   This is the interrupt service routine written in assembly code. It is well described in 
;   AN-1011-PPT.pdf on pages 15-30, AN-1011.pdf on page 29, and the text MicroC-OSII on page 104-105
;
;   It does the following:
;     1) Pushes the working registers (R1-R3) onto the ISR stack because they will be used by the ISR
;     2) Adjusts them to account for offsets
;     3) Save the interrupted tasks registers to its stack
;     4) Increment OSNesting to tell mC/OS-II that we are starting an ISR
;        Note: the text says we can do this by either 
;              a) entering OSIntEnter()
;              b) incrementing OSIntNesting (it turns out this is faster than a)
;     5) Check to see if this leve is the first interrupt level
;        If it is: immediately save the stack pointer into the current task OS_TC
;     6) Clear the interrupt source so as not to re-enter the ISR
;     7) Switch back to system mode and return to task level code
;     8) Call the uC/OS-II schedule to determine whether this or any other nested interrupt made 
;        a higher priority task ready to run
;     9) Restore all the registers
;
;----------------------------------------------------------------------------
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
OS_CPU_FIQ_ISR:
  STR   R3,     [SP, #-4]!        ;Put R3 on the IRQ stack
  STR   R2,     [SP, #-4]!        ;Put R2 on the IRQ stack
  STR   R1,     [SP, #-4]!        ;Put R1 on the IRQ stack
 
  MOV   R1,     SP                ;Save SP in R1 for future use
  ADD   SP,     SP, #12           ;Adjust IRQ stack pointer to remove the stacked data
  
  SUB   R2,     LR, #4            ;Adjust return address because CPU saves PC+4 into LR during interrupt
  
  MRS   R3,     SPSR              ;Copy CPSR of interrupted task into R3 for future use
  
  MSR   CPSR_cxsf, #0xDF          ;Switch to SYS mode with IRQ & FIQ disabled
 
  STR   R2,     [SP, #-4]!        ;Put the return address onto the stack in the place of the return PC
  STR   LR,     [SP, #-4]!        ;Put the R14_sys LR into R14
  STR   R12,    [SP, #-4]!        ;Put R12 on the stack
  STR   R11,    [SP, #-4]!        ;Put R11 on the stack
  STR   R10,    [SP, #-4]!        ;Put R10 on the stack
  STR   R9,     [SP, #-4]!        ;Put R9 on the stack
  STR   R8,     [SP, #-4]!        ;Put R8 on the stack
  STR   R7,     [SP, #-4]!        ;Put R7 on the stack
  STR   R6,     [SP, #-4]!        ;Put R6 on the stack
  STR   R5,     [SP, #-4]!        ;Put R5 on the stack
  STR   R4,     [SP, #-4]!        ;Put R4 on the stack
 
  LDR   R4,     [R1], #4          ;Get R1 of interrupted task (SP was pointing at it) into R4
  LDR   R5,     [R1], #4          ;Get R2 of interrupted task into R5
  LDR   R6,     [R1], #4          ;Get R3 of interrupted task into R6
  STR   R6,     [SP, #-4]!        ;Put R3 on the stack
  STR   R5,     [SP, #-4]!        ;Put R2 on the stack
  STR   R4,     [SP, #-4]!        ;Put R1 on the stack
  
  STR   R0,     [SP, #-4]!        ;Put R0 on the stack
  STR   R3,     [SP, #-4]!        ;Put SPSR of interrupted task into CPSR of stack
  
  LDR   R0,     ??OSIntNesting    ;Put OSIntNesting address into R0
  LDRB  R1,     [R0]              ;Put value of OSIntNesting into R1
  ADD   R1,     R1, #1            ;Increment OSIntNesting by 1
  STRB  R1,     [R0]              ;Put it back in the global location
  
  CMP   R1,     #1                ;Check if OSIntNesting==1
  BNE   OS_CPU_FIQ_ISR_1          ;Jump to OS_CPU_IRQ_ISR_1 if it is not
  
  LDR   R4,     ??OSTCBCur        ;Get the current OS_TCBCur address in R4
  LDR   R5,     [R4]              ;Get the value of OS_TCBCur in R5
  STR   SP,     [R5]              ;Store the SP of the current task into the OS_TCB of the ISR
  
OS_CPU_FIQ_ISR_1:
  
  MSR   CPSR_c, #0xD2             ;Switch to IRQ mode with interrupts disabled
  BL    OS_CPU_FIQ_ISR_Handler    ;Call the code that will handle the ISR (written in C)
  
  MSR   CPSR_c, #0xDF             ;Switch to SYS mode with interrupts disabled
  BL    OSIntExit
  
  LDR   R4,   [SP], #4            ;Get the new task CPSR
  MSR   CPSR_cxsf, R4             ;Put the value of CPSR back
  
  LDR   R0,   [SP], #4            ;Put R0 back (this was pdata)
  LDR   R1,   [SP], #4            ;Put R1 back
  LDR   R2,   [SP], #4            ;Put R2 back
  LDR   R3,   [SP], #4            ;Put R3 back
  LDR   R4,   [SP], #4            ;Put R4 back
  LDR   R5,   [SP], #4            ;Put R5 back
  LDR   R6,   [SP], #4            ;Put R6 back
  LDR   R7,   [SP], #4            ;Put R7 back
  LDR   R8,   [SP], #4            ;Put R8 back
  LDR   R9,   [SP], #4            ;Put R9 back
  LDR   R10,  [SP], #4            ;Put R10 back
  LDR   R11,  [SP], #4            ;Put R11 back
  LDR   R12,  [SP], #4            ;Put R12 back
  LDR   LR,   [SP], #4            ;Put R14=LR back
  LDR   PC,   [SP], #4            ;Put R13=PC back, this will start the instructions running again
  
;----------------------------------------------------------------------------
;
;8) DISABLE_CACHE
;   This function disables caching of data memory.  We will most likely not 
;   use this.  Rather, we will individually disable caching for pages of 
;   memory.  But for right now it will serve for testing.
;  
;   For reference, see the PXA270 Developers Guide pg. 1219, Intel XScale 
;   Developers Guide pg. 87 and 83.
;
;----------------------------------------------------------------------------
;
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
DISABLE_CACHE:
  MCR   P15, 0, R0, C7, C10, 4    ;Drain Write and Fill Buffer in the Cache
  MRC   P15, 0, R0, C1, C0,  0    ;Get the current ARM Control Register in r0
  AND   R0, R0, #0xFFFFFFFB       ;Disable cache by setting ARM Control Reg[2]=0
  MCR   P15, 0, R0, C1, C0,  0    ;Update the control register

;----------------------------------------------------------------------------
;
;9) RETURN_MMU_TABLE_ADDR
;   This function obtains the address of the MMU Section Table in memory.
;   For the PXA270 running on top of LOLO, this happens to be at the address 
;   0xA0048000
;  
;   The address is returned in R0.  To use this function in C, use the 
;   following syntax
;  
;   INT32U MMU_Table_Addr;
;   MMU_Table_Addr = Return_MMU_Table_ADDR();
;
;----------------------------------------------------------------------------
;
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
Return_MMU_Table_ADDR:
  MRC    P15, 0, R0, C2, C0, 0    ;Get the value of the Translation Table Address
  BX     LR
  
;----------------------------------------------------------------------------
;
;10) UPDATE_MMU_TABLE
;   This function updates the MMU Table so that caching is disabled for 
;   certain areas of memory.
;
;   To do this, it obtains the original address for the MMU Section Table that is set 
;   by the LOLO firmware or program is booted up (0xA0048000).  It copies all of the 
;   entries in this table to a new table at address MMU_SectionTable (this array 
;   is forced to address 0xA3F00000 because the base address for the translation 
;   table (Coprocessor Register 15, Cn=C2, Cm=0) should only have bits [31:14] 
;   written.  So it has to be on a 64KB boundary, I believe.
; 
;   After the copy, it clears the cache the Translation Lookaside Buffer by 
;   invalidating all of the entries.  Then it updates the Translation Table 
;   Address with MMU_SectionTable[0].
;
;----------------------------------------------------------------------------
;
  SECTION `.text`:CODE:NOROOT(2)
  CODE32
  
Update_MMU_Table:

  MRC    P15, 0, R1, C2, C0, 0    ;Get the address of the current Translation Table in R1
  LDR    R2, =MMU_SectionTable    ;Get the address of the new Translation Table in R2
  
  MOV    R3, #4096                ;Load 4096 to cover the full 4096 MegaBytes of Address Space
  MOV    R4, #0                   ;Load 0 for some reason

  MOV    R6, #0xA3000000          ;Load starting address for non-cached RAM
  MOV    R7, #0xA4000000          ;Load ending address for non-cached RAM
  
  mmu_loop:
  LDR    R5, [R1]                 ;Put the current Translation Table element in R5
  
  CMP    R5, R6                   ;Check if Table Address is below uncached region 
  BLT    cache_memory             ;Keep cache enable/disable for this region
  CMP    R5, R7                   ;Check if Table Address is above uncached region 
  BGE    cache_memory             ;Keep cache enable/disable for this region
  AND    R5, R5, #0xFFFFFFF3      ;Disable cache for 0xA3000000-0xA4000000
  
  cache_memory:
  STR    R5, [R2]                 ;Copy this to the new Translation Table
  ADDS   R1, R1, #4               ;Increment the current Translation Table address
  ADDS   R2, R2, #4               ;Increment the new Translation Table address

  CMP    R3, #0                   ;See if loop is finished
  SUBS   R3, R3, #1               ;If not, subtract 1 and jump back to mmu_loop
  BNE    mmu_loop                 
    
  MOV    R5, #0                   ;Move a zero to  
  MCR    p15, 0, R5, C7, C7       ;Flush the D Cache
  MCR    P15, 0, R5, C7, C10, 4   ;Drain Write and Fill Buffer in the Cache
  MCR    P15, 0, R5, C8, C7       ;Invalidate the I and D TLBs
  
  LDR    R0, =MMU_SectionTable    ;Get the address of the new MMU Section Translation Table
  MCR    P15, 0, R0, C2, C0, 0    ;Write the TTB so the CPU knows where the table is
  BX      LR
  
;--------------------------------------------------------------------------------------------------
;32 BIT ADDRESSES
;--------------------------------------------------------------------------------------------------

??OSTCBCur:
  DC32  OSTCBCur
  
??OSTCBHighRdy:
  DC32  OSTCBHighRdy
  
??OSPrioCur:
  DC32  OSPrioCur
  
??OSPrioHighRdy:
  DC32  OSPrioHighRdy
  
??OSRunning:
  DC32  OSRunning

??OSIntNesting
  DC32  OSIntNesting
  
  END
