#include <stdio.h>
#include "types.h"
#include "exceptions.h"
#define ENTRY(x) asm(" .globl _" #x "; .align 2; _" #x ": .word 0");
#define RET asm(" ret")

ENTRY(GetStackPointer)
asm(" movl sp,r0");
RET;

ENTRY(SetStackPointer)
/* used kludge to allow RET to be used to return, and then set SP after */
asm(" movl 4(ap),r0");
asm(" movl 16(fp),r1"); /* save return PC */
asm(" movl $SetSP__,16(fp)"); /* hack return PC */
RET;
asm("SetSP__:");
asm(" movl r0,sp"); /* finally! */
asm(" jmp (r1)");

#define UNIMPLEMENTED(name) \
    fprintf(stderr, "Unimplemented: %s\n", name); fflush(stderr);  abort();

ReadSymbolTable(char *fileName)
{ UNIMPLEMENTED("ReadSymbolTable"); }

struct Module *
LoadModule(struct Module *module, FILE *bfile)
{ UNIMPLEMENTED("LoadModule"); }

HandleAuxFile(struct Module *module, char *auxFileName)
{ UNIMPLEMENTED("HandleAuxFile"); }

DoRelocations()
{ UNIMPLEMENTED("DoRelocations"); }

long TmpSaveRegs[2];
/* DummyFrame has a pseudo-RL that can be safely stored into
 * when various routines patch FRAME_RL(_Handler_->patched_FP) */
char DummyFrame[4*6] = {0};
Continuation NullHandler[1] =
    {NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0x7FFFFFFF,}, DummyFrame};
Continuation InteractiveHandler[1] =
    {NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0x7FFFFFFF,}, DummyFrame};
struct { long RL; long FP; } Patched = { 0, (long)DummyFrame };
#define Patched_RL Patched.RL
#define Patched_FP Patched.FP

asm(" .globl _Patch_Return");
asm("_Patch_Return:");
asm(" movq r0,_TmpSaveRegs"); /* save r0,r1 in TmpSaveRegs */

/* Move patch to our caller */
asm(" movq _Patched,r0"); /* r0 = Patched_RL, r1 = Patched_FP */
asm(" movl r0,16(r1)"); /* FRAME_RL(Patched_FP) = Patched_RL */
asm(" movl 12(r1),r1"); /* Patched_FP = FRAME_OP_FP(Patched_FP) */
asm(" movl r1,_Patched+4");
asm(" movl 16(r1),_Patched+0"); /* Patched_RL  = FRAME_RL(Patched_FP) */
 /* FRAME_RL(Patched_FP) = (char*)Patch_Return */
asm(" movl $_Patch_Return,16(r1)");

/* keep Handler->save_SP in global? */
asm(" movl __Handler_,r1"); /* SP = MIN(SP,_Handler_->save_SP) */
asm(" movl 56(r1),r1");
asm(" cmpl r1,sp"); /* if (a0>sp) return */
asm(" bgtru Ljump");
asm(" movl r1,sp");
/* check for argument-popping after return, and compensate for it */
asm("Ljump:");
asm(" pushl r0");
asm(" movq _TmpSaveRegs,r0"); /* restore r0,r1 from TmpSaveRegs */
asm(" rsb");

ENTRY(SaveOrHandler)
asm(" movl 8(ap),r0"); /* cont */
asm(" movw $2,68(r0)"); /* cont->kind = 2 */
asm(" jbr BothCreate");

ENTRY(CreateContinuation);
asm(" movl 8(ap),r0"); /* cont */
asm(" movw $1,68(r0)"); /* cont->kind = 1 */
asm("BothCreate:");
asm(" movl _Patched+4,r1"); /* FRAME_RL(Patched_FP) = Patched_RL */
asm(" movl _Patched+0,16(r1)");
asm(" movl 12(fp),r1"); /* old fp */
asm(" movl r1,_Patched+4"); /* Patched_FP = BACK_SAVE_FP(_Handler_) */
asm(" movl 16(r1),_Patched+0"); /* Patched_RL = FRAME_RL(Patched_FP) */
asm(" movl $_Patch_Return,16(r1)");
asm("SaveCommonHandler:");
asm(" movl 4(ap),60(r0)"); /* cont->PC = label */
asm(" movl _UndoTrail,4(r0)"); /* cont->undo_trail = UndoTrail */
asm(" movl $__Handler_,r1");
asm(" movl r1,64(r0)"); /* cont->prev = &_Handler_ */
asm(" movl 0(r1),ap"); /* use ap as temp. */
asm(" movl r0,0(r1)");
asm(" movl ap,r1");
asm(" movl r0,64(r1)");
asm(" movl r1,0(r0)"); /* cont->next = next */
asm(" addl2 $8,r0");
asm(" movq r2,(r0)+"); /* 8 */
asm(" movq r4,(r0)+"); /* 16 */
asm(" movq r6,(r0)+"); /* 24 */
asm(" movq r8,(r0)+"); /* 32 */
asm(" movq r10,(r0)+"); /* 40 */
asm(" movq 8(fp),(r0)+"); /* save old AP,FP 48 */
asm(" addl3 sp,$32,(r0)"); /* 52 */
RET;

ENTRY(SaveIfHandler)
asm(" movl 8(ap),r0"); /* cont */
asm(" movw $2,68(r0)"); /*cont->kind = 2 */
asm(" jbr SaveCommonHandler");

InvokeContinuation(Continuation *cont, void *result /*IGNORED*/)
{
    char *new_SP = CONT_SAVE_SP(_Handler_);
    if (cont->kind == 0) BadContinuation();
    _InvokeContinuation(new_SP, cont);
}

ENTRY(_InvokeContinuation)
asm(" movl 8(ap),r1"); /* cont */
asm(" movl _Patched+4,r3");
asm(" movl _Patched+0,16(r3)"); /*FRAME_RL(PPatched_FP) + Patched_RL */
asm(" movl 52(r1),r3");
asm(" movl r3,_Patched+4");/* Patched_FP = CONT_SAVE_FP(cont) */
asm(" movl 16(r3),_Patched+0"); /* Patched_RL = FRAME_RL(Patched_FP) */
asm(" movl $_Patch_Return,16(r3)");
  /* FRAME_RL(Patched_FP) = (char*)Patch_Return; */
  /* restore registers from cont, except SP */
asm(" movl 4(ap),sp"); /* SP = new_SP */
asm(" addl2 $8,r1");
asm(" movq (r1)+,r2");
asm(" movq (r1)+,r4");
asm(" movq (r1)+,r6");
asm(" movq (r1)+,r8");
asm(" movq (r1)+,r10");
asm(" movq (r1)+,r12");
asm(" jmp *4(r1)"); /* PC = cont->handler_PC */
