/*****************************************************************
 ** MuFastRom                                                   **
 **                                                             **
 ** A MMU-Library compatible ROM remapper                       **
 ** Version 40.8 28.11.1999 © THOR-Software, by Thomas Richter  **
 *****************************************************************/

/// Includes
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <dos/dos.h>
#include <mmu/mmubase.h>
#include <mmu/context.h>
#include <mmu/mmutags.h>
#include <workbench/startup.h>

#include <proto/exec.h>
#include <proto/mmu.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <string.h>
///
/// Defines
#define STRINGDATE "28.11.99"
#define STRINGVERSION "40.8"
#define ROMEND 0x01000000L
#define MAGIC_ROMEND 0x14L
#define CACHEFLAGS (MAPP_CACHEINHIBIT|MAPP_COPYBACK|MAPP_NONSERIALIZED|MAPP_IMPRECISE)
#define TEMPLATE "ON=FASTROM/S,OFF=NOFASTROM/S,HEAD/S,PROTECT/S,NOPROTECT/S"

#define OPT_ON  0
#define OPT_OFF 1
#define OPT_HEAD 2
#define OPT_PROTECT 3
#define OPT_NOPROTECT 4
#define OPT_WINDOW 5
#define OPT_COUNT 6
///
/// Statics
struct MMUBase *MMUBase;
struct DosLibrary *DOSBase;
struct ExecBase *SysBase;
struct Library *IconBase;
///
/// Protos
int __asm __saveds main(void);
int BuildFastRom(LONG head,LONG protect,LONG noprotect);
int RemoveFastRom(void);
BOOL MapToRom(struct MMUContext *ctx,ULONG source,ULONG mem,ULONG size);
BOOL RemapToRam(struct MMUContext *ctx,ULONG source,ULONG pmem,ULONG mem,ULONG size,LONG protect,LONG noprotect,ULONG cacheflags);
struct RDArgs *ReadTTArgs(struct WBStartup *msg,LONG args[],struct RDArgs **tmp);
///
/// Strukturen
struct FastRomPort {
        struct MsgPort          frp_Port;
        UBYTE                   frp_NoProtect;
        UBYTE                   frp_Protect;
        void                   *frp_Logical;            /* Logical address of the remapped ROM */
        void                   *frp_Physical;           /* Physical location of the RAM used for remapping */
        void                   *frp_RomStart;
        ULONG                   frp_Size;               /* size of the allocated memory */
        struct Library         *frp_Base;               /* Keep the library open */
        ULONG                   frp_CacheFlags;         /* keeps the cache mode of the remapped ROM */
        char                    frp_Name[32];           /* keeps the name of the port */
};
///

char version[]="$VER: MuFastRom " STRINGVERSION " (" STRINGDATE ") © THOR";

/// main
int __asm __saveds main(void)
{
LONG args[OPT_COUNT];
struct RDArgs *rd,*myrd;
struct Process *proc;
int rc=20;
LONG err;
struct WBStartup *msg;
BPTR oldout;
struct MsgPort *oldconsole;



        SysBase=*((struct ExecBase **)(4L));

        memset(args,0,sizeof(LONG)*OPT_COUNT);
        /* Wait for the workbench startup, if any */
        proc=(struct Process *)FindTask(NULL);

        if (!(proc->pr_CLI)) {
                WaitPort(&(proc->pr_MsgPort));
                msg=(struct WBStartup *)GetMsg(&(proc->pr_MsgPort));
        } else  msg=NULL;

        if (DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37L)) {
                if (MMUBase=(struct MMUBase *)OpenLibrary("mmu.library",41L)) {

                        err=ERROR_REQUIRED_ARG_MISSING;

                        myrd=NULL;      /* reset the temporary ReadArgs */
                        oldout=NULL;
                        oldconsole=NULL;
                        if (msg) {
                                oldout=SelectOutput(NULL);
                                oldconsole=SetConsoleTask(NULL);
                                rd=ReadTTArgs(msg,args,&myrd);
                        } else  rd=ReadArgs(TEMPLATE,args,NULL);

                        if (rd) {
                                if (!GetMMUType()) {
                                        Printf("MuFastRom requires a working MMU.\n");
                                        err=10;
                                } else {
                                        /* Argument parser worked, call main routine */
                                        if ((args[OPT_ON]) || (!args[OPT_OFF])) /* ON is the default if OFF is not given */
                                                err=BuildFastRom(args[OPT_HEAD],args[OPT_PROTECT],args[OPT_NOPROTECT]);

                                        if (args[OPT_OFF])
                                                err=RemoveFastRom();
                                }

                                FreeArgs(rd);
                                if (myrd) FreeDosObject(DOS_RDARGS,myrd);
                                if (msg)  Close(SelectOutput(NULL));
                        } else  err=IoErr();

                        if (msg) {
                                SelectOutput(oldout);
                                SetConsoleTask(oldconsole);
                        }

                        if (err<64) {
                                rc=err;
                                err=0;
                        } else {
                                if (!msg) PrintFault(err,"MuFastRom failed");
                                rc=10;
                        }
                        SetIoErr(err);

                        CloseLibrary((struct Library *)MMUBase);
                } else if (!msg) PrintFault(ERROR_OBJECT_NOT_FOUND,"MuFastRom requires the mmu.library V41 or better");
                CloseLibrary((struct Library *)DOSBase);
        }

        if (msg) {
                Forbid();
                ReplyMsg((struct Message *)msg);
        }

        return rc;
}
///
/// ReadTTArgs
struct RDArgs *ReadTTArgs(struct WBStartup *msg,LONG args[],struct RDArgs **tmp)
{
struct WBArg *wbarg;
struct DiskObject *dop;
char **tt;                      /* ToolTypes array */
char *wbstr;                    /* Our self-made workbench argument string */
char *here;
BPTR oldlock;
ULONG len;
struct RDArgs *rd=NULL,*myrd=NULL;
LONG err=0;
BPTR newout;

        if (IconBase=OpenLibrary("icon.library",37L)) {
                if (wbarg=msg->sm_ArgList) {
                        /* use a project icon if there is one... */
                        if (msg->sm_NumArgs > 1) wbarg++;

                        /* go into the directory */
                        oldlock=CurrentDir(wbarg->wa_Lock);

                        if (dop=GetDiskObject(wbarg->wa_Name)) {
                                if (tt=dop->do_ToolTypes) {
                                        /* Read a special tool type for the output window */

                                        /* Calc the size of the argument string */

                                        len = 3;        /* reserve space for SPC,LF,NUL */
                                        while (*tt) {
                                                len += strlen(*tt)+1;   /* string, plus space */
                                                tt++;
                                        }

                                        if (wbstr=AllocVec(len,MEMF_PUBLIC)) {
                                                /* Now copy the arguments into this string, one by one
                                                   and check whether the argument string is still valid. */

                                                tt=dop->do_ToolTypes;
                                                here=wbstr;
                                                do{
                                                        *here='\0';                     /* terminate string */
                                                        /* Check whether this tool type is
                                                           commented out. Just ignore it in this case */
                                                        if (*tt) {
                                                                if (**tt=='(' || **tt==';')
                                                                        continue;

                                                                strcpy(here,*tt);      /* Add TT string */
                                                        }
                                                        len=strlen(here);
                                                        here[len]='\n';
                                                        here[len+1]='\0';               /* terminate string */

                                                        /* Now try to ReadArg' this string */

                                                        /* release old arguments left over from last loop */
                                                        if (rd) FreeArgs(rd);
                                                        if (myrd) FreeDosObject(DOS_RDARGS,myrd);
                                                        rd=NULL;
                                                        memset(args,0,sizeof(LONG)*OPT_COUNT);

                                                        if (myrd=AllocDosObject(DOS_RDARGS,NULL)) {
                                                                /* Allocate and setup the ReadArgs source */
                                                                myrd->RDA_Source.CS_Buffer=wbstr;
                                                                myrd->RDA_Source.CS_Length=strlen(wbstr);

                                                                if (rd=ReadArgs(TEMPLATE ",WINDOW/K",args,myrd)) {
                                                                        /* Is this still valid? */
                                                                        here[len]=' ';
                                                                        here+=len+1;
                                                                        /* if so, accept this argument and go on */
                                                                } else {
                                                                        err=IoErr();
                                                                        if (err==ERROR_NO_FREE_STORE) break;
                                                                        else    err=0;  /* Ignore unknown or invalid arguments silently */
                                                                }
                                                        } else {
                                                                err=ERROR_NO_FREE_STORE;
                                                                break;
                                                        }
                                                }while(*tt++);

                                                FreeVec(wbstr);
                                        } else err=ERROR_NO_FREE_STORE;
                                } else err=ERROR_REQUIRED_ARG_MISSING; /* Huh, how should this happen ? */
                                FreeDiskObject(dop);
                        } else err=IoErr();
                        CurrentDir(oldlock);
                } else err=ERROR_REQUIRED_ARG_MISSING; /* This should not happen either */
                CloseLibrary(IconBase);
        } else err=ERROR_OBJECT_NOT_FOUND;    /* This should not happen */

        /* Open an output stream */

        if (err==0) {
                if (newout=Open((args[OPT_WINDOW])?((char *)args[OPT_WINDOW]):("NIL:"),MODE_NEWFILE)) {
                        SelectOutput(newout);
                        /* Hack in the output console. Well, well... */
                        SetConsoleTask(((struct FileHandle *)(BADDR(newout)))->fh_Type);
                } else err=IoErr();
        }

        if (err) {
                if (rd)   FreeArgs(rd);
                if (myrd) FreeDosObject(DOS_RDARGS,myrd);
                SetIoErr(err);
                rd=NULL;
                myrd=NULL;
        }

        *tmp=myrd;
        return rd;
}
///
/// BuildFastRom
int BuildFastRom(LONG head,LONG protect,LONG noprotect)
{
void *mem,*source,*pmem;
ULONG size,psize;
ULONG props;
struct FastRomPort *port;
struct MMUContext *ctx,*sctx;   /* default context, supervisorcontext */
struct MinList *ctxl,*sctxl;
int err=ERROR_NO_FREE_STORE;


        Forbid();                               /* Shut up PatchWork */

        /* If this is already installed, warn and return. */

        if (FindPort("« MuFastRom »")) {
                Permit();
                Printf("MuFastRom already running.\n");
                return 5;
        }

        Permit();

        ctx=DefaultContext();   /* get the default context */
        sctx=SuperContext(ctx); /* get the supervisor context for this one */

        /* Read the size of the ROM */
        size=*((ULONG *)(ROMEND-MAGIC_ROMEND));

        /* The start of the ROM */
        source=((UBYTE *)ROMEND)-size;

        props=GetProperties(sctx,(ULONG)source,TAG_DONE);
        props|=GetProperties(ctx,(ULONG)source,TAG_DONE);

        if (props & MAPP_REMAPPED) {
                Printf("The ROM is already remapped to RAM.\n");
                return 5;
        }


        /* now allocate the memory for the ROM image. To keep the
           MMU tables as effective as possible, we try to align
           this */

        /* We request FAST here, explicitly. Taking chip memory for the
           ROM image doesn't make sense, this isn't fast either... */

        mem=AllocAligned(size,(head)?(MEMF_PUBLIC|MEMF_FAST):(MEMF_REVERSE|MEMF_PUBLIC|MEMF_FAST),RemapSize(ctx));
        if (mem) {

                pmem=mem;
                psize=size;
                PhysicalLocation(ctx,&pmem,&psize);
                if (psize!=size) {
                        Printf("The destination memory is fragmentated, unsupported.\n");
                } else {

                 /* Read the cache flags */
                 props=GetProperties(ctx,(ULONG)mem,TAG_DONE) & CACHEFLAGS;

                 /* Copy the ROM */
                 CopyMemQuick(source,mem,size);

                 /* Write it out, really */
                 CacheClearU();

                 port=AllocMem(sizeof(struct FastRomPort),MEMF_PUBLIC|MEMF_CLEAR);
                 if (port) {
                        LockContextList();
                        LockMMUContext(ctx);
                        LockMMUContext(sctx);
                        if (ctxl=GetMapping(ctx)) {
                         if (sctxl=GetMapping(ctx)) {
                          if (RemapToRam(ctx,(ULONG)source,(ULONG)pmem,(ULONG)mem,size,protect,noprotect,props)) {
                           if (RemapToRam(sctx,(ULONG)source,(ULONG)pmem,(ULONG)mem,size,protect,noprotect,props)) {
                            if (RebuildTrees(ctx,sctx,NULL)) {

                                        /* we're done now */
                                        port->frp_Port.mp_Node.ln_Type=NT_MSGPORT;
                                        port->frp_Port.mp_Node.ln_Name=port->frp_Name;
                                        strcpy(port->frp_Name,"« MuFastRom »");
                                        port->frp_Port.mp_Flags=PA_IGNORE;
                                        NewList(&(port->frp_Port.mp_MsgList));

                                        port->frp_RomStart=source;
                                        port->frp_Physical=pmem;
                                        port->frp_Logical=mem;
                                        port->frp_Size=size;
                                        port->frp_NoProtect=noprotect;
                                        port->frp_Protect=protect;
                                        port->frp_CacheFlags=props;
                                        port->frp_Base=OpenLibrary("mmu.library",0L);
                                        AddPort(&(port->frp_Port));

                                        /* Do not release the memory or the port */
                                        mem=NULL;
                                        port=NULL;

                                        err=0;
                            }
                           }
                          }

                          if (err) {
                                  SetPropertyList(ctx,ctxl);
                                  SetPropertyList(sctx,sctxl);
                          }

                          ReleaseMapping(sctx,sctxl);
                         }
                         ReleaseMapping(ctx,ctxl);
                        }

                        UnlockMMUContext(sctx);
                        UnlockMMUContext(ctx);
                        UnlockContextList();
                        if (port) FreeMem(port,sizeof(struct FastRomPort));
                 }
                }
                if (mem) FreeMem(mem,size);
        }

        return err;
}
///
/// RemoveFastRom
int RemoveFastRom(void)
{
struct MMUContext *ctx,*sctx;
struct MinList *ctxl,*sctxl;
struct FastRomPort *port;
ULONG size,props;
void *mem,*pmem,*source;
int err=ERROR_NO_FREE_STORE;


        Forbid();                               /* Shut up PatchWork */

        /* If this is not installed, warn and return. */

        port=(struct FastRomPort *)FindPort("« MuFastRom »");

        if (port==NULL) {
                Permit();
                Printf("MuFastRom not installed.\n");
                return 5;
        }
        /* Remove it, to ensure nobody else tries to remove it */
        RemPort(&(port->frp_Port));
        Permit();

        ctx=DefaultContext();   /* get the default context */
        sctx=SuperContext(ctx); /* get the supervisor context for this one */

        size=port->frp_Size;
        pmem=port->frp_Physical;
        mem=port->frp_Logical;
        source=port->frp_RomStart;
        props=port->frp_CacheFlags;

        /* Remove the properties right here... */

        LockContextList();
        LockMMUContext(ctx);
        LockMMUContext(sctx);

        if (ctxl=GetMapping(ctx)) {
         if (sctxl=GetMapping(sctx)) {
          if (MapToRom(ctx,(ULONG)source,(ULONG)mem,size)) {
           if (MapToRom(sctx,(ULONG)source,(ULONG)mem,size)) {
            if (RebuildTrees(ctx,sctx,NULL)) {

                        FreeMem(mem,size);

                        /* Close the mmu.lib so it can be flushed... */

                        if (port->frp_Base)
                                CloseLibrary(port->frp_Base);

                        FreeMem(port,sizeof(struct FastRomPort));
                        err=0;
            }
           }
          }

          if (err) {
                  SetPropertyList(ctx,ctxl);
                  SetPropertyList(sctx,sctxl);
          }

          ReleaseMapping(sctx,sctxl);
         }
         ReleaseMapping(ctx,ctxl);
        }

        UnlockMMUContext(sctx);
        UnlockMMUContext(ctx);
        UnlockContextList();

        if (err) {
                /* Better luck next time... */
                AddPort(&(port->frp_Port));
                Printf("Can't remove the FastRom node, sorry.\n");
        }

        return err;
}
///
/// RemapToRam
BOOL RemapToRam(struct MMUContext *ctx,ULONG source,ULONG pmem,ULONG mem,ULONG size,LONG protect,LONG noprotect,ULONG cacheflags)
{
ULONG oldprops;
ULONG rommask,mirrormask;

        /* get the properties of the ROM */
        oldprops=GetProperties(ctx,source,TAG_DONE);

        oldprops |= MAPP_REMAPPED;
        rommask=MAPP_REMAPPED|MAPP_COPYBACK;   /* Define these bits for the ROM image */
        mirrormask=0;                          /* ...and these for the RAM mirror */

        /* Protection enabled? */
        if (protect) {
                /* active or passive protection? */
                if (oldprops & MAPP_WRITEPROTECTED) {
                        /* here aggressive, enable protection
                           and allow MuForce to repair access faults */
                        rommask |= MAPP_WRITEPROTECTED|MAPP_REPAIRABLE;
                        mirrormask |= MAPP_WRITEPROTECTED|MAPP_REPAIRABLE;
                } else {
                        rommask |= MAPP_ROM;
                        mirrormask |= MAPP_ROM;
                        oldprops |= MAPP_ROM;
                }
        } else if (noprotect) {
                /* disable all kinds of protection */
                oldprops &= ~(MAPP_ROM|MAPP_WRITEPROTECTED);
                rommask |= MAPP_ROM | MAPP_WRITEPROTECTED;
                mirrormask |= MAPP_ROM | MAPP_WRITEPROTECTED;
        }

        /* Enter caching mode */
        oldprops &= ~CACHEFLAGS;
        oldprops |= cacheflags;
        rommask |= CACHEFLAGS;

        if (SetProperties(ctx,oldprops,rommask,source,size,MAPTAG_DESTINATION,pmem,TAG_DONE)) {
         if (SetProperties(ctx,oldprops,mirrormask,mem,size,TAG_DONE)) {
           return TRUE;
         }
        }

        return FALSE;
}
///
/// MapToRom
BOOL MapToRom(struct MMUContext *ctx,ULONG source,ULONG mem,ULONG size)
{

        if (SetProperties(ctx,0,MAPP_REMAPPED|CACHEFLAGS,source,size,TAG_DONE)) {
         if (SetProperties(ctx,0,MAPP_ROM|MAPP_WRITEPROTECTED|MAPP_REPAIRABLE,mem,size,TAG_DONE)) {
           return TRUE;
         }
        }

        return FALSE;
}
///






