amiga-request@abcfd20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (08/15/90)
Submitted-by: daveh@cbmvax.commodore.com (Dave Haynie)
Posting-number: Volume 90, Issue 222
Archive-name: util/setcpu-1.60/part02
#!/bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 2 (of 4)."
# Contents: SetCPU.h coolhand.c memory.c mmu.c
# Wrapped by tadguy@abcfd20 on Tue Aug 14 17:35:35 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'SetCPU.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'SetCPU.h'\"
else
echo shar: Extracting \"'SetCPU.h'\" \(10417 characters\)
sed "s/^X//" >'SetCPU.h' <<'END_OF_FILE'
X/*
X SetCPU V1.60
X by Dave Haynie, April 13, 1990
X Released to the Public Domain
X
X HEADER FILE
X*/
X
X
X#define PROGRAM_VERSION 160
X
X#include <exec/types.h>
X#include <exec/execbase.h>
X#include <exec/nodes.h>
X#include <exec/lists.h>
X#include <exec/memory.h>
X#include <exec/io.h>
X#include <devices/trackdisk.h>
X#include <libraries/expansionbase.h>
X#include <libraries/configregs.h>
X#include <libraries/configvars.h>
X#include <graphics/gfxbase.h>
X#include <graphics/gfxmacros.h>
X#include <intuition/intuition.h>
X#include <intuition/intuitionbase.h>
X#include <libraries/dosextens.h>
X#include <libraries/filehandler.h>
X#include <functions.h>
X#include <stdio.h>
X#include <ctype.h>
X
X/* ====================================================================== */
X
X/* Define all bit components used for manipulation of the Cache Control
X Register. */
X
X#define CACR_INST ((ULONG)(1L<<0))
X#define CACR_DATA ((ULONG)(1L<<8))
X#define CACR_FIXED ((ULONG)(CACR_DATA<<CACR_WALLOC))
X
X#define CACR_WALLOC 5
X#define CACR_BURST 4
X#define CACR_CLEAR 3
X#define CACR_ENTRY 2
X#define CACR_FREEZE 1
X#define CACR_ENABLE 0
X
X#define CACR_ENABLE40 15
X#define CACR_DATA40 16
X
X/* ====================================================================== */
X
X/* Define important bits used in various MMU registers. */
X
X/* Here are the CRP definitions. The CRP register is 64 bits long, but
X only the first 32 bits are control bits, the next 32 bits provide the
X base address of the table. */
X
X#define CRP_UPPER (1L<<31) /* Upper/lower limit mode */
X#define CRP_LIMIT(x) ((ULONG)((x)&0x7fff)<<16)/* Upper/lower limit value */
X#define CRP_SG (1L<<9) /* Indicates shared space */
X#define CRP_DT_INVALID 0x00 /* Invalid root descriptor */
X#define CRP_DT_PAGE 0x01 /* Fixed offset, auto-genned */
X#define CRP_DT_V4BYTE 0x02 /* Short root descriptor */
X#define CRP_DT_V8BYTE 0x03 /* Long root descriptor */
X
X/* Here are the TC definitions. The TC register is 32 bits long. */
X
X#define TC_ENB (1L<<31) /* Enable the MMU */
X#define TC_SRE (1L<<25) /* For separate Supervisor */
X#define TC_FCL (1L<<24) /* Use function codes? */
X#define TC_PS(x) ((ULONG)((x)&0x0f)<<20) /* Page size */
X#define TC_IS(x) ((ULONG)((x)&0x0f)<<16) /* Logical shift */
X#define TC_TIA(x) ((ULONG)((x)&0x0f)<<12) /* Table indices */
X#define TC_TIB(x) ((ULONG)((x)&0x0f)<<8)
X#define TC_TIC(x) ((ULONG)((x)&0x0f)<<4)
X#define TC_TID(x) ((ULONG)((x)&0x0f)<<0)
X
X/* Here are the page descriptor definitions, for short desctriptors only,
X since that's all I'm using at this point. */
X
X#define PD_ADDR(x) ((ULONG)(x)&~0x0fL) /* Translated Address */
X#define IV_ADDR(x) ((ULONG)(x)&~0x03L) /* Invalid unused field */
X#define PD_WP (1L<<2) /* Write protect it! */
X#define PD_CI (1L<<6) /* Cache inhibit */
X#define PD_DT_TYPE 0x03 /* Page descriptor type */
X#define PD_DT_INVALID 0x00 /* Invalid root descriptor */
X#define PD_DT_PAGE 0x01 /* Fixed offset, auto-genned */
X#define PD_DT_V4BYTE 0x02 /* Short root descriptor */
X#define PD_DT_V8BYTE 0x03 /* Long root descriptor */
X
X/* This is needed for identification of bogus systems that test positive
X for MMUs. */
X
X#define BOGUSMMU 0xffffffffL
X
X/* Here's the MMU support stuff. */
X
X#define ROMROUND 0x00020000L /* ROM/Main level at 128K grain */
X#define SMALLROMSIZE 0x00040000L
X#define BIGROMSIZE 0x00080000L
X#define DEVROUND 0x00004000L /* Device level at 16K grain */
X#define STKROUND 0x00000400L /* Stack level at 1K grain */
X#define TABROUND 0x00000400L /* Table alignment matches page size */
X#define PAGESIZE 0x00020000L
X#define MAINTABSIZE (128L * sizeof(ULONG))
X#define SUBTABSIZE (8L * sizeof(ULONG))
X#define STKTABSIZE (16L * sizeof(ULONG))
X
X/* Magic ROM numbers */
X
X#define MAGIC_256 0x11114ef9
X#define MAGIC_512 0x11144ef9
X
X/* ====================================================================== */
X
X/* Some external system declarations. */
X
Xextern BPTR Lock(), Open();
X
X/* Checking logic */
X
X#define CK68000 0
X#define CK68010 1
X#define CK68020 2
X#define CK68030 3
X#define CK68040 4
X#define CK68851 5
X#define CK68881 6
X#define CK68882 7
X#define CKFPU 8
X#define CKMMU 9
X#define CKMMUON 10
X#define CKMMUROM 11
X#define CKMMUALIEN 12
X#define CHECKS 13
X
X#define WARNING 5
X#define READOK 0L
X
X#define SizeOf(x) ((ULONG)sizeof(x))
X#define min(a,b) ((a<b)?a:b)
X#define max(a,b) ((a>b)?a:b)
X
X/* ====================================================================== */
X
X/* From the CONTROL.A module */
X
Xextern void SetCACR(),
X GetCRP(),
X SetCRP(),
X SetTC();
X
Xextern ULONG GetCACR(),
X *GetVBR(),
X GetTC();
X
X/* ====================================================================== */
X
X/* From the IDENTS.A module */
X
Xextern ULONG GetCPUType(),
X GetMMUType(),
X GetFPUType();
X
X/* ====================================================================== */
X
X/* From the OTHER.A module */
X
Xextern void SetMMUTag(),
X FlushATC(),
X SetKeyDelay();
X
Xextern ULONG KeyCode,
X KeyCodeSize,
X BerrCode,
X BerrCodeSize;
X
X/* ====================================================================== */
X
X/* From the REBOOT.A module. */
X
Xextern void ROMBoot(),
X CleanBoot();
X
Xextern ULONG BootCode,
X BootCodeSize,
X ResetCode,
X ResetCodeSize;
X
X
X/* ====================================================================== */
X
X/* From the EXPDEV.C module. */
X
X/* This structure is device information used by the memory mapping routine. */
X
Xstruct ExpROMData {
X struct ExpROMData *next;
X ULONG ROMbase;
X ULONG ROMsize;
X ULONG imagebase;
X ULONG tablebase;
X char *name;
X};
X
Xextern LONG ReadExpDevs();
Xextern struct ExpROMData *GetExpROM();
Xextern void FreeExpROM(),
X SafeConfigDevs();
X
X/* ====================================================================== */
X
X/* From the MISC.C module. */
X
X/* Patch types */
X
X#define PT_STRING 1
X#define PT_JSR 2
X#define PT_END 3
X#define PT_IGNORE 4
X#define PT_KEYBOARD 5
X
X/* This is a item */
X
Xstruct pitem {
X UWORD Type; /* The type of patch to apply */
X UWORD Pad; /* Nothing here yet... */
X ULONG Offset; /* The offset at which to apply the patch */
X ULONG Length; /* The length of the patch item */
X UWORD *Code; /* The actual patch item */
X};
X
X/* This is the patch structure. */
X
Xstruct patch {
X struct patch *next; /* Next patchlist */
X struct pitem *list; /* First item in this patchlist */
X UWORD Version; /* Which ROM version */
X UWORD Revision; /* and revision */
X};
X
X/* These are the some patch system variables. */
X
X#define KEYPATCH 0
X
Xextern struct patch SystemPatch[];
Xextern struct MemChunk *lastpatch;
X
X/* The actual functions. */
X
Xextern LONG striequ(),
X strniequ();
X
Xextern void MotorOff();
Xextern BYTE ReadBuf();
Xextern LONG CheckTDDev();
X
Xextern LONG AddPatch();
X
X/* ====================================================================== */
X
X/* From the MMU.C module. */
X
Xextern void Phantom(),
X SetMMURegs(),
X FillBasicTable(),
X FreeMMUTable(),
X MakeExpTable(),
X MakeFastStack(),
X FreeFastStack();
X
X/* ====================================================================== */
X
X/* From the MEMORY.C module. */
X
X/* This section describes the system tag structure, where I stick all the
X information that I like to keep around. */
X
X#define ROM_NOP 0x0000 /* No ROM operation called for */
X#define ROM_FAST 0x0002 /* Normally installed FASTROM image */
X#define ROM_KICK 0x0003 /* Installed as a KICK image */
X#define ROM_FKICK 0x0004 /* A KICK image made into a FAST image */
X
X/* This was originally the patchtag structure, which looked like a patch. I
X decided that was real kludgy. I hook the systag onto an invalid page
X descriptor which I locate in the physical ROM image, a place that should
X always be safe to have an invalid tag structure. Don't change this without
X checking in the "030Stuff.a" file -- some of these structure members are
X used by the reboot code. */
X
Xstruct systag {
X ULONG tagsize; /* Size of this tag */
X ULONG progver; /* The program version */
X ULONG *maintable; /* The main ROM table */
X ULONG *romhi; /* The main ROM image */
X UWORD romtype; /* Type of MMU ROM */
X UWORD patches; /* The number of other patches applied */
X struct MemChunk *patchlist; /* List of installed patches */
X struct ExpROMData *devs; /* Translated device ROMs */
X ULONG TC; /* Precomputed TC used for KICK. */
X ULONG CRP[2]; /* Precomputed CRP used for KICK. */
X UWORD config; /* Configuration status. */
X ULONG BerrSize; /* Size of bus error handler. */
X char *OldBerr; /* The old BERR routine. */
X char *BerrHandler; /* My BERR routine. */
X short wrapup; /* Upper address wrap bound. */
X short wrapdown; /* Lower address wrap bound. */
X ULONG tablesize; /* Main table size. */
X char *ResetCode; /* Actual reset routine */
X ULONG romsize; /* Size of ROM image */
X ULONG romloc; /* Where does the fool thing go? */
X ULONG romstart; /* And where do we start it up? */
X ULONG *romlo; /* Secondary ROM image, if needed */
X char *sysstack; /* Physical system stack image */
X ULONG sysstksize; /* System Stack allocated size. */
X ULONG ResetSize; /* Size of the reset code. */
X char *OldReset; /* Old Reset Code */
X};
X
X/* The actual functions */
X
Xextern void MemCopy(),
X SetMMURegs(),
X *AllocAligned(),
X SnoopSpecial(),
X FillBasicTable(),
X FreeSAFEImage();
XULONG * SizeROM();
Xextern struct systag *AllocROMImage(),
X *AllocDISKImage(),
X *AllocSAFEImage(),
X *GetSysTag();
Xextern LONG SmartlyGetRange();
X
X/* ====================================================================== */
X
X/* From the DISKIO.C module. */
X
Xextern struct systag *AllocKSImage(),
X *AllocFILEImage(),
X *AllocDISKImage();
X
X/* ====================================================================== */
X
X/* From the COOLHAND.C module. */
X
Xstruct Window *CoolHand();
X
X/* ====================================================================== */
X
X/* Intermodule globals */
X
Xstruct CfgBlock {
X ULONG Addr, Size;
X};
X
X#ifdef MAIN_MODULE
Xstruct ExpansionBase *ExpansionBase = NULL; /* The expansion library */
Xunsigned short LoadErr = 16;
Xstruct CfgBlock Bridge = { 0L, 0L }, A26x0 = { 0L, 0L };
X#else
Xextern struct ExpansionBase *ExpansionBase;
Xextern unsigned short LoadErr, forcewrap;
Xextern struct CfgBlock Bridge, A26x0;
Xextern BOOL allochead,aliens;
Xextern ULONG cpu;
X#endif
X
END_OF_FILE
if test 10417 -ne `wc -c <'SetCPU.h'`; then
echo shar: \"'SetCPU.h'\" unpacked with wrong size!
fi
# end of 'SetCPU.h'
fi
if test -f 'coolhand.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'coolhand.c'\"
else
echo shar: Extracting \"'coolhand.c'\" \(7518 characters\)
sed "s/^X//" >'coolhand.c' <<'END_OF_FILE'
X/*
X SetCPU V1.60
X by Dave Haynie, April 13, 1990
X Released to the Public Domain
X
X COOLHAND MODULE
X
X This module contains the code used to display a cool looking
X KickStart prompt hand on the screen.
X*/
X
X#include "setcpu.h"
X
X/* ====================================================================== */
X
X/* This is the KickStart hand design as in the A1000. Only, Intuition makes
X it considerably easier to build than they way the Kernel ROM does it. */
X
X/* Define the vectors. The hand is a drawing composed of a series of line
X vectors. Intuition provides a handy structure, called a boarder, that'll
X render this whole mess with a single function call. */
X
Xstatic WORD vect1[] = { 0, 0, 23, 0, 23, 22, 78, 22, 78, 0, 90, 0,101, 11,
X 101, 83, 92, 83, 92, 45, 29, 45, 27, 43, 18, 43, 17, 45,
X 10, 45, 10, 54, 0, 61, 0, 0 };
Xstatic WORD vect2[] = { 0, 0, 0,-62, 93,-62,105,-50,105, 23, 53, 23, 53, 28,
X 49, 36, 45, 41, 41, 44, 35, 53, 27, 57, 27, 68,-23, 68,
X -23, 37,-24, 37,-24, 9,-20, 3,-13, -3,-12, -7, -8,-14,
X -3,-17, 0,-18, 0,-18, -3,-16, -7,-14,-11, -7,-12, -3,
X -19, 3,-23, 9,-23, 36,-22, 37,-22, 67, 7, 67, 7, 46,
X 15, 46, 19, 44, 19, 23, 17, 20, 17, 10, 32, -3, 32,-15,
X 29,-17, 26,-17, 29,-14, 29, -7, 28, -6, 21, -6, 18, -9,
X 9, -2, -3, 4,-15, 13,-15, 12, -3, 3, -7, 2,-10, -1,
X -7, 1, -3, 2, 0, 0 };
Xstatic WORD vect3[] = { 0, 0, 2, -7, 10, -6, 11, -3, 11, 4, 4, 4, 0, 0 };
Xstatic WORD vect4[] = { 0, 0, 0,-10, 15,-23, 15,-35, 74,-35, 74, 2, 1, 2,
X 0, -2 };
Xstatic WORD vect5[] = { 0, 0, 51, 0, 51, 21, 0, 21, 0, 0 };
Xstatic WORD vect6[] = { 0, 0, 11, 0, 11, 14, 0, 14, 0, 0 };
Xstatic WORD vect7[] = { 0, 0, 7, 0, 7, 12, 0, 12, 0, 0 };
Xstatic WORD vect8[] = { 0, 0, 3, 0, 3, 2, 0, 6, 0, 0 };
Xstatic WORD vect9[] = { 0, 0, 0,-20, 7,-20, 12,-25, 12,-34, 16,-32, 24,-32,
X 24,-30, 28,-25, 32,-25, 32,-23, 26,-14, 18,-11, 18, 0,
X 0, 0 };
Xstatic WORD vect10[] = { 0, 0, 0, 5, 5, 2, 0, 0 };
Xstatic WORD vect11[] = { 0, 0, -4, 2, -4, -3, 17, -3, 7, 7, 6, 7, 8, 5,
X 3, 0, 0, 0 };
X
Xstatic WORD vect12[] = { 0, 0, 7, 0, 7, 5, 3, 13, 0, 17, -5, 18, -8, 18,
X -11, 14,-11, 11, 0, 0 };
X
Xstatic WORD vect13[] = { 0, 0, 0, 3, 2, 6, 2, 6, 8, 5, 9, 2, 5, -1,
X 0, 0 };
Xstatic WORD vect14[] = { 0, 0, 8, 2, 10, -1, 6, -5, 3, -5, 0, -3, 0, 0 };
Xstatic WORD vect15[] = { 0, 0, 11, 11, 11, 83 };
X
Xstatic struct Border HandLines[] = {
X { 105, 51,1,0,JAM1,18, &vect1[0], &HandLines[1] },
X { 103,112,1,0,JAM1,59, &vect2[0], &HandLines[2] },
X { 120,101,1,0,JAM1, 7, &vect3[0], &HandLines[3] },
X { 121,132,1,0,JAM1, 8, &vect4[0], &HandLines[4] },
X { 130, 51,1,0,JAM1, 5, &vect5[0], &HandLines[5] },
X { 166, 54,1,0,JAM1, 5, &vect6[0], &HandLines[6] },
X { 168, 55,1,0,JAM1, 5, &vect7[0], &HandLines[7] },
X { 117, 97,1,0,JAM1, 5, &vect8[0], &HandLines[8] },
X { 111,179,1,0,JAM1,15, &vect9[0], &HandLines[9] },
X { 123,135,1,0,JAM1, 4,&vect10[0],&HandLines[10] },
X { 127,138,1,0,JAM1, 9,&vect11[0],&HandLines[11] },
X { 148,135,1,0,JAM1,10,&vect12[0],&HandLines[12] },
X { 138,146,1,0,JAM1, 8,&vect13[0],&HandLines[13] },
X { 124,144,1,0,JAM1, 7,&vect14[0],&HandLines[14] },
X { 196, 51,1,0,JAM1, 3,&vect15[0],NULL },
X};
X
X/* This is a list of area fills to perform; the first number is the "A" pen
X to set, the next two numbers are the screen position to start filling
X from. */
X
Xstatic WORD FillList[][3] = {
X { 2, 107,109 }, { 3, 132, 71 }, { 2, 169, 66 }, { 1, 104, 51 },
X { 1, 129, 51 }, { 1, 167, 55 }, { 1, 176, 67 }, { 1, 182, 55 },
X { 1, 196,134 }, { 1, 145,136 }, { 1, 116, 97 }
X};
X
X#define FILLCNT 11
X
X/* Finally, I've got a few images to render, which I do using the standard
X Intuition Image structures. */
X
XUWORD image0[] = { /* AMIGA */
X 0x1f9f,0xfe3f,0xff77,0xfe7c, 0x070c,0xef8f,0x1e71,0xdc30,
X 0x07f8,0xe7ce,0x1cf3,0x1fe0, 0x0731,0xf3ce,0x1df6,0x1cc0,
X 0x0760,0x03dc,0x3ffc,0x1d80, 0x07c0,0x879c,0x3f78,0x1f00,
X 0x0780,0xcf78,0x7e78,0x1e00, 0x0700,0xfeff,0xfc7c,0x1c00,
X 0x1001,0x8220,0x4050,0x0004, 0x0404,0x8889,0x1210,0x5010,
X 0x0408,0x2448,0x1090,0x1020, 0x0410,0x1042,0x0510,0x1040,
X 0x0420,0x0050,0x2690,0x1080, 0x0440,0x0484,0x0900,0x1100,
X 0x0480,0x4960,0x4208,0x1200, 0x0500,0x0683,0x8404,0x1400,
X};
X
XUWORD image1[] = { /* Kick */
X 0xc71f,0x1e63,0x8000, 0x6631,0x8c63,0x0000, 0x3e01,0x8c33,0x0000,
X 0x6631,0x8c1f,0x0000, 0xc61f,0x0e33,0x0000, 0x0600,0x0063,0x0000,
X 0x0700,0x0c63,0x8000
X};
X
XUWORD image2[] = { /* Start */
X 0x703c,0xdc38,0x3e00, 0x9818,0x664c,0x6000, 0x1998,0x780c,0x3c00,
X 0x19b8,0x600c,0x0600, 0x7cdc,0x3c3e,0x7c00, 0x1800,0x000c,0x0000,
X 0x1000,0x0008,0x0000
X};
X
Xstruct Image NameImage[] = {
X { 126,122,64,8,2,&image0[0],3,0,&NameImage[1] },
X { 144,112,48,7,1,&image1[0],2,0,&NameImage[2] },
X { 140,102,48,7,1,&image2[0],2,0,NULL }
X};
X
X/* Here's the color scheme. */
X
Xstatic WORD coolColor[] = { 0x0fff,0x0000,0x077c,0x0bbb };
X
X/* ====================================================================== */
X
X/* This section contains data for the mechanics of the CoolHand. */
X
Xstatic struct NewScreen newScreen = {
X 0,0,320,200,2,0,1,0,CUSTOMSCREEN,NULL,NULL,NULL,NULL
X};
X
Xstatic struct NewWindow newWindow = {
X 0,0,320,200,2,0,0,BORDERLESS|ACTIVATE,NULL,NULL,NULL,NULL,NULL,0,0,0,0,
X CUSTOMSCREEN
X};
X
Xstatic struct Screen *coolScreen;
Xstatic struct Window *coolWindow, *coolShade;
X
Xstruct GfxBase *GfxBase;
Xstruct IntuitionBase *IntuitionBase;
X
X/* ====================================================================== */
X
X/* This is the main section. Since I consider the CoolHand prompt the end
X of this life and the beginning of the next, I'm not going to worry about
X freeing stuff up. */
X
Xstruct Window *CoolHand() {
X struct RastPort *rp;
X struct ViewPort *vp;
X struct Image *image;
X LONG size;
X UWORD i,*bits,*pointer;
X BOOL ok = TRUE;
X
X GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
X IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
X if (!GfxBase || !IntuitionBase) return;
X if (!(coolScreen = OpenScreen(&newScreen))) return;
X ShowTitle(coolScreen,0L);
X LoadRGB4(vp = &coolScreen->ViewPort,&coolColor[0],4L);
X newWindow.Screen = coolScreen;
X pointer = (UWORD *)AllocMem(64L,MEMF_CHIP|MEMF_CLEAR);
X if (!(coolShade = OpenWindow(&newWindow))) return;
X if (!(coolWindow = OpenWindow(&newWindow))) return;
X WindowToFront(coolShade);
X SetPointer(coolWindow,pointer,2L,2L,0L,0L);
X Delay(25L);
X DrawBorder(rp = coolWindow->RPort,&HandLines[0],0L,0L);
X
X SetOPen(rp,1L);
X for (i = 0; i < FILLCNT; ++i) {
X SetAPen(rp,(long)FillList[i][0]);
X Flood(rp,0L,(long)FillList[i][1],(long)FillList[i][2]);
X }
X for (image = &NameImage[0]; image; image = image->NextImage) {
X size = (LONG) (((image->Width+15)/16)*image->Height)*2*image->Depth;
X movmem((char *)image->ImageData,
X (char *)(bits = (UWORD *)AllocMem(size,MEMF_CHIP)),
X (int)size);
X image->ImageData = bits;
X }
X DrawImage(rp,&NameImage[0],0L,0L);
X CloseWindow(coolShade);
X return coolWindow;
X}
END_OF_FILE
if test 7518 -ne `wc -c <'coolhand.c'`; then
echo shar: \"'coolhand.c'\" unpacked with wrong size!
fi
# end of 'coolhand.c'
fi
if test -f 'memory.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'memory.c'\"
else
echo shar: Extracting \"'memory.c'\" \(11245 characters\)
sed "s/^X//" >'memory.c' <<'END_OF_FILE'
X/*
X SetCPU V1.60
X by Dave Haynie, April 13, 1990
X Released to the Public Domain
X
X MEMORY.C MODULE
X
X This module is responsible for ROM image allocation, MMU table
X allocation and creation, and other functions based on memory.
X*/
X
X#include "setcpu.h"
X
X/* ====================================================================== */
X
X/* Local data types */
X
Xstruct range {
X ULONG first;
X ULONG last;
X};
X
X/* ====================================================================== */
X
X/* This function copies from the source to the destination, by longword, with
X BYTE length "length". */
X
Xvoid MemCopy(src,des,len)
XULONG *src, *des;
XULONG len;
X{
X len = (len + 3)>>2;
X while (len--) *des++ = *src++;
X}
X
X/* ====================================================================== */
X
X/* This section contains the memory allocation code. There are two
X problems here. First of all, we'd like to use 32 bit FAST memory if
X at all possible. Next, block translation and page tables must be on at
X least a page sized boundary, if not a block boundary. */
X
X/* This routine finds the memory block for me to use in AllocAligned().
X It takes into account either A2620 or A2630 systems, where I can snoop
X out the memory for that particular board, knowing it's the fastest. I
X can adjust for 1.4's automatic memory merging in this case too, to be
X sure I have 32 bit RAM. If I don't have one of my boards, I'll return
X a pointer to the first non-$C00000 memory list marked as FAST. */
X
Xstatic struct range SRange = { 0L, 0L };
Xstatic ULONG MaxMem = 0;
X
XLONG SmartlyGetRange() {
X struct ExecBase *eb = *((struct ExecBase **)4L);
X register struct MemHeader *mem;
X
X /* First try for either A2620 or A2630 */
X
X if (A26x0.Addr && A26x0.Size) {
X SRange.first = A26x0.Addr;
X SRange.last = A26x0.Size + SRange.first;
X }
X
X /* For another critters, but we go here to find MaxMem, even
X if we know it's an A26x0. */
X
X for (mem = (struct MemHeader *)eb->MemList.lh_Head; mem->mh_Node.ln_Succ;
X mem = (struct MemHeader *)mem->mh_Node.ln_Succ) {
X if ((ULONG)(mem->mh_Upper) > MaxMem) MaxMem = (ULONG)mem->mh_Upper;
X if (mem->mh_Attributes & MEMF_CHIP) continue;
X if (((ULONG)mem >= 0xc00000 && (ULONG)mem <= 0xc80000)) continue;
X if (!SRange.first) {
X SRange.first = (ULONG)mem->mh_Lower;
X SRange.last = (ULONG)mem->mh_Upper;
X }
X }
X if (SRange.first)
X return TRUE;
X return FALSE;
X}
X
X/* This routine allocates such an aligned block of memory from a specified
X memory list. */
X
Xvoid *AllocAligned(size,bound)
Xregister ULONG size;
Xregister ULONG bound;
X{
X register ULONG target;
X void *mem = NULL;
X
X Forbid();
X if (!allochead) {
X target = (SRange.last-size) & ~(bound-1);
X while (target > SRange.first && !(mem = AllocAbs(size,target)))
X target -= bound;
X SRange.last = (ULONG)mem;
X } else {
X target = (SRange.first+size+bound-1) & ~(bound-1);
X while (target < SRange.last && !(mem = AllocAbs(size,target)))
X target += bound;
X SRange.first = (ULONG)mem+size;
X }
X Permit();
X return mem;
X}
X
X/* This routine finds the memory wrap and appropriate MMU table size for
X the given configuration. It requires the value of MaxMem to have been
X already calculated. */
X
Xvoid FindWrap(tag)
Xstruct systag *tag;
X{
X ULONG test;
X
X if (forcewrap == -1) {
X tag->wrapdown = 0;
X for (test = MaxMem; !(test & 0x80000000) && tag->wrapdown < 8; test <<= 1)
X tag->wrapdown++;
X } else
X tag->wrapdown = forcewrap;
X tag->tablesize = (128 << (8 - tag->wrapdown)) * sizeof(ULONG);
X}
X
X/* This routine computes the ROM size from the magic tag values. */
X
XULONG CalcROMSize(tagval)
XULONG tagval;
X{
X if (tagval == MAGIC_256) return SMALLROMSIZE;
X if (tagval == MAGIC_512) return BIGROMSIZE;
X return 0L;
X}
X
X/* This function sizes the ROM based on its base value and correctly splits
X a base address between possible ROM halves. This routine uses the Commodore
X ROM file format, which is like this:
X
X 0: 00000000
X 4: size
X 8: start of ROM
X
X The ROM, in any case, begins with either "$11114ef9", for 256L ROMs, or
X "$11144ef9", for 512K ROMs. The next longword can be used to figure out
X where the ROM actually goes, in memory. */
X
XULONG *SizeROM(tag,base,getram)
Xstruct systag *tag;
XULONG *base, getram;
X{
X ULONG *rom = NULL;
X
X if (!(tag->romsize = CalcROMSize(base[0]))) return NULL;
X
X if (getram)
X if (!(rom = (ULONG *)AllocAligned(tag->romsize,ROMROUND))) return NULL;
X
X if (tag->romsize == SMALLROMSIZE) {
X tag->romstart = base[1];
X tag->romloc = base[1] & 0xfffc0000;
X tag->romlo = 0L;
X tag->romhi = rom;
X } else {
X tag->romstart = base[1];
X tag->romloc = base[1] & 0xfff80000;
X tag->romlo = rom;
X tag->romhi = (ULONG *)((ULONG)rom + SMALLROMSIZE);
X }
X return rom;
X}
X
X/* ====================================================================== */
X
X/* This section contains routines that manage different ROM image types. */
X
X/* This function gets an aligned ROM image copied from system ROM. */
X
Xstruct systag *AllocROMImage(type)
XUWORD type;
X{
X struct systag *tag = NULL, *oldtag = NULL;
X ULONG *rom = NULL, *table = NULL, *base;
X
X /* Let's make the allocations. I allocate the ROM first, then the table,
X then the tag; since we're coming from the end of memory, this should
X result in the least fragging. */
X
X SmartlyGetRange();
X if (!(tag = AllocAligned(SizeOf(struct systag),8L))) goto fail;
X if (oldtag = GetSysTag())
X base = (ULONG *)oldtag->romloc;
X else {
X if (*(base = (ULONG *)0x00f80000) != MAGIC_512)
X base = (ULONG *)0x00fc0000;
X }
X rom = SizeROM(tag,base,TRUE);
X FindWrap(tag);
X if (!(table = AllocAligned(tag->tablesize+4,TABROUND))) goto fail;
X tag->maintable = table;
X tag->romtype = type;
X MemCopy(tag->romloc,rom,tag->romsize);
X FillBasicTable(tag,FALSE);
X return tag;
X
Xfail:
X if (rom) FreeMem(rom,tag->romsize);
X if (table) FreeMem(table,tag->tablesize+4);
X if (tag) FreeMem(tag,SizeOf(struct systag));
X return NULL;
X}
X
X/* This function gets an aligned, reset-safe image in either $00C00000 or
X CHIP memory, copies the ROM code from the passed temporary image, and
X then sets up it's MMU table such that the memory used for the safe image
X will be missed by the Amiga's memory-sizing logic on reboot. */
X
Xstruct systag *AllocSAFEImage(temp)
Xstruct systag *temp;
X{
X struct ExecBase *eb = *((struct ExecBase **)4L);
X struct MemHeader *safe, *safeC000 = NULL, *safeCHIP = NULL, *tmem;
X struct systag *tag;
X ULONG upper, base, *table;
X
X for (safe = (struct MemHeader *)eb->MemList.lh_Head; safe->mh_Node.ln_Succ;
X safe = (struct MemHeader *)safe->mh_Node.ln_Succ) {
X tmem = (struct MemHeader *)safe;
X if ((ULONG)(tmem->mh_Upper) > MaxMem) MaxMem = (ULONG)tmem->mh_Upper;
X if (tmem->mh_Attributes & MEMF_CHIP) {
X if (!safeCHIP || safeCHIP->mh_Upper < tmem->mh_Upper)
X safeCHIP = tmem;
X } else if ((ULONG)safe >= 0xc00000 && (ULONG)safe <= 0xc80000) {
X if (!safeC000 || safeC000->mh_Upper < tmem->mh_Upper)
X safeC000 = tmem;
X }
X }
X
X /* Will it fit? You need at least 1 meg of memory. */
X
X if (!safeC000 && safeCHIP->mh_Upper < 0x080000L) return NULL;
X
X /* Now, where should it go. */
X
X if (safeC000)
X upper = ((ULONG)safeC000->mh_Upper+ROMROUND-1L) & ~(ROMROUND-1L);
X else if (safeCHIP)
X upper = ((ULONG)safeCHIP->mh_Upper+ROMROUND-1L) & ~(ROMROUND-1L);
X
X FindWrap(temp);
X table = (ULONG *)(base = upper-ROMROUND);
X tag = (struct systag *)(base = (base + temp->tablesize + 36L) & ~7L);
X *tag = *temp;
X tag->maintable = table;
X
X if (temp->romlo) {
X tag->romlo = (ULONG *)(upper - SMALLROMSIZE - ROMROUND);
X if (safeC000) {
X upper = ((ULONG)safeCHIP->mh_Upper+ROMROUND-1L) & ~(ROMROUND-1L);
X tag->romhi = (ULONG *)(upper - SMALLROMSIZE);
X } else
X tag->romhi = (ULONG *)(upper - SMALLROMSIZE*2 - ROMROUND);
X } else {
X tag->romhi = (ULONG *)(upper - temp->romsize - ROMROUND);
X tag->romlo = 0L;
X }
X
X /* Other tag initializations. */
X
X tag->romtype = ROM_KICK;
X FillBasicTable(tag,TRUE);
X
X base = (base + SizeOf(struct systag) + 32L) & ~7L;
X
X tag->BerrHandler = (char *)(base = (base + SizeOf(struct systag)+32L) & ~7L);
X tag->BerrSize = (BerrCodeSize + 4) & ~3L;
X
X if (tag->romlo) MemCopy(temp->romlo,tag->romlo,SMALLROMSIZE);
X MemCopy(temp->romhi,tag->romhi,SMALLROMSIZE);
X
X tag->ResetCode = (char *)(base = (base + BerrCodeSize + 32L) & ~7L);
X tag->ResetSize = BerrCodeSize;
X MemCopy(BootCode,tag->ResetCode,BootCodeSize);
X
X return tag;
X}
X
X/* This function returns memory to the system, automatically setting the
X appropriate memory flags. */
X
Xvoid ReturnMem(size,mem)
XULONG size;
XULONG mem;
X{
X if (mem < 0x00200000)
X AddMemList(size,MEMF_CHIP|MEMF_PUBLIC,-15L,(char *)mem,NULL);
X else if (mem >= 0x00c00000 && mem < 0x00c80000)
X AddMemList(size,MEMF_FAST|MEMF_PUBLIC,-15L,(char *)mem,NULL);
X else
X AddMemList(size,MEMF_FAST|MEMF_PUBLIC,0L,(char *)mem,NULL);
X}
X
X/* This function can be used after rebooting to remove the specially allocated
X SAFE image. This is normally used after copying the safe image into a
X standard FASTROM image and adjusting the MMU accordingly. The SAFE RAM
X at this point isn't in any memory list, so we aren't really freeing it,
X just linking it into a list. */
X
Xvoid FreeSAFEImage(kick)
Xstruct systag *kick;
X{
X if (kick->romlo == 0 || (ULONG)kick->romhi+SMALLROMSIZE == (ULONG)kick->romlo)
X ReturnMem(ROMROUND+kick->romsize,kick->romhi);
X else {
X ReturnMem(ROMROUND+SMALLROMSIZE,kick->romlo);
X ReturnMem(SMALLROMSIZE,kick->romhi);
X }
X}
X
X/* ====================================================================== */
X
X/* This routine gets the system tag from the patched system, if it's
X there. This tag is stored in an invalid table entry that's within the
X physical ROM image. If the MMU isn't on, there's no system tag, by
X definition. This has been enhanced to perform a sanity check on the
X tag encountered -- the systag contains a pointer to the table, this
X can be checked. */
X
Xstruct systag *GetSysTag() {
X struct systag *maybetag;
X ULONG i, myCRP[2], *table = NULL,size;
X APTR oldTrap;
X struct Task *task;
X
X if (cpu == 68040 || !(GetTC() & TC_ENB) || aliens) return NULL;
X
X GetCRP(myCRP);
X table = (ULONG *)myCRP[1];
X
X /* In case the MMU is alien and has some of this memory read protected... */
X task = FindTask(0L);
X oldTrap = task->tc_TrapCode;
X task->tc_TrapCode = (APTR)BerrCode;
X
X /* The tag is now in an easier-to-locate place. This isn't SetCPU V1.5
X compatible; SetCPU V1.5 FASTROM will appear alien to SetCPU V1.6. But
X this is much better for modern systems and modern software. */
X size = (myCRP[0]>>16)+1;
X maybetag = (struct systag *)IV_ADDR(table[size]);
X if (!maybetag || maybetag->maintable != table || maybetag->tablesize != size<<2)
X maybetag = NULL;
X
X /* Restore the trap vector */
X task->tc_TrapCode = oldTrap;
X return maybetag;
X}
X
END_OF_FILE
if test 11245 -ne `wc -c <'memory.c'`; then
echo shar: \"'memory.c'\" unpacked with wrong size!
fi
# end of 'memory.c'
fi
if test -f 'mmu.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mmu.c'\"
else
echo shar: Extracting \"'mmu.c'\" \(8963 characters\)
sed "s/^X//" >'mmu.c' <<'END_OF_FILE'
X/*
X SetCPU V1.60
X by Dave Haynie, April 13, 1990
X Released to the Public Domain
X
X MMU.C MODULE
X
X This module is responsible for some of the MMU table creation
X functions.
X*/
X
X#include "setcpu.h"
X
X
X/* ====================================================================== */
X
X/* This function maps the phantom area for a given memory location. */
X
Xvoid Phantom(tag,loc)
Xstruct systag *tag;
XULONG loc;
X{
X ULONG xindex = loc/ROMROUND;
X
X if (xindex < 60)
X tag->maintable[xindex] = PD_ADDR(0x00800000)|PD_DT_PAGE;
X else
X tag->maintable[xindex] = PD_ADDR(0x00ce0000)|PD_DT_PAGE;
X}
X
X
X/* This table contains a list of the blocks of memory that should be
X cache-inhibited. */
X
Xstatic ULONG InTable[] = { 0x00000000,0x00200000,0x00b00000,0x00f00000,0L,0L };
X
X
X/* This function fills the basic MMU table and other standard tag itemd from
X an allocated image. Some tables may need additional information in the
X table. The basic table consists of one table level and uses direct page
X translation on a grain of 128K per entry. Everything's directly mapped
X except for the last two entries, which are for the $FC0000-$FFFFFF area. */
X
Xvoid FillBasicTable(tag,phantoms)
Xstruct systag *tag;
XLONG phantoms;
X{
X register ULONG i, image, j;
X ULONG page,size,loc;
X
X tag->tagsize = SizeOf(struct systag);
X tag->progver = PROGRAM_VERSION;
X tag->patches = 0;
X tag->patchlist = tag->devs = NULL;
X tag->config = 1;
X tag->BerrSize = 0L;
X tag->OldBerr = tag->BerrHandler = NULL;
X tag->sysstack = NULL;
X tag->sysstksize = 0;
X tag->OldReset = tag->ResetCode = NULL;
X tag->ResetSize = 0;
X
X /* Here I make a table that maps everything straight through. */
X for (i = 0; i < tag->tablesize/sizeof(ULONG); i++)
X tag->maintable[i] = PD_ADDR(i<<17)|PD_DT_PAGE;
X tag->maintable[i] = IV_ADDR(tag)|PD_DT_INVALID;
X
X /* Mark the inhibited pages. */
X for (i = 0; InTable[i+1]; i += 2)
X for (j = InTable[i]/PAGESIZE; j < InTable[i+1]/PAGESIZE; ++j)
X tag->maintable[j] |= PD_CI;
X
X /* Here I map the ROM image to the ROM location. */
X image = (ULONG)tag->romlo;
X loc = tag->romloc/PAGESIZE;
X if (image) {
X if (phantoms) Phantom(tag,image);
X tag->maintable[loc++] = PD_ADDR(image)|PD_WP|PD_DT_PAGE;
X if (phantoms) Phantom(tag,image+PAGESIZE);
X tag->maintable[loc++] = PD_ADDR(image+PAGESIZE)|PD_WP|PD_DT_PAGE;
X }
X image = (ULONG)tag->romhi;
X if (phantoms) Phantom(tag,image);
X tag->maintable[loc++] = PD_ADDR(image)|PD_WP|PD_DT_PAGE;
X if (phantoms) Phantom(tag,image+PAGESIZE);
X tag->maintable[loc] = PD_ADDR(image+PAGESIZE)|PD_WP|PD_DT_PAGE;
X
X /* Here I look for Bridge Cards, which are known to have problems with
X the data cache enabled, so I always disable it, reguardless of
X whether caching is actually enabled in the CACR. */
X
X if (Bridge.Addr && Bridge.Size) {
X page = Bridge.Addr/PAGESIZE;
X size = max((ULONG)Bridge.Size/PAGESIZE,1);
X while (size-- && page < tag->tablesize)
X if ((tag->maintable[page] & PD_DT_TYPE) == PD_DT_PAGE)
X tag->maintable[page++] |= PD_CI;
X }
X}
X
X/* This routine sets up the MMU registers (shadowed in tag fields) in the
X standard SetCPU fashion. The CPU Root Pointer tells the MMU about the
X table I've set up, and all that's left to do is bang the Translation
X Control Register to turn the thing on. Note that the first half of the
X CRP is control data, the second the address of my table. */
X
Xvoid SetMMURegs(tag)
Xstruct systag *tag;
X{
X tag->CRP[0] = CRP_LIMIT(tag->tablesize/sizeof(ULONG)-1)|CRP_SG|CRP_DT_V4BYTE;
X tag->CRP[1] = (ULONG)(tag->maintable);
X
X tag->TC = TC_PS(0x0a)|TC_IS(tag->wrapup)|
X TC_TIA(0x0f-tag->wrapup)|TC_TIB(0x03)|TC_TIC(0x04)|TC_TID(0);
X}
X
X/* ====================================================================== */
X
X/* This function frees all MMU tables. */
X
Xvoid FreeMMUTable(tag)
Xstruct systag *tag;
X{
X ULONG i,j, *subtab;
X
X for (i = 0; i < 128; ++i)
X if ((tag->maintable[i] & PD_DT_TYPE) == PD_DT_V4BYTE) {
X for (subtab = (ULONG *)PD_ADDR(tag->maintable[i]), j = 0; j < 8; ++j)
X if ((subtab[j] & PD_DT_TYPE) == PD_DT_V4BYTE)
X FreeMem((char *)PD_ADDR(subtab[j]),STKTABSIZE);
X FreeMem((char *)subtab,SUBTABSIZE);
X }
X FreeMem((char *)tag->maintable,tag->tablesize);
X}
X
X/* ====================================================================== */
X
X/* This function makes subtables for CardROMFile I/O devices. */
X
Xvoid MakeExpTable(tag)
Xstruct systag *tag;
X{
X ULONG i, *SUBTable, iospace;
X struct ExpROMData *er;
X
X while (er = GetExpROM()) {
X er->next = tag->devs;
X tag->devs = er;
X
X iospace = ((ULONG)er->ROMbase)/ROMROUND;
X
X /* If necessary, modify the table */
X if (tag->maintable[iospace] == PD_ADDR(iospace<<17)|PD_DT_PAGE) {
X if (SUBTable = AllocAligned(SUBTABSIZE,TABROUND)) {
X for (i = 0; i < 8; ++i)
X SUBTable[i] = PD_ADDR((iospace<<17)+(i<<14))|PD_CI|PD_DT_PAGE;
X tag->maintable[iospace] = PD_ADDR(SUBTable)|PD_DT_V4BYTE;
X }
X } else
X SUBTable = (ULONG *)PD_ADDR(tag->maintable[iospace]);
X
X if (SUBTable) {
X er->imagebase = (ULONG)AllocAligned(er->ROMsize,DEVROUND);
X er->tablebase = (ULONG)&SUBTable[0];
X
X MemCopy(er->ROMbase,er->imagebase,er->ROMsize);
X iospace = (er->ROMbase - (iospace * ROMROUND))/DEVROUND;
X for (i = 0; i < er->ROMsize/DEVROUND; ++i)
X SUBTable[iospace+i] = PD_ADDR(er->imagebase+(DEVROUND*i))|PD_CI|PD_DT_PAGE;
X }
X }
X}
X
X/* ====================================================================== */
X
X/* This function hunts down the system stack and translates it into a
X block of 32 bit memory if it's found to be located in Chip RAM. Note
X that this routine must only be called when the MMU is turned on. The
X current functions used to turn the MMU on make use of the supervisor
X stack. This code must be run in user mode to insure that the supervisor
X stack image and the actual stack are the same during the transfer of
X control. */
X
Xvoid MakeFastStack(tag)
Xstruct systag *tag;
X{
X struct ExecBase *eb = *((struct ExecBase **)4L);
X ULONG i, *SUBTable, *STKTable, tabndx, subndx, stkndx, tmem, base;
X
X base = (((ULONG)eb->SysStkLower)/STKROUND)*STKROUND;
X
X if (base >= 0x00200000L) return;
X
X tag->sysstksize = (((ULONG)eb->SysStkUpper - (ULONG)eb->SysStkLower + 1)/STKROUND)*STKROUND;
X tabndx = base/ROMROUND;
X tmem = tabndx * ROMROUND;
X
X /* Make the I/O level subtable (level 2). Most entries are going to be
X a straight translation. */
X
X if (!(SUBTable = AllocAligned(SUBTABSIZE,TABROUND)) ||
X !(STKTable = AllocAligned(STKTABSIZE,TABROUND))) return;
X
X for (i = 0; i < 8; ++i)
X SUBTable[i] = PD_ADDR(tmem+(i*DEVROUND))|PD_DT_PAGE;
X tag->maintable[tabndx] = PD_ADDR(SUBTable)|PD_DT_V4BYTE;
X
X /* Make the Stack level subtable (level 3). Fill all 16 entries with a
X straight translation. */
X
X subndx = (base - tmem)/DEVROUND;
X tmem += subndx * DEVROUND;
X
X for (i = 0; i < 16; ++i)
X STKTable[i] = PD_ADDR((tmem)+(i*STKROUND))|PD_DT_PAGE;
X SUBTable[subndx] = PD_ADDR(STKTable)|PD_DT_V4BYTE;
X
X /* Now take care of the actual stack translation. */
X
X tag->sysstack = (char *)AllocAligned(tag->sysstksize,STKROUND);
X MemCopy(base,tag->sysstack,tag->sysstksize);
X
X stkndx = (base - tmem)/STKROUND;
X
X for (i = 0; i < tag->sysstksize/STKROUND && i < 16; ++i)
X STKTable[i+stkndx] = PD_ADDR(tag->sysstack+(STKROUND*i))|PD_DT_PAGE;
X FlushATC();
X}
X
X/* This function removes the fast stack translation. Like the fast stack
X creation routine, this must be called with the MMU turned on, in user
X mode, so that both stack images can be identical when the switch is
X made. This routine doesn't clean up the MMU tables, it just deals
X with the stack image. */
X
Xvoid FreeFastStack(tag)
Xstruct systag *tag;
X{
X struct ExecBase *eb = *((struct ExecBase **)4L);
X ULONG *SUBTable, *STKTable, tabndx, subndx, tmem, base;
X
X base = (((ULONG)eb->SysStkLower)/STKROUND)*STKROUND;
X
X /* Find the stack's translation table. */
X
X tabndx = base/ROMROUND;
X tmem = tabndx * ROMROUND;
X SUBTable = (ULONG *)PD_ADDR(tag->maintable[tabndx]);
X
X subndx = (base - tmem)/DEVROUND;
X
X /* Set things back the way they were. */
X
X Disable();
X STKTable = (ULONG *)PD_ADDR(SUBTable[subndx]);
X MemCopy(tag->sysstack,base,tag->sysstksize);
X SUBTable[subndx] = PD_ADDR(tmem+(subndx*DEVROUND))|PD_DT_PAGE;
X FlushATC();
X Enable();
X
X /* Get rid of the fast image stuff. */
X
X FreeMem((char *)STKTable,STKTABSIZE);
X FreeMem((char *)tag->sysstack,tag->sysstksize);
X}
X
X/* ====================================================================== */
X
X/* This routine knows the best way to reset the system, in an attempt to
X avoid MMU-based troubles. */
X
Xvoid CleverReset()
X{
X geta4();
X CleanBoot();
X}
END_OF_FILE
if test 8963 -ne `wc -c <'mmu.c'`; then
echo shar: \"'mmu.c'\" unpacked with wrong size!
fi
# end of 'mmu.c'
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 4 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
Mail comments to the moderator at <amiga-request@uunet.uu.net>.
Post requests for sources, and general discussion to comp.sys.amiga.