/*
    SoftBoot V3.31 for any A3000/68030 or 68040 system; Tested OK
        if run in startup-sequence on an A4000.

        by Greg Tibbs 10/25/1992 ; some code from  SetCPU V1.4 by Dave Haynie

    MAIN PROGRAM

        Assumptions: The 040 for the A3000 only works under 2.0+, so there are
                     assumptions that exec knows about an 040, that ROMsize
                     is 512K. ZorroIII space is automatically found and MMU
                     tables allocated and initialized. ZorroII caching
                     defaults to on unless explicitly turned off or a
                     BridgeBoard is found in the system. Compiled using
                     SAS/C V6.00. Assembly module SofTag.asm must be
                     linked in. The Assembly module has the magic to
                     perform reboot recovery as well as talk to the 030
                     and 040 MMUs directly.

*/

#define PROGRAM_VERSION "V3.31"

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <libraries/configvars.h>
#include <proto/all.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include "proto.h"

#define ROUND030    	0x00008000L
#define ROUND       	0x00002000L
#define ROMSIZE     	0x00080000L
#define BONUSSIZE1_3    0x00045000L
#define FASTROMLOC  	0x07f80000L
#define BASEROMLOC  	0x00f80000L
#define TABSIZE     	(128L * sizeof(ULONG))
#define LEV3TABSIZE     0x2000L
#define Z3STARTADDR 	0x10000000L
#define Z3ENDADDR   	0xffffffffL
#define WALL        	0x100L
#define WALLSIZE    	2*WALL
#define PFLUSHA040  	0xf518
#define CINVA040    	0xf4d8
#define MOVEC           0x4e7b
#define NOP             0x4e71
#define TCMASK030   	0x80000000L
#define FASTROMADDRMASK 0x7000000L
#define MMU030TABLESIZE 0x8000L
#define Z3L3SIZEPERBLOCK 0x4000L
#define Z3L2SIZEPERBLOCK 0X200L
#define PAGESIZE030 	0X80000L
#define PDTRESIDENT     0x01L
#define UDTRESIDENT     0x03L
#define CM_GLOBAL       0x400L
#define WR_PROTECT      0x04L
#define CM_COPYBACK     0x020L
#define CM_INH_SER      0x040L
#define CM_INH_NONSER   0x060L
#define L1MASK 		~0x1ffL  /* Masks used in MMU table addresses for Level 1, 2 & 3 tables */
#define L2MASK 		~0x7fL
#define L3MASK 		~0x1fffL

int CXBRK(void) {return(0L);}

/* External Variables */

extern struct ExecBase *SysBase;

/* Communication to the assembly module is via common
   memory areas, rather than passing the data on the stack or via
   a register. */

__far extern ULONG PhyRomStart, LogRomStart, SRP, CRP0;
__far extern ULONG CRP1, TC030;
__far extern ULONG StartRom, BusErr, CatchRom, Z3cache;
__far extern ULONG TC, MemListArray[24], NumMemAreas;
__far extern ULONG GetTC030(void), GetTC040(void),  GetCPUType(void);
__far extern void  MyColdReboot(void), JTRom(void), SpeedRom(void), MakeRomTag(void);
__far extern void  JTRom13030(void), MakeRomTag13(void), ForceTTX(void);
__far extern void  KillRomTags(void), SF_Supervisor(void), CachesOff(void);

struct ConfigureIt 
{
  ULONG StartAddr;
  ULONG EndAddr;
  ULONG BridgeBoard;
};

/* WallFreemem must be used to  return all WALLed memory allocations */

void WallFreeMem( void *Address, ULONG size)
{
  Address=(void *)((ULONG)Address-WALL);
  size += WALLSIZE;
  FreeMem(Address,size);
}

/* KillRomTags() is an assembly function to overwrite the ID so leftover
   copies of the romtags embedded in this executable won't accidentially get 
   found and run on reboot. Fastrom in particular was dangerous as if
   the romtag was found in RAM which was loaded during the fastrom operation,
   the MMU might get setup with a working MMU environment which would get
   stomped on as soon as any program overwrites that area as the MMU memory 
   areas would not be autoallocated and protected. The Ram occupied by the 
   romtag would not be protected either, BTW.

   A Real RomTag is only made when it is intended to softload a new 
   operating system. Currently, the romtag must be in a list in ExecBase, 
   or in special areas of ram to be autodetected, but the romtag IDs are
   obliterated just in case of future OS change where other areas may
   be autodetected. In any situation that exit() is to be called, we are 
   going to return to the OS, and not softload, so the IDs are overwritten 
   to be safe. */

void exit2(ULONG error)
{
   KillRomTags();
   exit(error);
}

/* MyExit returns RAM allocated for the ROM before exiting */

void MyExit(error,TempROM)
ULONG error, *TempROM;
{
  if((ULONG)TempROM==FASTROMLOC)
  {
    FreeMem((ULONG *)FASTROMLOC,ROMSIZE);
  }
  else
  {
    if(TempROM) WallFreeMem(TempROM,ROMSIZE);
  }
  exit2(error);
}

/* TableFill13 build 68040 MMU tables for a 1.3 Bonus kickstart image.
   This improved version writes the 040 MMU tables into the temporary
   ram covered by the 1.3 rom image, rather than at the final address (unless
   they coinside).    This allows me to delay Disable()ing the system until 
   much later, and to not corrupt any possible previous rom or data at 
   7f80000-7ffffff until I am very close to rebooting. */

void TableFill13(TempROM, cache)
ULONG *TempROM, cache;
{
   ULONG *TempTable, PageField;
   int i;
/*
   The 1.3 bonus is very sneaky. It is setup with built-in 030 MMU tables,
   and code to add the motherboard fast ram, read the real time clock and
   to start the hard drives. The 1.3 ROM and Bonus is more than 256K but
   less than 512K. There is plenty of room to add 68040 MMU tables as well.
   The area from 7f80000 to 7fbffff is standard 256K 1.3 kickstart. MMU tables
   map, or mirror that to f80000 to fbffff and to fc0000 to ffffff, to make 
   it look like a standard ROM system. The area in 7fc0000 to 7ffffff is 
   mapped to  f00000 to f7ffff. 1.3 Kickstart will read this area for any 
   romtags; they do not have to be linked into any system list as with other 
   locations. In the mirror is a romtag which initializes the clock, ram, and 
   hard drive. In the assembly module, I built my own romtag, which
   SetFunction()s ColdCapture(). 2.0 and later exec honors the 1.3 execbase
   structure and will run the 1.3 execbase ColdCapture() vector before it 
   realizes that it is not a 2.0+ execbase and relocates it. My Romtag
   then reloads the MMU and jumps back into 1.3, providing hardware
   (Keyboard) reboot recovery. Obviously anything which overwrites
   ColdCapture() without chaining it will cause the hardware reboot 
   recovery process to fail. As a result, I blast my vector in (I do not
   chain) each time the romtag is run.
*/
   /* Calculate Pointer to 1st level MMU table in temporary RAM */

   TempTable = (ULONG *)(0x46000L + (ULONG)TempROM);

   /*Fill 1st level table */

   for (i = 1; i < 128; i++) *(TempTable+i) = 0L;

   /* Point entry for lowest 32 Meg block to 2nd level table */

   *TempTable = ((0x7fc8000L & L1MASK) | UDTRESIDENT); /* Point to Level 2 Table */

   /* Calculate 2nd Level table location in temporary ROM space */

   TempTable = (ULONG *)(0x48000L + (ULONG)TempROM);

   /* Fill in 2nd level table with pointers to 3rd level table */

   for (i=64; i<128; i++) *(TempTable+i)=0L;
   for (i=0; i<64; i++)
         *(TempTable+i)=(((0x7fca000L+i*128) & L2MASK) | UDTRESIDENT);

   /* Calculate 3rd level table address */

   TempTable = (ULONG *)(0x4a000L + (ULONG)TempROM);

   /* Fill in 3rd level table for lowest 15 Meg */

   PageField = CM_GLOBAL | CM_INH_SER | PDTRESIDENT;
   for (i=0; i<1984; i++) /*Romaddr/pagesize = 0xf80000/0x2000 = 1984 pages*/
      *(TempTable+i) = (((8192L*i) & L3MASK) | PageField);

   /* Turn on Z2 caching, if asked for */

   if(cache>0)
   {
     PageField = CM_GLOBAL | CM_COPYBACK | PDTRESIDENT;
     for(i=256; i<1280; i++) *(TempTable+i)=(((8192L*i) & L3MASK) | PageField);
   }

   /* Do ROM area, make write protected */ 

   PageField = CM_GLOBAL | CM_COPYBACK | WR_PROTECT | PDTRESIDENT;
   for (i=0; i<64; i++)  /* Romsize/pagesize = 512K/8K = 64 pages */
      *(TempTable+i+1984)=(((FASTROMLOC+(8192L*i)) & L3MASK) | PageField);

   /* Fold over 7fc0000-7ffffff into F00000-F7ffff so romtags can be found */

   PageField = CM_GLOBAL | CM_COPYBACK | WR_PROTECT | PDTRESIDENT;
   for (i=0; i<32; i++) 
      *(TempTable+i+2016)=(((FASTROMLOC+(8192L*i)) & L3MASK) | PageField);

   /* Make the area from f80000 to fc0000 mirror at fc0000 - ffffff */

   PageField = CM_GLOBAL | CM_COPYBACK | PDTRESIDENT;
   for (i=0;i<32;i++)
      *(TempTable+i+1920)=(((0x7fc0000L+(8192L*i)) & L3MASK) | PageField);
}

/* TableFill is the general purpose MMU table fill algorithm and requires
   considerably more information. The Pointers to the MMU tables must
   point to properly allocated and aligned memory space. */

void 
TableFill(MMUTable, MMUTable2, MMUTable3, Z3MMUTable2, Z3MMUTable3, ROMADDR,
          cache2, cache3, MyConfigStruct, numblocks)
ULONG *MMUTable, *MMUTable2, *MMUTable3, *Z3MMUTable2, *Z3MMUTable3,
       ROMADDR, cache2, cache3, numblocks;
struct ConfigureIt *MyConfigStruct;
{
   ULONG PageField;
   int i,j;
        
   /* The MMU address translation tree has been optimized into three memory
      areas for memory defragmentation purposes. The upper level is 128
      32 bit pointers. These must be adjacent, by definition. However,
      the second and third level tables do not. Once allocated on a suitable
      boundary, all 2nd and 3rd level tables are adjacent in two blocks of
      RAM. The same technique is used later for Zorro III MMU tables */

    /* Each element of MMUTable[n] points to a 2nd level table representing
       32 Megabytes. We only care about the lowest 16 Meg, so zero out
       all but the first entry */

    for (i = 1; i < 128; i++) *(MMUTable+i) = 0L;

    *MMUTable = (((ULONG)MMUTable2 & L1MASK) | UDTRESIDENT); /* Point to Level 2 Table */

    /* 2nd Level Table initialization for lowest 32 Meg. There are
       128 entries in the 2nd level table, each pointing to a
       third level table which, in this MMU setup, contains the
       translated addresses for each block of memory. */

    /* Zero out pointers to 3rd level tables for upper 16 Meg block */

    for (i=64; i<128; i++) *(MMUTable2+i)=0L;

    /*Set 2nd Level  pointers to 3rd level tables covering lowest 16 Meg. */

    for (i=0; i<64; i++)
       *(MMUTable2+i)=(((ULONG)&MMUTable3[i*32] & L2MASK) | UDTRESIDENT);

    /* Set up 3rd level Tables for lowest 16 Meg. Each third
       Level table represents 256K. */

    /* Since, in this implementation, all third level tables reside adjacent 
       to one another, a total of 2048 8K (2K*8K = 16M) entries exist. The 
       following 'for' loops takes advantage of this fact for table 
       initialization purposes */

    /* Do from 0 to just prior to ROM (All non-cacheable)*/

    PageField = CM_GLOBAL | CM_INH_SER | PDTRESIDENT;
    for (i=0; i<1984; i++) /*Romaddr/pagesize = 0xf80000/0x2000 = 1984 pages*/
       *(MMUTable3+i) = (((8192L*i) & L3MASK) | PageField);

    /* Do ROM area, make write protected */ 

    PageField = CM_GLOBAL | CM_COPYBACK | WR_PROTECT | PDTRESIDENT;
    for (i=0; i<64; i++)  /* Romsize/pagesize = 512K/8K = 64 pages */
       *(MMUTable3+i+1984)=(((ROMADDR+(8192L*i)) & L3MASK) | PageField);

    /* Turn on Z2 caching, if asked for */

    if(cache2>0)
    {
      PageField = CM_GLOBAL | CM_COPYBACK | PDTRESIDENT;
      for(i=256; i<1280; i++) *(MMUTable3+i)=(((8192L*i) & L3MASK) | PageField);
    }

    /* ZorroIII initialization */

    if(MyConfigStruct->StartAddr>=Z3STARTADDR) 
    {
      /* Level 1 Table Generation for Zorro III area; Tables are
         built regardless of the Transparent Translation Registers */
   
      /* Assumption: MyConfigStruct->StartAddr is on a 32Meg Boundary 
                     (FindZorro3Start() ensures this). Any Board less 
                     than 32 Meg will generate a 32 MB MMU table for Zorro 
                     III areas */

    for(i=0;i<numblocks;i++)
       *(MMUTable+(MyConfigStruct->StartAddr/0x02000000L)+i)=
                ((((ULONG)Z3MMUTable2+i*0x200L) & L1MASK) | UDTRESIDENT);  

    /* Level 2 Tables for Zorro III space */

    for (i=0L;i<(128L*numblocks);i++)
    {
      *(Z3MMUTable2+i)=
         (((ULONG)&Z3MMUTable3[i*32] & L2MASK) | UDTRESIDENT);

      /* Level 3 Tables for Zorro III space */

      PageField = CM_GLOBAL | CM_COPYBACK | PDTRESIDENT;        
      if(!cache3) PageField = CM_GLOBAL | CM_INH_SER | PDTRESIDENT;

      for (j=0L; j<32L; j++) 
           *(Z3MMUTable3+i*32L+j) = 
             (((8192L*(32L*i+j)+MyConfigStruct->StartAddr) & L3MASK) | PageField);
    }
  }
}

/* FindZorro3Start() determines whether or not a Bridgeboard exists and
   and determines upper and lower bounds for MMU tables to cover
   the Zorro III ram and I/O cards. The start address is rounded down to
   the next lowest 32 MB boundary if it is not on one. The end address is
   rounded up to the next higher 32 MB boundary if not on one. The MMU
   tables must be filled in a 32 MB increment, so this is a valid choice */

ULONG FindZorro3Start(struct ConfigureIt *MyConfigStruct)
{
  ULONG temp;
  struct ConfigDev *myCD=NULL;
  struct Library *ExpansionBase = NULL;
  struct MemHeader *mem;

  MyConfigStruct->StartAddr = Z3ENDADDR;
  MyConfigStruct->EndAddr = 0L;
  MyConfigStruct->BridgeBoard = 0L;

  if((ExpansionBase=OpenLibrary("expansion.library",0L))==NULL) 
  {
      MyConfigStruct->StartAddr = 0L;
      return(0L);
  }

/* Loop though autoconfigured boards */

  while(myCD=FindConfigDev(myCD,-1L,-1L))
  {
     /* Check and mark if Bridgeboard is found */

     if(myCD->cd_Rom.er_Manufacturer==0x201 && myCD->cd_Rom.er_Product==1)
        MyConfigStruct->BridgeBoard=1L;

     /* Check for a Zorro III board (any type). Z3STARTADDR = 0x10000000 */

     if ((ULONG)(myCD->cd_BoardAddr) >= Z3STARTADDR) 
     {
       /* Check for board less than Z3 end address; In case the OS 
          does not store the config items in increasing order,
          or in a random order */

       if ((ULONG)(myCD->cd_BoardAddr) < MyConfigStruct->StartAddr)
             MyConfigStruct->StartAddr = (ULONG)(myCD->cd_BoardAddr);

       /* I am assuming that when you take a previous board address and
          add its length, you will get the address of the next board,
          therefore, I test with >= rather than > in the following statement: */

       if ((ULONG)(myCD->cd_BoardAddr) >= MyConfigStruct->EndAddr)
       {
         /* I am not sure how Slot Size and Board Size relate. I have seen
            a Progressive Zorro III ram card which only had 16 of 64 Meg
            populated. In that case 64K * SlotSize and BoardSize did not
            agree. To be conservative, I will take the larger range of the
            two. This ensures that the area will be covered, although it might
            be a bit much. */

         if(65536L*(ULONG)myCD->cd_SlotSize > (ULONG)myCD->cd_BoardSize)
         {    
            MyConfigStruct->EndAddr = 
                (ULONG)(myCD->cd_BoardAddr) + 
                     65536L*(ULONG)(myCD->cd_SlotSize);
         }
         else /* BoardSize > than 64K * SlotSize */
         {
            MyConfigStruct->EndAddr = 
                (ULONG)(myCD->cd_BoardAddr) + 
                     (ULONG)(myCD->cd_BoardSize);
         }
       }
     }
  }

/* This code double checks the memory lists against my board config sizing
   logic above. Should a ZorroIII memory board not correctly work due to the
   above logic, it should be found via the memory lists and corrected. 
   Zorro III space is defined as anywhere between 0x10000000 and 0xffffffff */
  
  /* Prevent anyone from walking the memory lists while we are using them */

  Forbid(); 

  /* Get pointer to system memory lists */

  mem=(struct MemHeader *)(SysBase->MemList.lh_Head); 

  /* Loop until all nodes have been checked */

  while (mem->mh_Node.ln_Succ)
  {
    /* Check lower bounds of memory area against the beginning of Zorro III
       space and the lowest Zorro III address detected */

    if((ULONG)mem->mh_Lower >= Z3STARTADDR)
    {
      if( (ULONG)mem->mh_Lower <  MyConfigStruct->StartAddr)
      {
         MyConfigStruct->StartAddr = (ULONG)(mem->mh_Lower);
      }

      /* Check upper memory list bounds against the largest Z3 board found */

      if((ULONG)mem->mh_Upper > MyConfigStruct->EndAddr)
      {
         MyConfigStruct->EndAddr = (ULONG)(mem->mh_Upper);
      }
    }
    
    /* Go to next node in memory list */
 
    mem=(struct MemHeader *)(mem->mh_Node.ln_Succ);
  }

  /* We are done, so let system continue */

  Permit(); 

  /* If StartAddr was never modified, then there is no Zorro III ram or I/O
     in this system and set it to a value other routines will detect */

  if(MyConfigStruct->StartAddr == Z3ENDADDR) MyConfigStruct->StartAddr=0L;
  CloseLibrary(ExpansionBase);
 
  /* Now round start and end address to nearest full 32 MB increment */

  if(MyConfigStruct->StartAddr >= Z3STARTADDR)
  {
     /* The following code will round StartAddr to the next lowest 32MB boundary
        if it is not already on one. */
  
     MyConfigStruct->StartAddr = MyConfigStruct->StartAddr/0x02000000L;
     MyConfigStruct->StartAddr *= 0x02000000L;

     /* We want to round the end address up to the next higher 32 MB boundary
        if it does not end on a 32 MB boundary. */

     temp = MyConfigStruct->EndAddr/0x02000000L;
     temp *= 0x02000000L;
     if (temp != MyConfigStruct->EndAddr) MyConfigStruct->EndAddr = temp + 0x02000000L;
  }
  return (0L); 
}

/* This replaces the Lattice "stricmp()" function, plus it's a better form
   for my needs here. */
   
static BOOL striequ(s1,s2)
char *s1,*s2;
{
   BOOL aok;
   
   while (*s1 && *s2 && (aok = (*s1++ & 0xdf) == (*s2++ & 0xdf)));
   return (BOOL) (!*s1 && aok);
}

/* Page tables and other MMU stuff must be on a page sized boundary, and
   that boundary must be a power of two.  This routine allocates such an
   aligned block of memory. */

/* AllocAligned now allocates a 32 byte wall around the the Allocation to
   help protect against memlist tail overwrites and other programs which
   overrun their adjacent memory allocations. The address returned is the 
   'safe' aligned address. 32 bytes before and 32+size bytes past the 
   'safe' address are allocated. */

void *AllocAligned(size,bound)
ULONG size;
ULONG bound;
{
   void *mem, *aligned;
   ULONG length;
   
   length=size+bound+WALLSIZE;
   if (!(mem = (void *)AllocMem(length,MEMF_FAST))) return NULL;   
   Forbid();
   aligned = (void *)((((ULONG)mem + bound - 1) & ~(bound - 1))-WALL);
   FreeMem(mem,length);
   mem = (void *)AllocAbs(size+WALLSIZE,aligned);
   Permit();
   mem=(void *)((ULONG)mem+WALL);
   return mem;
}

void PatchRom(RomAddr)
ULONG *RomAddr;
{
ULONG i,DStart, *LTemp;
UWORD Test,*Start;

LTemp=(ULONG *)(LogRomStart+4);
DStart=(*(LTemp)-LogRomStart)+(ULONG)RomAddr;
Start=(UWORD *)DStart;

/* Patch # 1 -  Remove Rom Checksum error branch */

for(i=0;i<5000;i++) /* 5000 word limit from Start address for search */
{
  if((Test=*(Start+i))==0x4685)
    if((Test=*(Start+i+1))==0x6600) 
      {
    *(Start+i)=0x7A00; /* Patch out Rom checksum test */
    break;         /* Not d5 -> moveq #0,d5 following branch test then*/
      }                    /* Error check Always passes! */
}

/* Patch # 2 - Prevent 040 mTTx registers from being blown away
           by patching in NOPS over offending opcodes */

for(i=0;i<25000;i++)    /*Search up to 25K words away from start for patch */
{
  if(*(Start+i)==MOVEC)
  {
    /* Test for 0x4E7B0003 thru 0x4E7B0007 and 0x4E7B0805 thru 0x4E7B0807 
        and change to 0x4E714E71 (Dual NOPs) */

    Test  = *(Start+i+1) & 0xFFF;
    if(Test > 2 && Test < 8 || Test > 804 && Test < 808)
      {
        *(Start+i)   = NOP;
        *(Start+i+1) = NOP;
      }
   }
 }
}

int main(argc,argv)
int argc;
char *argv[];
{
   ULONG i, cpu, myTC, myTC30, file, cache, FastRom, numblocks;
   ULONG *MMUTable2,*MMUTable3, *ROM32, *MMUTable, *Z3MMUTable2, *Z3MMUTable3;
   ULONG Z3L2size, Z3L3size, *MMU030Table, both, Z3All, rm13, *TempROM;
   ULONG count, MMUOverride, enttx;

   struct ConfigureIt MyConfigStruct;

   /* If they're just asking for help */

   if (argc >= 2 && argv[1][0] == '?') {
      printf("\n\2337mSoftBoot %s\2330m [NOCACHEZ2][KILLROM][NOCATCHROM][ONETHREE]\n",
            PROGRAM_VERSION);
      printf("               [BOTH][SOFTBOOT][NOBUSERRORS][FASTROM]\n");
      printf("               [ENTTX][OVRMMU][NOCACHEZ3][Z3ALL]\n");
      exit2(0L);
   }

   enttx=0L;          /* Flag for command line parsing for enttx option */
   rm13=0L;           /* Not loading 1.3 Rom by default */
   both=0L;           /* Not building both 030 and 040 MMU tables by default */
   cache=1L;          /* Turn on Zorro II caching by default */
   Z3cache=1L;        /* Turn on Zorro III caching by default */
   BusErr=0L;         /* Do not turn off the bus error HW by default */
   FastRom=0L;        /* Not performing just a fastrom */
   CatchRom=0L;       /* Do not patch the exception handler for invalid access */
   MMUTable=NULL;     /* Set Pointer to NULL */
   MMU030Table=NULL;  /* Set Pointer to NULL */
   LogRomStart=BASEROMLOC;  /* Logical ROM start address */
   ROM32 = (ULONG *)FASTROMLOC;  /* Physical (relocated) Rom Address */
   MMUOverride=0L;    /* Flag to override existing MMU setup */
   Z3All = 0L;        /* Do not make 040 MMU tables for all of Z3 space */

   /* Search for Zorro III start address and length */

   (void)FindZorro3Start((struct ConfigureIt *)&MyConfigStruct);
  
   /* Find if there is a bridgeboard and turn off Z2 cache if so */  

   if (MyConfigStruct.BridgeBoard) cache=0L;

   cpu=GetCPUType();

   /* Parse Command line */

   if (argc>1) 
   {
     for(i=1;i<argc;i++) 
     {
 
      if ((int)striequ(argv[i],"NOCACHEZ2"))    cache=0L;
      if ((int)striequ(argv[i],"NOCACHEZ3"))    Z3cache=0L;
      if ((int)striequ(argv[i],"ONETHREE"))     {both=1L; rm13=1L; 
                                                 LogRomStart=0x00fc0000L;}
      if ((int)striequ(argv[i],"BOTH"))         both=1L;
      if ((int)striequ(argv[i],"KILLROM"))      {KillRomTags();     
                                                 MyColdReboot();}
      if ((int)striequ(argv[i],"NOBUSERRORS"))  BusErr=1L; 
      if ((int)striequ(argv[i],"FASTROM"))      FastRom=1L;
      if ((int)striequ(argv[i],"NOCATCHROM"))   CatchRom=1L;
      if ((int)striequ(argv[i],"SOFTBOOT"))     {KillRomTags();
                                                 ColdReboot();}
      if ((int)striequ(argv[i],"ENTTX"))        if(cpu==68040L) enttx = 1L;
      if ((int)striequ(argv[i],"OVRMMU"))       MMUOverride=1L;
      if ((int)striequ(argv[i],"Z3ALL"))        Z3All=1L;
      
     }
   }

  if(enttx>0L) {ForceTTX(); exit2(0);}

 /* Let's Test as much as we can, before we get in too far to back out */

   printf("%ld CPU found.\n",cpu);

   if (cpu !=68020L && cpu != 68030L && cpu !=68040L)
   {
     printf("SoftBoot Requires a 68030 or 68040 CPU!\n");
     exit2(20L);
   }

   if(cpu==68020L) cpu=68030L; /* I'm assuming an '851 */

   myTC=0L;
   myTC30=0L;
   if(cpu==68040L) myTC=GetTC040();
   if(cpu==68030L) myTC30=GetTC030();
   if (!MMUOverride && ((cpu == 68040L && myTC) || 
                  (cpu == 68030L && (myTC30 & TCMASK030))))
   {
     printf("FastRom MMU already in operation!\n");
     exit2(0L);
   }

   if(cache) printf("Zorro II area will be cached!\n");

   if (FastRom)
   {
     both=0L; /* Override Both parameter if Fastrom is called for */

     ROM32  = (ULONG *)AllocAligned(ROMSIZE,ROUND030);
     if(!ROM32) 
     {
        printf("FASTROM Memory Allocation Failure! Reboot to defragment!\n"); 
        exit2(20L);
     }
     TempROM=ROM32;
   }
   else /* We're softbooting a new OS */
   {
     TempROM=AllocAbs(ROMSIZE,(APTR)FASTROMLOC);
     if(!TempROM)
     {
       TempROM=AllocAligned(ROMSIZE,0x1000L);
      
    /* Check for No memory or overlap with final destination */ 

       if (!TempROM || (((ULONG)TempROM+ROMSIZE+WALL >= FASTROMLOC) &&
                     ((ULONG)TempROM & FASTROMADDRMASK)))
       {
          printf("TempROM Memory Allocation Failure. Reboot to defragment memory!\n"); 
          exit2(20L);
       }
     }

/*   The final ROM destination is FASTROMLOC (0x7f80000). At a later point, I 
     will Disable() so I can stomp on this area without crashing someone else.
     So I copy the disk file to 0x7f80000 or a temporary buffer now if 0x7f80000 
     was not directly available, so the read() will not break the Disable() and 
     allow the system to crash and burn by having another task use memory
     I just overwrote. */

     if(!rm13) 
     {
       file=open("devs:Kickstart",0L);
       if(file==-1L)
       {
         printf("Error opening devs:Kickstart!\n");
         close(file);
         MyExit(20L,TempROM);
       }
       count=read(file, TempROM, ROMSIZE);
       if(count != ROMSIZE)
       {
         printf("Devs:Kickstart wrong length!\n");
         close(file);
         MyExit(20L,TempROM);
       }
     }
     else
     {
       file=open("devs:Kickstart1.3",0L);
       if(file==-1L)
       {
         printf("Error opening devs:Kickstart1.3!\n");
         close(file);
         MyExit(20L,TempROM);
       }
       count=read(file, TempROM, BONUSSIZE1_3);
       if(count != BONUSSIZE1_3)
       {
         printf("Devs:Kickstart1.3 wrong length!\n");
         close(file);
         MyExit(20L,TempROM);
       }
     }
   }

   /* No need to turn caches off if fastromming */

   if(!FastRom)CachesOff();

   if ((cpu==68030L || both) && !rm13)
   {
     MMU030Table  = (ULONG *)AllocAligned(MMU030TABLESIZE,ROUND030);

     if (!MMU030Table || !FastRom && ((ULONG)MMU030Table > Z3STARTADDR))
     { 
       if(FastRom) 
       {
         printf("Out of Memory For MMU tables! Reboot and Retry\n");
       }
       else
       {
         printf("No Memory on A3000|A4000 Motherboard for MMU tables! Reboot to defragment.\n");
       }
       MyExit(20L,TempROM);
     }
   }
   
   if(cpu==68040L || both)
   {
     numblocks=0L;

     if(!rm13 && MyConfigStruct.StartAddr>=Z3STARTADDR) 
     {
       printf("Zorro III enabled via MMU at 0x%08lx\n",MyConfigStruct.StartAddr);

       /* Count Number of 32 Meg Blocks needed for Zorro III devices */

       if (MyConfigStruct.StartAddr >= Z3STARTADDR ) /* if there was Z3 Ram... */
       {
          /* Estimate number of 32 Megabyte RAM areas */

          numblocks = (MyConfigStruct.EndAddr - MyConfigStruct.StartAddr)/0x2000000L;

          /* Z3ALL flag forces all Zorro III ram to be mapped. This case is
             required if CBM moves start of Zorro III RAM or separates I/O
             space from ram space for 040 mTTx registers. In such a case, the
             old operating system's idea of Zorro III space would not match
             the new operating system, and faulty MMU tables would be built.
             The Z3ALL flag builds MMU tables at great expense in ram, but
             allows the new OS to work until a new softboot is made available.
         
              So, to make the Z3 routines map all of Z3 space, we set
              MyConfig.StartAddr to the beginning of Z3 space and set
              numblocks to cover the entire range.  */

          if(Z3All > 0L) 
          {
            numblocks=(Z3ENDADDR - Z3STARTADDR + 1L)/0x02000000L;
            MyConfigStruct.StartAddr=Z3STARTADDR;
          }
        }
      }

     Z3L3size=numblocks*Z3L3SIZEPERBLOCK; /*Third level tables' size */
     Z3L2size=numblocks*Z3L2SIZEPERBLOCK;  /*Second Level Tables' size */
       
     NumMemAreas=3L; /* Number of areas to protect via Kickmemlist */

     /* Get the memory for the basic 68040 MMU tables. */

     if(!rm13)
     {
       MMUTable  = (ULONG *)AllocAligned(TABSIZE,ROUND);
       MMUTable2 = (ULONG *)AllocAligned(TABSIZE,ROUND);
       MMUTable3 = (ULONG *)AllocAligned(LEV3TABSIZE,ROUND);
       Z3MMUTable2 = NULL;
       Z3MMUTable3 = NULL;

       /* Allocate Zorro III 2nd and third level tables */

       if(MyConfigStruct.StartAddr>=Z3STARTADDR) 
       {
         Z3MMUTable2 =      (ULONG *)AllocAligned(Z3L2size,ROUND);
         Z3MMUTable3 =      (ULONG *)AllocAligned(Z3L3size,ROUND);

         /* Update KickMemList Tables for protection of Zorro III tables
            Note areas are protected via one WALL before and after actual
            Area used. */

         if(!FastRom)
         {
            MemListArray[6] =  (ULONG)Z3MMUTable2-WALL;
            MemListArray[7] =  Z3L2size+WALLSIZE;
            MemListArray[8] =  (ULONG)Z3MMUTable3-WALL;
            MemListArray[9] =  Z3L3size+WALLSIZE;
            NumMemAreas=5L;   /* Now five memory areas to protect */
         }
       }

/* 
          Last safety check that all MMU tables were allocated. If not,
          return memory that was allocated and exit the program.
          For stability, when booting a new OS, the MMU tables must be
          in A3000|A4000 motherboard range, which are constant and
          available through reset. For FastRom, any RAM is OK. 
 */

       if (!MMUTable || !MMUTable2 || !MMUTable3 ||
           !FastRom && ((ULONG)MMUTable > Z3STARTADDR) ||  
           !FastRom && ((ULONG)MMUTable2 > Z3STARTADDR) ||
           !FastRom && ((ULONG)MMUTable3 > Z3STARTADDR) ||
           !FastRom && ((ULONG)Z3MMUTable2 > Z3STARTADDR) ||
           !FastRom && ((ULONG)Z3MMUTable3 > Z3STARTADDR) ||
          ((MyConfigStruct.StartAddr>=Z3STARTADDR) && (!Z3MMUTable3 || !Z3MMUTable2)))
       {
         if (both && MMU030Table) WallFreeMem(MMU030Table,MMU030TABLESIZE);
         if (MMUTable)            WallFreeMem(MMUTable,TABSIZE);
         if (MMUTable2)           WallFreeMem(MMUTable2,TABSIZE);
         if (MMUTable3)           WallFreeMem(MMUTable3,LEV3TABSIZE);
         if (Z3MMUTable2)         WallFreeMem(Z3MMUTable2,Z3L2size);
         if (Z3MMUTable3)         WallFreeMem(Z3MMUTable3,Z3L3size);
         printf("No Memory on A3000|A4000 Motherboard for MMU tables! Reboot to defragment.\n");
         MyExit(20L,TempROM);
       }

      /* Setup KickMemList via MemListArray - Note Safety wall is included! */

       if(!FastRom)
       {    
          MemListArray[0] = (ULONG)MMUTable-WALL;
          MemListArray[1] = TABSIZE+WALLSIZE;
          MemListArray[2] = (ULONG)MMUTable2-WALL;
          MemListArray[3] = TABSIZE+WALLSIZE;
          MemListArray[4] = (ULONG)MMUTable3-WALL;
          MemListArray[5] = LEV3TABSIZE+WALLSIZE;

          /* Now fill the MMU tables that we have successfully allocated -
             The final location of the new ROM will always be FASTROMLOC */

          TableFill(MMUTable, MMUTable2, MMUTable3, Z3MMUTable2, Z3MMUTable3,
                    FASTROMLOC, cache, Z3cache, 
                    (struct ConfigureIt *)&MyConfigStruct, numblocks);
       }
       else /* Fill MMU tables for FastRom */
       {
         TableFill(MMUTable, MMUTable2, MMUTable3, Z3MMUTable2, Z3MMUTable3,
                   (ULONG)TempROM, cache, Z3cache, 
                   (struct ConfigureIt *)&MyConfigStruct, numblocks);
       } 
     }
     else /* rm13 */
     {
       /* Perform 1.3 MMU magic */

       TableFill13(TempROM, cache);
       MMUTable=(ULONG *)0x7fc6000L;
     }
     SRP        = (ULONG)MMUTable;
   }       
   
    if (cpu==68030L || both)
    {
      /* Fill 030 MMU tables */

      if(!rm13) /* Note 1.3 bonus builds KS1.3 030 MMU tables */
      {
         for (i=0;i<32;i++) MMU030Table[i]=((PAGESIZE030 * i) + 0x41L);
         for (i=32;i<8160;i++) MMU030Table[i]=((PAGESIZE030 * i) + 1L);
         for (i=8160;i<8192;i++) MMU030Table[i]=((PAGESIZE030 * i) + 0x41L);
         if (cache) for (i=4;i<21;i++) MMU030Table[i]=((PAGESIZE030 * i) + 1L);
         MMU030Table[31] = (ULONG)ROM32 | 5L;

         if(!FastRom) /*update kickmemList if we are booting a new OS */
         {
            if (both) /* 040 MMU tables are already loaded into the kickmemlist*/
            {
               MemListArray[NumMemAreas*2]  = (ULONG)MMU030Table-WALL;
               MemListArray[NumMemAreas*2+1]= MMU030TABLESIZE+WALLSIZE;
               NumMemAreas++;
            }
            else /* Only the 030 table needs to be protected */
            {
               MemListArray[0]= (ULONG)MMU030Table-WALL;
               MemListArray[1]= MMU030TABLESIZE+WALLSIZE;
               NumMemAreas=1L;
            }
         }
      }
    }

   /* 030 MMU Register values */

   TC030 =0x80f0d400L;
   CRP0  =0x7fff0002L;
   CRP1  =(ULONG)(MMU030Table)&~0xfL;
   PhyRomStart= (ULONG)ROM32; /* Wrote into romtag, for its use */

  /* Now I have to set up the MMU.  The Supervisor Root Pointer tells the MMU about
     the table I've set up, and the Translation Control register will turn
     the thing on.  */

   if(!FastRom)
   {
     /* Softloading a  new OS */

     if(rm13)
     { 
       Disable();
       SF_Supervisor();
       CachesOff();
       if((ULONG)TempROM != FASTROMLOC)
                for(i=0;i<0x20000;i++) *((ULONG *)FASTROMLOC+i) = *(TempROM+i);
       MakeRomTag13();
       CachesOff();
       if(cpu!=68040L)
       {
         JTRom13030();
       }
       else 
       {
         JTRom();
       }
     }
     else /* 2.0 or later */
     {
       PatchRom(TempROM);
       Disable();
       SF_Supervisor();
       MakeRomTag();
       if((ULONG)TempROM!=FASTROMLOC)
                for(i=0;i<0x20000;i++) *((ULONG *)FASTROMLOC+i) = *(TempROM+i);
       CachesOff();
       JTRom();
     }
   }
   else   /* Fastrom Operation */
   {
     KillRomTags(); /* Blow away rontag headers so they can't ever be found */
     CopyMemQuick((ULONG *)BASEROMLOC,ROM32,ROMSIZE);
     CachesOff();
     SpeedRom();
   }
   exit(0L);
}
