/*****************************************************************
 ** MuSetCacheMode                                              **
 **                                                             **
 ** Reprogram MMU tables selectively.                           **
 ** Release 40.6, © 1999 THOR-Software inc.                     **
 ** 9.8.1999 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 <thor/conversions.h>

#include <proto/exec.h>
#include <proto/mmu.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <string.h>
///
/// Defines
#define STRINGDATE "8.8.99"
#define STRINGVERSION "40.6"
#define TEMPLATE "ADDRESS=FROM/A,SIZE/A,COPYBACK/S,WRITETHROUGH/S,NOCACHESERIALIZED=CACHEINHIBIT/S,NONSERIAL/S,NOCACHE=IMPRECISE/S,VALID/S,INVALID/S,BLANK/S,IO=IOSPACE/S,NOIO=NOIOSPACE/S,ROM/S,NOROM/S,WRITEPROTECTED/S,NOTWRITEPROTECTED/S,USERONLY/S,SUPERVISORONLY/S,VERBOSE/S"
#define CACHEFLAGS (MAPP_CACHEINHIBIT|MAPP_NONSERIALIZED|MAPP_IMPRECISE|MAPP_COPYBACK)
#define ROMFLAGS (MAPP_ROM|MAPP_WRITEPROTECTED)

#define OPT_ADDRESS 0
#define OPT_SIZE 1
#define OPT_COPYBACK 2
#define OPT_WRITETHROUGH 3
#define OPT_NOCACHE 4
#define OPT_NOCACHESERIALIZED 5
#define OPT_NOCACHEIMPRECISE 6
#define OPT_VALID 7
#define OPT_INVALID 8
#define OPT_BLANK 9
#define OPT_IO 10
#define OPT_NOIO 11
#define OPT_ROM 12
#define OPT_NOROM 13
#define OPT_WRITEPROTECTED 14
#define OPT_NOTWRITEPROTECTED 15
#define OPT_USERONLY 16
#define OPT_SUPERVISORONLY 17
#define OPT_VERBOSE 18
#define OPT_WINDOW 19
#define OPT_COUNT 20

#define WHICH_ALL       3
#define WHICH_USER      1
#define WHICH_SUPER     2
///
/// Statics
struct MMUBase *MMUBase;
struct DosLibrary *DOSBase;
struct ExecBase *SysBase;
struct Library *IconBase;
///
/// Protos
int __asm __saveds main(void);
int SetCacheMode(ULONG from,ULONG size,ULONG flags,ULONG mask,ULONG which);
struct RDArgs *ReadTTArgs(struct WBStartup *msg,LONG args[],struct RDArgs **tmp);
int SetCache(struct MMUContext *ctx,ULONG from,ULONG size,ULONG flags,ULONG mask);
///

char version[]="$VER: MuSetCacheMode " 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;
LONG flags,which;
char *whichmsg;
int from=0,size=0;
ULONG mask;
char flagsmsg[256];


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

        memset(args,0,sizeof(LONG)*OPT_COUNT);
        flagsmsg[0]='\0';                       /* Clear this string */

        /* 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",0L)) {

                        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("MuSetCacheMode requires a working MMU.\n");
                                        err=10;
                                } else {

                                        err=0;

                                        if (args[OPT_ADDRESS]==NULL || args[OPT_SIZE]==NULL)
                                                err=ERROR_REQUIRED_ARG_MISSING;

                                        if (err==0) {

                                                if (!StrToL((char *)(args[OPT_ADDRESS]),NULL,&from,16))
                                                        err=ERROR_BAD_NUMBER;

                                                if (!StrToL((char *)(args[OPT_SIZE]),NULL,&size,16)) {
                                                        err=ERROR_BAD_NUMBER;
                                                } else if (size==0) err=ERROR_BAD_NUMBER;


                                        }       
                                        if (err==0) {

                                                flags=0;
                                                mask=0;

                                                if (args[OPT_VALID]) {
                                                        mask |= MAPP_INVALID | MAPP_BLANK | MAPP_REPAIRABLE;
                                                        strcat(flagsmsg,"valid ");
                                                }
                                                if (args[OPT_IO]) {
                                                        flags &= ~CACHEFLAGS;
                                                        flags |= MAPP_IO|MAPP_CACHEINHIBIT;
                                                        mask |= MAPP_IO|CACHEFLAGS;
                                                        strcat(flagsmsg,"IO space ");
                                                }
                                                if (args[OPT_NOIO]) {
                                                        mask |= MAPP_IO;
                                                        flags &= ~MAPP_IO;
                                                        strcat(flagsmsg,"memory space ");
                                                }
                                                if (args[OPT_COPYBACK]) {
                                                        flags &= ~CACHEFLAGS;
                                                        flags |= MAPP_COPYBACK;
                                                        mask |= CACHEFLAGS;
                                                        strcat(flagsmsg,"copyback ");
                                                }
                                                if (args[OPT_WRITETHROUGH]) {
                                                        flags &= ~CACHEFLAGS;    /* writethrough is the default */
                                                        mask |= CACHEFLAGS;
                                                        strcat(flagsmsg,"writethrough ");
                                                }
                                                if (args[OPT_NOCACHE]) {
                                                        flags &= ~CACHEFLAGS;
                                                        flags |= MAPP_CACHEINHIBIT;
                                                        mask |= CACHEFLAGS;
                                                        strcat(flagsmsg,"cacheinhibit ");
                                                }
                                                if (args[OPT_NOCACHESERIALIZED]) {
                                                        flags &= ~CACHEFLAGS;
                                                        flags |= MAPP_CACHEINHIBIT | MAPP_NONSERIALIZED;
                                                        mask |= CACHEFLAGS;
                                                        strcat(flagsmsg,"cacheinhibit non-serialized ");
                                                }
                                                if (args[OPT_NOCACHEIMPRECISE]) {
                                                        flags &= ~CACHEFLAGS;
                                                        flags |= MAPP_CACHEINHIBIT | MAPP_IMPRECISE;
                                                        mask |= CACHEFLAGS;
                                                        strcat(flagsmsg,"cacheinhibit imprecise ");
                                                }
                                                if (args[OPT_ROM]) {
                                                        flags &= ~ROMFLAGS;
                                                        flags |= MAPP_ROM;
                                                        mask |= ROMFLAGS;
                                                        strcat(flagsmsg,"ROM ");
                                                }
                                                if (args[OPT_NOROM]) {
                                                        mask |= MAPP_ROM;
                                                        flags &= ~MAPP_ROM;
                                                        strcat(flagsmsg,"no ROM ");
                                                }
                                                if (args[OPT_WRITEPROTECTED]) {
                                                        flags &= ~ROMFLAGS;
                                                        flags |= MAPP_WRITEPROTECTED;
                                                        mask |= ROMFLAGS;
                                                        strcat(flagsmsg,"read only ");
                                                }
                                                if (args[OPT_NOTWRITEPROTECTED]) {
                                                        mask |= MAPP_WRITEPROTECTED;
                                                        flags &= ~MAPP_WRITEPROTECTED;
                                                        strcat(flagsmsg,"read/write ");
                                                }
                                                if (args[OPT_INVALID]) {
                                                        mask = 0xffffffff;
                                                        flags = MAPP_INVALID | MAPP_REPAIRABLE;
                                                        strcpy(flagsmsg,"invalid ");
                                                }
                                                if (args[OPT_BLANK]) {
                                                        mask = 0xffffffff;
                                                        flags = MAPP_BLANK;
                                                        strcpy(flagsmsg,"blank ");
                                                }
                                                which=WHICH_ALL;
                                                whichmsg = "user and supervisor";
                                                if (args[OPT_USERONLY]) {
                                                        which = WHICH_USER;
                                                        whichmsg = "user";
                                                }
                                                if (args[OPT_SUPERVISORONLY]) {
                                                        which=WHICH_SUPER;
                                                        whichmsg = "supervisor";
                                                }

                                                if (flagsmsg[0]=='\0') {
                                                        err=ERROR_REQUIRED_ARG_MISSING;
                                                } else {
                                                        err=SetCacheMode((ULONG)from,(LONG)size,flags,mask,which);
                                                        if ((err==0) && args[OPT_VERBOSE]) {
                                                                Printf("%s.\n"
                                                                       "Set %s mode from 0x%lx, size 0x%lx to %s\n",
                                                                       version+6,whichmsg,from,size,flagsmsg);
                                                        }
                                                }
                                        }
                                }

                                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,"MuSetCacheMode failed");
                                rc=10;
                        }
                        SetIoErr(err);

                        CloseLibrary((struct Library *)MMUBase);
                } else PrintFault(ERROR_OBJECT_NOT_FOUND,"MuSetCacheMode requires the mmu.library");
                CloseLibrary((struct Library *)DOSBase);
        }

        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;
}
///
/// SetCacheMode
int SetCacheMode(ULONG from,ULONG size,ULONG flags,ULONG mask,ULONG which)
{
struct MMUContext *ctx,*sctx;   /* default context, supervisorcontext */
ULONG psize;
int err;

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

        psize=GetPageSize(ctx);
        if (size & (psize-1)) {
                Printf("The given size 0x%lx is not divisible by the page size 0x%lx.\n",size,psize);
                return ERROR_BAD_NUMBER;
        }
        if (from & (psize-1)) {
                Printf("The given address 0x%lx is not divisible by the page size 0x%lx.\n",from,psize);
                return ERROR_BAD_NUMBER;
        }
        if (psize!=GetPageSize(sctx)) {
                Printf("MuSetCacheMode does not support different user and supervisor page sizes.\n");
                return ERROR_OBJECT_WRONG_TYPE;
        }

        if (which & WHICH_USER) {
                if (err=SetCache(ctx,from,size,flags,mask))
                        return err;
        }

        if (which & WHICH_SUPER) {
                if (err=SetCache(sctx,from,size,flags,mask))
                        return err;
        }

        return 0;
}
///
/// SetCache
int SetCache(struct MMUContext *ctx,ULONG from,ULONG size,ULONG flags,ULONG mask)
{
struct MinList *ctxl;

        if (ctxl=GetMapping(ctx)) {
         if (SetProperties(ctx,flags,mask,from,size,TAG_DONE)) {
          if (RebuildTree(ctx)) {
                  ReleaseMapping(ctx,ctxl);
                  return 0;     /* Everything worked fine */
          } /* the MMU tree could not be build. It is unmodified in this case */
         }
         /* Re-install the old properties. This can't fail. */

         SetPropertyList(ctx,ctxl);
         ReleaseMapping(ctx,ctxl);
        }

        return ERROR_NO_FREE_STORE;
}
///

