NetBoot/romdrv1.2/romdrv.c

1 line
30 KiB
C

#include <Memory.h>
#include <Devices.h>
#include <Files.h>
#include <Disks.h>
#include <Errors.h>
#include <Events.h>
#include <OSUtils.h>
#include <QuickDraw.h>
#include "fc8-decompress-68000.h"
#define NUM_CACHE_BLOCKS 16
#define CACHE_BLOCK_SIZE (64L*1024)
#define NEED_DRVSTAT 1
#define NEED_ACCRUN 0
/* If NEED_ACCRUN is set, the dNeedTime (0x60) flag should be set in the header, and the
* delayticks field in the header should be set to something sensible.
*/
unsigned long kRomDrvSize = 0x00380000L;
Ptr kRomDrvLocation = (Ptr)0x40880000;
// Macintosh low-memory globals
unsigned long* BufPtr = (unsigned long*)0x010C;
unsigned long* MemTop = (unsigned long*)0x0108;
unsigned char* MMU32Bit = (unsigned char*)0x0CB2;
unsigned char* LLKeyMap = (unsigned char*)0x0174;
#pragma parameter __D0 ReadXPRam(__D0, __D1, __A0)
short ReadXPRam(short size, short offset, char *where) = {0x4840, 0x3001, _ReadXPRam};
typedef void (*RomDrvCopyFunc)(unsigned char *, unsigned char *, unsigned long);
struct CacheRecord {
unsigned int isValid : 1;
unsigned int isModified : 1;
unsigned short hitCount;
unsigned long timestamp;
unsigned long diskOffset;
unsigned char* pData;
};
typedef struct CacheRecord CacheRecord;
struct RomDrvContext {
DrvSts2 drvsts;
Ptr origcopyfunc;
Ptr ramdisk;
RomDrvCopyFunc copyfunc;
unsigned char * disk;
char initialized;
char useram;
char ram24at8mb;
char usecache;
char showedFullError;
unsigned short numModifiedBlocks;
unsigned long marker;
unsigned long ioCount;
unsigned long blocksLoaded;
unsigned long blocksFlushed;
CacheRecord cacheRecords[NUM_CACHE_BLOCKS];
};
typedef struct RomDrvContext RomDrvContext;
#define ICON_NONE 0
#define ICON_DISK 1
#define ICON_TITLE 2
const unsigned char DiskIcon[258] = {
0xf, 0xff, 0xff, 0xe0, 0x10, 0x00, 0x00, 0x10, 0x10,
0x00, 0x00, 0x10, 0x11, 0xff, 0xff, 0x10, 0x12, 0x1, 0x00,
0x90, 0x12, 0x00, 0x81, 0x90, 0x12, 0x00, 0x7e, 0x90, 0x12,
0x11, 0x3c, 0x90, 0x12, 0x11, 0x3c, 0x90, 0x12, 0x1, 0x18,
0x90, 0x12, 0x1, 0x00, 0x90, 0x12, 0x3, 0x00, 0x90, 0x12,
0x00, 0x00, 0x90, 0x12, 0x8, 0x40, 0x90, 0x12, 0x7, 0x80,
0x90, 0x12, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, 0x90, 0x11,
0xff, 0xff, 0x10, 0x10, 0x00, 0x00, 0x10, 0x8, 0x00, 0x00,
0x20, 0x3f, 0xff, 0xff, 0xf8, 0x20, 0x00, 0x00, 0x8, 0x2f,
0xff, 0xff, 0xe8, 0x20, 0x00, 0x00, 0x8, 0x2f, 0xff, 0xff,
0xe8, 0x20, 0x00, 0x00, 0x8, 0x20, 0x00, 0x00, 0x8, 0x20,
0x00, 0xf, 0xe8, 0x2c, 0x00, 0x00, 0x8, 0x20, 0x00, 0x00,
0x68, 0x20, 0x00, 0x00, 0x8, 0x3f, 0xff, 0xff, 0xf8,
// mask
0xf, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xff,
0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f,
0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff,
0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f,
0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff,
0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f,
0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff,
0xf0, 0x1f, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xf0, 0xf,
0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff,
0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f,
0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff,
0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f,
0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff,
0xf8, 0, 0};
const unsigned char bmow_title [] = {
0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0x80, 0x00, 0x3F, 0x80, 0x07, 0xFF, 0xE0, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xFF, 0xFE, 0x00, 0x7F, 0xC0, 0x00, 0x7F, 0x80, 0x0F, 0xFF, 0xF0, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xFF, 0xFF, 0x80, 0x7F, 0xF0, 0x01, 0xFF, 0x80, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xE0, 0x1F, 0xC0, 0x7F, 0xF8, 0x03, 0xFF, 0x80, 0x7F, 0x00, 0xFE, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xE0, 0x1F, 0xE0, 0x7F, 0xFC, 0x07, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xFE, 0x0F, 0xFF, 0x81, 0xFF, 0x00, 0xFF, 0x81, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xFF, 0x1F, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xE0, 0x1F, 0xE0, 0x7F, 0xFF, 0xBF, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x81, 0x03, 0xFE,
0x7F, 0xFF, 0xFF, 0x80, 0x7F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x83, 0x83, 0xFE,
0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x87, 0xC3, 0xFE,
0x7F, 0xFF, 0xFF, 0x80, 0x7F, 0xEF, 0xFE, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x8F, 0xE3, 0xFE,
0x7F, 0xE0, 0x1F, 0xC0, 0x7F, 0xE7, 0xFC, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0x9F, 0xF3, 0xFE,
0x7F, 0xE0, 0x1F, 0xE0, 0x7F, 0xE3, 0xF8, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xBF, 0xFB, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE1, 0xF0, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE0, 0xE0, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE0, 0x40, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE0, 0x00, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xFE, 0xFF, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE0, 0x00, 0xFF, 0x83, 0xFF, 0x00, 0xFF, 0xC1, 0xFF, 0xFC, 0x7F, 0xFE,
0x7F, 0xE0, 0x1F, 0xF0, 0x7F, 0xE0, 0x00, 0xFF, 0x81, 0xFF, 0x00, 0xFF, 0x81, 0xFF, 0xF8, 0x3F, 0xFE,
0x7F, 0xE0, 0x1F, 0xE0, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0xFF, 0x00, 0xFF, 0x01, 0xFF, 0xF0, 0x1F, 0xFE,
0x7F, 0xE0, 0x1F, 0xC0, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x7F, 0x00, 0xFE, 0x01, 0xFF, 0xE0, 0x0F, 0xFE,
0x7F, 0xFF, 0xFF, 0x80, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x3F, 0xFF, 0xFC, 0x01, 0xFF, 0xC0, 0x07, 0xFE,
0x7F, 0xFF, 0xFF, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x1F, 0xFF, 0xF8, 0x01, 0xFF, 0x80, 0x03, 0xFE,
0x7F, 0xFF, 0xFE, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x0F, 0xFF, 0xF0, 0x01, 0xFF, 0x00, 0x01, 0xFE,
0x7F, 0xFF, 0xFC, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0x80, 0x07, 0xFF, 0xE0, 0x01, 0xFE, 0x00, 0x00, 0xFE,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/* AddDrive is declared in Files.h, but it's defined in MacOS.lib, which we don't want,
* so just shortcut the whole thing here.
*/
asm void RomDrvAddDrive(short drvrRefNum, short drvNum, DrvQElPtr dq)
{
fralloc +
CLR.L D0
MOVE.W drvNum, D0
SWAP D0
MOVE.W drvrRefNum, D0
MOVEA.L dq, A0
0xA04E
frfree
RTS
}
#pragma parameter __D0 RomDrvStripAddress(__D0)
pascal Ptr RomDrvStripAddress(void *addr) = 0xA055;
char DiskIsCompressed()
{
unsigned long signature = *(unsigned long*)kRomDrvLocation;
return (signature & 0xFFFFFF00) == 0x46433800; // 'FC8'
}
char DiskIsWholeCompressed()
{
unsigned long signature = *(unsigned long*)kRomDrvLocation;
return signature == 0x4643385F; // 'FC8_'
}
char DiskIsBlockCompressed()
{
unsigned long signature = *(unsigned long*)kRomDrvLocation;
return signature == 0x46433862; // 'FC8b'
}
unsigned long UncompressedDiskSize()
{
if (DiskIsCompressed())
return *(unsigned long*)(kRomDrvLocation+4);
else
return kRomDrvSize;
}
void RomDrvCopy(unsigned char *source, unsigned char *dest, unsigned long count)
{
signed char mode = true32b;
SwapMMUMode(&mode);
BlockMoveData(source, dest, count);
SwapMMUMode(&mode);
}
void PStrCpy(unsigned char* str, const unsigned char* str2)
{
unsigned char len = str2[0];
str[0] = len;
for (; len>0; len--)
str[len] = str2[len];
}
void PStrCat(unsigned char* str, const unsigned char* extra)
{
unsigned char baseLen = str[0];
unsigned char extraLen = extra[0];
str[0] += extraLen;
for (; extraLen>0; extraLen--)
str[baseLen+extraLen] = extra[extraLen];
}
void PUShortToStr(unsigned char* str, unsigned short num)
{
unsigned short temp = num;
unsigned char power = 1;
unsigned char len = 0;
while (temp >= 10)
{
temp /= 10;
power *= 10;
}
while (power > 0)
{
str[1+len] = '0' + num/power;
num -= ((num/power)*power);
power /= 10;
len++;
}
str[0] = len;
}
short RomDrvOpen(IOParamPtr /*p*/, DCtlPtr d)
{
DrvSts2 *dsptr;
DrvQElPtr dq;
int i, drvnum = 1;
RomDrvContext *ctx;
unsigned long driveSize;
if (d->dCtlStorage == NULL)
{
// alloc some memory for driver context
d->dCtlStorage = NewHandleSysClear(sizeof(RomDrvContext));
HLock(d->dCtlStorage);
ctx = *(RomDrvContext**)d->dCtlStorage;
// init the cache records
for (i=0; i<NUM_CACHE_BLOCKS; i++)
{
ctx->cacheRecords[i].isValid = 0;
ctx->cacheRecords[i].pData = NULL;
}
ctx->marker = 0xBEEFBABE;
ctx->usecache = 0;
ctx->showedFullError = 0;
ctx->numModifiedBlocks = 0;
ctx->ioCount = 1;
ctx->blocksLoaded = 0;
ctx->blocksFlushed = 0;
// copy the copy function into RAM, for 32-bit safeness
ctx->origcopyfunc = NewPtrSys(64);
if (!ctx->origcopyfunc)
{
SysBeep(8);
return openErr;
}
BlockMove(&RomDrvCopy, ctx->origcopyfunc, 64);
ctx->copyfunc = (RomDrvCopyFunc)RomDrvStripAddress(ctx->origcopyfunc);
// find an available drive number
for (dq = (DrvQElPtr)(GetDrvQHdr())->qHead; dq; dq = (DrvQElPtr)dq->qLink)
{
if(dq->dQDrive >= drvnum) drvnum = dq->dQDrive+1;
}
// set drive parameters, and add it to the drive queue
ctx->ramdisk = NULL;
ctx->disk = (unsigned char *)kRomDrvLocation;
dsptr = &ctx->drvsts;
dsptr->writeProt = 0xFF;
dsptr->diskInPlace = 8;
dsptr->dQDrive = drvnum;
dsptr->dQRefNum = d->dCtlRefNum;
driveSize = UncompressedDiskSize();
dsptr->driveSize = (driveSize/512L) & 0xFFFF;
dsptr->driveS1 = ((driveSize/512L) & 0xFFFF0000) >> 16;
RomDrvAddDrive(dsptr->dQRefNum, drvnum, (DrvQElPtr)&dsptr->qLink);
}
return noErr;
}
char CanAllocAbove8MB(unsigned long bytesNeeded)
{
// 24-bit can alloc above 8MB?
if (GetMMUMode() == false32b && 0x00800000 + bytesNeeded <= *MemTop)
return 1;
return 0;
}
char CanAllocWithBufPtr(unsigned long bytesNeeded)
{
// top of system startup blocks is the lowest point to which BufPtr can safely be lowered
// BufPtr = address of the highest byte of allocatable memory
// See explanation in Apple's Memory Manager documentation
// https://developer.apple.com/legacy/library/documentation/mac/pdf/Memory/Memory_Manager.pdf
unsigned long systemStartupBlocksTop = (*MemTop) / 2 + 1024;
// 4/24/2016 - The systemStartupBlocksTop formula doesn't seem to work reliably
// 1.5MB alloc on a 4MB system passes this test, but then System 7 reports out of memory
// same with 3.5MB alloc on an 8MB system
// use a more conservative formula instead
//if (*BufPtr - bytesNeeded >= systemStartupBlocksTop)
if (*BufPtr - (bytesNeeded + 0x80000) >= systemStartupBlocksTop)
return 1;
return 0;
}
void EraseBox()
{
struct QDStorage
{
QDGlobals qd;
long qdGlobalsPtr;
} qds;
GrafPort gp;
GrafPtr oldport;
long olda5;
Rect r;
short cx, cy, extraHeight = 20;
const short boxWidth = 300;
// setup Quickdraw
GetPort(&oldport);
olda5 = SetA5((long)&qds.qdGlobalsPtr);
InitGraf(&qds.qd.thePort);
OpenPort(&gp);
cx = gp.portBits.bounds.right / 2;
cy = gp.portBits.bounds.bottom / 2;
SetRect(&r, cx - boxWidth/2 - 10, cy - 16 - 10, cx + boxWidth/2 + 10, cy + 16 + 40 + extraHeight);
FillRect(&r, &qds.qd.gray);
ClosePort(&gp);
SetA5(olda5);
}
short DisplayBoxedStrings(char iconType, const unsigned char* str, const unsigned char* str2)
{
struct QDStorage
{
QDGlobals qd;
long qdGlobalsPtr;
} qds;
GrafPort gp;
GrafPtr oldport;
long olda5;
Rect r;
BitMap sourceBits, destBits;
short cx, cy, width, width2 = 0, extraHeight = 20;
const short boxWidth = 300;
// setup Quickdraw
GetPort(&oldport);
olda5 = SetA5((long)&qds.qdGlobalsPtr);
InitGraf(&qds.qd.thePort);
OpenPort(&gp);
destBits = gp.portBits;
cx = gp.portBits.bounds.right / 2;
cy = gp.portBits.bounds.bottom / 2;
width = StringWidth(str);
if (str2)
{
width2 = StringWidth(str2);
}
if (iconType != ICON_NONE)
{
// draw a centered box, scaled to the text
SetRect(&r, cx - boxWidth/2 - 10, cy - 16 - 10, cx + boxWidth/2 + 10, cy + 16 + 40 + extraHeight);
EraseRect(&r);
FrameRect(&r);
if (iconType == ICON_DISK)
{
SetRect(&r, cx - 16, cy - 16, cx + 16, cy + 16);
sourceBits.rowBytes = 4;
SetRect(&sourceBits.bounds, 0, 0, 32, 32);
sourceBits.baseAddr = (char*)DiskIcon;
}
else if (iconType == ICON_TITLE)
{
SetRect(&r, cx - 68, cy - 14, cx + 68, cy + 14);
sourceBits.rowBytes = 17;
SetRect(&sourceBits.bounds, 0, 0, 136, 28);
sourceBits.baseAddr = (char*)bmow_title;
}
CopyBits(&sourceBits, &destBits, &sourceBits.bounds, &r, srcCopy, NULL);
}
else
{
// erase the old text area
SetRect(&r, cx - boxWidth/2 - 10, cy + 16 + 30 - 12, cx + boxWidth/2 + 10, cy + 16 + 40 + extraHeight);
InsetRect(&r, 1, 1);
EraseRect(&r);
}
MoveTo(cx - width/2, cy + 16 + 30);
DrawString(str);
if (str2)
{
MoveTo(cx - width2/2, cy + 16 + 30 + extraHeight);
DrawString(str2);
}
ClosePort(&gp);
SetA5(olda5);
return boxWidth;
}
void DisplayErrorString(const unsigned char* str)
{
DisplayBoxedStrings(ICON_DISK, str, "\pPress RETURN to continue");
// wait for Enter/Return key, keymap bit 36 or 42 (international keyboard)
while(1)
{
if((LLKeyMap[4] & 0x10) || (LLKeyMap[5] & 0x04))
break;
}
EraseBox();
}
void DisplayDiskError(short err)
{
// draw a centered box, scaled to the prompt text
if (err == 1)
DisplayErrorString("\pDisk decompression error");
else if (err == 2)
DisplayErrorString("\pNot enough RAM");
}
asm void MakeSafeRomPC()
{
// modify the return address on the stack
andi.l #0x00FFFFFF, (sp)
ori.l #0x40000000, (sp)
rts
}
void CopyDiskDataToIObuf(RomDrvContext *ctx, unsigned char* src, char* dest, unsigned long count)
{
if (GetMMUMode() == false32b && (!ctx->ramdisk || ctx->ram24at8mb))
{
// 24-bit mode with rom disk, or 24-bit mode with 32-bit ram disk
// must switch to 32-bit mode and perform the read using a stripped address
ctx->copyfunc((unsigned char *)(src), (unsigned char*)RomDrvStripAddress(dest), count);
}
else
{
// already in 32-bit mode, or in 24-bit mode with a 24-bit ram disk
// ok to just perform the read
BlockMoveData(src, dest, count);
}
}
void CopyDiskDataFromIObuf(RomDrvContext *ctx, char* src, unsigned char* dest, unsigned long count)
{
if (ctx->ram24at8mb)
{
// 24-bit mode with 32-bit ram disk
// must switch to 32-bit mode and perform the write using a stripped address
ctx->copyfunc((unsigned char *)RomDrvStripAddress(src), (unsigned char *)(dest), count);
}
else
{
// already in 32-bit mode, or in 24-bit mode with a 24-bit ram disk
// ok to just perform the write
BlockMoveData(src, dest, count);
}
}
unsigned long GetBlockOffset(unsigned char* pCompressedData, unsigned long blockNumber)
{
const unsigned long kFC8bBlockOffsets = 12;
unsigned char* pBlockOffset = pCompressedData + kFC8bBlockOffsets + blockNumber * 4;
//signed char addressMode = true32b;
unsigned long blockOffset;
// ensure the PC's high byte is 0x40 (ROM), so we can safely switch to 32-bit addressing mode
//MakeSafeRomPC();
//SwapMMUMode(&addressMode);
blockOffset =
((unsigned long)pBlockOffset[0]) << 24 |
((unsigned long)pBlockOffset[1]) << 16 |
((unsigned long)pBlockOffset[2]) << 8 |
((unsigned long)pBlockOffset[3]);
// restore the old addressing mode
//SwapMMUMode(&addressMode);
return blockOffset;
}
short GetDiskDataFromROMToRamdisk(RomDrvContext *ctx, unsigned char* src, unsigned char* dest, unsigned long count)
{
short result = 0;
// don't need to strip address on dest, because it was already done when dest RAM was allocated
if (DiskIsCompressed())
{
unsigned short decompressStatus;
signed char addressMode = true32b;
char isBlockCompressed = DiskIsBlockCompressed();
unsigned char* pCompressedData = (unsigned char*)kRomDrvLocation;
// ensure the PC's high byte is 0x40 (ROM), so we can safely switch to 32-bit addressing mode
MakeSafeRomPC();
SwapMMUMode(&addressMode);
// decompress ROM disk data into the RAM disk.
// is this a decompression of the entire disk image, for an image that's block compressed?
if (count > CACHE_BLOCK_SIZE && isBlockCompressed)
{
unsigned long i, numBlocks = count / CACHE_BLOCK_SIZE;
for (i=0; i<numBlocks; i++)
{
unsigned long blockOffset = GetBlockOffset(pCompressedData, i);
decompressStatus = fc8_decode(pCompressedData + blockOffset, dest + i * CACHE_BLOCK_SIZE, CACHE_BLOCK_SIZE);
// error?
if (!decompressStatus)
break;
}
}
else
{
decompressStatus = fc8_decode(src, dest, count);
}
// restore the old addressing mode
SwapMMUMode(&addressMode);
if (!decompressStatus)
{
result = 1; // decompression error
}
}
else
{
// fill the RAM disk with the ROM disk image
if (GetMMUMode() == false32b)
ctx->copyfunc(src, dest, count);
else
BlockMoveData(src, dest, count);
}
return result;
}
void FillCacheBlock(RomDrvContext *ctx, unsigned long entry, long offset)
{
short result = 0;
if (!DiskIsBlockCompressed())
{
result = GetDiskDataFromROMToRamdisk(ctx, (unsigned char*)kRomDrvLocation + offset, ctx->cacheRecords[entry].pData, CACHE_BLOCK_SIZE);
}
else
{
unsigned long blockNumber = offset / CACHE_BLOCK_SIZE, blockOffset;
blockOffset = GetBlockOffset((unsigned char*)kRomDrvLocation, blockNumber);
if (ctx->cacheRecords[entry].isValid)
ctx->blocksFlushed++;
result = GetDiskDataFromROMToRamdisk(ctx, (unsigned char*)kRomDrvLocation + blockOffset, ctx->cacheRecords[entry].pData, CACHE_BLOCK_SIZE);
ctx->blocksLoaded++;
}
if (result)
DisplayDiskError(result);
}
char SetupRAMDisk(RomDrvContext *ctx, char useCache)
{
char result = 0;
unsigned long bytesNeeded = useCache ? (CACHE_BLOCK_SIZE * NUM_CACHE_BLOCKS) : UncompressedDiskSize();
ctx->useram = 1;
// In 24-bit mode, first try to use normally inaccessable RAM beyond 8MB for the RAM disk
if (CanAllocAbove8MB(bytesNeeded))
{
ctx->ram24at8mb = 1;
ctx->ramdisk = NewPtrSys(1); // just a dummy pointer to make rest of code work
ctx->disk = (unsigned char *)(0x00800000);
}
// Both 24 and 32-bit mode try to allocate RAM by directly lowering BufPtr
else if (CanAllocWithBufPtr(bytesNeeded))
{
// carve out space for the RAM disk by lowering BufPtr
*BufPtr -= bytesNeeded;
ctx->ramdisk = (char*)*BufPtr;
ctx->disk = (unsigned char *)RomDrvStripAddress((Ptr)ctx->ramdisk);
}
else
{
// allocation attempts failed.
result = 2; // not enough RAM for decompression
ctx->useram = 0;
ctx->drvsts.writeProt = 0xFF;
}
if (ctx->ramdisk)
{
if (useCache)
{
// setup pointers to cache block storage in RAM. Data will be loaded on demand
int i;
for (i=0; i<NUM_CACHE_BLOCKS; i++)
ctx->cacheRecords[i].pData = ctx->disk + CACHE_BLOCK_SIZE * i;
ctx->usecache = 1;
}
else
{
// copy/decompress the whole disk image into RAM
long startTime, totalTime;
if (DiskIsCompressed())
DisplayBoxedStrings(ICON_NONE, "\pDecompressing disk image...", NULL);
else
DisplayBoxedStrings(ICON_NONE, "\pPreparing disk image...", NULL);
startTime = TickCount();
result = GetDiskDataFromROMToRamdisk(ctx, (unsigned char*)kRomDrvLocation, ctx->disk, UncompressedDiskSize());
totalTime = TickCount() - startTime;
if (result == 0)
{
// show the copy/decompression time, then wait for key
/*Str255 s;
Str32 s2;
PStrCpy(s, "\pDecompression time: ");
PUShortToStr(s2, (unsigned short)totalTime);
PStrCat(s,s2);
DisplayErrorString(s);
*/
}
}
}
return result;
}
char DrawIntroScreen()
{
long wait;
long diagWait;
char diskmode = 0;
Str255 s,sd;
Str32 s2;
unsigned short megs;
unsigned char drewPrompt;
// find megabytes of installed RAM
PStrCpy(s, "\p Detected ");
megs = (*MemTop) >> 20;
PUShortToStr(s2, megs);
PStrCat(s, s2);
PStrCat(s, "\p MB RAM, ");
// set the diagnostic info
if (GetMMUMode() == true32b)
PStrCat(s, "\p32-bit mode ");
else
PStrCat(s, "\p24-bit mode ");
if (DiskIsCompressed())
{
unsigned long diskBytes = UncompressedDiskSize();
megs = (diskBytes + 512L * 1024) / (1024L * 1024L);
PUShortToStr(sd, megs);
if (DiskIsBlockCompressed())
PStrCat(sd, "\p MB block-compressed disk image");
else
PStrCat(sd, "\p MB whole-compressed disk image");
}
else
{
megs = (kRomDrvSize + 512L * 1024) / (1024L * 1024L);
PUShortToStr(sd, megs);
if (DiskIsBlockCompressed())
PStrCat(sd, "\p MB uncompressed disk image");
}
DisplayBoxedStrings(ICON_TITLE, s, sd);
wait = TickCount() + 360;
diagWait = TickCount() + 120;
drewPrompt = 0;
while (TickCount() < wait)
{
// overwrite diagnotic info after 1 second
if (TickCount() > diagWait && drewPrompt == 0)
{
DisplayBoxedStrings(ICON_DISK, "\pPress R for rom disk, A for ram disk", NULL);
drewPrompt = 1;
}
// check for 'r' key
if(LLKeyMap[1] & 0x80)
{
diskmode = 1;
break;
}
// check for 'a' key
else if(LLKeyMap[0] & 0x01)
{
diskmode = 2;
break;
}
// check for 'x' key
else if(LLKeyMap[0] & 0x80)
{
diskmode = 3;
break;
}
}
return diskmode;
}
unsigned long GetCacheEntryForOffset(RomDrvContext *ctx, long offset)
{
unsigned long lowestTimestamp = 0xFFFFFFFF;
unsigned long entry, replaceableEntry = NUM_CACHE_BLOCKS;
unsigned long desiredBlockOffset = offset & (~(CACHE_BLOCK_SIZE-1));
for (entry=0; entry<NUM_CACHE_BLOCKS; entry++)
{
CacheRecord* pCacheRecord = &ctx->cacheRecords[entry];
if (pCacheRecord->isValid)
{
// is the desired data in this cache block?
if (pCacheRecord->diskOffset == desiredBlockOffset)
{
break;
}
// could this entry be used to load a new cache block?
if (!pCacheRecord->isModified && lowestTimestamp != 0 && pCacheRecord->timestamp < lowestTimestamp)
{
// old, unmodified entry can be replaced if necessary
replaceableEntry = entry;
lowestTimestamp = pCacheRecord->timestamp;
}
}
else if (lowestTimestamp != 0)
{
// unused/invalid entry can be replaced if necessary
replaceableEntry = entry;
lowestTimestamp = 0;
}
}
// didn't find the necessary block in the cache? load it into the cache
if (entry == NUM_CACHE_BLOCKS)
{
// is there a replaceable entry?
if (replaceableEntry != NUM_CACHE_BLOCKS)
{
CacheRecord* pCacheRecord = &ctx->cacheRecords[replaceableEntry];
FillCacheBlock(ctx, replaceableEntry, offset);
pCacheRecord->isValid = 1;
pCacheRecord->isModified = 0;
pCacheRecord->hitCount = 0;
pCacheRecord->diskOffset = desiredBlockOffset;
entry = replaceableEntry;
}
}
return entry;
}
short HandleDiskRead(RomDrvContext *ctx, IOParamPtr p, DCtlPtr d, long off)
{
if (!ctx->usecache)
{
CopyDiskDataToIObuf(ctx, ctx->disk + off, p->ioBuffer, p->ioReqCount);
}
else
{
long subOff = off;
long subCount = p->ioReqCount;
unsigned long entry;
// get data from the necessary cache block(s)
while (subCount != 0)
{
entry = GetCacheEntryForOffset(ctx, subOff);
// copy the data from the cache block
if (entry == NUM_CACHE_BLOCKS)
{
// cache block isn't loaded, and nothing could be unloaded to make room for it!
// TODO: prevent this from happening
return offLinErr;
}
else
{
CacheRecord* pCacheRecord = &ctx->cacheRecords[entry];
unsigned long copySize = subCount;
if (copySize > (pCacheRecord->diskOffset + CACHE_BLOCK_SIZE) - subOff)
copySize = (pCacheRecord->diskOffset + CACHE_BLOCK_SIZE) - subOff;
CopyDiskDataToIObuf(ctx, pCacheRecord->pData + subOff - pCacheRecord->diskOffset, p->ioBuffer + subOff - off, copySize);
subOff += copySize;
subCount -= copySize;
pCacheRecord->timestamp = ctx->ioCount;
pCacheRecord->hitCount++;
}
}
}
p->ioActCount = p->ioReqCount;
d->dCtlPosition = off + p->ioReqCount;
p->ioPosOffset = d->dCtlPosition;
return noErr;
}
short HandleDiskWrite(RomDrvContext *ctx, IOParamPtr p, DCtlPtr d, long off)
{
if (!ctx->usecache)
{
CopyDiskDataFromIObuf(ctx, p->ioBuffer, ctx->disk + off, p->ioReqCount);
}
else
{
long subOff = off;
long subCount = p->ioReqCount;
unsigned long entry;
// write data to the necessary cache block(s)
while (subCount != 0)
{
entry = GetCacheEntryForOffset(ctx, subOff);
// copy the data from the cache block
if (entry == NUM_CACHE_BLOCKS)
{
// cache block isn't loaded, and nothing could be unloaded to make room for it!
// this should never happen
return verErr;
}
else
{
CacheRecord* pCacheRecord = &ctx->cacheRecords[entry];
unsigned long copySize = subCount;
// if this block would be newly modified, and would leave fewer than 2 unmodified blocks
// remaining, report an error and fail
if (!ctx->showedFullError && !pCacheRecord->isModified && ctx->numModifiedBlocks >= NUM_CACHE_BLOCKS - 2)
{
DisplayErrorString("\pWrite cache is full. Try rom disk X option.");
subOff += copySize;
subCount -= copySize;
pCacheRecord->timestamp = ctx->ioCount;
ctx->drvsts.writeProt = 0xFF;
ctx->showedFullError = 1;
return verErr;
}
else
{
if (copySize > (pCacheRecord->diskOffset + CACHE_BLOCK_SIZE) - subOff)
copySize = (pCacheRecord->diskOffset + CACHE_BLOCK_SIZE) - subOff;
CopyDiskDataFromIObuf(ctx, p->ioBuffer + subOff - off, pCacheRecord->pData + subOff - pCacheRecord->diskOffset, copySize);
subOff += copySize;
subCount -= copySize;
pCacheRecord->timestamp = ctx->ioCount;
pCacheRecord->hitCount++;
if (!pCacheRecord->isModified)
ctx->numModifiedBlocks++;
pCacheRecord->isModified = 1;
}
}
}
}
p->ioActCount = p->ioReqCount;
d->dCtlPosition = off + p->ioReqCount;
p->ioPosOffset = d->dCtlPosition;
return noErr;
}
short RomDrvPrime(IOParamPtr p, DCtlPtr d)
{
long off;
RomDrvContext *ctx;
char startup;
char ram;
if (!d->dCtlStorage)
{
p->ioResult = offLinErr;
return offLinErr;
}
ctx = *(RomDrvContext**)d->dCtlStorage;
if (!ctx->drvsts.diskInPlace)
{
return offLinErr;
}
// on first call to Prime, setup the disk
if (!ctx->initialized)
{
// initialize as a ready-only disk
ctx->initialized = 1;
// check the startup and ram disk preferences from the control panel
// TBD: how do we know something else isn't using this area of PRAM?
ReadXPRam(1, 4, &startup);
ReadXPRam(1, 5, &ram);
if (startup && 0)
{
if(ram)
SetupRAMDisk(ctx, 1);
// else continue as a read-only ROM disk
}
else
{
char diskmode = DrawIntroScreen(ctx);
// confirm the block size
if (diskmode > 0 && DiskIsBlockCompressed())
{
unsigned long blockSize =
((unsigned long)kRomDrvLocation[8]) << 24 |
((unsigned long)kRomDrvLocation[9]) << 16 |
((unsigned long)kRomDrvLocation[10]) << 8 |
((unsigned long)kRomDrvLocation[11]);
if (blockSize != CACHE_BLOCK_SIZE)
{
Str255 s;
Str32 sd;
PStrCpy(s, "\pCompressed image block size is not ");
PUShortToStr(sd, CACHE_BLOCK_SIZE / 1024);
PStrCat(s, sd);
PStrCat(s, "\pK");
DisplayErrorString(s);
}
}
// check which mode was selected
if (diskmode == 0)
{
// disable the disk, by flagging it as not in place
ctx->drvsts.diskInPlace = 0;
EraseBox();
return offLinErr;
}
else if ((diskmode == 1 && DiskIsBlockCompressed()) ||
(diskmode == 2 && !DiskIsWholeCompressed()))
{
// R: create read-only disk directly from ROM, when disk image is compressed
// A: create read/write disk with a small RAM cache
char result = SetupRAMDisk(ctx, 1);
if (result)
DisplayDiskError(result);
else if (diskmode == 2)
ctx->drvsts.writeProt = 0;
}
else if (DiskIsWholeCompressed() || diskmode == 3)
{
// X: create read/write disk as a fully-RAM resident disk
char result = SetupRAMDisk(ctx, 0);
if (result)
DisplayDiskError(result);
else if (diskmode == 2 || diskmode == 3)
ctx->drvsts.writeProt = 0;
}
EraseBox();
}
}
// now process the I/O request
switch (p->ioPosMode & 0x000F)
{
case fsAtMark: off = d->dCtlPosition; break;
case fsFromStart: off = p->ioPosOffset; break;
case fsFromMark: off = d->dCtlPosition + p->ioPosOffset; break;
default: break;
}
ctx->ioCount++;
if ((p->ioTrap & 0x00ff) == aRdCmd)
{
short result = noErr;
/* bit 6 indicates this should be a verify operation */
if (!(p->ioPosMode & 0x40))
{
result = HandleDiskRead(ctx, p, d, off);
}
return result;
}
else if((p->ioTrap & 0x00ff) == aWrCmd && ctx->ramdisk)
{
return HandleDiskWrite(ctx, p, d, off);
}
SysBeep(0);
return wPrErr;
}
short RomDrvClose(IOParamPtr /*p*/, DCtlPtr d)
{
RomDrvContext *ctx;
if (!d->dCtlStorage) return noErr;
ctx = *(RomDrvContext**)d->dCtlStorage;
if (ctx->ramdisk) DisposePtr(ctx->ramdisk);
DisposePtr(ctx->origcopyfunc);
HUnlock(d->dCtlStorage);
DisposeHandle(d->dCtlStorage);
d->dCtlStorage = NULL;
return noErr;
}
short main(IOParamPtr p, DCtlPtr d, short cmd)
{
switch(cmd) {
case 0: return RomDrvOpen(p, d);
case 1: return RomDrvPrime(p, d);
case 2:
if (((CntrlParamPtr)p)->csCode == 21)
{
*(Ptr*)&((CntrlParamPtr)p)->csParam = (Ptr)&DiskIcon;
return noErr;
}
else if (((CntrlParamPtr)p)->csCode == 22)
{
*(Ptr*)&((CntrlParamPtr)p)->csParam = (Ptr)&DiskIcon;
return noErr;
}
#if NEED_ACCRUN
if (((CntrlParamPtr)p)->csCode == accRun)
{
RomDrvContext *ctx;
if (!d->dCtlStorage) return noErr;
ctx = *(RomDrvContext**)d->dCtlStorage;
// if we want a ram disk, but haven't allocated one yet, do it now
if (ctx->useram && !ctx->ramdisk)
{
// create a buffer for the RAM disk in the system heap
// this doesn't seem to work reliabily - OK in System 7.0.1, but hangs at Finder in 7.1
ctx->ramdisk = NewPtrSys(UncompressedDiskSize());
if(ctx->ramdisk)
{
ctx->disk = (unsigned char *)ctx->ramdisk;
FillRAMDisk(ctx);
}
else
{
ctx->useram = 0;
}
}
d->dCtlDelay = 0;
d->dCtlFlags &= ~dNeedTimeMask;
return noErr;
}
#endif
return controlErr;
case 3:
#if NEED_DRVSTAT
if (!d->dCtlStorage) return statusErr;
if(((CntrlParamPtr)p)->csCode == drvStsCode) {
BlockMove(*d->dCtlStorage, &((CntrlParamPtr)p)->csParam, sizeof(DrvSts2));
return noErr;
}
#endif
return statusErr;
case 4: return RomDrvClose(p, d);
default: return noErr;
}
}