#include <exec/types.h>
#include <proto/exec.h>
#include <exec/execbase.h>
#include <emul/emulinterface.h>
#include <emul/emulregs.h>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "a2ixlibrary.h"

#ifdef DEBUG
void dprintf(const char *,...);
#define DB(x) x
#else
#define DB(x)
#endif

struct Library * BASE = 0;

/* externs from crt0.c */
extern void *ixemulbase;
extern int errno;
extern char *_ctype_;
extern void *__sF;
extern void *__stk_limit;

void CLOSEINST(void)
{
  struct EmulCaos caos;
  caos.caos_Un.Offset = -30;
  caos.reg_a6 = (LONG)BASE;
  MyEmulHandle->EmulCallOS(&caos);
}

int RELOCINST(char * const *bases, const unsigned long *relocs, char *old_data, char *new_data)
{
  struct EmulCaos caos;
  caos.reg_d0 = (LONG)bases;
  caos.reg_d1 = (LONG)relocs;
  caos.reg_d2 = (LONG)old_data;
  caos.reg_d3 = (LONG)new_data;
  caos.reg_a6 = (LONG)BASE;
  caos.caos_Un.Offset = -36;
  return MyEmulHandle->EmulCallOS(&caos);
}

void SETVARSINST(int argc, void *ixbase,
		 int *errnoptr, char *ctype, void **sf,
		 void **stk_limit)
{
  struct EmulCaos caos;
  caos.reg_d0 = (long)argc;
  caos.reg_d1 = (long)ixbase;
  caos.reg_d2 = (long)errnoptr;
  caos.reg_d3 = (long)ctype;
  caos.reg_d4 = (long)sf;
  caos.reg_d5 = (long)stk_limit;
  caos.reg_a6 = (LONG)BASE;
  caos.caos_Un.Offset = -42;
  MyEmulHandle->EmulCallOS(&caos);
}

#define STRING(a) a, sizeof (a) - 1

extern const unsigned long * const RELOCTABLE __attribute__((section(".rodata")));
extern char * const __section_table[];

static void
constructor()
{
  DB(dprintf("constructor: opening " SNAME "\n");)

  if (!(BASE = OpenLibrary ("lib" SNAME ".ixlibrary", VERSION)))
    {
      write(2, STRING("Can't open lib" SNAME ".ixlibrary!\n"));
      exit(100);
    }

  DB(dprintf("sections_table = %lx, reloc table = %lx\n", __section_table, RELOCTABLE);)

  if (!(RELOCINST(__section_table, RELOCTABLE, NULL, NULL)))
    {
      CLOSEINST();
      CloseLibrary(BASE);
      BASE = 0;
      write(2, STRING("Some externals are missing in lib" SNAME ".ixlibrary!\n"));
      exit(100);
    }

  DB(dprintf("setvars\n");)
  SETVARSINST(5, ixemulbase, &errno, _ctype_, __sF, &__stk_limit);
#ifdef CONSTRUCTOR
  {
    CONSTRUCTOR;
  }
#endif
  DB(dprintf("done\n");)
}

static void
destructor()
{
  DB(dprintf("destructor\n");)
  if (BASE)
    {
#ifdef DESTRUCTOR
      {
        DESTRUCTOR;
      }
#endif
      CLOSEINST();
      CloseLibrary(BASE);
      BASE = 0;
    }
}

const unsigned long * const RELOCTABLE __attribute__((section(".rodata"))) = 0;

asm(".section \".ctors\",\"a\"");
asm(".long constructor");
/*asm(".long 78");*/

asm(".section \".dtors\",\"a\"");
asm(".long destructor");
/*asm(".long 78");*/

