#include <sys/types.h>
#include <string.h>
#include <debug.h>
#include <kernel/thread.h>
#include <arch/arm.h>

struct context_switch_frame {
	vaddr_t usp;
	vaddr_t ulr;
	vaddr_t spsr;
	vaddr_t r4;
	vaddr_t r5;
	vaddr_t r6;
	vaddr_t r7;
	vaddr_t r8;
	vaddr_t r9;
	vaddr_t r10;
	vaddr_t r11;
	vaddr_t lr;
};

extern void arm_context_switch(addr_t *old_sp, addr_t new_sp);

static void initial_thread_func(void) __NO_RETURN;
static void initial_thread_func(void)
{
	int ret;

//	dprintf("initial_thread_func: thread %p calling %p with arg %p\n", current_thread, current_thread->entry, current_thread->arg);
//	dump_thread(current_thread);

	/* exit the implicit critical section we're within */
	exit_critical_section();

	ret = current_thread->entry(current_thread->arg);

	dprintf("initial_thread_func: thread %p exiting with %d\n", current_thread, ret);

	thread_exit(ret);
}

void arch_thread_initialize(struct thread *t)
{
	// create a default stack frame on the stack
	struct context_switch_frame *frame = (struct context_switch_frame *)((vaddr_t)t->stack + t->stack_size);
	frame--;

	// fill it in
	memset(frame, 0, sizeof(*frame));
	frame->lr = (vaddr_t)&initial_thread_func;
	
	// set the stack pointer
	t->arch.sp = (vaddr_t)frame;
}

void arch_context_switch(struct thread *oldthread, struct thread *newthread)
{
	dprintf("arch_context_switch: old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
	arm_context_switch(&oldthread->arch.sp, newthread->arch.sp);
}

