]> Shamusworld >> Repos - apple2/blobdiff - src/floppy.cpp
Fixed misc. bugs preventing certain games from working, added pause mode.
[apple2] / src / floppy.cpp
index e811bb0f53d67d58ef17aa7b222e6ce4ef7361e2..193e5cb5bd734cae90cb9a4d00ec20158fe1c49e 100755 (executable)
 #include "floppy.h"
 
 #include <stdio.h>
-#include <string>
+#include <string.h>
 #include "apple2.h"
 #include "log.h"
 #include "applevideo.h"                                        // For message spawning... Though there's probably a better approach than this!
 
-using namespace std;
+//using namespace std;
 
 // Useful enums
 
@@ -29,14 +29,15 @@ enum { IO_MODE_READ, IO_MODE_WRITE };
 
 // 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...
 
@@ -46,6 +47,7 @@ FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), ph
        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
 }
 
@@ -58,8 +60,10 @@ FloppyDrive::~FloppyDrive()
                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);
+
        if (driveNum > 1)
        {
                WriteLog("FLOPPY: Attempted to load image to drive #%u!\n", driveNum);
@@ -82,7 +86,7 @@ bool FloppyDrive::LoadImage(const char * filename, uint8 driveNum/*= 0*/)
        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);
@@ -90,10 +94,25 @@ bool FloppyDrive::LoadImage(const char * filename, uint8 driveNum/*= 0*/)
        DetectImageType(filename, driveNum);
        strcpy(imageName[driveNum], filename);
 
+#if 0
+       WriteLog("FLOPPY: Opening image for drive #%u.\n", driveNum);
+       FILE * fp2 = fopen("bt-nybblized.nyb", "wb");
+
+       if (fp2 == NULL)
+               WriteLog("FLOPPY: Failed to open image file 'bt-nybblized.nyb' for writing...\n");
+       else
+       {
+               fwrite(nybblizedImage[driveNum], 1, 232960, fp2);
+               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*/)
 {
        if (driveNum > 1)
        {
@@ -129,33 +148,37 @@ bool FloppyDrive::SaveImage(uint8 driveNum/*= 0*/)
        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 !!!"
        strcpy(imageName[driveNum], filename);
        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);
@@ -166,25 +189,29 @@ void FloppyDrive::SwapImages(void)
        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;
 
@@ -226,14 +253,22 @@ WRT to the disk image itself.
 //*/
                }
 
+// Actually, it just might matter WRT to nybblyzing/denybblyzing
+// Here, we check for BT3
+//Nope, no change...
+//diskType[driveNum] = DT_PRODOS;
+
                NybblizeImage(driveNum);
        }
+
+#warning "Should we attempt to nybblize unknown images here? Definitely SHOULD issue a warning!"
+
 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"))));
 }
 
-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])
@@ -242,7 +277,7 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
        // 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,
@@ -250,7 +285,7 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
                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,
@@ -260,15 +295,15 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
                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
 
@@ -280,7 +315,7 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
                        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);
@@ -291,7 +326,7 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
 
                        // 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)
@@ -310,14 +345,22 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
 
                        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
+{
+WriteLog("FL: i = %u, img[i] = %02X, diskbyte = %02X\n", i, img[i], diskbyte[img[i] >> 2]);
+#endif
                                img[i] = diskbyte[img[i] >> 2];
-
+#ifdef TEST_NYBBLIZATION
+//WriteLog("            img[i] = %02X\n", img[i]);
+}
+#endif
                        img += 343;
 
                        // Done with the nybblization, now for the epilogue...
@@ -328,9 +371,9 @@ void FloppyDrive::NybblizeImage(uint8 driveNum)
        }
 }
 
-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,
@@ -356,19 +399,19 @@ void FloppyDrive::DenybblizeImage(uint8 driveNum)
                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]
@@ -377,7 +420,7 @@ void FloppyDrive::DenybblizeImage(uint8 driveNum)
                                        && 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)
@@ -389,7 +432,7 @@ void FloppyDrive::DenybblizeImage(uint8 driveNum)
                        }
 
                        // 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);
@@ -398,24 +441,24 @@ void FloppyDrive::DenybblizeImage(uint8 driveNum)
 
                        // 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);
@@ -429,6 +472,96 @@ void FloppyDrive::DenybblizeImage(uint8 driveNum)
        }
 }
 
+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
 
 /*
@@ -442,7 +575,7 @@ also more versatile and can represent the older 13-sector disks, many copy-prote
 other unusual encodings.
 */
 
-void FloppyDrive::ControlStepper(uint8 addr)
+void FloppyDrive::ControlStepper(uint8_t addr)
 {
        // $C0E0 - 7
 /*
@@ -452,7 +585,7 @@ bit 0 is the "do something" bit.
 */
        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)
@@ -470,24 +603,25 @@ bit 0 is the "do something" bit.
 SpawnMessage("Stepping to track %u...", track);
        }
 
-//     return something if read mode...        
+//     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("%sing %s track %u, sector %u...", (ioMode == IO_MODE_READ ? "Read" : "Write"),
+SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
+       (ioMode == IO_MODE_READ ? "Read" : "Write"),
        (ioMode == IO_MODE_READ ? "from" : "to"), track, currentPos / 396);
        // $C0EC
 /*
@@ -498,23 +632,32 @@ Which we now do. :-)
 */
        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;
@@ -531,3 +674,38 @@ 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
+*/