;/*
; * Copyright (c) 2006-2018, RT-Thread Development Team
; *
; * SPDX-License-Identifier: Apache-2.0
; *
; * Change Logs:
; * Date           Author       Notes
; * 2011-01-13     weety      first version
; * 2015-04-15     ArdaFu     Split from AT91SAM9260 BSP
; * 2015-04-21     ArdaFu     Remove remap code. Using mmu to map vector table
; * 2015-06-04     aozima     Align stack address to 8 byte.
; */

#include "rt_low_level_init.h"

#define S_FRAME_SIZE    (18*4)   ;72

;#define S_SPSR          (17*4)   ;SPSR
;#define S_CPSR          (16*4)   ;CPSR
#define S_PC            (15*4)   ;R15
;#define S_LR            (14*4)   ;R14
;#define S_SP            (13*4)   ;R13

;#define S_IP            (12*4)   ;R12
;#define S_FP            (11*4)   ;R11
;#define S_R10           (10*4)
;#define S_R9            (9*4)
;#define S_R8            (8*4)
;#define S_R7            (7*4)
;#define S_R6            (6*4)
;#define S_R5            (5*4)
;#define S_R4            (4*4)
;#define S_R3            (3*4)
;#define S_R2            (2*4)
;#define S_R1            (1*4)
;#define S_R0            (0*4)

#define MODE_SYS        0x1F
#define MODE_FIQ        0x11
#define MODE_IRQ        0x12
#define MODE_SVC        0x13
#define MODE_ABT        0x17
#define MODE_UND        0x1B
#define MODEMASK        0x1F

#define NOINT           0xC0

;----------------------- Stack and Heap Definitions ----------------------------
    MODULE ?cstartup
    SECTION .noinit:DATA:NOROOT(3)
    DATA

    ALIGNRAM 3
    DS8 UND_STK_SIZE
    PUBLIC UND_STACK_START
UND_STACK_START:

    ALIGNRAM 3
    DS8 ABT_STK_SIZE
    PUBLIC ABT_STACK_START
ABT_STACK_START:

    ALIGNRAM 3
    DS8 FIQ_STK_SIZE
    PUBLIC FIQ_STACK_START
FIQ_STACK_START:

    ALIGNRAM 3
    DS8 IRQ_STK_SIZE
    PUBLIC IRQ_STACK_START
IRQ_STACK_START:

    ALIGNRAM 3
    DS8 SYS_STK_SIZE
    PUBLIC SYS_STACK_START
SYS_STACK_START:

    ALIGNRAM 3
    DS8 SVC_STK_SIZE
    PUBLIC SVC_STACK_START
SVC_STACK_START:

;--------------Jump vector table------------------------------------------------
    SECTION .intvec:CODE:ROOT(2)
    ARM
    PUBLIC Entry_Point
Entry_Point:
__iar_init$$done:               ; The interrupt vector is not needed
                                ; until after copy initialization is done
    LDR     PC, vector_reset
    LDR     PC, vector_undef
    LDR     PC, vector_swi
    LDR     PC, vector_pabt
    LDR     PC, vector_dabt
    LDR     PC, vector_resv
    LDR     PC, vector_irq
    LDR     PC, vector_fiq

vector_reset:
    DC32 Reset_Handler
vector_undef:
    DC32 Undef_Handler
vector_swi:
    DC32 SWI_Handler
vector_pabt:
    DC32 PAbt_Handler
vector_dabt:
    DC32 DAbt_Handler
vector_resv:
    DC32 Resv_Handler
vector_irq:
    DC32 IRQ_Handler
vector_fiq:
    DC32 FIQ_Handler

;----------------- Reset Handler -----------------------------------------------
    EXTERN rt_low_level_init
    EXTERN ?main
    PUBLIC __iar_program_start
__iar_program_start:
Reset_Handler:
    ; Set the cpu to SVC32 mode
    MRS     R0, CPSR
    BIC     R0, R0, #MODEMASK
    ORR     R0, R0, #MODE_SVC|NOINT
    MSR     CPSR_cxsf, R0
    
    ; Set CO-Processor
    ; little-end，disbale I/D Cache MMU, vector table is 0x00000000
    MRC     P15, 0, R0, C1, C0, 0   ; Read CP15
    LDR     R1, =0x00003085         ; set clear bits
    BIC     R0, R0, R1
    MCR     P15, 0, R0, C1, C0, 0   ; Write CP15

    ; Call low level init function,
    ; disable and clear all IRQs, Init MMU, Init interrupt controller, etc.
    LDR     SP, =SVC_STACK_START
    LDR     R0, =rt_low_level_init
    BLX     R0

Setup_Stack:
    ; Setup Stack for each mode
    MRS     R0, CPSR
    BIC     R0, R0, #MODEMASK

    ORR     R1, R0, #MODE_UND|NOINT
    MSR     CPSR_cxsf, R1            ; Undef mode
    LDR     SP, =UND_STACK_START

    ORR     R1,R0,#MODE_ABT|NOINT
    MSR     CPSR_cxsf,R1             ; Abort mode
    LDR     SP, =ABT_STACK_START

    ORR     R1,R0,#MODE_IRQ|NOINT
    MSR     CPSR_cxsf,R1             ; IRQ mode
    LDR     SP, =IRQ_STACK_START

    ORR     R1,R0,#MODE_FIQ|NOINT
    MSR     CPSR_cxsf,R1             ; FIQ mode
    LDR     SP, =FIQ_STACK_START

    ORR     R1,R0,#MODE_SYS|NOINT
    MSR     CPSR_cxsf,R1             ; SYS/User mode
    LDR     SP, =SYS_STACK_START

    ORR     R1,R0,#MODE_SVC|NOINT
    MSR     CPSR_cxsf,R1             ; SVC mode
    LDR     SP, =SVC_STACK_START
    
    ; Enter the C code 
    LDR     R0, =?main
    BLX     R0

;----------------- Exception Handler -------------------------------------------
    IMPORT rt_hw_trap_udef
    IMPORT rt_hw_trap_swi
    IMPORT rt_hw_trap_pabt
    IMPORT rt_hw_trap_dabt
    IMPORT rt_hw_trap_resv
    IMPORT rt_hw_trap_irq
    IMPORT rt_hw_trap_fiq

    IMPORT rt_interrupt_enter
    IMPORT rt_interrupt_leave
    IMPORT rt_thread_switch_interrupt_flag
    IMPORT rt_interrupt_from_thread
    IMPORT rt_interrupt_to_thread

    SECTION .text:CODE:ROOT(2)
    ARM
Undef_Handler:
    SUB     SP, SP, #S_FRAME_SIZE
    STMIA   SP, {R0 - R12}          ; Calling R0-R12
    ADD     R8, SP, #S_PC
    STMDB   R8, {SP, LR}            ; Calling SP, LR
    STR     LR, [R8, #0]            ; Save calling PC
    MRS     R6, SPSR
    STR     R6, [R8, #4]            ; Save CPSR
    STR     R0, [R8, #8]            ; Save SPSR
    MOV     R0, SP
    BL      rt_hw_trap_udef

SWI_Handler:
    BL      rt_hw_trap_swi

PAbt_Handler:
    BL      rt_hw_trap_pabt

DAbt_Handler:
    SUB     SP, SP, #S_FRAME_SIZE
    STMIA   SP, {R0 - R12}          ; Calling R0-R12
    ADD     R8, SP, #S_PC
    STMDB   R8, {SP, LR}            ; Calling SP, LR
    STR     LR, [R8, #0]            ; Save calling PC
    MRS     R6, SPSR
    STR     R6, [R8, #4]            ; Save CPSR
    STR     R0, [R8, #8]            ; Save SPSR
    MOV     R0, SP
    BL      rt_hw_trap_dabt

Resv_Handler:
    BL      rt_hw_trap_resv

IRQ_Handler:
    STMFD   SP!, {R0-R12,LR}
    BL      rt_interrupt_enter
    BL      rt_hw_trap_irq
    BL      rt_interrupt_leave

    ; If rt_thread_switch_interrupt_flag set,
    ; jump to rt_hw_context_switch_interrupt_do and don't return
    LDR     R0, =rt_thread_switch_interrupt_flag
    LDR     R1, [R0]
    CMP     R1, #1
    BEQ     rt_hw_context_switch_interrupt_do

    LDMFD   SP!, {R0-R12,LR}
    SUBS    PC, LR, #4

FIQ_Handler:
    STMFD   SP!, {R0-R7,LR}
    BL      rt_hw_trap_fiq
    LDMFD   SP!, {R0-R7,LR}
    SUBS    PC, LR, #4

;------ void rt_hw_context_switch_interrupt_do(rt_base_t flag) -----------------
rt_hw_context_switch_interrupt_do:
    MOV     R1,  #0                 ; Clear flag
    STR     R1,  [R0]               ; Save to flag variable

    LDMFD   SP!, {R0-R12,LR}        ; Reload saved registers
    STMFD   SP, {R0-R2}             ; Save R0-R2
    SUB     R1,  SP, #4*3           ; Save old task's SP to R1
    SUB     R2,  LR, #4             ; Save old task's PC to R2

    MRS     R0,  SPSR               ; Get CPSR of interrupt thread

    MSR     CPSR_c, #MODE_SVC|NOINT ; Switch to SVC mode and no interrupt

    STMFD   SP!, {R2}               ; Push old task's PC
    STMFD   SP!, {R3-R12,LR}        ; Push old task's LR,R12-R3
    LDMFD   R1, {R1-R3}
    STMFD   SP!, {R1-R3}            ; Push old task's R2-R0
    STMFD   SP!, {R0}               ; Push old task's CPSR

    LDR     R4,  =rt_interrupt_from_thread
    LDR     R5,  [R4]               ; R5 = stack ptr in old tasks's TCB
    STR     SP,  [R5]               ; Store SP in preempted tasks's TCB

    LDR     R6,  =rt_interrupt_to_thread
    LDR     R6,  [R6]               ; R6 = stack ptr in new tasks's TCB
    LDR     SP,  [R6]               ; Get new task's stack pointer

    LDMFD   SP!, {R4}               ; Pop new task's SPSR
    MSR     SPSR_cxsf, R4

    LDMFD   SP!, {R0-R12,LR,PC}^    ; pop new task's R0-R12,LR & PC SPSR to CPSR
    END
