#define  _USEOLDEXEC_ 1
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <dos/dostags.h>
#include <PowerUP/PPCLib/Interface.h>
#include <PowerUP/PPCLib/tasks.h>
#include <PowerUP/PPCLib/ppc.h>
#include <PowerUP/PPCLib/object.h>
#include <PowerUP/PPCDisslib/PPCDiss.h>
#include <PowerUP/pragmas/ppc_pragmas.h>
#include <PowerUP/clib/ppc_protos.h>
#include <PowerUP/pragmas/ppcdiss_pragmas.h>
#include <PowerUP/clib/ppcdiss_protos.h>


/* Prototypes */
ULONG __asm _LibExpunge( register __a6 struct MyLibrary *libbase );
ULONG __asm _LibInit   ( register __a0 APTR seglist,
                         register __d0 struct MyLibrary *libbase );

int  __saveds __asm __UserLibInit   (register __a6 struct MyLibrary *libbase);
void __saveds __asm __UserLibCleanup(register __a6 struct MyLibrary *libbase);

int  __saveds __asm __UserDevInit   (register __d0 long unit,
                                     register __a0 struct IORequest *ior,
                                     register __a6 struct MyLibrary *libbase);
void __saveds __asm __UserDevCleanup(register __a0 struct IORequest *ior,
                                     register __a6 struct MyLibrary *libbase);

int  __saveds __asm __libfpinit     (register __a6 struct MyLibrary *libbase);
void __saveds __asm __libfpterm     (register __a6 struct MyLibrary *libbase);
void __saveds __asm Set_ElfEntry    (register __a6 struct MyLibrary *libbase, 
                                     register __d0 long value);

struct MyLibrary {
        struct             Library ml_Lib;
        ULONG              ml_SegList;
        ULONG              ml_Flags;
        APTR               ml_ExecBase; /* pointer to exec base  */
#ifndef ONE_GLOBAL_SECTION
        long *             ml_relocs;   /* pointer to relocs.    */
        struct MyLibrary * ml_origbase; /* pointer to original library base  */
        long               ml_numjmps;
#endif
        void *             ml_MyObject; /* pointer to ELF Object */
        long               ml_ElfEntry; /* Entrypoint of Elf stub */

};

typedef LONG (*myPFL)();   /* pointer to function returning 32-bit int      */

/* library initialization table, used for AUTOINIT libraries                */
struct InitTable {
        ULONG        *it_DataSize;       /* library data space size         */
        myPFL        *it_FuncTable;      /* table of entry points           */
        APTR         it_DataInit;        /* table of data initializers      */
        myPFL        it_InitFunc;        /* initialization function to run  */
};

#ifndef ONE_GLOBAL_SECTION
long _OSERR;
long __base;
#endif

/* symbols generated by blink */
extern char __far _LibID[];             /* ID string                        */
extern char __far _LibName[];           /* Name string                      */
extern char __far RESLEN;               /* size of init data                */
extern long __far NEWDATAL;             /* size of global data              */
extern long __far NUMJMPS;              /* number of jmp vectors to copy    */
extern myPFL _LibFuncTab[];             /* my function table                */
extern long __far _LibVersion;          /* Version of library               */
extern long __far _LibRevision;         /* Revision of library              */

long _StackPtr;
long __stack;
long __base;
long _ONEXIT;
struct WBStartup *_WBenchMsg;
struct WBStartup *WBenchMsg;


#define MYVERSION ((long)&_LibVersion)
#define MYREVISION ((long)&_LibRevision)
#define DATAWORDS ((long)&NEWDATAL)     /* magic to get right tpye of reloc */ 
#define SIZEJMPTAB ((((long)libbase->ml_origbase->ml_numjmps)+3)&~3)
                                        /* size in bytes of jmp table       */

/* From libent.o, needed to determine where data is loaded by loadseg       */
extern long far _Libmergeddata; 

#define MYLIBRARYSIZE ((sizeof(struct MyLibrary) +3) & ~3)



struct InitTable __far _LibInitTab =  {
        (long *)(&RESLEN+MYLIBRARYSIZE),
        _LibFuncTab,
        NULL,                        /* will initialize my own data */
        _LibInit,
};


int _chkabort(void)
{
    return 0;
}



__asm ULONG _LibInit( register __a0 APTR seglist,
                      register __d0 struct MyLibrary *libbase )
{
#ifdef ONE_GLOBAL_SECTION
    long *reloc;
#endif
    long *sdata;
    char *ddata;
    long nrelocs;
    struct TagItem MyTags[3];
    extern __far long _ElfObject;
    struct Library *PPCLibBase;

    libbase->ml_SegList = (ULONG) seglist;

    /* init. library structure (since I don't do automatic data init.) */
#ifdef DEVICE
    libbase->ml_Lib.lib_Node.ln_Type = NT_DEVICE;
#else
    libbase->ml_Lib.lib_Node.ln_Type = NT_LIBRARY;
#endif
    libbase->ml_Lib.lib_Node.ln_Name =  _LibName;
    libbase->ml_Lib.lib_Flags = LIBF_SUMUSED | LIBF_CHANGED;
    libbase->ml_Lib.lib_Version = MYVERSION;
    libbase->ml_Lib.lib_Revision = MYREVISION;
    libbase->ml_Lib.lib_IdString = (APTR) _LibID;
#ifndef ONE_GLOBAL_SECTION
    libbase->ml_relocs = NULL;
    libbase->ml_origbase = libbase;
    sdata = (long *)_LibInitTab.it_FuncTable;
    libbase->ml_numjmps = (long)&NUMJMPS;
#endif

    /* load the PPC ELF stub */
    MyTags[0].ti_Tag = PPCELFLOADTAG_ELFADDRESS;
    MyTags[0].ti_Data = (long)&_ElfObject;
    MyTags[1].ti_Tag = PPCELFLOADTAG_ELFNAME;
    MyTags[1].ti_Data = (long)_LibName;
    MyTags[2].ti_Tag = TAG_DONE;


    if ((PPCLibBase = OpenLibrary("ppc.library",0)) == NULL)
       return NULL;

    if (libbase->ml_MyObject=PPCLoadObjectTagList(MyTags))
    {
        struct PPCObjectInfo MyInfo;

        MyInfo.Address   =        0;
        MyInfo.Name      =        "__Entry";
        MyTags[0].ti_Tag =        TAG_END;

        PPCGetObjectAttrs(libbase->ml_MyObject,
                          &MyInfo,
                          MyTags);
        libbase->ml_ElfEntry = MyInfo.Address;
        Set_ElfEntry(libbase, MyInfo.Address);
        
    }
    else
    {
       CloseLibrary(PPCLibBase);
       return NULL;
    }
    CloseLibrary(PPCLibBase);


     /* Start of copy of global data after structure */
    ddata = (char *)libbase + MYLIBRARYSIZE; 

    sdata = (long *)&_Libmergeddata; /* where loadseg loaded the data */
    memcpy(ddata, (void *)sdata, DATAWORDS*4);

    /* perform relocs if we want one global section for all programs */
    /* that have this lib open. If we want a global section for each */
    /* open, copy the relocs, and do them on each open call.         */
    sdata = sdata + DATAWORDS;
    nrelocs = *sdata;
#ifdef ONE_GLOBAL_SECTION
    sdata++;
    while (nrelocs > 0)
    {
       reloc = (long *)((long)ddata + *sdata++);
       *reloc += (long)ddata;
       nrelocs--;
    }
    
    
#ifndef DEVICE
    if (__UserLibInit(libbase) != 0)
       return NULL; /* abort if user init failed */
#endif

#else
    if (nrelocs) 
    {
      if ((libbase->ml_relocs = AllocMem((nrelocs * 4) + 4, MEMF_PUBLIC)) == NULL)
        return 0;
      memcpy((void *)libbase->ml_relocs, (void *)sdata, (nrelocs * 4) + 4);
    }
#endif

    return ( (ULONG) libbase );
}

LONG __asm _LibOpen( 
#ifdef DEVICE
                     register __d0 long unit,
                     register __a1 struct IORequest *ior,
#endif
                     register __a6 struct MyLibrary *libbase )
{
#ifndef ONE_GLOBAL_SECTION
    struct MyLibrary *origbase = libbase;
    struct ExecBase *SysBase = *(struct ExecBase **)4;
    char *newlib;
    long *sdata, *ddata, *reloc;
    long nrelocs;
#endif

    /* mark us as having another customer */
    libbase->ml_Lib.lib_OpenCnt++;

    /* clear delayed expunges (standard procedure) */
    libbase->ml_Lib.lib_Flags &= ~LIBF_DELEXP;

#ifndef ONE_GLOBAL_SECTION
    /* Allocate new lib base */
    newlib = AllocMem((long)(MYLIBRARYSIZE + 
                             ((long)&RESLEN) + SIZEJMPTAB), 
                             MEMF_PUBLIC|MEMF_CLEAR);

    if (newlib == NULL) goto error;
        
    /* copy over data */
    memcpy(newlib, (char *)libbase - SIZEJMPTAB, 
           (long)(MYLIBRARYSIZE + DATAWORDS*4 + SIZEJMPTAB));
    
    libbase = (struct MyLibrary *)(newlib+SIZEJMPTAB);
    libbase->ml_relocs = NULL;
    
    /* perform relocs */       
    ddata = (long *)((char *)libbase + MYLIBRARYSIZE); 
    sdata = libbase->ml_origbase->ml_relocs;
    if (sdata)
    {
       nrelocs = *sdata++;
       while (nrelocs > 0)
       {
          reloc = (long *)((long)ddata + *sdata++);
          *reloc += (long)ddata;
          nrelocs--;
       }
    }


    Set_ElfEntry(libbase, libbase->ml_ElfEntry);
        

    /* now we need to flush the cache because we copied the jmp table */
    if (SysBase->LibNode.lib_Version >= 36) 
      CacheClearU();

#ifdef DEVICE
    ior->io_Device = (struct Device *)libbase; /* local copy of libary base */
#endif

    if (__libfpinit(libbase) || 
#ifdef DEVICE
        __UserDevInit(unit, ior, libbase) != 0
#else
        __UserLibInit(libbase) != 0
#endif
       )
    {
       __libfpterm(libbase);
       FreeMem(newlib, (long)(MYLIBRARYSIZE + 
                       ((long)&RESLEN) + SIZEJMPTAB));
error:
       origbase->ml_origbase->ml_Lib.lib_OpenCnt--;
       return NULL; /* abort if user init failed */
    }

#else
#ifdef DEVICE
    if (__UserDevInit(unit, ior, libbase) != 0)
    {
        libbase->ml_Lib.lib_OpenCnt--;
        return NULL;
    }    
#endif
#endif

    return ( (LONG) libbase );
}

ULONG __asm _LibClose( 
#ifdef DEVICE
                       register __a1 struct IORequest *ior,
#endif                       
                       register __a6 struct MyLibrary *libbase )
{
    ULONG retval = 0;
    
#ifndef ONE_GLOBAL_SECTION
    struct MyLibrary *origbase;

    if (libbase != libbase->ml_origbase)
    {
#ifdef DEVICE
       __UserDevCleanup(ior, libbase);
#else
       __UserLibCleanup(libbase);
#endif
       __libfpterm(libbase);
       origbase = libbase->ml_origbase;
       FreeMem((char *)libbase-SIZEJMPTAB, 
               (long)(MYLIBRARYSIZE + ((long)&RESLEN)+SIZEJMPTAB));
       libbase = origbase;
    }
#else
#ifdef DEVICE
    __UserDevCleanup(ior, libbase);
#endif
#endif

    if (( --libbase->ml_Lib.lib_OpenCnt == 0 ) &&
                        ( libbase->ml_Lib.lib_Flags & LIBF_DELEXP ))
    {
        /* no more people have me open,
         * and I have a delayed expunge pending
         */
         retval = _LibExpunge( libbase ); /* return segment list        */
    }

    return (retval);
}

ULONG __asm _LibExpunge( register __a6 struct MyLibrary *libbase )
{
    ULONG seglist = 0;
    LONG  libsize;
    struct Library *PPCLibBase;

#ifndef ONE_GLOBAL_SECTION
    libbase = libbase->ml_origbase;
#endif

    libbase->ml_Lib.lib_Flags |= LIBF_DELEXP;
    if ( libbase->ml_Lib.lib_OpenCnt == 0 )
    {
        /* really expunge: remove libbase and freemem        */
#ifndef ONE_GLOBAL_SECTION
        if (libbase->ml_relocs)
           FreeMem(libbase->ml_relocs, (*libbase->ml_relocs * 4) + 4);
#else
        __UserLibCleanup(libbase);
#endif

        if (PPCLibBase = OpenLibrary("ppc.library",0))
        {
           PPCUnLoadObject(libbase->ml_MyObject);
           CloseLibrary(PPCLibBase);
        }
        
        seglist = libbase->ml_SegList;

        Remove( (struct Node *) libbase);

        libsize = libbase->ml_Lib.lib_NegSize + libbase->ml_Lib.lib_PosSize;
        FreeMem( (char *) libbase - libbase->ml_Lib.lib_NegSize,(LONG) libsize );
    }

    /* return NULL or real seglist                                */
    return ( (ULONG) seglist );
}


