home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 2: PC
/
frozenfish_august_1995.bin
/
bbs
/
d04xx
/
d0400.lha
/
SetCPU
/
SetCPU.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-11-03
|
24KB
|
754 lines
/*
SetCPU V1.60
by Dave Haynie, April 13, 1990
Released to the Public Domain
MAIN PROGRAM
This program is a CPU identification and MMU support tool for the
Amiga operating system. It will identify various CPU system
elements, and allow decisions to be made based on those elements
in script files. It will also use the MMU on systems so equipped
to translate system ROM, or alternate system ROMs, into system
memory, with preference given to any 32 bit memory it might be
able to detect.
While this program does attempt to support the 68040, it hasn't
been tested on a 68040 yet. It doesn't attempt to support any
of the MMU setups on a 68040 system just yet.
*/
#define MAIN_MODULE
#include "setcpu.h"
/* ====================================================================== */
/* Global options */
BOOL tags[CHECKS], /* Check system tags */
verbose = FALSE, /* Display lots of info? */
helpmode = FALSE, /* Do we just want help? */
handler = TRUE, /* Install trap handler? */
allochead = FALSE, /* Allocate from head of memory */
keepexec = FALSE, /* Preserve ExecBase on KICKROM? */
stack = TRUE, /* Try a stack translation? */
aliens = FALSE; /* MMU setup from space? */
short fastrom = 0, /* Set up the FASTROM? */
wrapbits = 8, /* How does the MMU wrap addresses? */
forcewrap = -1, /* Forced addressing size */
quitcode = 0; /* Termination code */
char *romfile = NULL; /* Where to look for a kick image */
ULONG bootdelay = 0x00400000, /* Basic reboot-loop delay */
cpu, /* CPU type */
fpu, /* FPU type */
mmu, /* MMU type */
oldCACR, /* CACR when we start. */
newCACR; /* New CACR value. */
/* Do we want autoconfig? I think a default level 2 makes more sense. */
short configlevel = 2;
BOOL configset = FALSE;
/* ====================================================================== */
/* This is the termination routine, which also displays numbered errors. */
void quit(err)
int err;
{
if (err >= 10) printf("Error: ");
switch(err) {
case 1:
printf("Usage: SetCPU [INST|DATA] [[NO]CACHE|[NO]BURST] [CONFIG n] [BITS n] [TRAP n]\n");
printf(" [KICKROM path|dfN: [DELAY n] [KEEPEXEC]] [CARDROM path] [VERBOSE]\n");
printf(" [[NO]FASTROM [path] [KEYPATCH n] [HEAD] [NOSTACK]] [ROMBOOT]\n");
printf(" [CHECK 680x0|68851|6888x|MMU|FPU|MMUON|MMUROM|MMUALIEN]\n");
break;
case 11: printf("Invalid numeric parameter value\n"); break;
case 12: printf("Illegal Command Line Option\n"); break;
case 13: printf("KICKROM translation can't be removed\n");break;
case 14: printf("Can't get memory for FASTROM\n"); break;
case 15: printf("Can't locate specified file\n"); break;
case 16: printf("Invalid device specified\n"); break;
case 17: printf("KEYPATCH requires FASTROM\n"); break;
case 18: printf("Can't get memory for KICKROM\n"); break;
case 19: printf("Option requires a file name argument\n");break;
case 20: printf("System is already FASTKICKed\n"); break;
case 21: printf("File/ROM version mismatch, use KICKROM\n"); break;
case 22: printf("KICKROM file format invalid\n"); break;
case 23: printf("KICKROM file too short\n"); break;
case 24: printf("KICKROM file not found\n"); break;
case 25: printf("ROMBOOT can't be used with other options\n"); break;
case 26: printf("68040 MMU setups not supported\n"); break;
case 27: printf("Alien MMU setup prevents FASTROM\n"); break;
default: break;
}
if (ExpansionBase) CloseLibrary(ExpansionBase);
if (cpu >= 68020L) SetCACR(((err>10)?oldCACR:newCACR) | CACR_FIXED);
exit((err>10)?10:err);
}
/* ====================================================================== */
/* A bit of magic for the smart reset code. */
#define COLDREBOOTVECT -726L
/* This routine installs the given valid rom image as a fast ROM. It will
install any I/O translations that are appropriate, and also apply the
system patch list to the ROM image, before engaging the MMU. */
static BOOL CreateFastROM(tag,wrapbits)
struct systag *tag;
short wrapbits;
{
ULONG *VBR = GetVBR();
struct ExecBase *eb = *((struct ExecBase **)4L);
if (!tag) return FALSE;
tag->wrapup = (wrapbits < tag->wrapdown) ? wrapbits : tag->wrapdown;
/* Here I'll add in subtables for any I/O devices that we've been told
about, that I can locate. */
MakeExpTable(tag);
/* Here I apply the patches to this ROM. */
AddPatch((UWORD *)tag->romhi,SystemPatch,tag);
tag->patchlist = lastpatch;
/* How 'bout that exception handler. I get the Vector Base Register,
save the old vector, allocate space for the new one if needed, copy it
from my SetCPU code, and assign it. The tag keeps track of all of this
so that I can easily remove the whole thing if FASTROM is turned off. */
if (handler) {
tag->OldBerr = (char *)VBR[2];
tag->BerrSize = BerrCodeSize;
tag->BerrHandler = (char *)AllocMem(tag->BerrSize,MEMF_PUBLIC);
MemCopy(BerrCode,tag->BerrHandler,tag->BerrSize);
VBR[2] = (ULONG)tag->BerrHandler;
}
/* Should we have a smart reset bit? */
if (eb->LibNode.lib_Version >= 36) {
tag->ResetCode = (char *)AllocMem(tag->ResetSize = ResetCodeSize,0L);
MemCopy((char *)ResetCode,tag->ResetCode,tag->ResetSize);
tag->OldReset = SetFunction(eb,COLDREBOOTVECT,tag->ResetCode);
}
/* Bang the MMU my way */
SetMMURegs(tag);
Disable();
SetMMUTag(tag);
/* How about that system stack. This is done here, since the MMU is already
on, and I need to do supervisor-mode stuff to turn it on, but no to set
up the fast stack. */
if (stack) MakeFastStack(tag);
Enable();
return TRUE;
}
/* This routine creates a kickable ROM, based on the image in the systag.
If the Kick ROM can't be allocated, the routine returns FALSE, otherwise,
TRUE. */
static BOOL CreateKickROM(temptag,wrapbits)
struct systag *temptag;
short wrapbits;
{
struct systag *tag;
ULONG *VBR = GetVBR();
if (!temptag || LoadErr) return FALSE;
/* We disable so that nothing else is running; the RAMBoot() routine
counts on the instruction cache, so we might as well turn that on
now as well. */
Forbid();
Disable();
/* Now I need a new tag, based on safe memory. Note that calling the safe
allocator will very likely crash the system if we're not disabled. */
if (!(tag = AllocSAFEImage(temptag))) {
Enable();
return FALSE;
}
tag->wrapup = (wrapbits < tag->wrapdown) ? wrapbits : tag->wrapdown;
if (configlevel == 1 || configlevel == 0) {
tag->maintable[0xe80000L/ROMROUND] = PD_ADDR(0x800000)|PD_DT_PAGE;
tag->config = FALSE;
}
/* How 'bout that exception handler. I get the Vector Base Register,
save the old vector, allocate space for the new one if needed, copy it
from my SetCPU code, and assign it. The tag keeps track of all of this
so that I can easily remove the whole thing if FASTROM is turned off. */
if (handler) {
tag->BerrSize = BerrCodeSize;
MemCopy(BerrCode,tag->BerrHandler,tag->BerrSize);
VBR[2] = (ULONG)tag->BerrHandler;
}
SetMMURegs(tag);
RAMBoot(tag,(LONG)keepexec,bootdelay);
return FALSE;
}
/* This routine removes the Fast ROM, and re-claims the memory previously
allocated. We've already checked to make sure that the MMU was
switched on. */
static void DeleteFastROM(tag)
struct systag *tag;
{
ULONG *VBR;
struct MemChunk *mem, *del;
struct ExecBase *eb = *((struct ExecBase **)4L);
/* First off, turn off the MMU and caches. This lets us muck with the table
and reclaim memory without any trouble. */
if (tag->romtype != ROM_FAST) quit(13);
if (tag->sysstack) FreeFastStack(tag);
SetTC(0L);
/* First I free any subtable stuff that was allocated. */
FreeExpROM(tag->devs);
/* Now I delete the patches for any patch lists I've got. They only get
deleted if they've been applied. */
mem = tag->patchlist;
while (mem) {
del = mem;
mem = mem->mc_Next;
FreeMem(del,del->mc_Bytes);
}
/* Remove the bus error handler, if there is one. */
if (tag->BerrHandler) {
VBR = GetVBR();
VBR[2] = (ULONG)tag->OldBerr;
if (tag->OldBerr) FreeMem(tag->BerrHandler,tag->BerrSize);
}
/* Did we have a smart reset bit? */
if (tag->ResetCode && tag->OldReset) {
SetFunction(eb,COLDREBOOTVECT,tag->OldReset);
FreeMem(tag->ResetCode,tag->ResetSize);
}
/* Now I just free up table and ROM image memory, and I'm done! I can free
the table I built for any subtable here, and then the main table and
image. */
FreeMMUTable(tag);
if (tag->tagsize <= 66L || !tag->romlo)
FreeMem(tag->romhi,SMALLROMSIZE);
else if (tag->romlo)
FreeMem(tag->romlo,tag->romsize);
FreeMem(tag,tag->tagsize);
}
/* This routine displays the ROM image information. */
static void PrintFastROM() {
struct systag *tag;
struct ExpROMData *dev;
struct ExecBase *eb = *((struct ExecBase **)4L);
if (!(tag = GetSysTag())) return;
printf("TABLE : (PADDR: $%8lx) (SIZE: %ld) (WRAP: %d)\n",
tag->maintable,tag->tablesize/4,tag->wrapup);
if (tag->romlo)
printf("KERNEL: (PADLO: $%8lx) (PADHI: $%8lx) (VADDR: $%8lx) (SIZE: %ldK)\n",
tag->romlo,tag->romhi,tag->romloc,tag->romsize/1024);
else
printf("KERNEL: (PADDR: $%8lx) (VADDR: $%8lx) (SIZE: %ldK)\n",
tag->romhi,tag->romloc,tag->romsize/1024);
if (tag->sysstack)
printf("SSTACK: (PADDR: $%8lx) (VADDR: $%8lx) (SIZE: %ldK)\n",
tag->sysstack,eb->SysStkLower,((ULONG)eb->SysStkUpper - (ULONG)eb->SysStkLower + 1)/1024);
if (tag->ResetCode)
printf("REBOOT: (NVECT: $%8lx) (OVECT: $%8lx) (SIZE: $%lx)\n",
tag->ResetCode,tag->OldReset,tag->ResetSize);
/* Now we'll explain any device stuff that's here. */
for (dev = tag->devs; dev; dev = dev->next) {
printf("DEVICE: %sE\n",dev->name);
printf(" (PADDR: $%8lx) (TABLE: $%8lx) (SIZE: %ldK)\n",
dev->imagebase, dev->tablebase, dev->ROMsize/1024);
}
}
/* Basic "Do I do configuration" logic */
void SetupConfig(tag)
struct systag *tag;
{
if (configlevel > 0) {
tag->maintable[0xe80000L/ROMROUND] = PD_ADDR(0xe80000L)|PD_DT_PAGE;
if (configlevel > 1) SafeConfigDevs();
}
}
/* ====================================================================== */
/* This function scopes out the two key expansion devices, Bridge Card or
known-to-be-32-bit memory. */
void SnoopDevs() {
struct ConfigDev *cd;
if ((cd = FindConfigDev(NULL,0x201L,0x01L)) || (cd = FindConfigDev(NULL,0x201L,0x02L))) {
Bridge.Addr = (ULONG)cd->cd_BoardAddr;
Bridge.Size = (ULONG)cd->cd_BoardSize;
}
if ((cd = FindConfigDev(NULL,0x202L,0x50L)) || (cd = FindConfigDev(NULL,0x202L,0x51L))) {
A26x0.Addr = (ULONG)cd->cd_BoardAddr;
A26x0.Size = (ULONG)cd->cd_BoardSize;
}
}
/* ====================================================================== */
/* Codes for the FASTROM action. */
#define FR_NO_ACTION 0
#define FR_DELETE 1
#define FR_MKFAST ROM_FAST
#define FR_MKKICK ROM_KICK
/* Command line tokens */
static char *CLITokens[] =
{ "68000","68010","68020","68030","68040","68851","68881","68882","FPU",
"MMU","MMUON","MMUROM","MMUALIEN","CHECK", "FASTROM", "NOFASTROM",
"NOPATCH","KEYPATCH","TRAP", "DATA", "INST", "CACHE", "NOCACHE", "BURST",
"NOBURST","VERBOSE", "KICKROM", "CARDROM", "CONFIG", "HEAD", "DELAY",
"KEEPEXEC","ROMBOOT","NOSTACK","BITS","?" };
#define CT_CHECK CHECKS+0
#define CT_FASTROM CHECKS+1
#define CT_NOFROM CHECKS+2
#define CT_NOPATCH CHECKS+3
#define CT_KEYPAT CHECKS+4
#define CT_TRAP CHECKS+5
#define CT_DATA CHECKS+6
#define CT_INST CHECKS+7
#define CT_CACHE CHECKS+8
#define CT_NOCACHE CHECKS+9
#define CT_BURST CHECKS+10
#define CT_NOBURST CHECKS+11
#define CT_VERBOSE CHECKS+12
#define CT_KICKROM CHECKS+13
#define CT_CARDROM CHECKS+14
#define CT_CONFIG CHECKS+15
#define CT_HEAD CHECKS+16
#define CT_DELAY CHECKS+17
#define CT_KEEPEX CHECKS+18
#define CT_ROMBOOT CHECKS+19
#define CT_NOSTACK CHECKS+20
#define CT_BITS CHECKS+21
#define CT_HELP CHECKS+22
/* This function fetches a bounded numeric value, or takes the error trap
for numeric arguments if things aren't right. */
short getnumber(arg,low,high)
char *arg;
short low, high;
{
short num;
if (!arg || !isdigit(*arg) || (num = atoi(arg)) < low || num > high)
quit(11);
return num;
}
/* This is basically the DiskSalv command-line parser re-written for the
SetCPU command set. */
static USHORT ParseCommandLine(argc,argv)
int argc;
char **argv;
{
short i,j, trapmode = 0, bitcnt;
struct patch *p;
BPTR lock;
ULONG mode = CACR_INST | CACR_DATA;
char *file;
BOOL f_FASTROM = FALSE, f_KEYPAT = FALSE;
for (i = 1; i < argc; i++) {
for (j = 0; CLITokens[j] && !striequ(argv[i],CLITokens[j]); ++j);
if (j < CHECKS) {
if (quitcode != WARNING) return 12;
tags[j] = TRUE;
} else switch (j) {
case CT_CHECK : quitcode = WARNING; break;
case CT_NOFROM : fastrom = FR_DELETE; break;
case CT_DATA : mode = CACR_DATA; break;
case CT_INST : mode = CACR_INST; break;
case CT_CACHE : newCACR |= mode << CACR_ENABLE; break;
case CT_NOCACHE: newCACR &= ~(mode << CACR_ENABLE); break;
case CT_BURST : newCACR |= mode << CACR_BURST; break;
case CT_NOBURST: newCACR &= ~(mode << CACR_BURST); break;
case CT_VERBOSE: verbose = TRUE; break;
case CT_HEAD : allochead = TRUE; break;
case CT_KEEPEX : keepexec = FALSE; break;
case CT_HELP : helpmode = TRUE; break;
case CT_NOSTACK: stack = FALSE; break;
case CT_ROMBOOT:
if (argc != 2)
quit(25);
else
CleanBoot();
case CT_TRAP :
if (argv[i+1] && isdigit(argv[i+1][0]))
trapmode = getnumber(argv[++i],0,2);
if (trapmode == 0) wrapbits = 0;
handler = (trapmode == 2);
break;
case CT_BITS :
if (argv[i+1] && isdigit(argv[i+1][0]))
bitcnt = getnumber(argv[++i],24,32);
forcewrap = 32-bitcnt;
break;
case CT_CONFIG :
configset = TRUE;
configlevel = getnumber(argv[++i],0,2);
break;
case CT_DELAY:
bootdelay = 0x60000L*(ULONG)getnumber(argv[++i],0,100);
break;
case CT_KEYPAT :
SetKeyDelay(100L * (ULONG)getnumber(argv[++i],1,100));
for (p = SystemPatch; p; p = p->next)
if (p->list[KEYPATCH].Type == PT_KEYBOARD) {
p->list[KEYPATCH].Length = KeyCodeSize;
p->list[KEYPATCH].Code = (UWORD *)KeyCode;
p->list[KEYPATCH].Type = PT_JSR;
}
f_KEYPAT = TRUE;
break;
case CT_NOPATCH:
for (p = SystemPatch; p; p = p->next) {
j = 0;
while (p->list[j].Type != PT_END) {
if (j != KEYPATCH && p->list[j].Type < PT_END)
p->list[j++].Type = PT_IGNORE;
else
++j;
}
}
break;
case CT_FASTROM:
fastrom = FR_MKFAST;
if (i+1 < argc && (lock = Lock(argv[i+1],ACCESS_READ))) {
UnLock(lock);
romfile = argv[++i];
}
f_FASTROM = TRUE;
break;
case CT_KICKROM:
fastrom = FR_MKKICK;
if (!(romfile = argv[++i])) return 19;
break;
case CT_CARDROM:
if (!(file = argv[++i])) return 19;
if (!ReadExpDevs(file)) return 15;
break;
default: return 12;
}
}
if (f_KEYPAT && !f_FASTROM) return 17;
return 0;
}
/* ====================================================================== */
/* This routine prints FPU codes and sets things accordingly. */
void PrintFPU()
{
if (fpu == 68881L) {
printf("68881 ");
if (tags[CK68881]) quitcode = 0;
} else if (fpu == 68882L) {
printf("68882 ");
if (tags[CK68882]) quitcode = 0;
}
if (fpu && tags[CKFPU]) quitcode = 0;
}
/* This program displays the system CPU setup and sets any appropriate check
flags. */
void PrintSystem() {
ULONG mmuon,dmask = CACR_DATA,imask = CACR_INST,shft = CACR_ENABLE;
struct systag *tag;
printf("SYSTEM: ");
/* If they're not on a 68020/68030, we can't set anything. For
compatibility across systems, I don't consider a cache setting
request an error, just ignore it. */
if (cpu <= 68010L) {
if (cpu == 68010L) {
printf("68010 ");
if (tags[CK68010]) quitcode = 0;
} else {
printf("68000 ");
if (tags[CK68000]) quitcode = 0;
}
PrintFPU();
printf("\n");
return;
}
/* The 32 bit system might have ROMs and things... */
if (cpu == 68040L) {
dmask = CACR_DATA40;
shft = CACR_ENABLE40;
printf("68040 ");
if (tags[CK68040]) quitcode = 0;
} else if (cpu == 68030L) {
printf("68030 ");
if (tags[CK68030]) quitcode = 0;
} else {
printf("68020 ");
if (tags[CK68020]) quitcode = 0;
}
PrintFPU();
if (mmu == 68851L) {
printf("68851 ");
if (tags[CK68851]) quitcode = 0;
}
if (mmu && mmu != BOGUSMMU && mmu != 68040) {
mmuon = (GetTC() & TC_ENB);
if (tags[CKMMU]) quitcode = 0;
if (tags[CKMMUON] && mmuon) quitcode = 0;
if (tag = GetSysTag()) {
if (tags[CKMMUROM]) quitcode = 0;
switch (tag->romtype) {
case ROM_FAST:
printf("FASTROM "); break;
case ROM_KICK:
printf("SLOWKICK "); break;
case ROM_FKICK:
printf("FASTKICK "); break;
default:
printf("NEWKICK? "); break;
}
} else if (mmuon) {
if (tags[CKMMUALIEN]) quitcode = 0;
printf("ALIENMMU ");
}
} else if (mmu == BOGUSMMU)
printf("(FPU LOGIC ERROR) ");
/* We always print the results, even if nothing has changed. */
SetCACR(newCACR | CACR_FIXED);
newCACR = GetCACR();
printf("(INST: ");
if (!(newCACR & (imask << shft))) printf("NO");
printf("CACHE");
if (cpu >= 68030L) {
if (cpu == 68030) {
printf(" ");
if (!(newCACR & (CACR_INST << CACR_BURST))) printf("NO");
printf("BURST");
}
printf(") (DATA: ");
if (!(newCACR & (dmask << shft))) printf("NO");
printf("CACHE");
if (cpu == 68030) {
printf(" ");
if (!(newCACR & (CACR_DATA << CACR_BURST))) printf("NO");
printf("BURST");
}
}
printf(")\n");
}
/* ====================================================================== */
/* This function returns TRUE is the tag ROM passed is the same version
as the actual ROM in use, FALSE otherwise. */
BOOL CheckVersion(tag)
struct systag *tag;
{
UWORD *tagver,*kickver = (UWORD *)(0x100000C - (*(ULONG *)0xFFFFEC));
ULONG *rom = (ULONG *)((ULONG)tag->romhi);
tagver = (UWORD *)(((ULONG)rom + SMALLROMSIZE + 0x0C) - rom[0xfffb]);
return (BOOL) (kickver[0] == tagver[0] && kickver[1] == tagver[1]);
}
/* This function deals with the FASTROM/KICKROM patches and the like. */
void DoROMStuff(tag)
struct systag *tag;
{
struct systag *newtag;
switch (fastrom) {
case FR_MKFAST:
if (tag) switch (tag->romtype) {
case ROM_FAST:
DeleteFastROM(tag);
if (romfile) {
if (!(newtag = AllocDISKImage(ROM_FAST,romfile)))
if (LoadErr) quit(LoadErr); else quit(14);
if (!CheckVersion(newtag)) {
DeleteFastROM(newtag);
quit(21);
}
} else
newtag = AllocROMImage(ROM_FAST);
if (!CreateFastROM(newtag,wrapbits)) quit(14);
break;
case ROM_KICK:
SetupConfig(tag);
if (!CreateFastROM(AllocROMImage(ROM_FKICK),wrapbits)) quit(14);
FreeSAFEImage(tag);
break;
case ROM_FKICK:
quit(20);
} else {
if (cpu == 68040) quit(26);
if (aliens) quit(27);
if (romfile) {
if (!(newtag = AllocDISKImage(ROM_FAST,romfile)))
if (LoadErr) quit(LoadErr); else quit(14);
if (!CheckVersion(newtag)) {
DeleteFastROM(newtag);
quit(21);
}
} else
newtag = AllocROMImage(ROM_FAST);
if (!CreateFastROM(newtag,wrapbits)) quit(14);
}
break;
case FR_MKKICK:
if (tag) switch (tag->romtype) {
case ROM_FAST:
DeleteFastROM(tag);
case ROM_FKICK:
if (!CreateKickROM(AllocDISKImage(ROM_KICK,romfile),wrapbits))
if (LoadErr) quit(LoadErr); else quit(18);
case ROM_KICK:
SetupConfig(tag);
if (!CreateFastROM(AllocROMImage(ROM_FKICK),wrapbits)) quit(14);
FreeSAFEImage(tag);
break;
} else {
if (cpu == 68040) quit(26);
if (!CreateKickROM(AllocDISKImage(ROM_KICK,romfile),wrapbits))
if (LoadErr) quit(LoadErr); else quit(18);
}
break;
case FR_DELETE:
if (fastrom == FR_DELETE && tag) {
if (tag->romtype != ROM_FAST) quit(13);
DeleteFastROM(tag);
}
break;
case FR_NO_ACTION:
if (configset && tag) SetupConfig(tag);
break;
}
}
/* ====================================================================== */
/* This be the main program. */
int main(argc,argv)
int argc;
char *argv[];
{
struct systag *tag;
USHORT i,err;
/* First we parse the command line. The default cache operation acts
on both data and instruction caches. The way all the cache control
functions are defined, they're just NOPs on machines without the
appropriate caches. */
if ((cpu = GetCPUType()) >= 68020L) {
newCACR = oldCACR = GetCACR();
SetCACR(CACR_FIXED|(CACR_INST<<CACR_CLEAR)|(CACR_DATA<<CACR_CLEAR));
}
for (i = 0; i < CHECKS; ++i) tags[i] = FALSE;
if (argc > 1 && (err = ParseCommandLine(argc,argv))) quit(err);
/* If they're just asking for help */
if (verbose || helpmode)
printf("\23333mSetCPU V%1.2f by Dave Haynie\2330m\n",
((float)PROGRAM_VERSION)/100.0);
if (helpmode) quit(1);
/* The FastROM routine uses the expansion library. It's possible to have
an old version of the OS without this, so I'll be careful to avoid
using any ExpansionBase function without checking for the base being
valid. */
if (ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",0L))
SnoopDevs();
/* Let's find out what we have, and perform the ROM translation, if it's
requested and hasn't been done already. */
fpu = GetFPUType();
if ((mmu = GetMMUType()) && mmu != BOGUSMMU) {
tag = GetSysTag();
aliens = (GetTC() & TC_ENB) && !tag;
}
if (mmu && mmu != BOGUSMMU) DoROMStuff(tag);
PrintSystem();
if (verbose && cpu >= 68020 && (GetTC() & TC_ENB)) PrintFastROM();
quit(quitcode);
}