Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
2 changes: 2 additions & 0 deletions lib/BlueSCSI_platform_RP2MCU/rp2040-template.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
55 changes: 55 additions & 0 deletions lib/SCSI2SD/src/firmware/floppy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2026 Eric Helgeson <eric@bluescsi.com>
//
// 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 <http://www.gnu.org/licenses/>.
#include "floppy.h"

#include <stddef.h>

// 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;
}
49 changes: 49 additions & 0 deletions lib/SCSI2SD/src/firmware/floppy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2026 Eric Helgeson <eric@bluescsi.com>
//
// 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 <http://www.gnu.org/licenses/>.
#ifndef S2S_FLOPPY_H
#define S2S_FLOPPY_H

#include <stdint.h>

#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
79 changes: 71 additions & 8 deletions lib/SCSI2SD/src/firmware/mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "mode.h"
#include "disk.h"
#include "inquiry.h"
#include "floppy.h"
#include "BlueSCSI_mode.h"
#include "bluescsi_toolbox.h"

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/BlueSCSI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
29 changes: 25 additions & 4 deletions src/BlueSCSI_disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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";
Expand All @@ -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)
{
Expand Down