Browse Source

Merge pull request #104 from ZuluSCSI/rp2040_rom_drive

RP2040 ROM drive support
Alex Perez 3 years ago
parent
commit
a83f3cbdcb

+ 16 - 0
README.md

@@ -98,6 +98,22 @@ Any read errors are logged into `zululog.txt`.
 Depending on hardware setup, you may need to mount diode `D205` and jumper `JP201` to supply `TERMPWR` to the SCSI bus.
 Depending on hardware setup, you may need to mount diode `D205` and jumper `JP201` to supply `TERMPWR` to the SCSI bus.
 This is necessary if the drives do not supply their own SCSI terminator power.
 This is necessary if the drives do not supply their own SCSI terminator power.
 
 
+ROM drive in microcontroller flash
+----------------------------------
+The RP2040 model supports storing up to 1660kB image as a read-only drive in the
+flash chip on the PCB itself. This can be used as e.g. a boot floppy that is available
+even without SD card.
+
+To initialize a ROM drive, name your image file as e.g. `HD0.rom`.
+The drive type, SCSI ID and blocksize can be set in the filename the same way as for normal images.
+On first boot, the LED will blink rapidly while the image is being loaded into flash memory.
+Once loading is complete, the file is renamed to `HD0.rom_loaded` and the data is accessed from flash instead.
+
+The status and maximum size of ROM drive are reported in `zululog.txt`.
+To disable a previously programmed ROM drive, create empty file called `HD0.rom`.
+If there is a `.bin` file with the same ID as the programmed ROM drive, it overrides the ROM drive.
+There can be at most one ROM drive enabled at a time.
+
 Project structure
 Project structure
 -----------------
 -----------------
 - **src/ZuluSCSI.cpp**: Main portable SCSI implementation.
 - **src/ZuluSCSI.cpp**: Main portable SCSI implementation.

+ 23 - 8
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp

@@ -15,9 +15,11 @@ extern "C" {
 // As of 2022-09-13, the platformio RP2040 core is missing cplusplus guard on flash.h
 // As of 2022-09-13, the platformio RP2040 core is missing cplusplus guard on flash.h
 // For that reason this has to be inside the extern "C" here.
 // For that reason this has to be inside the extern "C" here.
 #include <hardware/flash.h>
 #include <hardware/flash.h>
+#include "rp2040_flash_do_cmd.h"
 
 
 const char *g_azplatform_name = PLATFORM_NAME;
 const char *g_azplatform_name = PLATFORM_NAME;
 static bool g_scsi_initiator = false;
 static bool g_scsi_initiator = false;
+static uint32_t g_flash_chip_size = 0;
 
 
 void mbed_error_hook(const mbed_error_ctx * error_context);
 void mbed_error_hook(const mbed_error_ctx * error_context);
 
 
@@ -78,6 +80,13 @@ void azplatform_init()
         azlog("NOTE: SCSI termination is disabled");
         azlog("NOTE: SCSI termination is disabled");
     }
     }
 
 
+    // Get flash chip size
+    uint8_t cmd_read_jedec_id[4] = {0x9f, 0, 0, 0};
+    uint8_t response_jedec[4] = {0};
+    flash_do_cmd(cmd_read_jedec_id, response_jedec, 4);
+    g_flash_chip_size = (1 << response_jedec[3]);
+    azlog("Flash chip size: ", (int)(g_flash_chip_size / 1024), " kB");
+
     // SD card pins
     // SD card pins
     // Card is used in SDIO mode for main program, and in SPI mode for crash handler & bootloader.
     // Card is used in SDIO mode for main program, and in SPI mode for crash handler & bootloader.
     //        pin             function       pup   pdown  out    state fast
     //        pin             function       pup   pdown  out    state fast
@@ -463,15 +472,20 @@ const void * btldr_vectors[2] = {&__StackTop, (void*)&btldr_reset_handler};
 
 
 #ifdef PLATFORM_HAS_ROM_DRIVE
 #ifdef PLATFORM_HAS_ROM_DRIVE
 
 
-// Reserve up to 512 kB for firmware.
-#define ROMDRIVE_OFFSET (512 * 1024)
-
-// TODO: Actually read the flash chip size instead of assuming 2 MB.
-static uint32_t g_romdrive_max_size = 2048 * 1024 - ROMDRIVE_OFFSET;
+// Reserve up to 384 kB for firmware.
+#define ROMDRIVE_OFFSET (384 * 1024)
 
 
 uint32_t azplatform_get_romdrive_maxsize()
 uint32_t azplatform_get_romdrive_maxsize()
 {
 {
-    return g_romdrive_max_size;
+    if (g_flash_chip_size >= ROMDRIVE_OFFSET)
+    {
+        return g_flash_chip_size - ROMDRIVE_OFFSET;
+    }
+    else
+    {
+        // Failed to read flash chip size, default to 2 MB
+        return 2048 * 1024 - ROMDRIVE_OFFSET;
+    }
 }
 }
 
 
 bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count)
 bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count)
@@ -487,7 +501,7 @@ bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count)
     xip_ctrl_hw->stream_ctr = count / 4;
     xip_ctrl_hw->stream_ctr = count / 4;
 
 
     // Transfer happens in multiples of 4 bytes
     // Transfer happens in multiples of 4 bytes
-    assert(start < g_romdrive_max_size);
+    assert(start < azplatform_get_romdrive_maxsize());
     assert((count & 3) == 0);
     assert((count & 3) == 0);
     assert((((uint32_t)dest) & 3) == 0);
     assert((((uint32_t)dest) & 3) == 0);
 
 
@@ -507,8 +521,9 @@ bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count)
 
 
 bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count)
 bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count)
 {
 {
-    assert(start < g_romdrive_max_size);
+    assert(start < azplatform_get_romdrive_maxsize());
     assert((count % AZPLATFORM_ROMDRIVE_PAGE_SIZE) == 0);
     assert((count % AZPLATFORM_ROMDRIVE_PAGE_SIZE) == 0);
+
     __disable_irq();
     __disable_irq();
     flash_range_erase(start + ROMDRIVE_OFFSET, count);
     flash_range_erase(start + ROMDRIVE_OFFSET, count);
     flash_range_program(start + ROMDRIVE_OFFSET, data, count);
     flash_range_program(start + ROMDRIVE_OFFSET, data, count);

+ 1 - 1
lib/ZuluSCSI_platform_RP2040/rp2040.ld

@@ -1,6 +1,6 @@
 MEMORY
 MEMORY
 {
 {
-    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 512k
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 384k
     RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 240k  /* Leave space for pico-debug */
     RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 240k  /* Leave space for pico-debug */
     SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
     SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
     SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
     SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

+ 78 - 0
lib/ZuluSCSI_platform_RP2040/rp2040_flash_do_cmd.h

@@ -0,0 +1,78 @@
+// As of 2022-11-16, the raspberrypi platformio package ships with old version
+// of pico-sdk. This version lacks the flash_do_cmd() function in flash.h header.
+// This file backports the function from new versions.
+
+#pragma once
+
+#ifndef RP2040_HAS_FLASH_DO_CMD
+
+#include <hardware/flash.h>
+#include "pico/bootrom.h"
+#include "hardware/structs/ssi.h"
+#include "hardware/structs/ioqspi.h"
+
+#define BOOT2_SIZE_WORDS 64
+
+static uint32_t boot2_copyout[BOOT2_SIZE_WORDS];
+static bool boot2_copyout_valid = false;
+
+static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(void) {
+    if (boot2_copyout_valid)
+        return;
+    for (int i = 0; i < BOOT2_SIZE_WORDS; ++i)
+        boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i];
+    __asm__ volatile ("" : : : "memory");
+    boot2_copyout_valid = true;
+}
+
+static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
+    ((void (*)(void))((char*)boot2_copyout+1))();
+}
+
+// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs
+// are still running, and the FIFO bottoms out. (the bootrom does the same)
+static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) {
+    uint32_t field_val = high ?
+        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH :
+        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
+    hw_write_masked(&ioqspi_hw->io[1].ctrl,
+        field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
+        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS
+    );
+}
+
+static void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *rxbuf, size_t count) {
+    void (*connect_internal_flash)(void) = (void(*)(void))rom_func_lookup(rom_table_code('I', 'F'));
+    void (*flash_exit_xip)(void) = (void(*)(void))rom_func_lookup(rom_table_code('E', 'X'));
+    void (*flash_flush_cache)(void) = (void(*)(void))rom_func_lookup(rom_table_code('F', 'C'));
+    assert(connect_internal_flash && flash_exit_xip && flash_flush_cache);
+    flash_init_boot2_copyout();
+    __asm__ volatile ("" : : : "memory");
+    connect_internal_flash();
+    flash_exit_xip();
+
+    flash_cs_force(0);
+    size_t tx_remaining = count;
+    size_t rx_remaining = count;
+    // We may be interrupted -- don't want FIFO to overflow if we're distracted.
+    const size_t max_in_flight = 16 - 2;
+    while (tx_remaining || rx_remaining) {
+        uint32_t flags = ssi_hw->sr;
+        bool can_put = !!(flags & SSI_SR_TFNF_BITS);
+        bool can_get = !!(flags & SSI_SR_RFNE_BITS);
+        if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) {
+            ssi_hw->dr0 = *txbuf++;
+            --tx_remaining;
+        }
+        if (can_get && rx_remaining) {
+            *rxbuf++ = (uint8_t)ssi_hw->dr0;
+            --rx_remaining;
+        }
+    }
+    flash_cs_force(1);
+
+    flash_flush_cache();
+    flash_enable_xip_via_boot2();
+}
+
+#endif

+ 7 - 4
lib/ZuluSCSI_platform_RP2040/rp2040_sdio.cpp

@@ -181,10 +181,13 @@ sdio_status_t rp2040_sdio_command_R1(uint8_t command, uint32_t arg, uint32_t *re
     {
     {
         if ((uint32_t)(millis() - start) > 2)
         if ((uint32_t)(millis() - start) > 2)
         {
         {
-            azdbg("Timeout waiting for response in rp2040_sdio_command_R1(", (int)command, "), ",
-                  "PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
-                  " RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
-                  " TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
+            if (command != 8) // Don't log for missing SD card
+            {
+                azdbg("Timeout waiting for response in rp2040_sdio_command_R1(", (int)command, "), ",
+                    "PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
+                    " RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
+                    " TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
+            }
 
 
             // Reset the state machine program
             // Reset the state machine program
             pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
             pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);

+ 1 - 1
lib/ZuluSCSI_platform_RP2040/sd_card_sdio.cpp

@@ -87,7 +87,7 @@ bool SdioCard::begin(SdioConfig sdioConfig)
 
 
     if (reply != 0x1AA || status != SDIO_OK)
     if (reply != 0x1AA || status != SDIO_OK)
     {
     {
-        azdbg("SDIO not responding to CMD8 SEND_IF_COND, status ", (int)status, " reply ", reply);
+        // azdbg("SDIO not responding to CMD8 SEND_IF_COND, status ", (int)status, " reply ", reply);
         return false;
         return false;
     }
     }
 
 

+ 69 - 26
src/ZuluSCSI.cpp

@@ -54,6 +54,8 @@
 
 
 SdFs SD;
 SdFs SD;
 FsFile g_logfile;
 FsFile g_logfile;
+static bool g_romdrive_active;
+static bool g_sdcard_present;
 
 
 /************************************/
 /************************************/
 /* Status reporting by blinking led */
 /* Status reporting by blinking led */
@@ -95,7 +97,7 @@ void save_logfile(bool always = false)
   static uint32_t prev_log_save = 0;
   static uint32_t prev_log_save = 0;
   uint32_t loglen = azlog_get_buffer_len();
   uint32_t loglen = azlog_get_buffer_len();
 
 
-  if (loglen != prev_log_len)
+  if (loglen != prev_log_len && g_sdcard_present)
   {
   {
     // When debug is off, save log at most every LOG_SAVE_INTERVAL_MS
     // When debug is off, save log at most every LOG_SAVE_INTERVAL_MS
     // When debug is on, save after every SCSI command.
     // When debug is on, save after every SCSI command.
@@ -314,8 +316,7 @@ bool findHDDImages()
         strcat(fullname, name);
         strcat(fullname, name);
 
 
         // Check whether this SCSI ID has been configured yet
         // Check whether this SCSI ID has been configured yet
-        const S2S_TargetCfg* cfg = s2s_getConfigByIndex(id);
-        if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))
+        if (s2s_getConfigById(id))
         {
         {
           azlog("-- Ignoring ", fullname, ", SCSI ID ", id, " is already in use!");
           azlog("-- Ignoring ", fullname, ", SCSI ID ", id, " is already in use!");
           continue;
           continue;
@@ -365,7 +366,7 @@ bool findHDDImages()
   }
   }
   root.close();
   root.close();
 
 
-  scsiDiskActivateRomDrive();
+  g_romdrive_active = scsiDiskActivateRomDrive();
 
 
   // Print SCSI drive map
   // Print SCSI drive map
   for (int i = 0; i < NUM_SCSIID; i++)
   for (int i = 0; i < NUM_SCSIID; i++)
@@ -477,34 +478,55 @@ extern "C" void zuluscsi_setup(void)
   azplatform_init();
   azplatform_init();
   azplatform_late_init();
   azplatform_late_init();
 
 
-  if(!mountSDCard())
+  g_sdcard_present = mountSDCard();
+
+  if(!g_sdcard_present)
   {
   {
     azlog("SD card init failed, sdErrorCode: ", (int)SD.sdErrorCode(),
     azlog("SD card init failed, sdErrorCode: ", (int)SD.sdErrorCode(),
            " sdErrorData: ", (int)SD.sdErrorData());
            " sdErrorData: ", (int)SD.sdErrorData());
     
     
+    blinkStatus(BLINK_ERROR_NO_SD_CARD);
+
+    if (scsiDiskCheckRomDrive())
+    {
+      reinitSCSI();
+      if (g_romdrive_active)
+      {
+        azlog("Enabled ROM drive without SD card");
+        return;
+      }
+    }
+
     do
     do
     {
     {
       blinkStatus(BLINK_ERROR_NO_SD_CARD);
       blinkStatus(BLINK_ERROR_NO_SD_CARD);
       delay(1000);
       delay(1000);
       azplatform_reset_watchdog();
       azplatform_reset_watchdog();
-    } while (!mountSDCard());
+      g_sdcard_present = mountSDCard();
+    } while (!g_sdcard_present);
     azlog("SD card init succeeded after retry");
     azlog("SD card init succeeded after retry");
   }
   }
 
 
-  if (SD.clusterCount() == 0)
+  if (g_sdcard_present)
   {
   {
-    azlog("SD card without filesystem!");
-  }
+    if (SD.clusterCount() == 0)
+    {
+      azlog("SD card without filesystem!");
+    }
 
 
-  print_sd_info();
+    print_sd_info();
   
   
-  reinitSCSI();
+    reinitSCSI();
+  }
 
 
-  azlog("Initialization complete!");
   azlog("Platform: ", g_azplatform_name);
   azlog("Platform: ", g_azplatform_name);
   azlog("FW Version: ", g_azlog_firmwareversion);
   azlog("FW Version: ", g_azlog_firmwareversion);
+  azlog("Initialization complete!");
 
 
-  init_logfile();
+  if (g_sdcard_present)
+  {
+    init_logfile();
+  }
 }
 }
 
 
 extern "C" void zuluscsi_main_loop(void)
 extern "C" void zuluscsi_main_loop(void)
@@ -533,29 +555,50 @@ extern "C" void zuluscsi_main_loop(void)
     }
     }
   }
   }
 
 
-  // Check SD card status for hotplug
-  if (scsiDev.phase == BUS_FREE &&
-      (uint32_t)(millis() - sd_card_check_time) > 5000)
+  if (g_sdcard_present)
   {
   {
-    sd_card_check_time = millis();
-    uint32_t ocr;
-    if (!SD.card()->readOCR(&ocr))
+    // Check SD card status for hotplug
+    if (scsiDev.phase == BUS_FREE &&
+        (uint32_t)(millis() - sd_card_check_time) > 5000)
     {
     {
+      sd_card_check_time = millis();
+      uint32_t ocr;
       if (!SD.card()->readOCR(&ocr))
       if (!SD.card()->readOCR(&ocr))
       {
       {
-        azlog("SD card removed, trying to reinit");
-        do
+        if (!SD.card()->readOCR(&ocr))
         {
         {
-          blinkStatus(BLINK_ERROR_NO_SD_CARD);
-          delay(1000);
-          azplatform_reset_watchdog();
-        } while (!mountSDCard());
+          g_sdcard_present = false;
+          azlog("SD card removed, trying to reinit");
+        }
+      }
+    }
+  }
+
+  if (!g_sdcard_present)
+  {
+    // Try to remount SD card
+    do 
+    {
+      g_sdcard_present = mountSDCard();
+
+      if (g_sdcard_present)
+      {
         azlog("SD card reinit succeeded");
         azlog("SD card reinit succeeded");
         print_sd_info();
         print_sd_info();
 
 
         reinitSCSI();
         reinitSCSI();
         init_logfile();
         init_logfile();
       }
       }
-    }
+      else if (!g_romdrive_active)
+      {
+        blinkStatus(BLINK_ERROR_NO_SD_CARD);
+        delay(1000);
+        azplatform_reset_watchdog();
+      }
+    } while (!g_sdcard_present && !g_romdrive_active);
+  }
+  else
+  {
+    
   }
   }
 }
 }

+ 42 - 5
src/ZuluSCSI_disk.cpp

@@ -18,6 +18,7 @@
 extern "C" {
 extern "C" {
 #include <scsi2sd_time.h>
 #include <scsi2sd_time.h>
 #include <sd.h>
 #include <sd.h>
+#include <mode.h>
 }
 }
 
 
 #ifndef PLATFORM_MAX_SCSI_SPEED
 #ifndef PLATFORM_MAX_SCSI_SPEED
@@ -131,7 +132,12 @@ bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S
     uint32_t pages = (filesize + AZPLATFORM_ROMDRIVE_PAGE_SIZE - 1) / AZPLATFORM_ROMDRIVE_PAGE_SIZE;
     uint32_t pages = (filesize + AZPLATFORM_ROMDRIVE_PAGE_SIZE - 1) / AZPLATFORM_ROMDRIVE_PAGE_SIZE;
     for (uint32_t i = 0; i < pages; i++)
     for (uint32_t i = 0; i < pages; i++)
     {
     {
-        if (!file.read(scsiDev.data, AZPLATFORM_ROMDRIVE_PAGE_SIZE) ||
+        if (i % 2)
+            LED_ON();
+        else
+            LED_OFF();
+
+        if (file.read(scsiDev.data, AZPLATFORM_ROMDRIVE_PAGE_SIZE) <= 0 ||
             !azplatform_write_romdrive(scsiDev.data, (i + 1) * AZPLATFORM_ROMDRIVE_PAGE_SIZE, AZPLATFORM_ROMDRIVE_PAGE_SIZE))
             !azplatform_write_romdrive(scsiDev.data, (i + 1) * AZPLATFORM_ROMDRIVE_PAGE_SIZE, AZPLATFORM_ROMDRIVE_PAGE_SIZE))
         {
         {
             azlog("---- Failed to program ROM drive page ", (int)i);
             azlog("---- Failed to program ROM drive page ", (int)i);
@@ -140,6 +146,8 @@ bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S
         }
         }
     }
     }
 
 
+    LED_OFF();
+
     file.close();
     file.close();
 
 
     char newname[MAX_FILE_PATH * 2] = "";
     char newname[MAX_FILE_PATH * 2] = "";
@@ -151,6 +159,12 @@ bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S
     return true;
     return true;
 }
 }
 
 
+bool scsiDiskCheckRomDrive()
+{
+    romdrive_hdr_t hdr = {};
+    return check_romdrive(&hdr);
+}
+
 // Check if rom drive exists and activate it
 // Check if rom drive exists and activate it
 bool scsiDiskActivateRomDrive()
 bool scsiDiskActivateRomDrive()
 {
 {
@@ -287,6 +301,16 @@ public:
         }
         }
     }
     }
 
 
+    bool isWritable()
+    {
+        return !m_isrom;
+    }
+
+    bool isRom()
+    {
+        return m_isrom;
+    }
+
     bool isOpen()
     bool isOpen()
     {
     {
         if (m_israw)
         if (m_israw)
@@ -652,7 +676,11 @@ bool scsiDiskOpenHDDImage(int target_idx, const char *filename, int scsi_id, int
         }
         }
 
 
         uint32_t sector_begin = 0, sector_end = 0;
         uint32_t sector_begin = 0, sector_end = 0;
-        if (img.file.contiguousRange(&sector_begin, &sector_end) && sector_end != 0)
+        if (img.file.isRom())
+        {
+            // ROM is always contiguous, no need to log
+        }
+        else if (img.file.contiguousRange(&sector_begin, &sector_end))
         {
         {
             azlog("---- Image file is contiguous, SD card sectors ", (int)sector_begin, " to ", (int)sector_end);
             azlog("---- Image file is contiguous, SD card sectors ", (int)sector_begin, " to ", (int)sector_end);
         }
         }
@@ -919,7 +947,8 @@ const S2S_TargetCfg* s2s_getConfigById(int scsiId)
     for (i = 0; i < S2S_MAX_TARGETS; ++i)
     for (i = 0; i < S2S_MAX_TARGETS; ++i)
     {
     {
         const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);
         const S2S_TargetCfg* tgt = s2s_getConfigByIndex(i);
-        if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId)
+        if ((tgt->scsiId & S2S_CFG_TARGET_ID_BITS) == scsiId &&
+            (tgt->scsiId & S2S_CFG_TARGET_ENABLED))
         {
         {
             return tgt;
             return tgt;
         }
         }
@@ -1247,10 +1276,11 @@ static void doWrite(uint32_t lba, uint32_t blocks)
     azdbg("------ Write ", (int)blocks, "x", (int)bytesPerSector, " starting at ", (int)lba);
     azdbg("------ Write ", (int)blocks, "x", (int)bytesPerSector, " starting at ", (int)lba);
 
 
     if (unlikely(blockDev.state & DISK_WP) ||
     if (unlikely(blockDev.state & DISK_WP) ||
-        unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL))
+        unlikely(scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL) ||
+        unlikely(!img.file.isWritable()))
 
 
     {
     {
-        azlog("WARNING: Host attempted write to CD-ROM");
+        azlog("WARNING: Host attempted write to read-only drive ID ", (int)(img.scsiId & S2S_CFG_TARGET_ID_BITS));
         scsiDev.status = CHECK_CONDITION;
         scsiDev.status = CHECK_CONDITION;
         scsiDev.target->sense.code = ILLEGAL_REQUEST;
         scsiDev.target->sense.code = ILLEGAL_REQUEST;
         scsiDev.target->sense.asc = WRITE_PROTECTED;
         scsiDev.target->sense.asc = WRITE_PROTECTED;
@@ -1875,6 +1905,13 @@ int scsiDiskCommand()
 
 
         scsiDev.phase = DATA_IN;
         scsiDev.phase = DATA_IN;
     }
     }
+    else if (img.file.isRom())
+    {
+        // Special handling for ROM drive to make SCSI2SD code report it as read-only
+        blockDev.state |= DISK_WP;
+        commandHandled = scsiModeCommand();
+        blockDev.state &= ~DISK_WP;
+    }
     else
     else
     {
     {
         commandHandled = 0;
         commandHandled = 0;

+ 1 - 0
src/ZuluSCSI_disk.h

@@ -21,6 +21,7 @@ void scsiDiskLoadConfig(int target_idx);
 bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S2S_CFG_TYPE type);
 bool scsiDiskProgramRomDrive(const char *filename, int scsi_id, int blocksize, S2S_CFG_TYPE type);
 
 
 // Check if there is ROM drive configured in microcontroller flash
 // Check if there is ROM drive configured in microcontroller flash
+bool scsiDiskCheckRomDrive();
 bool scsiDiskActivateRomDrive();
 bool scsiDiskActivateRomDrive();
 
 
 // Returns true if there is at least one image active
 // Returns true if there is at least one image active