/*
 * File      : start.S
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://openlab.rt-thread.com/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-09-06     XuXinming    first version
 * 2006-09-20     Bernard      clean the code
 */

/**
 * @addtogroup S3C44B0
 */
/*@{*/

.section .init, "ax"
.code 32
.globl _start
_start:
	b 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_undef:	.word vector_undef
_vector_swi:	.word vector_swi
_vector_pabt:	.word vector_pabt
_vector_dabt:	.word vector_dabt
_vector_resv:	.word vector_resv
_vector_irq:	.word vector_irq
_vector_fiq:	.word vector_fiq

.text
.code 32

/*
 * rtthread kernel start and end
 * which are defined in linker script
 */
.globl _rtthread_start
_rtthread_start:.word _start
.globl _rtthread_end
_rtthread_end:	.word  _end

/*
 * rtthread bss start and end
 * which are defined in linker script
 */
.globl _bss_start
_bss_start:	.word __bss_start
.globl _bss_end
_bss_end:	.word __bss_end

#if defined(__FLASH_BUILD__)
/*
 * TEXT_BASE, 
 * which is defined in macro of make
 */
_TEXT_BASE: .word	TEXT_BASE
#endif

	.equ WTCON,		0x1d30000
	.equ INTCON,	0x1e00000
	.equ INTMSK, 	0x1e0000c

/* the system entry */
reset:
	/* enter svc mode */
	msr cpsr_c, #SVCMODE|NOINT

	/*watch dog disable */
	ldr r0,=WTCON
    ldr r1,=0x0 		
    str r1,[r0]
	
	/* all interrupt disable */
	ldr r0,=INTMSK
	ldr r1,=0x07ffffff
	str r1,[r0]
	
	ldr	r1, =INTCON
	ldr	r0, =0x05
	str	r0, [r1]

#if defined(__FLASH_BUILD__)
	/* init lowlevel */
	bl lowlevel_init
#endif

	/* setup stack */
	bl stack_setup
	
#if defined(__FLASH_BUILD__)
	mov r0, #0x0			/* r0 <- flash base address         */
	ldr r1, _TEXT_BASE		/* r1 <- the taget address          */
	
	ldr	r2, _rtthread_start
	ldr	r3, _bss_start
	sub	r2, r3, r2			/* r2 <- size of rtthread kernel    */
	add	r2, r0, r2			/* r2 <- source end address         */
	
copy_loop:
	ldmia	r0!, {r3-r10}	/* copy from source address [r0]    */
	stmia	r1!, {r3-r10}	/* copy to   target address [r1]    */
	cmp	r0, r2				/* until source end addreee [r2]    */
	ble	copy_loop
#endif
	
	/* start RT-Thread Kernel */
	ldr	pc, _rtthread_startup

_rtthread_startup: .word rtthread_startup

	.equ USERMODE, 	0x10
	.equ FIQMODE, 	0x11
	.equ IRQMODE, 	0x12
	.equ SVCMODE, 	0x13
	.equ ABORTMODE, 0x17
	.equ UNDEFMODE, 0x1b
	.equ MODEMASK, 	0x1f
	.equ NOINT,		0xc0

/* exception handlers */
vector_undef:	bl rt_hw_trap_udef
vector_swi:		bl rt_hw_trap_swi
vector_pabt: 	bl rt_hw_trap_pabt
vector_dabt:	bl rt_hw_trap_dabt
vector_resv: 	bl rt_hw_trap_resv

.globl rt_interrupt_enter
.globl rt_interrupt_leave
.globl rt_thread_switch_interrput_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
vector_irq:	
	stmfd	sp!, {r0-r12,lr}
	bl  led_off
	bl	rt_interrupt_enter
	bl	rt_hw_trap_irq
	bl	rt_interrupt_leave

	/* if rt_thread_switch_interrput_flag set, jump to _interrupt_thread_switch and don't return */
	ldr	r0, =rt_thread_switch_interrput_flag
	ldr	r1, [r0]
	cmp	r1, #1
	beq	_interrupt_thread_switch

	ldmfd	sp!, {r0-r12,lr}
	subs	pc, lr, #4

	.align	5
vector_fiq:
	stmfd sp!,{r0-r7,lr}
	bl rt_hw_trap_fiq
	ldmfd sp!,{r0-r7,lr}
	subs pc,lr,#4

_interrupt_thread_switch:
	mov	r1, #0				@ clear rt_thread_switch_interrput_flag
	str	r1, [r0]

	ldmfd sp!, {r0-r12,lr}	@ reload saved registers
	stmfd sp!, {r0-r3}		@ save r0-r3
	mov	r1, sp
	add	sp, sp, #16			@ restore sp
	sub	r2, lr, #4			@ save old task's pc to r2

	mrs	r3, spsr			@ disable interrupt
	orr	r0, r3, #NOINT
	msr	spsr_c, r0

	ldr	r0,  =.+8			@ switch to interrupted task's stack
	movs pc, r0

	stmfd sp!, {r2}			@ push old task's pc
	stmfd sp!, {r4-r12,lr}	@ push old task's lr,r12-r4
	mov	r4, r1				@ Special optimised code below
	mov	r5, r3
	ldmfd r4!, {r0-r3}
	stmfd sp!, {r0-r3}		@ push old task's r3-r0
	stmfd sp!, {r5}			@ push old task's psr
	mrs	r4, spsr
	stmfd sp!, {r4}			@ push old task's spsr

	ldr	r4, =rt_interrupt_from_thread
	ldr	r5, [r4]
	str	sp, [r5]			@ store sp in preempted tasks's TCB

	ldr	r6, =rt_interrupt_to_thread
	ldr	r6, [r6]
	ldr	sp, [r6]			@ get new task's stack pointer

	ldmfd sp!, {r4}			@ pop new task's spsr
	msr	SPSR_cxsf, r4
	ldmfd sp!, {r4}			@ pop new task's psr
	msr CPSR_cxsf, r4

	ldmfd sp!, {r0-r12,lr,pc}	@ pop new task's r0-r12,lr & pc

/* each mode stack memory */
UNDSTACK_START:	.word _undefined_stack_start + 128
ABTSTACK_START:	.word _abort_stack_start + 128
FIQSTACK_START:	.word _fiq_stack_start + 1024
IRQSTACK_START:	.word _irq_stack_start + 1024
SVCSTACK_START: .word _svc_stack_start + 4096

stack_setup:
	/* undefined instruction mode */
	msr cpsr_c, #UNDEFMODE|NOINT
	ldr sp, UNDSTACK_START

	/* abort mode */
	msr cpsr_c, #ABORTMODE|NOINT
	ldr sp, ABTSTACK_START

	/* FIQ mode */
	msr cpsr_c, #FIQMODE|NOINT
	ldr sp, FIQSTACK_START

	/* IRQ mode */
	msr cpsr_c, #IRQMODE|NOINT
	ldr sp, IRQSTACK_START

	/* supervisor mode */
	msr cpsr_c, #SVCMODE|NOINT
	ldr sp, SVCSTACK_START

	mov	pc,lr				@ The LR register may be not valid for the mode changes.

.globl led_on
led_on:
	ldr	r1,	=0x1d20014		@ r1<-PDATC
	ldr	r0,	[r1]			@ r0<-[r1]
	orr	r0,	r0, #0x0e		@ r0=r0 or 0x0e
	str	r0,	[r1]			@ r0->[r1]
	mov	pc, lr

.globl led_off
led_off:
	ldr	r1,	=0x1d20010		@ r1<-PCONC
	ldr	r0,	=0x5f555555		@ r0<-0x5f555555
	str	r0,	[r1]			@ r0->[r1]

	ldr	r1,	=0x1d20014		@ r1<-PDATC
	ldr	r0,	=0x0			@ r0<-00
	str	r0,	[r1]			@ r0->[r1]

	mov	pc, lr
