#include <stdio.h>
#include "types.h"
#include "exceptions.h"
#define ENTRY(x) asm(" .text"); asm(" .globl _" #x ); asm("_" #x ":");
#define RET asm(" rts")

/* offset of fields in Continuation */
#define save_regs_S "8"
#define save_FP_S "48"
#define save_SP_S "52"
#define PC_S "56"
#define prev_S "60"
#define kind_S "64"

ENTRY(GetStackPointer)
asm(" movl sp,d0");
asm(" addql #4,d0");
RET;

ENTRY(SetStackPointer)
asm(" movl sp@+,a1");	/* get return address */
asm(" movl sp@+,a0");	/* get argument == new stack pointer */
asm(" movl sp,d0");	/* return old stack pointer */
asm(" lea a0@(-4),sp");	/* compensate for addql after return */
asm(" jmp a1@");	/* return */

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

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[8] = {0};
Continuation NullHandler[1] =
    {NULL, 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,0x7FFFFFFF,}, DummyFrame};
struct { long RL; long FP; } Patched = { 0, (long)DummyFrame };
#define Patched_RL "_Patched+0"
#define Patched_FP "_Patched+4"

ENTRY(Patch_Return)
asm(" moveml #0x0201,_TmpSaveRegs"); /* save d0,a1 */

/* Move patch to our caller */
asm(" movl " Patched_RL ",a0"); /* real_RL = Patched_RL */
asm(" movl " Patched_FP ",a1"); /* FRAME_RL(Patched_FP) = real_RL */
asm(" movl a0,a1@(4)");
asm(" movl a1@,a1"); /* Patched_FP = FRAME_OLD_FP(Patched_FP) */
asm(" movl a1," Patched_FP "");
asm(" movl a1@(4)," Patched_RL ""); /* Patched_RL = FRAME_RL(Patched_FP) */
 /* FRAME_RL(Patched_FP) = (char*)Patch_Return */
asm(" movl #" UNDERSCORE "Patch_Return,a1@(4)");

asm(" movl __Handler_,a1"); /* SP = MIN(SP,_Handler_->save_SP) */
asm(" movl a1@(" save_SP_S "),a1");
asm(" cmpl sp,a1"); /* if (a0>sp) return */
asm(" jhi Ljump");
asm(" movl a1,sp");
/* check for argument-popping after return, and compensate for it */
asm(" movw a0@,d0"); /* test *(short*)RL */
asm(" cmpw #0x4FEF,d0");/* lea W(sp),sp */
asm(" jne Ltest1");
asm(" subw a0@(2),sp");
asm(" jra Ljump");
asm("Ltest1:");
asm(" cmpw #0xDEFC,d0"); /* addaw #W,sp */
asm(" jne Ltest2");
asm(" subw a0@(2),sp");
asm(" jra Ljump");
asm("Ltest2:");
asm(" cmpw #0xDFFC,d0"); /* addal #WW,sp */
asm(" jne Ltest3");
asm(" subl a0@(2),sp");
asm(" jra Ljump");
asm("Ltest3:");
asm(" cmpw #0x508F,d0"); /* addql #8,sp */
asm(" jne Ltest4");
asm(" subql #8,sp");
asm(" jra Ljump");
asm("Ltest4:");
asm(" cmpw #0x504F,d0"); /* addqw #8,sp */
asm(" jne Ltest5");
asm(" subql #8,sp");
asm(" jra Ljump");
asm("Ltest5:");
asm(" andw #0xF1FF,d0"); /* addql #N,sp */
asm(" cmpw #0x508F,d0");
asm(" jne Ljump");
asm(" movw a0@,d0");
asm(" lsrw #8,d0");
asm(" lsrw #1,d0");
asm(" andw #7,d0");
asm(" subw d0,sp");
asm("Ljump:");
asm(" moveml _TmpSaveRegs,#0x0201");
asm(" jmp a0@");

ENTRY(SaveOrHandler)
asm(" movl sp@(8),a0"); /* cont */
asm(" movw #2,a0@(" kind_S ")"); /* cont->kind = 2 */
asm(" jra BothCreate");

ENTRY(CreateContinuation);
asm(" movl sp@(8),a0"); /* cont */
asm(" movw #1,a0@(" kind_S ")"); /* cont->kind = 1 */
asm("BothCreate:");
asm(" movl " Patched_FP ",a1"); /* FRAME_RL(Patched_FP) = Patched_RL */
asm(" movl " Patched_RL ",a1@(4)");
asm(" movl a6," Patched_FP ""); /* Patched_FP = BACK_SAVE_FP(_Handler_) */
asm(" movl a6@(4)," Patched_RL ""); /* Patched_RL = FRAME_RL(Patched_FP) */
asm(" movl #_Patch_Return,a6@(4)");/*FRAME_RL(Patched_FP)=(char*)Patch_Return*/
asm("SaveCommonHandler:");
asm(" movl sp@(4),a0@(" PC_S ")"); /* handler->PC = label */
asm(" movl _UndoTrail,a0@(4)"); /* handler->undo_trail = UndoTrail */
asm(" movl #__Handler_,a1");
asm(" movl a1,a0@(" prev_S ")"); /* handler->prev = &_Handler_ */
asm(" movl a1@,d0"); /* save next = _Handler_ */
asm(" movl a0,a1@"); /* _Handler_ = handler */
asm(" movl d0,a1");
asm(" movl a0,a1@(" prev_S ")"); /* next->prev = &handler */
asm(" movl a1,a0@"); /* handler->next = next */
asm(" lea sp@(12),a1"); /* adjust SP for RL,label,cont */
asm(" movl a1,a0@(" save_SP_S ")"); /* handler->save_SP = adjusted(SP) */
asm(" moveml #0x7CFC,a0@(" save_regs_S ")"); /* save regs d2-d7,a2-a6 */
RET;

ENTRY(SaveIfHandler)
asm(" movl sp@(8),a0");
asm(" movw #1,a0@(" kind_S ")"); /* cont->kind = 1 */
asm(" jra SaveCommonHandler");

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

#if 0
_InvokeContinuation(void *new_sp, Continuation *cont)
{
  FRAME_RL(Patched.FP) = Patched.FL;
  Patched.FP = CONT_SAVE_FP(cont);
  Patched.RL = FRAME_RL(Patched.FP);
  FRAME_RL(Patched.FP) = (void*)&Patched_Return;
  asm(" movl ??@(" PC_S "),a0" : : ); /* PC = cont->handler_PC */
  asm(" moveml %0@(" save_regs_S "),#0x7CFC" : : "a" (cont));
  asm(" movl %0,sp" : : "?" (new_sp)); /* SP = new_SP */
  asm(" jmp a0@");
}
#else
ENTRY(_InvokeContinuation)
asm(" movl sp@(8),a0"); /* cont */
asm(" movl " Patched_FP ",a1");
asm(" movl " Patched_RL ",a1@(4)"); /* FRAME_RL(Patched_FP) = Patched_RL */
asm(" movl a0@(" save_FP_S "),a1");
asm(" movl a1," Patched_FP); /* Patched_FP = CONT_SAVE_FP(cont) */
asm(" movl a1@(4)," Patched_RL ""); /* Patched_RL = FRAME_RL(Patched_FP) */
asm(" movl #" UNDERSCORE "Patch_Return,a1@(4)");
  /* FRAME_RL(Patched_FP) = (char*)Patch_Return; */
  /* restore registers from cont, except SP */
asm(" moveml a0@(" save_regs_S "),#0x7CFC"); /* restore registers, including FP */
asm(" movl sp@(4),sp"); /* SP = new_SP */
asm(" movl a0@(" PC_S "),a0"); /* PC = cont->handler_PC */
asm(" jmp a0@");
#endif

/* MAGIC C++ STUFF */

/* See comment just before RecordType::new_subclass() (in types.C).
 * The stub has this format:
 *	movl #RECORD_CLASS,d0
 *	rts
 * for a total of 8 bytes.
 */

void MakeReturnPointerStub(char *stubAddress, void *ptr)
{
    *(unsigned short*)stubAddress = 0x203C; /* movl #...,d0 */
    *(void **)(stubAddress + 2) = ptr;
    *(unsigned short*)(stubAddress + 6) = 0x4e75; /* rts */
}

int ReturnPointerStubSize = 8;
int isA_Offset = 12; /* assumes isA is first method in VTable */
#if 0
int RecordVTableSize = -1;
extern struct Record EmptyRecord;
void FindRecordVTableSize()
{
    void *RecordVTable = *(void**)&EmptyRecord;
    RecordVTableSize = (((long*)RecordVTable)[1] + 1) * 8;
}
#endif

int GetVTableSize(void *vTable)
{
    return (((int*)vTable)[1] + 1) * 8;
}
