Forráskód Böngészése

Move SCSI2SD cdrom.c to ZuluSCSI_cdrom.cpp for extensibility

No functional changes in this commit, reorganizing code
to allow extending CD-ROM emulation functionality.
Petteri Aimonen 2 éve
szülő
commit
8bd888555d
4 módosított fájl, 281 hozzáadás és 167 törlés
  1. 166 23
      src/BlueSCSI_cdrom.cpp
  2. 18 0
      src/BlueSCSI_cdrom.h
  3. 33 143
      src/BlueSCSI_disk.cpp
  4. 64 1
      src/BlueSCSI_disk.h

+ 166 - 23
lib/SCSI2SD/src/firmware/cdrom.c → src/BlueSCSI_cdrom.cpp

@@ -1,25 +1,44 @@
-//	Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
-//
-//	This file is part of SCSI2SD.
-//
-//	SCSI2SD 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.
-//
-//	SCSI2SD 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 SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.
-
-#include "scsi.h"
-#include "config.h"
-#include "cdrom.h"
+/* Advanced CD-ROM drive emulation.
+ * Adds a few capabilities on top of the SCSI2SD CD-ROM emulation:
+ *
+ * - bin/cue support for support of multiple tracks
+ * - on the fly image switching
+ *
+ * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
+ * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ *
+ * This file is licensed under the GPL version 3 or any later version. 
+ * It is derived from cdrom.c in SCSI2SD V6
+ *
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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. 
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
 
 #include <string.h>
+#include "BlueSCSI_cdrom.h"
+#include "BlueSCSI_log.h"
+#include "BlueSCSI_config.h"
+
+extern "C" {
+#include <scsi.h>
+}
+
+/******************************************/
+/* Basic TOC generation without cue sheet */
+/******************************************/
 
 static const uint8_t SimpleTOC[] =
 {
@@ -313,14 +332,133 @@ void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
 	scsiDev.phase = DATA_IN;
 }
 
+/**************************************/
+/* Ejection and image switching logic */
+/**************************************/
+
+// Reinsert any ejected CDROMs on reboot
+void cdromReinsertFirstImage(image_config_t &img)
+{
+    if (img.image_index > 0)
+    {
+        // Multiple images for this drive, force restart from first one
+        debuglog("---- Restarting from first CD-ROM image");
+        img.image_index = 9;
+        cdromSwitchNextImage(img);
+    }
+    else if (img.ejected)
+    {
+        // Reinsert the single image
+        debuglog("---- Closing CD-ROM tray");
+        img.ejected = false;
+        img.cdrom_events = 2; // New media
+    }
+}
+
+// Check if we have multiple CD-ROM images to cycle when drive is ejected.
+bool cdromSwitchNextImage(image_config_t &img)
+{
+    // Check if we have a next image to load, so that drive is closed next time the host asks.
+    img.image_index++;
+    char filename[MAX_FILE_PATH];
+    int target_idx = img.scsiId & 7;
+    if (!scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename)))
+    {
+        img.image_index = 0;
+        scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename));
+    }
+
+    if (filename[0] != '\0')
+    {
+        log("Switching to next CD-ROM image for ", target_idx, ": ", filename);
+        img.file.close();
+        bool status = scsiDiskOpenHDDImage(target_idx, filename, target_idx, 0, 2048);
+
+        if (status)
+        {
+            img.ejected = false;
+            img.cdrom_events = 2; // New media
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static void doGetEventStatusNotification(bool immed)
+{
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
+
+    if (!immed)
+    {
+        // Asynchronous notification not supported
+        scsiDev.status = CHECK_CONDITION;
+        scsiDev.target->sense.code = ILLEGAL_REQUEST;
+        scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
+        scsiDev.phase = STATUS;
+    }
+    else if (img.cdrom_events)
+    {
+        scsiDev.data[0] = 0;
+        scsiDev.data[1] = 6; // EventDataLength
+        scsiDev.data[2] = 0x04; // Media status events
+        scsiDev.data[3] = 0x04; // Supported events
+        scsiDev.data[4] = img.cdrom_events;
+        scsiDev.data[5] = 0x01; // Power status
+        scsiDev.data[6] = 0; // Start slot
+        scsiDev.data[7] = 0; // End slot
+        scsiDev.dataLen = 8;
+        scsiDev.phase = DATA_IN;
+        img.cdrom_events = 0;
+
+        if (img.ejected)
+        {
+            // We are now reporting to host that the drive is open.
+            // Simulate a "close" for next time the host polls.
+            cdromSwitchNextImage(img);
+        }
+    }
+    else
+    {
+        scsiDev.data[0] = 0;
+        scsiDev.data[1] = 2; // EventDataLength
+        scsiDev.data[2] = 0x00; // Media status events
+        scsiDev.data[3] = 0x04; // Supported events
+        scsiDev.dataLen = 4;
+        scsiDev.phase = DATA_IN;
+    }
+}
+
+
+/**************************************/
+/* CD-ROM command dispatching         */
+/**************************************/
 
 // Handle direct-access scsi device commands
-int scsiCDRomCommand()
+extern "C" int scsiCDRomCommand()
 {
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
 	int commandHandled = 1;
 
 	uint8_t command = scsiDev.cdb[0];
-	if (command == 0x43)
+    if (command == 0x1B && (scsiDev.cdb[4] & 2))
+    {
+        // CD-ROM load & eject
+        int start = scsiDev.cdb[4] & 1;
+        if (start)
+        {
+            debuglog("------ CDROM close tray");
+            img.ejected = false;
+            img.cdrom_events = 2; // New media
+        }
+        else
+        {
+            debuglog("------ CDROM open tray");
+            img.ejected = true;
+            img.cdrom_events = 3; // Media removal
+        }
+    }
+	else if (command == 0x43)
 	{
 		// CD-ROM Read TOC
 		int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
@@ -358,6 +496,12 @@ int scsiCDRomCommand()
 			scsiDev.cdb[8];
 		doReadHeader(MSF, lba, allocationLength);
 	}
+    else if (command == 0x4A)
+    {
+        // Get event status notifications (media change notifications)
+        bool immed = scsiDev.cdb[1] & 1;
+        doGetEventStatusNotification(immed);
+    }
 	else
 	{
 		commandHandled = 0;
@@ -365,4 +509,3 @@ int scsiCDRomCommand()
 
 	return commandHandled;
 }
-

+ 18 - 0
src/BlueSCSI_cdrom.h

@@ -0,0 +1,18 @@
+// Advanced CD-ROM drive emulation.
+// Adds a few capabilities on top of the SCSI2SD CD-ROM emulation:
+//
+// - bin/cue support for support of multiple tracks
+// - on the fly image switching
+
+#pragma once
+
+#include "BlueSCSI_disk.h"
+
+// Called by scsi.c from SCSI2SD
+extern "C" int scsiCDRomCommand(void);
+
+// Reinsert ejected CD-ROM and restart from first image
+void cdromReinsertFirstImage(image_config_t &img);
+
+// Switch to next CD-ROM image if multiple have been configured
+bool cdromSwitchNextImage(image_config_t &img);

+ 33 - 143
src/BlueSCSI_disk.cpp

@@ -10,6 +10,7 @@
 #include "BlueSCSI_log.h"
 #include "BlueSCSI_config.h"
 #include "BlueSCSI_presets.h"
+#include "BlueSCSI_cdrom.h"
 #include "ImageBackingStore.h"
 #include "ROMDrive.h"
 #include <minIni.h>
@@ -145,25 +146,6 @@ bool scsiDiskActivateRomDrive()
 extern SdFs SD;
 SdDevice sdDev = {2, 256 * 1024 * 1024 * 2}; /* For SCSI2SD */
 
-struct image_config_t: public S2S_TargetCfg
-{
-    ImageBackingStore file;
-    // For CD-ROM drive ejection
-    bool ejected;
-    uint8_t cdrom_events;
-    bool reinsert_on_inquiry;
-    // Index of image, for when image on-the-fly switching is used for CD drives
-    int image_index;
-    // Right-align vendor / product type strings (for Apple)
-    // Standard SCSI uses left alignment
-    // This field uses -1 for default when field is not set in .ini
-    int rightAlignStrings;
-    // Maximum amount of bytes to prefetch
-    int prefetchbytes;
-    // Warning about geometry settings
-    bool geometrywarningprinted;
-};
-
 static image_config_t g_DiskImages[S2S_MAX_TARGETS];
 
 void scsiDiskResetImages()
@@ -392,6 +374,24 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int
             debuglog("---- Read prefetch enabled: ", (int)img.prefetchbytes, " bytes");
         }
 
+        if (img.deviceType == S2S_CFG_OPTICAL &&
+            strncasecmp(filename + strlen(filename) - 4, ".bin", 4) == 0)
+        {
+            char cuesheetname[MAX_FILE_PATH + 1] = {0};
+            strncpy(cuesheetname, filename, strlen(filename) - 4);
+            strlcat(cuesheetname, ".cue", sizeof(cuesheetname));
+            img.cuesheetfile = SD.open(cuesheetname, O_RDONLY);
+
+            if (img.cuesheetfile.isOpen())
+            {
+                log("---- Found CD-ROM CUE sheet at ", cuesheetname);
+            }
+            else
+            {
+                log("---- No CUE sheet found at ", cuesheetname, ", using as plain binary image");
+            }
+        }
+
         return true;
     }
 
@@ -469,9 +469,9 @@ static void scsiDiskLoadConfig(int target_idx, const char *section)
 }
 
 // Check if image file name is overridden in config
-static bool get_image_name(int target_idx, char *buf, size_t buflen)
+bool scsiDiskGetImageNameFromConfig(image_config_t &img, char *buf, size_t buflen)
 {
-    image_config_t &img = g_DiskImages[target_idx];
+    int target_idx = img.scsiId & 7;
 
     char section[6] = "SCSI0";
     section[4] = '0' + target_idx;
@@ -499,9 +499,9 @@ void scsiDiskLoadConfig(int target_idx)
 
     // Check if we have image specified by name
     char filename[MAX_FILE_PATH];
-    if (get_image_name(target_idx, filename, sizeof(filename)))
+    image_config_t &img = g_DiskImages[target_idx];
+    if (scsiDiskGetImageNameFromConfig(img, filename, sizeof(filename)))
     {
-        image_config_t &img = g_DiskImages[target_idx];
         int blocksize = (img.deviceType == S2S_CFG_OPTICAL) ? 2048 : 512;
         log("-- Opening ", filename, " for id:", target_idx, ", specified in " CONFIGFILE);
         scsiDiskOpenHDDImage(target_idx, filename, target_idx, 0, blocksize);
@@ -521,6 +521,12 @@ bool scsiDiskCheckAnyImagesConfigured()
     return false;
 }
 
+image_config_t &scsiDiskGetImageConfig(int target_idx)
+{
+    assert(target_idx >= 0 && target_idx < S2S_MAX_TARGETS);
+    return g_DiskImages[target_idx];
+}
+
 /*******************************/
 /* Config handling for SCSI2SD */
 /*******************************/
@@ -811,57 +817,6 @@ static void doReadCapacity()
 /* TestUnitReady command */
 /*************************/
 
-// Check if we have multiple CD-ROM images to cycle when drive is ejected.
-static bool checkNextCDImage()
-{
-    // Check if we have a next image to load, so that drive is closed next time the host asks.
-    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
-    img.image_index++;
-    char filename[MAX_FILE_PATH];
-    int target_idx = img.scsiId & 7;
-    if (!get_image_name(target_idx, filename, sizeof(filename)))
-    {
-        img.image_index = 0;
-        get_image_name(target_idx, filename, sizeof(filename));
-    }
-
-    if (filename[0] != '\0')
-    {
-        log("Switching to next CD-ROM image for ", target_idx, ": ", filename);
-        image_config_t &img = g_DiskImages[target_idx];
-        img.file.close();
-        bool status = scsiDiskOpenHDDImage(target_idx, filename, target_idx, 0, 2048);
-
-        if (status)
-        {
-            img.ejected = false;
-            img.cdrom_events = 2; // New media
-            return true;
-        }
-    }
-
-    return false;
-}
-
-// Reinsert any ejected CDROMs on reboot
-static void reinsertCDROM(image_config_t &img)
-{
-    if (img.image_index > 0)
-    {
-        // Multiple images for this drive, force restart from first one
-        debuglog("---- Restarting from first CD-ROM image");
-        img.image_index = 9;
-        checkNextCDImage();
-    }
-    else if (img.ejected)
-    {
-        // Reinsert the single image
-        debuglog("---- Closing CD-ROM tray");
-        img.ejected = false;
-        img.cdrom_events = 2; // New media
-    }
-}
-
 static int doTestUnitReady()
 {
     int ready = 1;
@@ -884,7 +839,7 @@ static int doTestUnitReady()
 
         // We are now reporting to host that the drive is open.
         // Simulate a "close" for next time the host polls.
-        checkNextCDImage();
+        cdromSwitchNextImage(img);
     }
     else if (unlikely(!(blockDev.state & DISK_PRESENT)))
     {
@@ -905,50 +860,6 @@ static int doTestUnitReady()
     return ready;
 }
 
-static void doGetEventStatusNotification(bool immed)
-{
-    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
-
-    if (!immed)
-    {
-        // Asynchronous notification not supported
-        scsiDev.status = CHECK_CONDITION;
-        scsiDev.target->sense.code = ILLEGAL_REQUEST;
-        scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
-        scsiDev.phase = STATUS;
-    }
-    else if (img.cdrom_events)
-    {
-        scsiDev.data[0] = 0;
-        scsiDev.data[1] = 6; // EventDataLength
-        scsiDev.data[2] = 0x04; // Media status events
-        scsiDev.data[3] = 0x04; // Supported events
-        scsiDev.data[4] = img.cdrom_events;
-        scsiDev.data[5] = 0x01; // Power status
-        scsiDev.data[6] = 0; // Start slot
-        scsiDev.data[7] = 0; // End slot
-        scsiDev.dataLen = 8;
-        scsiDev.phase = DATA_IN;
-        img.cdrom_events = 0;
-
-        if (img.ejected)
-        {
-            // We are now reporting to host that the drive is open.
-            // Simulate a "close" for next time the host polls.
-            checkNextCDImage();
-        }
-    }
-    else
-    {
-        scsiDev.data[0] = 0;
-        scsiDev.data[1] = 2; // EventDataLength
-        scsiDev.data[2] = 0x00; // Media status events
-        scsiDev.data[3] = 0x04; // Supported events
-        scsiDev.dataLen = 4;
-        scsiDev.phase = DATA_IN;
-    }
-}
-
 /****************/
 /* Seek command */
 /****************/
@@ -1507,24 +1418,8 @@ int scsiDiskCommand()
         // Enable or disable media access operations.
         //int immed = scsiDev.cdb[1] & 1;
         int start = scsiDev.cdb[4] & 1;
-	    int loadEject = scsiDev.cdb[4] & 2;
 
-        if (loadEject && img.deviceType == S2S_CFG_OPTICAL)
-        {
-            if (start)
-            {
-                debuglog("------ CDROM close tray");
-                img.ejected = false;
-                img.cdrom_events = 2; // New media
-            }
-            else
-            {
-                debuglog("------ CDROM open tray");
-                img.ejected = true;
-                img.cdrom_events = 3; // Media removal
-            }
-        }
-        else if (start)
+        if (start)
         {
             scsiDev.target->started = 1;
         }
@@ -1538,11 +1433,6 @@ int scsiDiskCommand()
         // TEST UNIT READY
         doTestUnitReady();
     }
-    else if (command == 0x4A)
-    {
-        bool immed = scsiDev.cdb[1] & 1;
-        doGetEventStatusNotification(immed);
-    }
     else if (unlikely(!doTestUnitReady()))
     {
         // Status and sense codes already set by doTestUnitReady
@@ -1765,7 +1655,7 @@ void scsiDiskPoll()
             image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
             if (img.deviceType == S2S_CFG_OPTICAL && img.reinsert_on_inquiry)
             {
-                reinsertCDROM(img);
+                cdromReinsertFirstImage(img);
             }
         }
     }
@@ -1793,7 +1683,7 @@ void scsiDiskReset()
         image_config_t &img = g_DiskImages[i];
         if (img.deviceType == S2S_CFG_OPTICAL)
         {
-            reinsertCDROM(img);
+            cdromReinsertFirstImage(img);
         }
     }
 }

+ 64 - 1
src/BlueSCSI_disk.h

@@ -1,3 +1,28 @@
+/** 
+ * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
+ * Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com
+ * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * 
+ * It is derived from disk.h in SCSI2SD V6.
+ * 
+ * This file is licensed under the GPL version 3 or any later version. 
+ * 
+ * https://www.gnu.org/licenses/gpl-3.0.html
+ * ----
+ * This program 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. 
+ * 
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+**/
+
 // SCSI disk access routines
 // Implements both SCSI2SD V6 disk.h functions and some extra.
 
@@ -6,6 +31,7 @@
 #include <stdint.h>
 #include <scsi2sd.h>
 #include <scsiPhy.h>
+#include "ImageBackingStore.h"
 
 extern "C" {
 #include <disk.h>
@@ -13,6 +39,34 @@ extern "C" {
 #include <scsi.h>
 }
 
+// Extended configuration stored alongside the normal SCSI2SD target information
+struct image_config_t: public S2S_TargetCfg
+{
+    ImageBackingStore file;
+
+    // For CD-ROM drive ejection
+    bool ejected;
+    uint8_t cdrom_events;
+    bool reinsert_on_inquiry;
+
+    // Index of image, for when image on-the-fly switching is used for CD drives
+    int image_index;
+
+    // Cue sheet file for CD-ROM images
+    FsFile cuesheetfile;
+
+    // Right-align vendor / product type strings (for Apple)
+    // Standard SCSI uses left alignment
+    // This field uses -1 for default when field is not set in .ini
+    int rightAlignStrings;
+
+    // Maximum amount of bytes to prefetch
+    int prefetchbytes;
+
+    // Warning about geometry settings
+    bool geometrywarningprinted;
+};
+
 void scsiDiskResetImages();
 bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int scsi_lun, int blocksize, S2S_CFG_TYPE type = S2S_CFG_FIXED);
 void scsiDiskLoadConfig(int target_idx);
@@ -27,4 +81,13 @@ bool scsiDiskCheckRomDrive();
 bool scsiDiskActivateRomDrive();
 
 // Returns true if there is at least one image active
-bool scsiDiskCheckAnyImagesConfigured();
+bool scsiDiskCheckAnyImagesConfigured();
+
+// Check if image file name is overridden in config,
+// including image index for multi-image CD-ROM emulation
+bool scsiDiskGetImageNameFromConfig(image_config_t &img, char *buf, size_t buflen);
+
+// Get pointer to extended image configuration based on target idx
+image_config_t &scsiDiskGetImageConfig(int target_idx);
+
+