//
// Apple 2 floppy disk support
//
-// by James L. Hammons
+// by James Hammons
// (c) 2005 Underground Software
//
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- ------------------------------------------------------------
// FloppyDrive class variable initialization
-uint8 FloppyDrive::header[21] = {
+uint8_t FloppyDrive::header[21] = {
0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xDE, 0xAA, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xD5, 0xAA, 0xAD };
-uint8 FloppyDrive::doSector[16] = {
+uint8_t FloppyDrive::doSector[16] = {
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
-uint8 FloppyDrive::poSector[16] = {
+uint8_t FloppyDrive::poSector[16] = {
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
+char FloppyDrive::nameBuf[MAX_PATH];
+
// FloppyDrive class implementation...
diskSize[0] = diskSize[1] = 0;
diskType[0] = diskType[1] = DT_UNKNOWN;
imageDirty[0] = imageDirty[1] = false;
+ writeProtected[0] = writeProtected[1] = false;
imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
}
+
FloppyDrive::~FloppyDrive()
{
if (disk[0])
delete[] disk[1];
}
-bool FloppyDrive::LoadImage(const char * filename, uint8 driveNum/*= 0*/)
+
+bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
{
WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
fseek(fp, 0, SEEK_END);
diskSize[driveNum] = ftell(fp);
fseek(fp, 0, SEEK_SET);
- disk[driveNum] = new uint8[diskSize[driveNum]];
+ disk[driveNum] = new uint8_t[diskSize[driveNum]];
fread(disk[driveNum], 1, diskSize[driveNum], fp);
fclose(fp);
fclose(fp2);
}
#endif
+//writeProtected[driveNum] = true;
WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
return true;
}
-bool FloppyDrive::SaveImage(uint8 driveNum/*= 0*/)
+
+bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
{
+ // Various sanity checks...
if (driveNum > 1)
{
WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
return false;
}
+ // Handle nybbylization, if necessary
if (diskType[driveNum] == DT_NYBBLE)
memcpy(disk[driveNum], nybblizedImage[driveNum], 232960);
else
DenybblizeImage(driveNum);
+ // Finally, write the damn image
FILE * fp = fopen(imageName[driveNum], "wb");
if (fp == NULL)
fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
fclose(fp);
+ WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
+
return true;
}
-bool FloppyDrive::SaveImageAs(const char * filename, uint8 driveNum/*= 0*/)
+
+bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
{
//WARNING: Buffer overflow possibility
#warning "Buffer overflow possible--!!! FIX !!!"
return SaveImage(driveNum);
}
-void FloppyDrive::CreateBlankImage(uint8 driveNum/*= 0*/)
+
+void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
{
if (disk[driveNum] != NULL)
delete disk[driveNum];
- disk[driveNum] = new uint8[143360];
+ disk[driveNum] = new uint8_t[143360];
diskSize[driveNum] = 143360;
memset(disk[driveNum], 0x00, 143360);
memset(nybblizedImage[driveNum], 0x00, 232960); // Set it to 0 instead of $FF for proper formatting...
diskType[driveNum] = DT_DOS33;
strcpy(imageName[driveNum], "newblank.dsk");
+ writeProtected[driveNum] = false;
SpawnMessage("New blank image inserted in drive %u...", driveNum);
}
+
void FloppyDrive::SwapImages(void)
{
- uint8 nybblizedImageTmp[232960];
+ uint8_t nybblizedImageTmp[232960];
char imageNameTmp[MAX_PATH];
memcpy(nybblizedImageTmp, nybblizedImage[0], 232960);
memcpy(imageName[0], imageName[1], MAX_PATH);
memcpy(imageName[1], imageNameTmp, MAX_PATH);
- uint8 * diskTmp = disk[0];
+ uint8_t * diskTmp = disk[0];
disk[0] = disk[1];
disk[1] = diskTmp;
- uint32 diskSizeTmp = diskSize[0];
+ uint32_t diskSizeTmp = diskSize[0];
diskSize[0] = diskSize[1];
diskSize[1] = diskSizeTmp;
- uint8 diskTypeTmp = diskType[0];
+ uint8_t diskTypeTmp = diskType[0];
diskType[0] = diskType[1];
diskType[1] = diskTypeTmp;
- uint8 imageDirtyTmp = imageDirty[0];
+ uint8_t imageDirtyTmp = imageDirty[0];
imageDirty[0] = imageDirty[1];
imageDirty[1] = imageDirtyTmp;
+
+ uint8_t writeProtectedTmp = writeProtected[0];
+ writeProtected[0] = writeProtected[1];
+ writeProtected[1] = writeProtectedTmp;
SpawnMessage("Drive 0: %s...", imageName[0]);
}
-void FloppyDrive::DetectImageType(const char * filename, uint8 driveNum)
+
+void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
{
diskType[driveNum] = DT_UNKNOWN;
WriteLog("FLOPPY: Found extension [%s]...\n", ext);
//Apparently .dsk can house either DOS order OR PRODOS order... !!! FIX !!!
-//[DONE, see below why we don't need it]
if (strcasecmp(ext, ".po") == 0)
diskType[driveNum] = DT_PRODOS;
else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
{
+ // We assume this, but check for a PRODOS fingerprint. Trust, but
+ // verify. ;-)
diskType[driveNum] = DT_DOS33;
-//WriteLog("Detected DOS 3.3 disk!\n");
-/*
-This doesn't seem to be accurate... Maybe it's just a ProDOS disk in a DOS33 order...
-That would seem to be the case--just because it's a ProDOS disk doesn't mean anything
-WRT to the disk image itself.
- // This could really be a ProDOS order disk with a .dsk extension, so let's see...
- char fingerprint[3][4] = {
- { 0x04, 0x00, 0x00, 0x00 }, // @ $500
- { 0x03, 0x00, 0x05, 0x00 }, // @ $700
- { 0x02, 0x00, 0x04, 0x00 } }; // @ $900
-
- if ((strcmp((char *)(disk[driveNum] + 0x500), fingerprint[0]) == 0)
- && (strcmp((char *)(disk[driveNum] + 0x700), fingerprint[1]) == 0)
- && (strcmp((char *)(disk[driveNum] + 0x900), fingerprint[2]) == 0))
+
+ uint8_t fingerprint[4][4] = {
+ { 0x00, 0x00, 0x03, 0x00 }, // @ $400
+ { 0x02, 0x00, 0x04, 0x00 }, // @ $600
+ { 0x03, 0x00, 0x05, 0x00 }, // @ $800
+ { 0x04, 0x00, 0x00, 0x00 } // @ $A00
+ };
+
+ bool foundProdos = true;
+
+ for(uint32_t i=0; i<4; i++)
+ {
+ for(uint32_t j=0; j<4; j++)
+ {
+ if (disk[driveNum][0x400 + (i * 0x200) + j] != fingerprint[i][j])
+ {
+ foundProdos = false;
+ break;
+ }
+ }
+ }
+
+ if (foundProdos)
diskType[driveNum] = DT_PRODOS;
-//*/
}
// Actually, it just might matter WRT to nybblyzing/denybblyzing
-// Here, we check for BT3
-//Nope, no change...
-//diskType[driveNum] = DT_PRODOS;
-
+// (and, it does... :-P)
+ NybblizeImage(driveNum);
+ }
+ else if (diskSize[driveNum] == 143488)
+ {
+ diskType[driveNum] = DT_DOS33_HDR;
NybblizeImage(driveNum);
}
WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
"Nybble image" : (diskType[driveNum] == DT_DOS33 ?
- "DOS 3.3 image" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown"))));
+ "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
+ "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
}
-void FloppyDrive::NybblizeImage(uint8 driveNum)
+
+void FloppyDrive::NybblizeImage(uint8_t driveNum)
{
// Format of a sector is header (23) + nybbles (343) + footer (30) = 396
// (short by 20 bytes of 416 [413 if 48 byte header is one time only])
// hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
// (not incl. 64 byte track marker)
- uint8 footer[48] = {
+ uint8_t footer[48] = {
0xDE, 0xAA, 0xEB, 0xFF, 0xEB, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
- uint8 diskbyte[0x40] = {
+ uint8_t diskbyte[0x40] = {
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
- uint8 * img = nybblizedImage[driveNum];
+ uint8_t * img = nybblizedImage[driveNum];
memset(img, 0xFF, 232960); // Doesn't matter if 00s or FFs...
- for(uint8 trk=0; trk<35; trk++)
+ for(uint8_t trk=0; trk<35; trk++)
{
memset(img, 0xFF, 64); // Write gap 1, 64 bytes (self-sync)
img += 64;
- for(uint8 sector=0; sector<16; sector++)
+ for(uint8_t sector=0; sector<16; sector++)
{
memcpy(img, header, 21); // Set up the sector header
img[10] = ((trk ^ sector ^ 0xFE) & 0x55) | 0xAA;
img += 21;
- uint8 * bytes = disk[driveNum];
+ uint8_t * bytes = disk[driveNum];
if (diskType[driveNum] == DT_DOS33)
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
+ else if (diskType[driveNum] == DT_DOS33_HDR)
+ bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
else if (diskType[driveNum] == DT_PRODOS)
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
else
// Convert the 256 8-bit bytes into 342 6-bit bytes.
- for(uint16 i=0; i<0x56; i++)
+ for(uint16_t i=0; i<0x56; i++)
{
img[i] = ((bytes[(i + 0xAC) & 0xFF] & 0x01) << 7)
| ((bytes[(i + 0xAC) & 0xFF] & 0x02) << 5)
img[342] = 0x00;
- for(uint16 i=342; i>0; i--)
+ for(uint16_t i=342; i>0; i--)
img[i] = img[i] ^ img[i - 1];
// Using a lookup table, convert the 6-bit bytes into disk bytes.
- for(uint16 i=0; i<343; i++)
+ for(uint16_t i=0; i<343; i++)
//#define TEST_NYBBLIZATION
#ifdef TEST_NYBBLIZATION
{
}
}
-void FloppyDrive::DenybblizeImage(uint8 driveNum)
+
+void FloppyDrive::DenybblizeImage(uint8_t driveNum)
{
- uint8 decodeNybble[0x80] = {
+ uint8_t decodeNybble[0x80] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
return;
}
- uint8 * srcImg = nybblizedImage[driveNum];
- uint8 * dstImg = disk[driveNum];
- uint8 buffer[345]; // 2 extra bytes for the unpack routine below...
+ uint8_t * srcImg = nybblizedImage[driveNum];
+ uint8_t * dstImg = disk[driveNum];
+ uint8_t buffer[345]; // 2 extra bytes for the unpack routine below...
- for(uint8 trk=0; trk<35; trk++)
+ for(uint8_t trk=0; trk<35; trk++)
{
- uint8 * trackBase = srcImg + (trk * 6656);
+ uint8_t * trackBase = srcImg + (trk * 6656);
- for(uint8 sector=0; sector<16; sector++)
+ for(uint8_t sector=0; sector<16; sector++)
{
- uint16 sectorStart = (uint16)-1;
+ uint16_t sectorStart = (uint16_t)-1;
- for(uint16 i=0; i<6656; i++)
+ for(uint16_t i=0; i<6656; i++)
{
if (trackBase[i] == header[0]
&& trackBase[(i + 1) % 6656] == header[1]
&& trackBase[(i + 4) % 6656] == header[4])
{
//Could also check the track # at +5,6...
- uint8 foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
+ uint8_t foundSector = ((trackBase[(i + 7) % 6656] & 0x55) << 1)
| (trackBase[(i + 8) % 6656] & 0x55);
if (foundSector == sector)
}
// Sanity check...
- if (sectorStart == (uint16)-1)
+ if (sectorStart == (uint16_t)-1)
{
WriteLog("FLOPPY: Failed to find sector %u (track %u) in nybble image!\n",
sector, trk);
// Using a lookup table, convert the disk bytes into 6-bit bytes.
- for(uint16 i=0; i<343; i++)
+ for(uint16_t i=0; i<343; i++)
buffer[i] = decodeNybble[trackBase[(sectorStart + i) % 6656] & 0x7F];
// XOR the data block with itself, offset by one byte.
- for(uint16 i=1; i<342; i++)
+ for(uint16_t i=1; i<342; i++)
buffer[i] = buffer[i] ^ buffer[i - 1];
// Convert the 342 6-bit bytes into 256 8-bit bytes (at buffer + $56).
- for(uint16 i=0; i<0x56; i++)
+ for(uint16_t i=0; i<0x56; i++)
{
buffer[0x056 + i] |= ((buffer[i] >> 3) & 0x01) | ((buffer[i] >> 1) & 0x02);
buffer[0x0AC + i] |= ((buffer[i] >> 5) & 0x01) | ((buffer[i] >> 3) & 0x02);
buffer[0x102 + i] |= ((buffer[i] >> 7) & 0x01) | ((buffer[i] >> 5) & 0x02);
}
- uint8 * bytes = dstImg;
+ uint8_t * bytes = dstImg;
if (diskType[driveNum] == DT_DOS33)
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
+ else if (diskType[driveNum] == DT_DOS33_HDR)
+ bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
else if (diskType[driveNum] == DT_PRODOS)
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
else
}
}
+
+const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
+{
+ // Set up a zero-length string for return value
+ nameBuf[0] = 0;
+
+ if (driveNum > 1)
+ {
+ WriteLog("FLOPPY: Attempted to get image name for drive #%u!\n", driveNum);
+ return nameBuf;
+ }
+
+ // Now we attempt to strip out extraneous paths/extensions to get just the filename
+ const char * startOfFile = strrchr(imageName[driveNum], '/');
+ const char * startOfExt = strrchr(imageName[driveNum], '.');
+
+ // If there isn't a path, assume we're starting at the beginning
+ if (startOfFile == NULL)
+ startOfFile = &imageName[driveNum][0];
+ else
+ startOfFile++;
+
+ // If there isn't an extension, assume it's at the terminating NULL
+ if (startOfExt == NULL)
+ startOfExt = &imageName[driveNum][0] + strlen(imageName[driveNum]);
+
+ // Now copy the filename (may copy nothing!)
+ int j = 0;
+
+ for(const char * i=startOfFile; i<startOfExt; i++)
+ nameBuf[j++] = *i;
+
+ nameBuf[j] = 0;
+
+ return nameBuf;
+}
+
+
+void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
+{
+ // Probably want to save a dirty image... ;-)
+ SaveImage(driveNum);
+
+ WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
+
+ if (disk[driveNum])
+ delete[] disk[driveNum];
+
+ disk[driveNum] = NULL;
+ diskSize[driveNum] = 0;
+ diskType[driveNum] = DT_UNKNOWN;
+ imageDirty[driveNum] = false;
+ writeProtected[driveNum] = false;
+ imageName[driveNum][0] = 0; // Zero out filenames
+ memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
+}
+
+
+bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
+{
+ if (driveNum > 1)
+ {
+ WriteLog("FLOPPY: Attempted DriveIsEmtpy() for drive #%u!\n", driveNum);
+ return true;
+ }
+
+ // This is kinda gay, but it works
+ return (imageName[driveNum][0] == 0 ? true : false);
+}
+
+
+bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
+{
+ if (driveNum > 1)
+ {
+ WriteLog("FLOPPY: Attempted DiskIsWriteProtected() for drive #%u!\n", driveNum);
+ return true;
+ }
+
+ return writeProtected[driveNum];
+}
+
+
+void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
+{
+ if (driveNum > 1)
+ {
+ WriteLog("FLOPPY: Attempted set write protect for drive #%u!\n", driveNum);
+ return;
+ }
+
+ writeProtected[driveNum] = state;
+}
+
+
// Memory mapped I/O functions
/*
other unusual encodings.
*/
-void FloppyDrive::ControlStepper(uint8 addr)
+void FloppyDrive::ControlStepper(uint8_t addr)
{
// $C0E0 - 7
/*
*/
if (addr & 0x01)
{
- uint8 newPhase = (addr >> 1) & 0x03;
+ uint8_t newPhase = (addr >> 1) & 0x03;
//WriteLog("*** Stepper change [%u]: track = %u, phase = %u, newPhase = %u\n", addr, track, phase, newPhase);
if (((phase + 1) & 0x03) == newPhase)
// return something if read mode...
}
-void FloppyDrive::ControlMotor(uint8 addr)
+
+void FloppyDrive::ControlMotor(uint8_t addr)
{
// $C0E8 - 9
motorOn = addr;
}
-void FloppyDrive::DriveEnable(uint8 addr)
+
+void FloppyDrive::DriveEnable(uint8_t addr)
{
// $C0EA - B
activeDrive = addr;
}
-uint8 FloppyDrive::ReadWrite(void)
+
+uint8_t FloppyDrive::ReadWrite(void)
{
SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
(ioMode == IO_MODE_READ ? "Read" : "Write"),
*/
if (ioMode == IO_MODE_WRITE && (latchValue & 0x80))
{
- nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
- imageDirty[activeDrive] = true;
+ // Does it behave like this?
+#warning "Write protection kludged in--investigate real behavior!"
+ if (!writeProtected[activeDrive])
+ {
+ nybblizedImage[activeDrive][(track * 6656) + currentPos] = latchValue;
+ imageDirty[activeDrive] = true;
+ }
+ else
+//doesn't seem to do anything
+ return 0;//is this more like it?
}
- uint8 diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
+ uint8_t diskByte = nybblizedImage[activeDrive][(track * 6656) + currentPos];
currentPos = (currentPos + 1) % 6656;
//WriteLog("FL: diskByte=%02X, currentPos=%u\n", diskByte, currentPos);
return diskByte;
}
-uint8 FloppyDrive::GetLatchValue(void)
+
+uint8_t FloppyDrive::GetLatchValue(void)
{
// $C0ED
return latchValue;
}
-void FloppyDrive::SetLatchValue(uint8 value)
+
+void FloppyDrive::SetLatchValue(uint8_t value)
{
// $C0ED
latchValue = value;
}
+
void FloppyDrive::SetReadMode(void)
{
// $C0EE
ioMode = IO_MODE_READ;
}
+
void FloppyDrive::SetWriteMode(void)
{
// $C0EF
ioMode = IO_MODE_WRITE;
}
+
+/*
+PRODOS 8 MLI ERROR CODES
+
+$00: No error
+$01: Bad system call number
+$04: Bad system call parameter count
+$25: Interrupt table full
+$27: I/O error
+$28: No device connected
+$2B: Disk write protected
+$2E: Disk switched
+$40: Invalid pathname
+$42: Maximum number of files open
+$43: Invalid reference number
+$44: Directory not found
+$45: Volume not found
+$46: File not found
+$47: Duplicate filename
+$48: Volume full
+$49: Volume directory full
+$4A: Incompatible file format, also a ProDOS directory
+$4B: Unsupported storage_type
+$4C: End of file encountered
+$4D: Position out of range
+$4E: File access error, also file locked
+$50: File is open
+$51: Directory structure damaged
+$52: Not a ProDOS volume
+$53: Invalid system call parameter
+$55: Volume Control Block table full
+$56: Bad buffer address
+$57: Duplicate volume
+$5A: File structure damaged
+*/