From 4b83bd0218efd4068ab9a2150ce6c274c8cbb878 Mon Sep 17 00:00:00 2001 From: Eric Helgeson Date: Sat, 22 Aug 2026 17:55:11 -0500 Subject: [PATCH] floppy: detect the format from image size instead of always reporting 1.44MB MODE SENSE page 05h was a fixed constant claiming 1 head, 18 sectors and 8192-byte sectors. Match the image against the standard formats so page 05h, the medium type and the CHS geometry describe the mounted disk. Fixes #33 --- CMakeLists.txt | 1 + .../rp2040-template.ld | 2 + lib/SCSI2SD/src/firmware/floppy.c | 55 +++++++++++++ lib/SCSI2SD/src/firmware/floppy.h | 49 ++++++++++++ lib/SCSI2SD/src/firmware/mode.c | 79 +++++++++++++++++-- src/BlueSCSI.cpp | 2 +- src/BlueSCSI_disk.cpp | 29 ++++++- 7 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 lib/SCSI2SD/src/firmware/floppy.c create mode 100644 lib/SCSI2SD/src/firmware/floppy.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0200dd58..9d973b48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,7 @@ set(SCSI2SD_SOURCES lib/SCSI2SD/src/firmware/network.c lib/SCSI2SD/src/firmware/vendor.c lib/SCSI2SD/src/firmware/geometry.c + lib/SCSI2SD/src/firmware/floppy.c lib/SCSI2SD/src/firmware/mo.c lib/SCSI2SD/src/firmware/AmigaWIFI/AmigaWIFI.c ) diff --git a/lib/BlueSCSI_platform_RP2MCU/rp2040-template.ld b/lib/BlueSCSI_platform_RP2MCU/rp2040-template.ld index 2039b271..7bd13f46 100644 --- a/lib/BlueSCSI_platform_RP2MCU/rp2040-template.ld +++ b/lib/BlueSCSI_platform_RP2MCU/rp2040-template.ld @@ -179,6 +179,8 @@ SECTIONS *(.text*extractFileName*) *(.text*setNameFromImage*) *(.text*getBlockSize*) + *(.text*s2s_floppy*) + *(.text*find_floppy_geometry*) /* Wi-Fi association, only used during init and reconnect. Keeps the * packet path (platform_network_send/receive) in RAM. */ diff --git a/lib/SCSI2SD/src/firmware/floppy.c b/lib/SCSI2SD/src/firmware/floppy.c new file mode 100644 index 00000000..bb641c3f --- /dev/null +++ b/lib/SCSI2SD/src/firmware/floppy.c @@ -0,0 +1,55 @@ +// Copyright (c) 2026 Eric Helgeson +// +// This file is part of BlueSCSI. +// +// BlueSCSI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// BlueSCSI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with BlueSCSI. If not, see . +#include "floppy.h" + +#include + +// Transfer rates are the SCSI-2 table 159 values, medium types table 153. +// Table 153 has one 90mm code (1Eh), so every 3.5" format shares it. +static const S2S_FloppyFormat FloppyFormats[] = +{ + // blocks bps rate cyl h spt medium name + { 800, 512, 0x00FA, 80, 1, 10, 0x01, "400K" }, + { 720, 512, 0x00FA, 40, 2, 9, 0x12, "360K" }, + { 1280, 512, 0x00FA, 80, 2, 8, 0x1E, "640K" }, + { 1440, 512, 0x00FA, 80, 2, 9, 0x1E, "720K" }, + { 1600, 512, 0x00FA, 80, 2, 10, 0x1E, "800K" }, + { 2400, 512, 0x01F4, 80, 2, 15, 0x1A, "1.2M" }, + { 2880, 512, 0x01F4, 80, 2, 18, 0x1E, "1.44M" }, + { 5760, 512, 0x03E8, 80, 2, 36, 0x1E, "2.88M" }, + // NEC 2HD, used by PC-98 and X68000. Shipped in both 5.25" and 3.5". + { 1232, 1024, 0x01F4, 77, 2, 8, 0x1E, "1.2M NEC" }, +}; + +const S2S_FloppyFormat* s2s_floppyFormat(uint32_t blocks, uint16_t bytesPerSector) +{ + size_t i; + for (i = 0; i < sizeof(FloppyFormats) / sizeof(FloppyFormats[0]); i++) + { + if (FloppyFormats[i].blocks == blocks && + FloppyFormats[i].bytesPerSector == bytesPerSector) + { + return &FloppyFormats[i]; + } + } + return NULL; +} + +uint8_t s2s_floppyMediumType(uint8_t heads) +{ + return (heads > 1) ? 0x02 : 0x01; +} diff --git a/lib/SCSI2SD/src/firmware/floppy.h b/lib/SCSI2SD/src/firmware/floppy.h new file mode 100644 index 00000000..f6a56232 --- /dev/null +++ b/lib/SCSI2SD/src/firmware/floppy.h @@ -0,0 +1,49 @@ +// Copyright (c) 2026 Eric Helgeson +// +// This file is part of BlueSCSI. +// +// BlueSCSI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// BlueSCSI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with BlueSCSI. If not, see . +#ifndef S2S_FLOPPY_H +#define S2S_FLOPPY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint32_t blocks; + uint16_t bytesPerSector; + uint16_t transferRate; // SCSI-2 table 159 + uint16_t cylinders; + uint8_t heads; + uint8_t sectorsPerTrack; + uint8_t mediumType; // SCSI-2 table 153 + const char* name; +} S2S_FloppyFormat; + +// Match an image against the known floppy formats by size. +// Returns NULL when the size is not a standard floppy. +const S2S_FloppyFormat* s2s_floppyFormat(uint32_t blocks, uint16_t bytesPerSector); + +// Medium type for an image that matched no format, from its head count. +uint8_t s2s_floppyMediumType(uint8_t heads); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/SCSI2SD/src/firmware/mode.c b/lib/SCSI2SD/src/firmware/mode.c index 492f50f1..8913e653 100755 --- a/lib/SCSI2SD/src/firmware/mode.c +++ b/lib/SCSI2SD/src/firmware/mode.c @@ -23,6 +23,7 @@ #include "mode.h" #include "disk.h" #include "inquiry.h" +#include "floppy.h" #include "BlueSCSI_mode.h" #include "bluescsi_toolbox.h" @@ -146,13 +147,13 @@ static const uint8_t FlexibleDiskDriveGeometry[] = { 0x05, // Page code 0x1E, // Page length -0x01, 0xF4, // Transfer Rate (500kbits) -0x01, // heads -18, // sectors per track -0x20,0x00, // bytes per sector -0x00, 80, // Cylinders -0x00, 0x80, // Write-precomp -0x00, 0x80, // reduced current, +0x00, 0x00, // Transfer Rate +0x00, // heads +0x00, // sectors per track +0x00, 0x00, // bytes per sector +0x00, 0x00, // Cylinders +0x00, 0x00, // Write-precomp (set to the cylinder count = disabled) +0x00, 0x00, // reduced current (set to the cylinder count = disabled) 0x00, 0x00, // Drive step rate 0x00, // pulse width 0x00, 0x00, // Head settle delay @@ -308,6 +309,47 @@ static void pageIn(int pc, int dataIdx, const uint8_t* pageData, int pageLen) } } +typedef struct +{ + uint16_t transferRate; + uint16_t cylinders; + uint16_t bytesPerSector; + uint8_t heads; + uint8_t sectorsPerTrack; + uint8_t mediumType; +} FloppyGeometry; + +// Describe the mounted image rather than a fixed 1.44MB disk. The configured +// geometry wins so an explicit INI SectorsPerTrack/HeadsPerCylinder still +// applies; the size match only supplies the transfer rate and medium type. +static void getFloppyGeometry(FloppyGeometry* geo) +{ + uint16_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector; + uint32_t blocks = scsiDev.target->cfg->scsiSectors; + const S2S_FloppyFormat* format = s2s_floppyFormat(blocks, bytesPerSector); + + geo->bytesPerSector = bytesPerSector; + geo->heads = scsiDev.target->cfg->headsPerCylinder; + geo->sectorsPerTrack = scsiDev.target->cfg->sectorsPerTrack; + + if (format) + { + geo->transferRate = format->transferRate; + geo->mediumType = format->mediumType; + if (geo->heads == 0) geo->heads = format->heads; + if (geo->sectorsPerTrack == 0) geo->sectorsPerTrack = format->sectorsPerTrack; + } + else + { + geo->transferRate = 0x01F4; + geo->mediumType = s2s_floppyMediumType(geo->heads); + } + + uint32_t track = (uint32_t)geo->heads * geo->sectorsPerTrack; + uint32_t cylinders = track ? (blocks / track) : 0; + geo->cylinders = (cylinders > 0xFFFF) ? 0xFFFF : (uint16_t)cylinders; +} + static void doModeSense( int sixByteCmd, int dbd, int pc, int pageCode, int allocLength) { @@ -318,6 +360,12 @@ static void doModeSense( int idx = 1; if (!sixByteCmd) ++idx; + FloppyGeometry floppyGeo = {0}; + if (scsiDev.target->cfg->deviceType == S2S_CFG_FLOPPY_14MB) + { + getFloppyGeometry(&floppyGeo); + } + uint8_t mediumType = 0; uint8_t deviceSpecificParam = 0; uint8_t density = 0; @@ -334,7 +382,7 @@ static void doModeSense( break; case S2S_CFG_FLOPPY_14MB: - mediumType = 0x1E; // 90mm/3.5" + mediumType = floppyGeo.mediumType; deviceSpecificParam = (blockDev.state & DISK_WP) ? 0x80 : 0; density = 0; // reserved for direct access @@ -571,6 +619,21 @@ static void doModeSense( { pageFound = 1; pageIn(pc, idx, FlexibleDiskDriveGeometry, sizeof(FlexibleDiskDriveGeometry)); + + if (pc != 0x01) + { + scsiDev.data[idx+2] = floppyGeo.transferRate >> 8; + scsiDev.data[idx+3] = floppyGeo.transferRate & 0xFF; + scsiDev.data[idx+4] = floppyGeo.heads; + scsiDev.data[idx+5] = floppyGeo.sectorsPerTrack; + scsiDev.data[idx+6] = floppyGeo.bytesPerSector >> 8; + scsiDev.data[idx+7] = floppyGeo.bytesPerSector & 0xFF; + scsiDev.data[idx+8] = floppyGeo.cylinders >> 8; + scsiDev.data[idx+9] = floppyGeo.cylinders & 0xFF; + memcpy(&scsiDev.data[idx+10], &scsiDev.data[idx+8], 2); + memcpy(&scsiDev.data[idx+12], &scsiDev.data[idx+8], 2); + } + idx += sizeof(FlexibleDiskDriveGeometry); } diff --git a/src/BlueSCSI.cpp b/src/BlueSCSI.cpp index 2f511af6..4408eac6 100644 --- a/src/BlueSCSI.cpp +++ b/src/BlueSCSI.cpp @@ -240,7 +240,7 @@ static const char * typeToChar(int deviceType) case S2S_CFG_FIXED: return "Fixed"; case S2S_CFG_FLOPPY_14MB: - return "Floppy1.4MB"; + return "Floppy"; case S2S_CFG_MO: return "MO"; case S2S_CFG_NETWORK: diff --git a/src/BlueSCSI_disk.cpp b/src/BlueSCSI_disk.cpp index 98d11a3f..d6f05121 100644 --- a/src/BlueSCSI_disk.cpp +++ b/src/BlueSCSI_disk.cpp @@ -28,6 +28,7 @@ // It is derived from disk.c in SCSI2SD V6. #include "BlueSCSI_disk.h" +#include "floppy.h" #include "BlueSCSI_log.h" #include "BlueSCSI_config.h" #include "BlueSCSI_settings.h" @@ -400,6 +401,28 @@ static bool find_chs_capacity(uint64_t lba, uint16_t max_cylinders, uint8_t min_ return found_chs; } +// Match a floppy image against the standard formats by size so the geometry +// and MODE SENSE page 05h describe the disk that is actually mounted. +static bool find_floppy_geometry(image_config_t &img, uint16_t &c, uint8_t &h, uint8_t &s) +{ + if (img.deviceType != S2S_CFG_FLOPPY_14MB) + return false; + + const S2S_FloppyFormat *format = s2s_floppyFormat(img.scsiSectors, img.bytesPerSector); + if (!format) + { + logmsg("---- WARNING: ", (int)img.scsiSectors, " x ", (int)img.bytesPerSector, + " byte blocks is not a standard floppy size, using a derived geometry"); + return false; + } + + logmsg("---- Floppy format: ", format->name); + c = format->cylinders; + h = format->heads; + s = format->sectorsPerTrack; + return true; +} + static void autoConfigGeometry(image_config_t &img) { const char *method = "INI config"; @@ -410,12 +433,10 @@ static void autoConfigGeometry(image_config_t &img) uint8_t sect = 63; bool found_chs = false; - if (img.deviceType == S2S_CFG_FLOPPY_14MB && img.scsiSectors <= 2880) + if (find_floppy_geometry(img, cyl, head, sect)) { - method = "device type floppy"; - sect = 18; - head = 80; found_chs = true; + method = "floppy format"; } else if (img.scsiSectors <= 1032192) {