Skip to content

Commit

Permalink
Merge pull request #679 from mike-wiese/improved-floppy-disk-image-su…
Browse files Browse the repository at this point in the history
…pport

Add .DO support. Look inside .DSK files to detect sector order.
  • Loading branch information
tschak909 authored Oct 22, 2023
2 parents 65adf25 + 0912d97 commit 9b60b41
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 9 deletions.
23 changes: 19 additions & 4 deletions lib/device/iwm/disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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");
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion lib/device/iwm/disk2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 31 additions & 1 deletion lib/media/apple/mediaType.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifdef BUILD_APPLE

#include "mediaType.h"
#include "utils.h"

#include <cstdint>
#include <cstring>
Expand Down Expand Up @@ -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
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
4 changes: 3 additions & 1 deletion lib/media/apple/mediaType.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
enum mediatype_t
{
MEDIATYPE_UNKNOWN = 0,
MEDIATYPE_DO,
MEDIATYPE_DSK,
MEDIATYPE_PO,
MEDIATYPE_WOZ,
MEDIATYPE_DSK,
MEDIATYPE_COUNT
};

Expand Down Expand Up @@ -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);
Expand Down
99 changes: 99 additions & 0 deletions lib/media/apple/mediaTypeDO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#ifdef BUILD_APPLE

#include "mediaTypeDO.h"

#include <cstring>
#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
26 changes: 26 additions & 0 deletions lib/media/apple/mediaTypeDO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef _MEDIATYPE_DO_
#define _MEDIATYPE_DO_

#include <stdio.h>

#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_
6 changes: 4 additions & 2 deletions lib/media/apple/mediaTypeDSK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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.
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/media/media.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 9b60b41

Please sign in to comment.