From 0912d978d763e720952eef392da51c53afe21886 Mon Sep 17 00:00:00 2001 From: Mike Wiese Date: Sun, 22 Oct 2023 14:13:51 -0700 Subject: [PATCH] Add .DO support. Look inside .DSK files to detect sector order. --- lib/device/iwm/disk.cpp | 23 ++++++-- lib/device/iwm/disk2.cpp | 4 +- lib/media/apple/mediaType.cpp | 32 ++++++++++- lib/media/apple/mediaType.h | 4 +- lib/media/apple/mediaTypeDO.cpp | 99 ++++++++++++++++++++++++++++++++ lib/media/apple/mediaTypeDO.h | 26 +++++++++ lib/media/apple/mediaTypeDSK.cpp | 6 +- lib/media/media.h | 1 + 8 files changed, 186 insertions(+), 9 deletions(-) create mode 100644 lib/media/apple/mediaTypeDO.cpp create mode 100644 lib/media/apple/mediaTypeDO.h diff --git a/lib/device/iwm/disk.cpp b/lib/device/iwm/disk.cpp index 9f927db02..b5022ca6c 100644 --- a/lib/device/iwm/disk.cpp +++ b/lib/device/iwm/disk.cpp @@ -515,17 +515,33 @@ mediatype_t iwmDisk::mount(FILE *f, const char *filename, uint32_t disksize, med } // Determine MediaType based on filename extension - if (disk_type == MEDIATYPE_UNKNOWN && filename != nullptr) + if (disk_type == MEDIATYPE_UNKNOWN && filename != nullptr) { disk_type = MediaType::discover_mediatype(filename); + if (disk_type == MEDIATYPE_DSK) { + // determine DO or PO based on file contents + disk_type = MediaType::discover_dsk_mediatype(f, disksize); + } + } if (deviceSlot < 4) // SP drive { switch (disk_type) { + case MEDIATYPE_DO: + Debug_printf("\r\nMedia Type DO"); + _disk = new MediaTypeDO(); + _disk->_media_host = host; + _disk->_mediatype = disk_type; + strcpy(_disk->_disk_filename, filename); + mt = _disk->mount(f, disksize); + + device_active = true; //change status only after we are mounted + break; case MEDIATYPE_PO: Debug_printf("\r\nMedia Type PO"); _disk = new MediaTypePO(); _disk->_media_host = host; + _disk->_mediatype = disk_type; strcpy(_disk->_disk_filename, filename); mt = _disk->mount(f, disksize); @@ -535,8 +551,6 @@ mediatype_t iwmDisk::mount(FILE *f, const char *filename, uint32_t disksize, med readonly = false; device_active = true; //change status only after we are mounted - //_disk->fileptr() = f; - // mt = MEDIATYPE_PO; break; default: Debug_printf("\r\nMedia Type UNKNOWN for SP Drive - no mount in disk.cpp"); @@ -548,7 +562,8 @@ mediatype_t iwmDisk::mount(FILE *f, const char *filename, uint32_t disksize, med { switch (disk_type) { - case MEDIATYPE_DSK: + case MEDIATYPE_DO: + case MEDIATYPE_PO: case MEDIATYPE_WOZ: theFuji._fnDisk2s[deviceSlot - 4].init(); theFuji._fnDisk2s[deviceSlot - 4].mount(f, disk_type); // modulo to ensure device 0 or 1 diff --git a/lib/device/iwm/disk2.cpp b/lib/device/iwm/disk2.cpp index a11862ad1..5916a5632 100644 --- a/lib/device/iwm/disk2.cpp +++ b/lib/device/iwm/disk2.cpp @@ -60,10 +60,12 @@ mediatype_t iwmDisk2::mount(FILE *f, mediatype_t disk_type)//, const char *filen mt = ((MediaTypeWOZ *)_disk)->mount(f); change_track(0); // initialize spi buffer break; - case MEDIATYPE_DSK: + case MEDIATYPE_DO: + case MEDIATYPE_PO: Debug_printf("\nMounting Media Type DSK"); device_active = true; _disk = new MediaTypeDSK(); + _disk->_mediatype = disk_type; mt = ((MediaTypeDSK *)_disk)->mount(f); change_track(0); // initialize spi buffer break; diff --git a/lib/media/apple/mediaType.cpp b/lib/media/apple/mediaType.cpp index 7fdd2a2c8..356682143 100644 --- a/lib/media/apple/mediaType.cpp +++ b/lib/media/apple/mediaType.cpp @@ -1,6 +1,7 @@ #ifdef BUILD_APPLE #include "mediaType.h" +#include "utils.h" #include #include @@ -58,8 +59,37 @@ mediatype_t MediaType::discover_mediatype(const char *filename) const char *ext = filename + l - 2; if (strcasecmp(ext, "PO") == 0) return MEDIATYPE_PO; + else if (strcasecmp(ext, "DO") == 0) + return MEDIATYPE_DO; } return MEDIATYPE_UNKNOWN; } -#endif // NEW_TARGET \ No newline at end of file +mediatype_t MediaType::discover_dsk_mediatype(FILE *f, uint32_t disksize) +{ + mediatype_t default_mt = MEDIATYPE_DO; + + const size_t header_size = 64; + uint8_t hdr[header_size]; + + // a ProDOS Volume Directory Header is always in block 2, + // if the file is in ProDOS order, it will be at offset 1024 + if (fseek(f, 1024, SEEK_SET) != 0) + return default_mt; + + if (fread(hdr, 1, header_size, f) != header_size) + return default_mt; + + uint16_t prevPointer = UINT16_FROM_HILOBYTES(hdr[1], hdr[0]); + uint8_t storage_type = hdr[4] >> 4; + uint16_t total_blocks = UINT16_FROM_HILOBYTES(hdr[0x2A], hdr[0x29]); + + if (prevPointer == 0 && storage_type == 0xF && total_blocks == (disksize / 512)) { + // looks like a valid volume header, assume file is in ProDOS order + return MEDIATYPE_PO; + } + + return default_mt; +} + +#endif // BUILD_APPLE diff --git a/lib/media/apple/mediaType.h b/lib/media/apple/mediaType.h index a5709e71f..b36007eef 100644 --- a/lib/media/apple/mediaType.h +++ b/lib/media/apple/mediaType.h @@ -17,9 +17,10 @@ enum mediatype_t { MEDIATYPE_UNKNOWN = 0, + MEDIATYPE_DO, + MEDIATYPE_DSK, MEDIATYPE_PO, MEDIATYPE_WOZ, - MEDIATYPE_DSK, MEDIATYPE_COUNT }; @@ -85,6 +86,7 @@ class MediaType virtual bool status() = 0; static mediatype_t discover_mediatype(const char *filename); + static mediatype_t discover_dsk_mediatype(FILE* f, uint32_t disksize); // void dump_percom_block(); // void derive_percom_block(uint16_t numSectors); diff --git a/lib/media/apple/mediaTypeDO.cpp b/lib/media/apple/mediaTypeDO.cpp new file mode 100644 index 000000000..ddfee8a57 --- /dev/null +++ b/lib/media/apple/mediaTypeDO.cpp @@ -0,0 +1,99 @@ +#ifdef BUILD_APPLE + +#include "mediaTypeDO.h" + +#include +#include "utils.h" +#include "../../include/debug.h" + +#define BYTES_PER_BLOCK 512 +#define BLOCKS_PER_TRACK 8 +#define BYTES_PER_SECTOR 256 +#define BYTES_PER_TRACK 4096 + +// .DO (DOS ordered) disk images are stored in DOS 3.3 logical sector order. +// Each 512 byte ProDOS block is stored as two 256 byte DOS sectors. +// see https://retrocomputing.stackexchange.com/questions/15056/converting-apple-ii-prodos-blocks-to-dos-tracks-and-sectors + +// This table maps ProDOS blocks to pairs of DOS logical sectors. +static const int prodos2dos[8][2] = { {0, 14}, {13, 12}, {11, 10}, {9, 8}, {7, 6}, {5, 4}, {3, 2}, {1, 15} }; + +bool MediaTypeDO::read(uint32_t blockNum, uint16_t *count, uint8_t* buffer) +{ + bool err = false; + uint32_t track = blockNum / BLOCKS_PER_TRACK; + const int* sectors = prodos2dos[blockNum % BLOCKS_PER_TRACK]; + + err = read_sector(track, sectors[0], buffer); + + if (!err) + err = read_sector(track, sectors[1], &buffer[BYTES_PER_SECTOR]); + + return err; +} + +bool MediaTypeDO::read_sector(int track, int sector, uint8_t* buffer) +{ + Debug_printf("\r\nMediaTypeDO read track %d sector %d", track, sector); + + bool err = false; + uint32_t offset = (track * BYTES_PER_TRACK) + (sector * BYTES_PER_SECTOR); + + err = fseek(_media_fileh, offset, SEEK_SET) != 0; + + if (!err) + err = fread(buffer, 1, BYTES_PER_SECTOR, _media_fileh) != BYTES_PER_SECTOR; + + return err; +} + +bool MediaTypeDO::write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) +{ + // Return an error if we're trying to write beyond the end of the disk + if (blockNum >= num_blocks) + { + Debug_printf("\r\nwrite block BEYOND END %lu > %lu", blockNum, num_blocks); + return true; + } + + bool err = false; + uint32_t track = blockNum / BLOCKS_PER_TRACK; + const int* sectors = prodos2dos[blockNum % BLOCKS_PER_TRACK]; + + err = write_sector(track, sectors[0], buffer); + + if (!err) + err = write_sector(track, sectors[1], &buffer[BYTES_PER_SECTOR]); + + return err; +} + +bool MediaTypeDO::write_sector(int track, int sector, uint8_t* buffer) +{ + Debug_printf("\r\nMediaTypeDO write track %d sector %d", track, sector); + + bool err = false; + uint32_t offset = (track * BYTES_PER_TRACK) + (sector * BYTES_PER_SECTOR); + + err = fseek(_media_fileh, offset, SEEK_SET) != 0; + + if (!err) + err = fwrite(buffer, 1, BYTES_PER_SECTOR, _media_fileh) != BYTES_PER_SECTOR; + + return err; +} + +bool MediaTypeDO::format(uint16_t *respopnsesize) +{ + return false; +} + +mediatype_t MediaTypeDO::mount(FILE *f, uint32_t disksize) +{ + diskiiemulation = false; + _media_fileh = f; + num_blocks = disksize / BYTES_PER_BLOCK; + return MEDIATYPE_DO; +} + +#endif // BUILD_APPLE diff --git a/lib/media/apple/mediaTypeDO.h b/lib/media/apple/mediaTypeDO.h new file mode 100644 index 000000000..b29c5bd2f --- /dev/null +++ b/lib/media/apple/mediaTypeDO.h @@ -0,0 +1,26 @@ +#ifndef _MEDIATYPE_DO_ +#define _MEDIATYPE_DO_ + +#include + +#include "mediaType.h" + +class MediaTypeDO : public MediaType +{ +private: + bool read_sector(int track, int sector, uint8_t* buffer); + bool write_sector(int track, int sector, uint8_t* buffer); + +public: + virtual bool read(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override; + virtual bool write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override; + + virtual bool format(uint16_t *respopnsesize) override; + + virtual mediatype_t mount(FILE *f, uint32_t disksize) override; + + virtual bool status() override {return (_media_fileh != nullptr);} +}; + + +#endif // _MEDIATYPE_DO_ diff --git a/lib/media/apple/mediaTypeDSK.cpp b/lib/media/apple/mediaTypeDSK.cpp index 2837e8d46..ccb40a27e 100644 --- a/lib/media/apple/mediaTypeDSK.cpp +++ b/lib/media/apple/mediaTypeDSK.cpp @@ -25,10 +25,11 @@ mediatype_t MediaTypeDSK::mount(FILE *f, uint32_t disksize) // allocated SPRAM const size_t dsk_image_size = 35 * 16 * 256; uint8_t *dsk = (uint8_t*)heap_caps_malloc(dsk_image_size, MALLOC_CAP_SPIRAM); + if (fseek(f, 0, SEEK_SET) != 0) + return MEDIATYPE_UNKNOWN; size_t bytes_read = fread(dsk, 1, dsk_image_size, f); if (bytes_read != dsk_image_size) return MEDIATYPE_UNKNOWN; - const bool is_prodos = false; dsk2woz_info(); dsk2woz_tmap(); @@ -87,6 +88,7 @@ bool MediaTypeDSK::dsk2woz_tracks(uint8_t *dsk) // Debug_printf("\nStart Block, Block Count, Bit Count"); + Debug_printf("\nMediaTypeDSK is_prodos: %s", _mediatype == MEDIATYPE_PO ? "Y" : "N"); // TODO: adapt this to that // Write out all 35 tracks. @@ -99,7 +101,7 @@ bool MediaTypeDSK::dsk2woz_tracks(uint8_t *dsk) { trk_ptrs[c] = temp_ptr; memset(trk_ptrs[c], 0, WOZ1_NUM_BLKS * 512); - serialise_track(trk_ptrs[c], &dsk[c * 16 * 256], c, false); + serialise_track(trk_ptrs[c], &dsk[c * 16 * 256], c, _mediatype == MEDIATYPE_PO); temp_ptr += WOZ1_TRACK_LEN; bytes_used = temp_ptr[0] + (temp_ptr[1] << 8); temp_ptr += sizeof(uint16_t); diff --git a/lib/media/media.h b/lib/media/media.h index c38252e15..1d6a8c298 100644 --- a/lib/media/media.h +++ b/lib/media/media.h @@ -31,6 +31,7 @@ #ifdef BUILD_APPLE # include "apple/mediaType.h" +# include "apple/mediaTypeDO.h" # include "apple/mediaTypePO.h" # include "apple/mediaTypeWOZ.h" # include "apple/mediaTypeDSK.h"