Bläddra i källkod

Merge pull request #529 from ZuluSCSI/feature/Blaster-support

Add platform support for upcoming RP2350B-based ZuluSCSI Blaster
Alex Perez 8 månader sedan
förälder
incheckning
ee0334088c
100 ändrade filer med 1477 tillägg och 309 borttagningar
  1. 1 1
      LICENSE
  2. 1 1
      boards/zuluscsi_blaster.json
  3. 1 1
      lib/SCSI2SD/src/firmware/mode.c
  4. 1 1
      lib/SCSI2SD/src/firmware/scsi.c
  5. 1 1
      lib/SCSI2SD/src/firmware/timings.h
  6. 1 1
      lib/SCSI2SD/src/firmware/toolbox.h
  7. 11 7
      lib/ZipParser/zip_parser.cpp
  8. 4 3
      lib/ZipParser/zip_parser.h
  9. 101 0
      lib/ZuluI2S/ZuluI2S.cpp
  10. 51 0
      lib/ZuluI2S/ZuluI2S.h
  11. 66 0
      lib/ZuluI2S/zulu_pio_i2s.pio
  12. 61 0
      lib/ZuluI2S/zulu_pio_i2s.pio.h
  13. 2 2
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp
  14. 1 1
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h
  15. 1 1
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform_config.h
  16. 1 1
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_v1_0_gpio.h
  17. 1 1
      lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_v1_1_gpio.h
  18. 7 7
      lib/ZuluSCSI_platform_GD32F205/audio_i2s.cpp
  19. 3 3
      lib/ZuluSCSI_platform_GD32F205/audio_i2s.h
  20. 1 1
      lib/ZuluSCSI_platform_GD32F205/bsp.h
  21. 1 1
      lib/ZuluSCSI_platform_GD32F205/greenpak.cpp
  22. 1 1
      lib/ZuluSCSI_platform_GD32F205/greenpak.h
  23. 1 1
      lib/ZuluSCSI_platform_GD32F205/greenpak_fw.h
  24. 1 1
      lib/ZuluSCSI_platform_GD32F205/platform_hw_config.cpp
  25. 1 1
      lib/ZuluSCSI_platform_GD32F205/platform_hw_config.h
  26. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi2sd_time.h
  27. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi2sd_timings.c
  28. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsiPhy.cpp
  29. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsiPhy.h
  30. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_asm.cpp
  31. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_asm.h
  32. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_dma.cpp
  33. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_dma.h
  34. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_greenpak.cpp
  35. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_greenpak.h
  36. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_sync.cpp
  37. 1 1
      lib/ZuluSCSI_platform_GD32F205/scsi_accel_sync.h
  38. 1 1
      lib/ZuluSCSI_platform_GD32F205/sd_card_sdio.cpp
  39. 1 1
      lib/ZuluSCSI_platform_GD32F205/sd_card_spi.cpp
  40. 1 1
      lib/ZuluSCSI_platform_GD32F205/usb_serial.cpp
  41. 1 1
      lib/ZuluSCSI_platform_GD32F205/usb_serial.h
  42. 1 1
      lib/ZuluSCSI_platform_GD32F205/zuluscsi_gd32f205.ld
  43. 1 1
      lib/ZuluSCSI_platform_GD32F205/zuluscsi_gd32f205_btldr.ld
  44. 1 1
      lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.cpp
  45. 1 1
      lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.h
  46. 1 1
      lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform_config.h
  47. 1 1
      lib/ZuluSCSI_platform_GD32F450/bsp.h
  48. 1 1
      lib/ZuluSCSI_platform_GD32F450/greenpak.cpp
  49. 1 1
      lib/ZuluSCSI_platform_GD32F450/greenpak.h
  50. 1 1
      lib/ZuluSCSI_platform_GD32F450/greenpak_fw.h
  51. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi2sd_time.h
  52. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi2sd_timings.c
  53. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsiPhy.cpp
  54. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsiPhy.h
  55. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_asm.cpp
  56. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_asm.h
  57. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_dma.cpp
  58. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_dma.h
  59. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_greenpak.cpp
  60. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_greenpak.h
  61. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_sync.cpp
  62. 1 1
      lib/ZuluSCSI_platform_GD32F450/scsi_accel_sync.h
  63. 1 1
      lib/ZuluSCSI_platform_GD32F450/sd_card_sdio.cpp
  64. 1 1
      lib/ZuluSCSI_platform_GD32F450/usb_hs.cpp
  65. 1 1
      lib/ZuluSCSI_platform_GD32F450/usb_hs.h
  66. 1 1
      lib/ZuluSCSI_platform_GD32F450/usb_serial.cpp
  67. 1 1
      lib/ZuluSCSI_platform_GD32F450/usb_serial.h
  68. 68 32
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp
  69. 4 4
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h
  70. 9 9
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_config.h
  71. 1 1
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_BS2.h
  72. 76 60
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Blaster.h
  73. 2 2
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico.h
  74. 2 2
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico_2.h
  75. 2 2
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2040.h
  76. 1 1
      lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.cpp
  77. 764 0
      lib/ZuluSCSI_platform_RP2MCU/audio_i2s.cpp
  78. 56 0
      lib/ZuluSCSI_platform_RP2MCU/audio_i2s.h
  79. 10 10
      lib/ZuluSCSI_platform_RP2MCU/audio_spdif.cpp
  80. 2 2
      lib/ZuluSCSI_platform_RP2MCU/audio_spdif.h
  81. 1 1
      lib/ZuluSCSI_platform_RP2MCU/bsp.h
  82. 16 6
      lib/ZuluSCSI_platform_RP2MCU/custom_timings.cpp
  83. 1 1
      lib/ZuluSCSI_platform_RP2MCU/custom_timings.h
  84. 1 1
      lib/ZuluSCSI_platform_RP2MCU/library.json
  85. 1 1
      lib/ZuluSCSI_platform_RP2MCU/program_flash.cpp
  86. 3 1
      lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld
  87. 1 1
      lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld
  88. 18 24
      lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld
  89. 1 1
      lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld
  90. 3 14
      lib/ZuluSCSI_platform_RP2MCU/run_pioasm.sh
  91. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsi2sd_time.h
  92. 3 3
      lib/ZuluSCSI_platform_RP2MCU/scsi2sd_timings.c
  93. 2 2
      lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp
  94. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.h
  95. 6 2
      lib/ZuluSCSI_platform_RP2MCU/scsiPhy.cpp
  96. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsiPhy.h
  97. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp
  98. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.h
  99. 1 1
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2MCU.pio
  100. 53 40
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.cpp

+ 1 - 1
LICENSE

@@ -1,7 +1,7 @@
 Software
 -----
 
-ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
 
 ZuluSCSI™ is based on both SCSI2SD V6 and BlueSCSI codebases,
 and licensed under the GPL version 3 or any later version.

+ 1 - 1
boards/zuluscsi_rp2350A.json → boards/zuluscsi_blaster.json

@@ -32,7 +32,7 @@
     "frameworks": [
         "arduino"
     ],
-    "name": "ZuluSCSI RP2350A",
+    "name": "ZuluSCSI Blaster",
     "upload": {
         "maximum_ram_size": 524288,
         "maximum_size": 16777216,

+ 1 - 1
lib/SCSI2SD/src/firmware/mode.c

@@ -1,7 +1,7 @@
 //	Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
 //  Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
 //  Copyright (C) 2019 Landon Rodgers <g.landon.rodgers@gmail.com>
-//	Copyright (C) 2024 Rabbit Hole Computing LLC
+//	Copyright (c) 2024-2025 Rabbit Hole Computing™
 //	Copyright (C) 2024 jokker <jokker@gmail.com>
 //	This file is part of SCSI2SD.
 //

+ 1 - 1
lib/SCSI2SD/src/firmware/scsi.c

@@ -1,7 +1,7 @@
 //	Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
 //	Copyright (c) 2023 joshua stein <jcs@jcs.org>
 //	Copyright (c) 2023 Andrea Ottaviani <andrea.ottaviani.69@gmail.com>
-//	Copyright (C) 2024 Rabbit Hole Computing LLC
+//	Copyright (c) 2024-2025 Rabbit Hole Computing™
 //
 //	This file is part of SCSI2SD.
 //

+ 1 - 1
lib/SCSI2SD/src/firmware/timings.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/SCSI2SD/src/firmware/toolbox.h

@@ -1,6 +1,6 @@
 /** 
  * Copyright (C) 2023 Eric Helgeson
- * Copyright (C) 2024 Rabbit Hole Computing
+ * Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * This file is originally part of BlueSCSI adopted for ZuluSCSI
  * 

+ 11 - 7
lib/ZipParser/zip_parser.cpp

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  *
@@ -20,6 +20,7 @@
 **/
 
 #include "zip_parser.h"
+#include <ctype.h>
 
 #define ZIP_PARSER_METHOD_DEFLATE_BYTE 0x08
 #define ZIP_PARSER_METHOD_UNCOMPRESSED_BYTE 0x00
@@ -32,10 +33,10 @@ namespace zipparser
         filename_len = 0;
         Reset();
     }
-    Parser::Parser(char const *filename, const size_t length)
+    Parser::Parser(char const *filename, const size_t length, const size_t target_total_length)
     {
         Reset();
-        SetMatchingFilename(filename, length);
+        SetMatchingFilename(filename, length, target_total_length);
     }
 
     void Parser::Reset()
@@ -46,7 +47,7 @@ namespace zipparser
         crc = 0;
     }
 
-    void Parser::SetMatchingFilename(char const *filename, const size_t length)
+    void Parser::SetMatchingFilename(char const *filename, const size_t length, const size_t target_total_length)
     {
         if (filename[0] == '\0')
             filename_len = 0;
@@ -54,6 +55,7 @@ namespace zipparser
         {
             this->filename = filename;
             filename_len = length;
+            target_zip_filename_len = target_total_length;
         }
     }
 
@@ -198,15 +200,17 @@ namespace zipparser
                 break;
                 case parsing_target::filename:
                     if (position <= current_zip_filename_len - 1)
-                    {    
-                        if (matching && position < filename_len && filename[position] != buf[idx])
+                    {
+                        // make sure zipped filename is the correct length
+                        if (current_zip_filename_len != target_zip_filename_len)
+                            matching = false; 
+                        if (matching && position < filename_len && tolower(filename[position]) != tolower(buf[idx]))
                             matching = false;
                         if (position == filename_len - 1 && matching)
                             filename_match = true;
                         if (position == current_zip_filename_len -1)
                         {
                             target = parsing_target::extra_field;
-                            matching = true;
                             position = 0;
                             if (extra_field_len == 0)
                             {

+ 4 - 3
lib/ZipParser/zip_parser.h

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  *
@@ -33,8 +33,8 @@ namespace zipparser
     {
         public:
             Parser();
-            Parser(char const *filename, const size_t length);
-            void SetMatchingFilename(char const *filename, const size_t length);
+            Parser(char const *filename, const size_t length, const size_t target_total_length);
+            void SetMatchingFilename(char const *filename, const size_t length, const size_t target_total_length);
             void Reset();
             static const int32_t PARSE_ERROR = -1;
             static const int32_t PARSE_CENTRAL_DIR = -2;
@@ -52,6 +52,7 @@ namespace zipparser
             char const *filename;
             size_t filename_len;
             size_t current_zip_filename_len;
+            size_t target_zip_filename_len;
             size_t extra_field_len;
             uint32_t compressed_data_size;
             uint32_t uncompressed_data_size;

+ 101 - 0
lib/ZuluI2S/ZuluI2S.cpp

@@ -0,0 +1,101 @@
+/*
+    I2SIn and I2SOut for Raspberry Pi Pico
+    Implements one or more I2S interfaces using DMA
+
+    Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <Arduino.h>
+#include "ZuluI2S.h"
+#include "zulu_pio_i2s.pio.h"
+#include <pico/stdlib.h>
+
+
+I2S::I2S() {
+    _running = false;
+    _div_int = 48;
+    _div_frac = 0;
+    _bps = 16;
+    _pio = pio0_hw;
+    _sm = 1;
+    _pinBCLK = 26;
+    _pinDOUT = 28;
+}
+
+I2S::~I2S() {
+    end();
+}
+
+bool I2S::setBCLK(pin_size_t pin) {
+    if (_running || (pin > 28)) {
+        return false;
+    }
+    _pinBCLK = pin;
+    return true;
+}
+
+bool I2S::setDATA(pin_size_t pin) {
+    if (_running || (pin > 29)) {
+        return false;
+    }
+    _pinDOUT = pin;
+    return true;
+}
+
+bool I2S::setBitsPerSample(int bps) {
+    if (_running || ((bps != 8) && (bps != 16) && (bps != 24) && (bps != 32))) {
+        return false;
+    }
+    _bps = bps;
+    return true;
+}
+
+volatile void *I2S::getPioFIFOAddr()
+{
+    return (volatile void *)&_pio->txf[_sm];
+}
+
+bool I2S::setDivider(uint16_t div_int, uint8_t div_frac) {
+    _div_int = div_int;
+    _div_frac = div_frac;
+    return true;
+}
+
+uint I2S::getPioDreq() {
+    return pio_get_dreq(_pio, _sm, true);
+}
+
+bool I2S::begin(PIO pio, uint sm) {
+    if (_running)
+        return true;
+    _pio = pio;
+    _sm = sm;
+    _running = true;
+    int off = 0;
+    pio_sm_claim(_pio, _sm);
+    off = pio_add_program(_pio, &pio_i2s_out_program);
+    pio_i2s_out_program_init(_pio, _sm, off, _pinDOUT, _pinBCLK, _bps);
+    pio_sm_set_clkdiv_int_frac(_pio, _sm, _div_int, _div_frac);
+    pio_sm_set_enabled(_pio, _sm, true);
+    return true;
+}
+
+void I2S::end() {
+    if (_running) {
+        pio_sm_set_enabled(_pio, _sm, false);
+        _running = false;
+    }
+}

+ 51 - 0
lib/ZuluI2S/ZuluI2S.h

@@ -0,0 +1,51 @@
+/*
+    I2SIn and I2SOut for Raspberry Pi Pico
+    Implements one or more I2S interfaces using DMA
+
+    Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#pragma once
+
+class I2S {
+public:
+    I2S();
+    virtual ~I2S();
+
+    bool setBCLK(pin_size_t pin);
+    bool setDATA(pin_size_t pin);
+    bool setBitsPerSample(int bps);
+
+    bool setDivider(uint16_t div_int, uint8_t div_frac);
+    uint getPioDreq();
+    volatile void *getPioFIFOAddr();
+
+    bool begin(PIO pio, uint sm);
+    void end();
+
+private:
+    pin_size_t _pinBCLK;
+    pin_size_t _pinDOUT;
+    uint16_t _div_int;
+    uint8_t _div_frac;
+    int _bps;
+    bool _running;
+
+    PIOProgram *_i2s;
+    PIO _pio;
+    int _sm;
+};

+ 66 - 0
lib/ZuluI2S/zulu_pio_i2s.pio

@@ -0,0 +1,66 @@
+; pio_i2s for the Raspberry Pi Pico RP2040
+;
+; Based loosely off of the MicroPython I2S code in
+; https://github.com/micropython/micropython/blob/master/ports/rp2/machine_i2s.c
+;
+; Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
+;
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library 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
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public
+; License along with this library; if not, write to the Free Software
+; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+.program pio_i2s_out
+.side_set 2   ; 0 = bclk, 1=wclk
+
+; The C code should place (number of bits/sample - 2) in Y and
+; also update the SHIFTCTRL to be 24 or 32 as appropriate
+
+;                           +----- WCLK
+;                           |+---- BCLK
+    mov x, y         side 0b01
+left:
+    out pins, 1      side 0b00
+    jmp x--, left    side 0b01
+    out pins, 1      side 0b10 ; Last bit of left has WCLK change per I2S spec
+
+    mov x, y         side 0b11
+right:
+    out pins, 1      side 0b10
+    jmp x--, right   side 0b11
+    out pins, 1      side 0b00 ; Last bit of right also has WCLK change
+    ; Loop back to beginning...
+
+% c-sdk {
+
+static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
+    pio_gpio_init(pio, data_pin);
+    pio_gpio_init(pio, clock_pin_base);
+    pio_gpio_init(pio, clock_pin_base + 1);
+
+    pio_sm_config sm_config =  pio_i2s_out_program_get_default_config(offset);
+
+    sm_config_set_out_pins(&sm_config, data_pin, 1);
+    sm_config_set_sideset_pins(&sm_config, clock_pin_base);
+    sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
+    sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
+
+    pio_sm_init(pio, sm, offset, &sm_config);
+
+    uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
+    pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
+    pio_sm_set_pins(pio, sm, 0); // clear pins
+
+    pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
+}
+
+%}

+ 61 - 0
lib/ZuluI2S/zulu_pio_i2s.pio.h

@@ -0,0 +1,61 @@
+// -------------------------------------------------- //
+// This file is autogenerated by pioasm; do not edit! //
+// -------------------------------------------------- //
+
+#pragma once
+
+#if !PICO_NO_HARDWARE
+#include "hardware/pio.h"
+#endif
+
+// ----------- //
+// pio_i2s_out //
+// ----------- //
+
+#define pio_i2s_out_wrap_target 0
+#define pio_i2s_out_wrap 7
+
+static const uint16_t pio_i2s_out_program_instructions[] = {
+            //     .wrap_target
+    0xa822, //  0: mov    x, y            side 1     
+    0x6001, //  1: out    pins, 1         side 0     
+    0x0841, //  2: jmp    x--, 1          side 1     
+    0x7001, //  3: out    pins, 1         side 2     
+    0xb822, //  4: mov    x, y            side 3     
+    0x7001, //  5: out    pins, 1         side 2     
+    0x1845, //  6: jmp    x--, 5          side 3     
+    0x6001, //  7: out    pins, 1         side 0     
+            //     .wrap
+};
+
+#if !PICO_NO_HARDWARE
+static const struct pio_program pio_i2s_out_program = {
+    .instructions = pio_i2s_out_program_instructions,
+    .length = 8,
+    .origin = -1,
+};
+
+static inline pio_sm_config pio_i2s_out_program_get_default_config(uint offset) {
+    pio_sm_config c = pio_get_default_sm_config();
+    sm_config_set_wrap(&c, offset + pio_i2s_out_wrap_target, offset + pio_i2s_out_wrap);
+    sm_config_set_sideset(&c, 2, false, false);
+    return c;
+}
+
+static inline void pio_i2s_out_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base, uint bits) {
+    pio_gpio_init(pio, data_pin);
+    pio_gpio_init(pio, clock_pin_base);
+    pio_gpio_init(pio, clock_pin_base + 1);
+    pio_sm_config sm_config =  pio_i2s_out_program_get_default_config(offset);
+    sm_config_set_out_pins(&sm_config, data_pin, 1);
+    sm_config_set_sideset_pins(&sm_config, clock_pin_base);
+    sm_config_set_out_shift(&sm_config, false, true, (bits <= 16) ? 2 * bits : bits);
+    sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
+    pio_sm_init(pio, sm, offset, &sm_config);
+    uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
+    pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
+    pio_sm_set_pins(pio, sm, 0); // clear pins
+    pio_sm_exec(pio, sm, pio_encode_set(pio_y, bits - 2));
+}
+
+#endif

+ 2 - 2
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -32,7 +32,7 @@
 #include <SdFat.h>
 #include <scsi.h>
 #include <assert.h>
-#include <audio.h>
+#include <audio_i2s.h>
 #include <ZuluSCSI_audio.h>
 #include <ZuluSCSI_settings.h>
 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform_config.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_v1_0_gpio.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_v1_1_gpio.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 7 - 7
lib/ZuluSCSI_platform_GD32F205/audio.cpp → lib/ZuluSCSI_platform_GD32F205/audio_i2s.cpp

@@ -1,6 +1,6 @@
 /** 
  * Copyright (C) 2023 saybur
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -19,8 +19,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 **/
-#ifdef ENABLE_AUDIO_OUTPUT
-#include "audio.h"
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
+#include "audio_i2s.h"
 #include "ZuluSCSI_platform.h"
 #include "ZuluSCSI_audio.h"
 #include "ZuluSCSI_v1_1_gpio.h"
@@ -255,7 +255,7 @@ void audio_poll()
     }
 }
 
-bool audio_play(uint8_t owner, ImageBackingStore* img, uint64_t start, uint64_t end, bool swap)
+bool audio_play(uint8_t owner, image_config_t* img, uint64_t start, uint64_t end, bool swap)
 {
     if (audio_is_active()) audio_stop(audio_owner);
 
@@ -272,7 +272,7 @@ bool audio_play(uint8_t owner, ImageBackingStore* img, uint64_t start, uint64_t
         return false;
     }
 
-    audio_file = img;
+    audio_file = &img->file;
     if (!audio_file->isOpen()) {
         logmsg("File not open for audio playback, ", owner);
         return false;
@@ -416,9 +416,9 @@ uint64_t audio_get_file_position()
     return fpos;
 }
 
-void audio_set_file_position(uint32_t lba)
+void audio_set_file_position(uint8_t id, uint32_t lba)
 {
     fpos = 2352 * (uint64_t)lba;
 }
 
-#endif // ENABLE_AUDIO_OUTPUT
+#endif // ENABLE_AUDIO_OUTPUT_I2S

+ 3 - 3
lib/ZuluSCSI_platform_GD32F205/audio.h → lib/ZuluSCSI_platform_GD32F205/audio_i2s.h

@@ -1,6 +1,6 @@
 /** 
  * Copyright (C) 2023 saybur
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -22,7 +22,7 @@
 
 
 #pragma once
-#ifdef ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
 
 extern bool g_audio_enabled;
 extern bool g_ode_audio_stopped;
@@ -58,4 +58,4 @@ void audio_setup();
  * Called from platform_poll() to fill sample buffer(s) if needed.
  */
 void audio_poll();
-#endif //ENABLE_AUDIO_OUTPUT
+#endif //ENABLE_AUDIO_OUTPUT_I2S

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/bsp.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2016 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from bsp.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/greenpak.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/greenpak.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/greenpak_fw.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/platform_hw_config.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/platform_hw_config.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi2sd_time.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from time.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi2sd_timings.c

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
  *

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsiPhy.cpp

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.c in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsiPhy.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_asm.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_asm.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_dma.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_dma.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_greenpak.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_greenpak.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_sync.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/scsi_accel_sync.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/sd_card_sdio.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/sd_card_spi.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/usb_serial.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/usb_serial.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/zuluscsi_gd32f205.ld

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F205/zuluscsi_gd32f205_btldr.ld

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/ZuluSCSI_platform_config.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/bsp.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2016 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from bsp.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/greenpak.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/greenpak.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/greenpak_fw.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi2sd_time.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from time.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi2sd_timings.c

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsiPhy.cpp

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.c in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsiPhy.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_asm.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_asm.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_dma.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_dma.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_greenpak.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_greenpak.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_sync.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/scsi_accel_sync.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/sd_card_sdio.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/usb_hs.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/usb_hs.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/usb_serial.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_GD32F450/usb_serial.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2023 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 68 - 32
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.cpp

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
  *
@@ -58,9 +58,11 @@ extern "C" {
 #include "ZuluSCSI_platform_msc.h"
 #endif
 
-#ifdef ENABLE_AUDIO_OUTPUT
-#  include "audio.h"
-#endif // ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
+#  include "audio_spdif.h"
+#elif defined(ENABLE_AUDIO_OUTPUT_I2S)
+#  include "audio_i2s.h"
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF
 
 extern bool g_rawdrive_active;
 
@@ -161,9 +163,15 @@ bool platform_reclock(zuluscsi_speed_grade_t speed_grade)
 
         if (do_reclock)
         {
+#ifdef  ENABLE_AUDIO_OUTPUT
+            if (g_zuluscsi_timings->audio.audio_clocked)
+                logmsg("Reclocking with these settings are compatible with CD audio playback");
+            else
+                logmsg("Reclocking with these settings may cause audio playback to be too fast or slow ");
+#endif
             logmsg("Initial Clock set to ", (int) platform_sys_clock_in_hz(), "Hz");
-            logmsg("Attempting reclock the MCU to ",(int) g_zuluscsi_timings->clk_hz, "Hz");
-            logmsg("Attempting to set SDIO clock to ", (int)((g_zuluscsi_timings->clk_hz / g_zuluscsi_timings->sdio.clk_div_pio + (5 * MHZ / 10)) / MHZ) , "MHz");
+            logmsg("Reclocking the MCU to ",(int) g_zuluscsi_timings->clk_hz, "Hz");
+            logmsg("Setting the SDIO clock to ", (int)((g_zuluscsi_timings->clk_hz / g_zuluscsi_timings->sdio.clk_div_pio + (5 * MHZ / 10)) / MHZ) , "MHz");
             usb_log_poll();
             reclock();
             logmsg("After reclocking, system reports clock set to ", (int) platform_sys_clock_in_hz(), "Hz");
@@ -266,7 +274,7 @@ void platform_init()
         termination = !gpio_get(DIP_TERM);
 
     }
-# else
+# elif defined(ZULUSCSI_V2_0)
     pin_setup_state_t dip_state = read_setup_ack_pin();
     if (dip_state == SETUP_UNDETERMINED)
     {
@@ -285,6 +293,11 @@ void platform_init()
     // dbglog DIP switch works in any case, as it does not have bus hold.
     dbglog = !gpio_get(DIP_DBGLOG);
     g_log_debug = dbglog;
+# else
+    g_scsi_initiator = !gpio_get(DIP_INITIATOR);
+    termination = !gpio_get(DIP_TERM);
+    dbglog = !gpio_get(DIP_DBGLOG);
+    g_log_debug = dbglog;
 # endif
 #else
     delay(10);
@@ -331,18 +344,6 @@ void platform_init()
     logmsg ("SCSI termination is handled by a hardware jumper");
 #endif  // HAS_DIP_SWITCHES
 
-#ifdef ENABLE_AUDIO_OUTPUT
-    logmsg("SP/DIF audio to expansion header enabled");
-    logmsg("Reclocking MCU for audio timings");
-    if (platform_reclock(SPEED_GRADE_AUDIO))
-    {
-        logmsg("Reclocked for Audio Ouput finished");
-    }
-    else
-    {
-        logmsg("Audio Output timings not found");
-    }
-#endif // ENABLE_AUDIO_OUTPUT
 
     // Get flash chip size
     uint8_t cmd_read_jedec_id[4] = {0x9f, 0, 0, 0};
@@ -366,7 +367,7 @@ void platform_init()
     // LED pin
     gpio_conf(LED_PIN,        GPIO_FUNC_SIO, false,false, true,  false, false);
 
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
 #ifdef GPIO_I2C_SDA
     // I2C pins
     //        pin             function       pup   pdown  out    state fast
@@ -378,7 +379,7 @@ void platform_init()
     gpio_conf(GPIO_EXP_AUDIO, GPIO_FUNC_SPI, true,false, false,  true, true);
     gpio_conf(GPIO_EXP_SPARE, GPIO_FUNC_SIO, true,false, false,  true, false);
     // configuration of corresponding SPI unit occurs in audio_setup()
-#endif  // ENABLE_AUDIO_OUTPUT
+#endif  // ENABLE_AUDIO_OUTPUT_SPDIF
 
 #ifdef GPIO_USB_POWER
     gpio_conf(GPIO_USB_POWER, GPIO_FUNC_SIO, false, false, false,  false, false);
@@ -451,15 +452,29 @@ void platform_late_init()
         gpio_conf(SCSI_IN_ATN,    GPIO_FUNC_SIO, true, false, false, true, false);
         gpio_conf(SCSI_IN_RST,    GPIO_FUNC_SIO, true, false, false, true, false);
 
-#ifndef PIO_FRAMEWORK_ARDUINO_NO_USB
-    Serial.begin();
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
+    logmsg("I2S audio to expansion header enabled");
+    if (!platform_reclock(SPEED_GRADE_AUDIO_I2S))
+    {
+        logmsg("Audio output timings not found");
+    }
 #endif
-
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
+    logmsg("S/PDIF audio to expansion header enabled");
+    if (platform_reclock(SPEED_GRADE_AUDIO_SPDIF))
+    {
+        logmsg("Reclocked for Audio Ouput at ", (int) platform_sys_clock_in_hz(), "Hz");
+    }
+    else
+    {
+        logmsg("Audio Output timings not found");
+    }
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF
 
 #ifdef ENABLE_AUDIO_OUTPUT
         // one-time control setup for DMA channels and second core
         audio_setup();
-#endif // ENABLE_AUDIO_OUTPUT
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF
     }
     else
     {
@@ -482,6 +497,9 @@ void platform_late_init()
         gpio_conf(SCSI_OUT_ATN,   GPIO_FUNC_SIO, false,false, true,  true, true);
 #endif  // PLATFORM_HAS_INITIATOR_MODE
     }
+#ifndef PIO_FRAMEWORK_ARDUINO_NO_USB
+    Serial.begin();
+#endif
     scsi_accel_rp2040_init();
 }
 
@@ -731,13 +749,17 @@ static void adc_poll()
         adc_init();
         adc_set_temp_sensor_enabled(true);
         adc_set_clkdiv(65535); // Lowest samplerate, about 2 kHz
+#ifdef ZULUSCSI_BLASTER
+        adc_select_input(8);
+#else
         adc_select_input(4);
+#endif
         adc_fifo_setup(true, false, 0, false, false);
         adc_run(true);
         initialized = true;
     }
 
-#ifdef ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
     /*
     * If ADC sample reads are done, either via direct reading, FIFO, or DMA,
     * at the same time a SPI DMA write begins, it appears that the first
@@ -746,7 +768,7 @@ static void adc_poll()
     * is playing.
     */
    if (audio_is_active()) return;
-#endif  // ENABLE_AUDIO_OUTPUT
+#endif  // ENABLE_AUDIO_OUTPUT_SPDIF
 
     int adc_value_max = 0;
     while (!adc_fifo_is_empty())
@@ -900,9 +922,9 @@ void platform_poll()
     usb_log_poll();
     adc_poll();
 
-#ifdef ENABLE_AUDIO_OUTPUT
+#if defined(ENABLE_AUDIO_OUTPUT_SPDIF) || defined(ENABLE_AUDIO_OUTPUT_I2S)
     audio_poll();
-#endif // ENABLE_AUDIO_OUTPUT
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF
 }
 
 void platform_reset_mcu()
@@ -914,14 +936,14 @@ uint8_t platform_get_buttons()
 {
     uint8_t buttons = 0;
 
-#if defined(ENABLE_AUDIO_OUTPUT)
+#if defined(ENABLE_AUDIO_OUTPUT_SPDIF)
     // pulled to VCC via resistor, sinking when pressed
     if (!gpio_get(GPIO_EXP_SPARE)) buttons |= 1;
 #elif defined(GPIO_I2C_SDA)
     // SDA = button 1, SCL = button 2
     if (!gpio_get(GPIO_I2C_SDA)) buttons |= 1;
     if (!gpio_get(GPIO_I2C_SCL)) buttons |= 2;
-#endif // defined(ENABLE_AUDIO_OUTPUT)
+#endif // defined(ENABLE_AUDIO_OUTPUT_SPDIF)
 
     // Simple debouncing logic: handle button releases after 100 ms delay.
     static uint32_t debounce;
@@ -1022,7 +1044,20 @@ bool platform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count
  */
 
 #define PARITY(n) ((1 ^ (n) ^ ((n)>>1) ^ ((n)>>2) ^ ((n)>>3) ^ ((n)>>4) ^ ((n)>>5) ^ ((n)>>6) ^ ((n)>>7)) & 1)
-#define X(n) (\
+#ifdef ZULUSCSI_BLASTER
+# define X(n) (\
+    ((n & 0x01) ? 0 : (1 << 0)) | \
+    ((n & 0x02) ? 0 : (1 << 1)) | \
+    ((n & 0x04) ? 0 : (1 << 2)) | \
+    ((n & 0x08) ? 0 : (1 << 3)) | \
+    ((n & 0x10) ? 0 : (1 << 4)) | \
+    ((n & 0x20) ? 0 : (1 << 5)) | \
+    ((n & 0x40) ? 0 : (1 << 6)) | \
+    ((n & 0x80) ? 0 : (1 << 7)) | \
+    (PARITY(n)  ? 0 : (1 << 8)) \
+)
+#else
+# define X(n) (\
     ((n & 0x01) ? 0 : (1 << SCSI_IO_DB0)) | \
     ((n & 0x02) ? 0 : (1 << SCSI_IO_DB1)) | \
     ((n & 0x04) ? 0 : (1 << SCSI_IO_DB2)) | \
@@ -1033,6 +1068,7 @@ bool platform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count
     ((n & 0x80) ? 0 : (1 << SCSI_IO_DB7)) | \
     (PARITY(n)  ? 0 : (1 << SCSI_IO_DBP)) \
 )
+#endif
 
 const uint16_t g_scsi_parity_lookup[256] __attribute__((aligned(512), section(".scratch_x.parity"))) =
 {

+ 4 - 4
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform.h

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
  *
@@ -38,9 +38,9 @@
 #elif defined(ZULUSCSI_BS2)
 // BS2 hardware variant, using Raspberry Pico board on a carrier PCB
 #include "ZuluSCSI_platform_gpio_BS2.h"
-#elif defined(ZULUSCSI_RP2350A)
-// RP2350A variant, using mcu chip directly
-#include "ZuluSCSI_platform_gpio_RP2350A.h"
+#elif defined(ZULUSCSI_BLASTER)
+// RP2350B variant, using mcu chip directly
+#include "ZuluSCSI_platform_gpio_Blaster.h"
 #else
 // Normal RP2040 variant, using RP2040 chip directly
 #include "ZuluSCSI_platform_gpio_RP2040.h"

+ 9 - 9
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_config.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -31,7 +31,7 @@
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define DISABLE_SWO
 # define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
-# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 #elif defined(ZULUSCSI_PICO_2)
 # ifdef ZULUSCSI_PICO_2_DAYNAPORT
 #   define PLATFORM_NAME "ZuluSCSI Pico 2 DaynaPORT"
@@ -39,16 +39,16 @@
 #   define PLATFORM_NAME "ZuluSCSI Pico 2"
 # endif
 # define PLATFORM_PID "Pico 2"
-# define PLATFORM_REVISION "2.0"
+# define PLATFORM_REVISION "2.3A"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define DISABLE_SWO
 # define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
 # define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 
-#elif defined(ZULUSCSI_RP2350A)
-# define PLATFORM_NAME "ZuluSCSI RP2350A"
-# define PLATFORM_PID "RP2350A"
-# define PLATFORM_REVISION "2.0"
+#elif defined(ZULUSCSI_BLASTER)
+# define PLATFORM_NAME "ZuluSCSI Blaster"
+# define PLATFORM_PID "Blaster"
+# define PLATFORM_REVISION "2.3B"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
 # define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
@@ -57,14 +57,14 @@
 # define PLATFORM_PID "BS2"
 # define PLATFORM_REVISION "1.0"
 # define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
-# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 #else
 # define PLATFORM_NAME "ZuluSCSI RP2040"
 # define PLATFORM_PID "RP2040"
 # define PLATFORM_REVISION "2.0"
 # define PLATFORM_HAS_INITIATOR_MODE 1
 # define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_20
-# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 10
+# define PLATFORM_DEFAULT_SCSI_SPEED_SETTING 20
 #endif
 
 #define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_BS2.h

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  *

+ 76 - 60
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2350A.h → lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Blaster.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -28,96 +28,112 @@
 // SCSI data input/output port.
 // The data bus uses external bidirectional buffer, with
 // direction controlled by DATA_DIR pin.
-#define SCSI_IO_DB0  0
-#define SCSI_IO_DB1  1
-#define SCSI_IO_DB2  2
-#define SCSI_IO_DB3  3
-#define SCSI_IO_DB4  4
-#define SCSI_IO_DB5  5
-#define SCSI_IO_DB6  6
-#define SCSI_IO_DB7  7
-#define SCSI_IO_DBP  8
-#define SCSI_IO_DATA_MASK 0x1FF
-#define SCSI_IO_SHIFT 0
+#define SCSI_IO_DB0  12
+#define SCSI_IO_DB1  13
+#define SCSI_IO_DB2  14
+#define SCSI_IO_DB3  15
+#define SCSI_IO_DB4  16
+#define SCSI_IO_DB5  17
+#define SCSI_IO_DB6  18
+#define SCSI_IO_DB7  19
+#define SCSI_IO_DBP  20
+#define SCSI_IO_DATA_MASK 0x1FF000
+#define SCSI_IO_SHIFT 12
 
 // Data direction control
-#define SCSI_DATA_DIR 17
+#define SCSI_DATA_DIR 22
 
 // SCSI output status lines
-#define SCSI_OUT_IO   12
-#define SCSI_OUT_CD   11
-#define SCSI_OUT_MSG  13
-#define SCSI_OUT_RST  28
-#define SCSI_OUT_BSY  26
-#define SCSI_OUT_REQ  9
-#define SCSI_OUT_SEL  24
+#define SCSI_OUT_IO   7
+#define SCSI_OUT_CD   23
+#define SCSI_OUT_MSG  26
+#define SCSI_OUT_RST  47
+#define SCSI_OUT_BSY  45
+#define SCSI_OUT_REQ  21
+#define SCSI_OUT_SEL  44
 
 // SCSI input status signals
-#define SCSI_IN_SEL  11
-#define SCSI_IN_ACK  10
-#define SCSI_IN_ATN  29
-#define SCSI_IN_BSY  13
-#define SCSI_IN_RST  27
+#define SCSI_IN_SEL  23
+#define SCSI_IN_ACK  27
+#define SCSI_IN_ATN  6
+#define SCSI_IN_BSY  26
+#define SCSI_IN_RST  46
 
 // Status line outputs for initiator mode
-#define SCSI_OUT_ACK  10
-#define SCSI_OUT_ATN  29
+#define SCSI_OUT_ACK  27
+#define SCSI_OUT_ATN  6
 
 // Status line inputs for initiator mode
-#define SCSI_IN_IO    12
-#define SCSI_IN_CD    11
-#define SCSI_IN_MSG   13
-#define SCSI_IN_REQ   9
+#define SCSI_IN_IO    7
+#define SCSI_IN_CD    23
+#define SCSI_IN_MSG   26
+#define SCSI_IN_REQ   21
 
 // Status LED pins
-#define LED_PIN      25
+#define LED_PIN      33
 
 // SD card pins in SDIO mode
-#define SDIO_CLK 18
-#define SDIO_CMD 19
-#define SDIO_D0  20
-#define SDIO_D1  21
-#define SDIO_D2  22
-#define SDIO_D3  23
+#define SDIO_CLK 34
+#define SDIO_CMD 35
+#define SDIO_D0  36
+#define SDIO_D1  37
+#define SDIO_D2  38
+#define SDIO_D3  39
 
 // SD card pins in SPI mode
 #define SD_SPI       spi0
-#define SD_SPI_SCK   18
-#define SD_SPI_MOSI  19
-#define SD_SPI_MISO  20
-#define SD_SPI_CS    23
+#define SD_SPI_SCK   SDIO_CLK
+#define SD_SPI_MOSI  SDIO_CMD
+#define SD_SPI_MISO  SDIO_D0
+#define SD_SPI_CS    SDIO_D3
 
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
     // IO expander I2C
-    #define GPIO_I2C_SDA 14
-    #define GPIO_I2C_SCL 15
+    #define GPIO_I2C_SDA 30
+    #define GPIO_I2C_SCL 31
 #else
     // IO expander I2C pins being used as SPI for audio
     #define AUDIO_SPI      spi1
-    #define GPIO_EXP_SPARE 14
-    #define GPIO_EXP_AUDIO 15
+    #define GPIO_EXP_SPARE 30
+    #define GPIO_EXP_AUDIO 31
+#endif
+
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
+    #define GPIO_I2S_BCLK 8
+    #define GPIO_I2S_WS   9
+    #define GPIO_I2S_DOUT 10
+    #define I2S_DMA_IRQ_NUM DMA_IRQ_2
 #endif
 
-// DIP switch pins
-#define HAS_DIP_SWITCHES
-#define DIP_INITIATOR 10
-#define DIP_DBGLOG 16
-#define DIP_TERM 9
 
 // Other pins
-#define SWO_PIN 16
+#define SWO_PIN 32
+
+// DIP switch pins
+#define HAS_DIP_SWITCHES
+#define DIP_INITIATOR   SCSI_OUT_ACK
+#define DIP_DBGLOG      SWO_PIN
+#define DIP_TERM        SCSI_OUT_REQ
 
 // Below are GPIO access definitions that are used from scsiPhy.cpp.
 
 // Write a single SCSI pin.
 // Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
 #define SCSI_OUT(pin, state) \
-    *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
+    ((SCSI_OUT_ ## pin) > 31 ? \
+        *(state ? &sio_hw->gpio_hi_clr : &sio_hw->gpio_hi_set) = 1 << ((SCSI_OUT_ ## pin) - 32) \
+    : \
+        *(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin) \
+    )
 
 // Read a single SCSI pin.
 // Example use: SCSI_IN(ATN), returns 1 for active low state.
 #define SCSI_IN(pin) \
-    ((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
+    ((SCSI_IN_ ## pin) > 31 ? \
+        ((sio_hw->gpio_hi_in & (1 << ((SCSI_IN_ ## pin) - 32))) ? 0 : 1) \
+    : \
+        ((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1) \
+    )
 
 // Set pin directions for initiator vs. target mode
 #define SCSI_ENABLE_INITIATOR() \
@@ -141,7 +157,7 @@
 // Write SCSI data bus, also sets REQ to inactive.
 #define SCSI_OUT_DATA(data) \
     gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
-                    g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
+                    (g_scsi_parity_lookup[(uint8_t)(data)] << SCSI_IO_SHIFT) | (1 << SCSI_OUT_REQ)), \
     SCSI_ENABLE_DATA_OUT()
 
 // Release SCSI data bus and REQ signal
@@ -157,10 +173,10 @@
     sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
                        (1 << SCSI_OUT_CD) | \
                        (1 << SCSI_OUT_MSG) | \
-                       (1 << SCSI_OUT_RST) | \
-                       (1 << SCSI_OUT_BSY) | \
-                       (1 << SCSI_OUT_REQ) | \
-                       (1 << SCSI_OUT_SEL)
+                       (1 << SCSI_OUT_REQ), \
+    sio_hw->gpio_hi_set =   (1 << (SCSI_OUT_RST - 32)) | \
+                            (1 << (SCSI_OUT_BSY - 32)) | \
+                            (1 << (SCSI_OUT_SEL - 32))
 
 // Read SCSI data bus
 #define SCSI_IN_DATA() \

+ 2 - 2
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -87,7 +87,7 @@
 #define SD_SPI_MISO  12
 #define SD_SPI_CS    15
 
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
     // No spare pins for I2C
     // IO expander I2C
     // #define GPIO_I2C_SDA 14

+ 2 - 2
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_Pico_2.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -87,7 +87,7 @@
 #define SD_SPI_MISO  12
 #define SD_SPI_CS    15
 
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
     // No spare pins for I2C
     // IO expander I2C
     // #define GPIO_I2C_SDA 14

+ 2 - 2
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_gpio_RP2040.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -87,7 +87,7 @@
 #define SD_SPI_MISO  20
 #define SD_SPI_CS    23
 
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
     // IO expander I2C
     #define GPIO_I2C_SDA 14
     #define GPIO_I2C_SCL 15

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/ZuluSCSI_platform_msc.cpp

@@ -231,7 +231,7 @@ extern "C" void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8],
 
   const char vid[] = "ZuluSCSI";
   const char pid[] = PLATFORM_PID; 
-  const char rev[] = "1.0";
+  const char rev[] = PLATFORM_REVISION;
 
   memcpy(vendor_id, vid, tu_min32(strlen(vid), 8));
   memcpy(product_id, pid, tu_min32(strlen(pid), 16));

+ 764 - 0
lib/ZuluSCSI_platform_RP2MCU/audio_i2s.cpp

@@ -0,0 +1,764 @@
+/**
+ * Copyright (C) 2023 saybur
+ * Copyright (C) 2024-2025 Rabbit Hole Computing™
+ *
+ * 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/>.
+**/
+
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
+
+#include <SdFat.h>
+#include <stdbool.h>
+#include <hardware/dma.h>
+#include <hardware/irq.h>
+#include <hardware/pio.h>
+#include <pico/multicore.h>
+#include "audio_i2s.h"
+#include <CUEParser.h>
+#include "timings_RP2MCU.h"
+#include "ZuluSCSI_audio.h"
+// #include "ZuluIDE_config.h"
+#include "ZuluSCSI_log.h"
+#include "ZuluSCSI_platform.h"
+// #include "ide_imagefile.h"
+// #include "ide_atapi.h"
+#include <ZuluI2S.h>
+
+
+extern SdFs SD;
+
+I2S i2s;
+
+static FsFile audio_parent;
+static FsFile audio_file;
+static FsFile *cuesheet_file;
+static CUEParser * g_cue_parser = nullptr;
+static char g_cuesheet[4096];
+// True is using the same filenames for the bin/cue, false if using a directory with multiple bin/wav files
+static bool single_bin_file = false;
+// DMA configuration info
+static dma_channel_config snd_dma_a_cfg;
+static dma_channel_config snd_dma_b_cfg;
+
+// some chonky buffers to store audio samples,
+// output and sample buffers are the same memory
+#define AUDIO_OUT_BUFFER_SIZE (AUDIO_BUFFER_SIZE / 4)
+static uint32_t out_len_a = AUDIO_OUT_BUFFER_SIZE;
+static uint32_t out_len_b = AUDIO_OUT_BUFFER_SIZE;
+static uint32_t * out_len = &out_len_a;
+static uint32_t output_buf_a[AUDIO_OUT_BUFFER_SIZE];
+static uint32_t output_buf_b[AUDIO_OUT_BUFFER_SIZE];
+
+static uint8_t *sample_buf_a = (uint8_t*) output_buf_a;
+static uint8_t *sample_buf_b = (uint8_t*) output_buf_b;
+
+// tracking for the state of the above buffers
+enum bufstate { STALE, FILLING, PROCESSING, READY };
+static volatile bufstate sbufst_a = STALE;
+static volatile bufstate sbufst_b = STALE;
+enum bufselect { A, B };
+static bufselect sbufsel = A;
+
+
+// tracking for audio playback
+static uint8_t audio_owner; // SCSI ID or 0xFF when idle
+static bool audio_idle = true;
+static bool audio_playing = false;
+static volatile bool audio_paused = false;
+static uint64_t fpos;
+static uint32_t fleft;
+static uint64_t gap_length = 0;
+static bool last_track_reached = false;
+static bool within_gap = false;
+static uint32_t gap_read = 0;
+static CUETrackInfo current_track = {0};
+
+// historical playback status information
+static audio_status_code audio_last_status[8] = {ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS,
+                                                 ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS};
+// volume information for targets
+static volatile uint16_t volumes[8] = {
+    DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH,
+    DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH
+};
+
+static volatile uint16_t channel[8] = {
+    AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK,
+    AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK
+};
+
+// mechanism for cleanly stopping DMA units
+static volatile bool audio_stopping = false;
+
+/*
+ * I2S format is directly compatible to CD 16-bit audio with left and right channels
+ * The only encoding needed is adjusting the volume and muting if one of the channels
+ * is disabled.
+ */
+static void snd_encode(int16_t* samples, int16_t* output_buf, uint16_t len) {
+    uint8_t vol_r = 0, vol_l = 0;
+    vol_l = (uint8_t)volumes[audio_owner];
+    vol_r = (uint8_t)(volumes[audio_owner] >> 8);
+    uint16_t chn = channel[audio_owner] & AUDIO_CHANNEL_ENABLE_MASK;
+    if (!(chn >> 8))   vol_r = 0;   // right
+    if (!(chn & 0xFF)) vol_l = 0; // left
+    int16_t temp = 0;
+    for (uint16_t i = 0; i < len; i++ )
+    {
+        if (samples == nullptr)
+            output_buf[i] = 0;
+        else
+        {
+            if (i % 2 == 0)
+            {
+                temp = output_buf[i+1];
+                output_buf[i+1] = (int16_t)(((int64_t)samples[i]) * (vol_l) * g_scsi_settings.getSystem()->maxVolume / 25500) ;
+            }
+            else
+            {
+                output_buf[i-1] = (int16_t)(((int64_t)temp) * (vol_r) * g_scsi_settings.getSystem()->maxVolume / 25500);
+            }
+        }
+    }
+}
+
+// functions for passing to Core1
+static void snd_process_a() {
+    snd_encode((int16_t *)(sample_buf_a), (int16_t*)(output_buf_a), AUDIO_BUFFER_SIZE/2);
+    sbufst_a = READY;
+}
+static void snd_process_b() {
+    snd_encode((int16_t *)sample_buf_b, (int16_t*)(output_buf_b), AUDIO_BUFFER_SIZE/2);
+    sbufst_b = READY;
+}
+
+
+
+/**********************************************************************************************
+ * Sets up playback via side effect for last_track_reached, within_gap, fpos and fleft, gap_read
+ * \param start - start of playback in lba
+ * \param length - length of playback in lba
+ * \param continued - true if updating values while audio is being played
+ *                  - false if setting up for the first time
+ **********************************************************************************************/
+static bool setup_playback(uint8_t id, uint32_t start, uint32_t length, bool continued)
+{
+    static uint32_t last_length = 0;
+    static uint32_t last_start = 0;
+    static uint8_t last_track_number = 0;
+
+    if (!continued)
+    {
+        last_start = start;
+        last_length = length;
+        last_track_number = 0;
+    }
+
+    // read in the first track and report errors
+    const CUETrackInfo *find_track_info;
+
+    // Init globals
+    within_gap = false;
+    last_track_reached = false;
+    gap_length = 0;
+    gap_read = 0;
+
+    uint64_t file_size = 0;
+    CUETrackInfo track_info = {0};
+    uint32_t start_of_next_track = 0;
+    int file_index = -1;
+
+    g_cue_parser->restart();
+
+    while ((find_track_info = g_cue_parser->next_track(file_size)) != nullptr )
+    {
+
+        if (!single_bin_file)
+        {
+            // opening the file for getting file size
+            if (find_track_info->file_index != file_index)
+            {
+                if (!(audio_parent.isDir() && audio_file.open(&audio_parent, find_track_info->filename, O_RDONLY)))
+                {
+                    dbgmsg("------ Audio playback - could not open the next track's bin file: ", find_track_info->filename);
+                    audio_file.close();
+                    return false;
+                }
+                file_index = find_track_info->file_index;
+            }
+        }
+        file_size = audio_file.size();
+
+
+        if (continued)
+        {
+            // looking up the next track
+            if (find_track_info->track_number < last_track_number + 1)
+                continue;
+            if (find_track_info->track_number == last_track_number + 1)
+            {
+                // set start to the new track because the last track has finished
+                start = find_track_info->track_start;
+            }
+        }
+
+        if (start < find_track_info->track_start)
+        {
+            // start began in the last track, stop looping
+            start_of_next_track = find_track_info->track_start;
+            break;
+        }
+
+        track_info = *find_track_info;
+
+    }
+
+    if (!single_bin_file)
+    {
+        if (!(audio_parent.isDir() && audio_file.open(&audio_parent, track_info.filename, O_RDONLY)))
+        {
+            dbgmsg("------ Audio playback - could not open the current track's bin file: ", track_info.filename);
+            audio_file.close();
+            return false;
+        }
+    }
+
+    if (find_track_info == nullptr)
+    {
+        // if the loop completed without breaking
+        last_track_reached = true;
+        if (track_info.track_number == 0)
+        {
+            dbgmsg("------ Audio continued playback could not find specified track");
+            return false;
+        }
+    }
+
+    // test if the current or new audio file is open or can be opened
+    if (single_bin_file && !audio_file.isOpen())
+    {
+        dbgmsg("------ Audio playback - CD's bin file is not open");
+        return false;
+    }
+
+    if (track_info.track_mode != CUETrack_AUDIO)
+    {
+        dbgmsg("------ Audio playback - track not CD Audio");
+        return false;
+    }
+
+    if (continued)
+    {
+        // adjust length for new track
+        length = last_length - (start - last_start);
+        last_length = length;
+        last_start = start;
+    }
+    last_track_number = track_info.track_number;
+
+    //  find the offset within the current audio file
+    uint64_t offset = track_info.file_offset;
+    if (start >= track_info.data_start)
+    {
+        // add to the offset the current playback position
+        offset += (start - track_info.data_start) * (uint64_t)track_info.sector_length;
+    }
+    else if (track_info.unstored_pregap_length != 0 && start >= track_info.data_start - track_info.unstored_pregap_length)
+    {
+        // Start is within the pregap position, offset is not increased due to no file data is being played
+        gap_length = (start - track_info.data_start) *(uint64_t) track_info.sector_length;
+        // offset += 0;
+        within_gap = true;
+        gap_read = 0;
+    }
+    else
+    {
+        // Get data from stored pregap (INDEX 0), which is in the file before trackinfo.file_offset.
+        uint32_t seek_back = (track_info.data_start - start) * track_info.sector_length;
+        if (seek_back > offset)
+        {
+            logmsg("WARNING: Host attempted CD read at sector ", start, "+", length,
+                    " pregap request ", (int)seek_back, " exceeded available ", (int)offset, " for track ", track_info.track_number,
+                    " (possible .cue file issue)");
+            offset = 0;
+            return false;
+        }
+        else
+        {
+            offset -= seek_back;
+        }
+    }
+
+    if (start_of_next_track != 0)
+    {
+        // There is a next track
+        if (start + length < start_of_next_track)
+        {
+            // playback ends before the next track
+            if (within_gap)
+                // adjust length unplayed file data within gap
+                fleft = (length - track_info.unstored_pregap_length) * (uint64_t)track_info.sector_length;
+            else
+                fleft = length * (uint64_t)track_info.sector_length;
+
+            last_track_reached = true;
+        }
+        else
+        {
+            // playback continues after this track
+            if (within_gap)
+                fleft = (start_of_next_track - track_info.data_start) * (uint64_t)track_info.sector_length;
+            else
+                fleft = (start_of_next_track - start) * (uint64_t)track_info.sector_length;
+            last_track_reached = false;
+        }
+    }
+    else
+    {
+        // if playback is with current bin file and there are no more tracks
+        volatile uint64_t size_of_playback;
+        volatile uint32_t start_lba = start;
+        size_of_playback = (start_lba + length - track_info.data_start) * (uint64_t)track_info.sector_length ;
+        volatile uint64_t last_track_byte_length = audio_file.size() - track_info.file_offset;
+        if (size_of_playback <= last_track_byte_length)
+        {
+            if (within_gap)
+                fleft = (length - (track_info.data_start - start)) * track_info.sector_length;
+            else
+                fleft = length *  track_info.sector_length;
+            last_track_reached = true;
+        }
+        else
+        {
+            dbgmsg("------ Audio playback - length ", (int) length ,", beyond the last file in cue ");
+            return false;
+        }
+    }
+    current_track = track_info;
+    fpos = offset;
+    return true;
+}
+
+// Allows execution on Core1 via function pointers. Each function can take
+// no parameters and should return nothing, operating via side-effects only.
+static void core1_handler() {
+    while (1) {
+        void (*function)() = (void (*)()) multicore_fifo_pop_blocking();
+        (*function)();
+    }
+}
+
+/* ------------------------------------------------------------------------ */
+/* ---------- VISIBLE FUNCTIONS ------------------------------------------- */
+/* ------------------------------------------------------------------------ */
+extern "C"
+{
+void audio_dma_irq() {
+    // Using dma irq raw register access, because the 2.1.0 pico-sdk function seem to cause issues
+    if (dma_hw->intr & (1 << SOUND_DMA_CHA)) {
+        dma_hw->ints2 = (1 << SOUND_DMA_CHA);
+        sbufst_a = STALE;
+        if (audio_stopping) {
+            channel_config_set_chain_to(&snd_dma_a_cfg, SOUND_DMA_CHA);
+        }
+        dma_channel_configure(SOUND_DMA_CHA,
+                &snd_dma_a_cfg,
+                i2s.getPioFIFOAddr(),
+                output_buf_a,
+                out_len_a / 4,
+                false);
+    } else if (dma_hw->intr & (1 << SOUND_DMA_CHB)) {
+        dma_hw->ints2 = (1 <<  SOUND_DMA_CHB);
+        sbufst_b = STALE;
+        if (audio_stopping) {
+            channel_config_set_chain_to(&snd_dma_b_cfg, SOUND_DMA_CHB);
+        }
+        dma_channel_configure(SOUND_DMA_CHB,
+                &snd_dma_b_cfg,
+                i2s.getPioFIFOAddr(),
+                output_buf_b,
+                out_len_b / 4,
+                false);
+    }
+}
+}
+bool audio_is_active() {
+    return !audio_idle;
+}
+
+bool audio_is_playing(uint8_t id) {
+//    return audio_playing;
+    return audio_owner == (id & 7) && audio_playing;
+}
+
+void audio_setup() {
+    // setup GPIOs
+    pio_gpio_init(I2S_PIO_HW, GPIO_I2S_BCLK);
+    pio_gpio_init(I2S_PIO_HW, GPIO_I2S_WS);
+    pio_gpio_init(I2S_PIO_HW, GPIO_I2S_DOUT);
+
+    // setup Arduino-Pico I2S library
+    i2s.setBCLK(GPIO_I2S_BCLK);
+    i2s.setDATA(GPIO_I2S_DOUT);
+    i2s.setBitsPerSample(16);
+    i2s.setDivider(g_zuluscsi_timings->audio.clk_div_pio, 0);
+    i2s.begin(I2S_PIO_HW, I2S_PIO_SM);
+    dma_channel_claim(SOUND_DMA_CHA);
+	dma_channel_claim(SOUND_DMA_CHB);
+
+    irq_set_exclusive_handler(I2S_DMA_IRQ_NUM, audio_dma_irq);
+    irq_set_enabled(I2S_DMA_IRQ_NUM, true);
+    irq_clear(I2S_DMA_IRQ_NUM);
+
+    logmsg("Starting Core1 for audio");
+    multicore_launch_core1(core1_handler);
+}
+
+
+void audio_poll() {
+    if (audio_idle) return;
+
+    static bool set_pause_buf = true;
+    if (audio_paused)
+    {
+        if (set_pause_buf)
+        {
+            memset(output_buf_a, 0, sizeof(output_buf_a));
+            memset(output_buf_b, 0, sizeof(output_buf_b));
+        }
+        set_pause_buf = false;
+        return;
+    }
+    set_pause_buf = true;
+
+
+    if (last_track_reached && fleft == 0 && sbufst_a == STALE && sbufst_b == STALE) {
+        // out of data and ready to stop
+        audio_stop(audio_owner);
+        return;
+    } else if (last_track_reached && fleft == 0) {
+        // out of data to read but still working on remainder
+        return;
+    } else if (!audio_file.isOpen()) {
+        // closed elsewhere, maybe disk ejected?
+        dbgmsg("------ Playback stop due to closed file");
+        audio_stop(audio_owner);
+        return;
+    }
+
+
+    if (fleft == 0)
+    {
+        if (!setup_playback(audio_owner, 0, 0, true))
+        {
+            dbgmsg("------ Playback stopped because of error loading next track");
+            audio_stop(audio_owner);
+            return;
+        }
+    }
+
+    // are new audio samples needed from the memory card?
+    uint8_t* audiobuf;
+    if (sbufst_a == STALE) {
+        sbufst_a = FILLING;
+        audiobuf = sample_buf_a;
+        out_len = &out_len_a;
+    } else if (sbufst_b == STALE) {
+        sbufst_b = FILLING;
+        audiobuf = sample_buf_b;
+        out_len = &out_len_b;
+    } else {
+        // no data needed this time
+        return;
+    }
+
+
+    platform_set_sd_callback(NULL, NULL);
+    uint16_t toRead = AUDIO_BUFFER_SIZE;
+    uint16_t gap_to_read = AUDIO_BUFFER_SIZE;
+    if (within_gap)
+    {
+        if (gap_length < gap_to_read) gap_to_read = gap_length;
+        memset(audiobuf, 0, AUDIO_BUFFER_SIZE);
+        gap_read += gap_to_read;
+        *out_len = gap_to_read;
+        if (gap_read >= gap_length)
+        {
+            within_gap = false;
+            gap_read = 0;
+            gap_length = 0;
+        }
+    }
+    else
+    {
+        if (fleft < toRead) toRead = fleft;
+        if (audio_file.position() != fpos) {
+            // should be uncommon due to SCSI command restrictions on devices
+            // playing audio; if this is showing up in logs a different approach
+            // will be needed to avoid seek performance issues on FAT32 vols
+            dbgmsg("------ Audio seek required");
+            if (!audio_file.seek(fpos)) {
+                logmsg("------ Audio error, unable to seek to ", fpos);
+            }
+        }
+        if (audio_file.read(audiobuf, toRead) != toRead) {
+            logmsg("------ Audio sample data read error");
+        }
+        *out_len = toRead;
+        fpos += toRead;
+        fleft -= toRead;
+    }
+
+
+    if (sbufst_a == FILLING) {
+        sbufst_a = PROCESSING;
+        multicore_fifo_push_blocking((uintptr_t) &snd_process_a);
+    } else if (sbufst_b == FILLING) {
+        sbufst_b = PROCESSING;
+        multicore_fifo_push_blocking((uintptr_t) &snd_process_b);
+    }
+}
+
+bool audio_play(uint8_t owner, image_config_t* img, uint32_t start, uint32_t length, bool swap) {
+    // Per Annex C terminate playback immediately if already in progress on
+    // the current target. Non-current targets may also get their audio
+    // interrupted later due to hardware limitations
+    // stop any existing playback first
+     if (!audio_idle)
+        audio_stop(audio_owner);
+
+    if(!img->cuesheetfile && img->cuesheetfile.isOpen())
+    {
+        logmsg("Error attempting to play CD Audio with no cue/bin image(s)");
+        return false;
+    }
+
+    if (img->bin_container.isOpen() && img->bin_container.isDir())
+    {
+        audio_parent.close();
+        audio_file.close();
+        audio_parent = img->bin_container;
+        single_bin_file = false;
+    }
+    else if (img->bin_container.isOpen())
+    {
+        audio_parent.close();
+        audio_file.close();
+        audio_file = img->bin_container;
+        single_bin_file = true;
+    }
+    else
+        return false;
+
+    if (&img->cuesheetfile != cuesheet_file)
+    {
+        cuesheet_file = &img->cuesheetfile;
+        cuesheet_file->seek(0);
+        cuesheet_file->read(g_cuesheet, sizeof(g_cuesheet));
+        delete g_cue_parser;
+        g_cue_parser = new CUEParser(g_cuesheet);
+    }
+    // dbgmsg("Request to play ('", file, "':", start, ":", end, ")");
+
+    // verify audio file is present and inputs are (somewhat) sane
+    platform_set_sd_callback(NULL, NULL);
+
+    // truncate playback end to end of file
+    // we will not consider this to be an error at the moment
+    // \todo reimplement
+    // if (end > len) {
+    //     dbgmsg("------ Truncate audio play request end ", end, " to file size ", len);
+    //     end = len;
+    //
+    audio_owner = owner;
+    if(!setup_playback(owner, start, length, false))
+        return false;
+
+    if (length == 0)
+    {
+        audio_last_status[owner] = ASC_NO_STATUS;
+        audio_paused = false;
+        audio_playing = false;
+        audio_idle = true;
+        return true;
+    }
+
+    audio_last_status[owner] = ASC_PLAYING;
+    audio_paused = false;
+    audio_playing = true;
+    audio_idle = false;
+
+    // read in initial sample buffers
+    if (within_gap)
+    {
+        sbufst_a = READY;
+        sbufst_b = READY;
+        memset(output_buf_a, 0, sizeof(output_buf_a));
+        memset(output_buf_b, 0, sizeof(output_buf_b));
+    }
+    else
+    {
+        sbufst_a = STALE;
+        sbufst_b = STALE;
+        sbufsel = B;
+        audio_poll();
+        sbufsel = A;
+        audio_poll();
+    }
+    // setup the two DMA units to hand-off to each other
+    // to maintain a stable bitstream these need to run without interruption
+	snd_dma_a_cfg = dma_channel_get_default_config(SOUND_DMA_CHA);
+	channel_config_set_transfer_data_size(&snd_dma_a_cfg, DMA_SIZE_32);
+	channel_config_set_dreq(&snd_dma_a_cfg, i2s.getPioDreq());
+	channel_config_set_read_increment(&snd_dma_a_cfg, true);
+	channel_config_set_chain_to(&snd_dma_a_cfg, SOUND_DMA_CHB);
+    channel_config_set_high_priority(&snd_dma_a_cfg, true);
+	dma_channel_configure(SOUND_DMA_CHA, &snd_dma_a_cfg, i2s.getPioFIFOAddr(),
+			output_buf_a, AUDIO_OUT_BUFFER_SIZE, false);
+    hw_set_bits(&dma_hw->inte2, 1 << SOUND_DMA_CHA );
+    // dma_irqn_set_channel_enabled(I2S_DMA_IRQ_NUM, SOUND_DMA_CHA, true);
+    // dma_channel_set_irq0_enabled(SOUND_DMA_CHA, true);
+	snd_dma_b_cfg = dma_channel_get_default_config(SOUND_DMA_CHB);
+	channel_config_set_transfer_data_size(&snd_dma_b_cfg, DMA_SIZE_32);
+	channel_config_set_dreq(&snd_dma_b_cfg, i2s.getPioDreq());
+	channel_config_set_read_increment(&snd_dma_b_cfg, true);
+	channel_config_set_chain_to(&snd_dma_b_cfg, SOUND_DMA_CHA);
+    channel_config_set_high_priority(&snd_dma_b_cfg, true);
+	dma_channel_configure(SOUND_DMA_CHB, &snd_dma_b_cfg, i2s.getPioFIFOAddr(),
+			output_buf_b, AUDIO_OUT_BUFFER_SIZE, false);
+    hw_set_bits(&dma_hw->inte2, 1 << SOUND_DMA_CHB );
+    // dma_irqn_set_channel_enabled(I2S_DMA_IRQ_NUM, SOUND_DMA_CHB, true);
+    // dma_channel_set_irq0_enabled(SOUND_DMA_CHB, true);
+    // ready to go
+    dma_channel_start(SOUND_DMA_CHA);
+    return true;
+}
+
+bool audio_set_paused(uint8_t id, bool paused) {
+    if (audio_idle) return false;
+    else if (audio_paused && paused) return false;
+    else if (!audio_paused && !paused) return false;
+
+    audio_paused = paused;
+
+    if (paused) {
+        audio_last_status[id] = ASC_PAUSED;
+    } else {
+        audio_last_status[id] = ASC_PLAYING;
+    }
+    return true;
+}
+
+void audio_stop(uint8_t id) {
+    if (audio_idle || (id & 7) != audio_owner) return;
+
+    memset(&current_track, 0, sizeof(current_track));
+    memset(output_buf_a, 0, sizeof(output_buf_a));
+    memset(output_buf_b, 0, sizeof(output_buf_b));
+
+    // then indicate that the streams should no longer chain to one another
+    // and wait for them to shut down naturally
+    audio_stopping = true;
+    while (dma_channel_is_busy(SOUND_DMA_CHA)) tight_loop_contents();
+    while (dma_channel_is_busy(SOUND_DMA_CHB)) tight_loop_contents();
+    // \todo check if I2S pio is done
+    // The way to check is the I2S pio is done would be to check
+    // if the fifo is empty and the PIO's program counter is at the first instruction
+    // while (spi_is_busy(AUDIO_SPI)) tight_loop_contents();
+    audio_stopping = false;
+    dma_channel_abort(SOUND_DMA_CHA);
+    dma_channel_abort(SOUND_DMA_CHB);
+    // idle the subsystem
+    audio_last_status[id] = ASC_COMPLETED;
+    audio_paused = false;
+    audio_playing = false;
+    audio_idle = true;
+    audio_file.close();
+}
+
+audio_status_code audio_get_status_code(uint8_t id) {
+    audio_status_code tmp = audio_last_status[id];
+    if (tmp == ASC_COMPLETED || tmp == ASC_ERRORED) {
+        audio_last_status[id] = ASC_NO_STATUS;
+    }
+    return tmp;
+}
+
+uint16_t audio_get_volume(uint8_t id) {
+    return volumes[id];
+}
+
+void audio_set_volume(uint8_t id, uint16_t vol)
+{
+    volumes[id] = vol;
+}
+
+uint16_t audio_get_channel(uint8_t id) {
+    return channel[id];
+}
+
+void audio_set_channel(uint8_t id, uint16_t chn) {
+    channel[id] = chn;
+}
+
+uint32_t audio_get_lba_position()
+{
+    if (current_track.track_number != 0 && audio_file.isOpen())
+    {
+        // We need the file position from the start of the track,
+        // current_track.file_offset equivalent to data_start (index 1 in cue file)
+        // index0_offset is the adjustment to current_track.file_offset
+        // to make it equivalent to current_track.track_start (index 0 in cue file)
+        uint64_t index0_offset = (current_track.data_start -  current_track.track_start) * current_track.sector_length;
+        return current_track.track_start + (audio_file.position() - (current_track.file_offset - index0_offset)) / current_track.sector_length;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+void audio_set_cue_parser(CUEParser *cue_parser, FsFile* file)
+{
+    g_cue_parser = cue_parser;
+    if (file != nullptr)
+    {
+        char filename[MAX_FILE_PATH] = {0};
+        if (file->isFile())
+        {
+            file->getName(filename, sizeof(filename));
+            audio_file.open(filename, O_RDONLY);
+            single_bin_file = true;
+        }
+        else if (file->isDir())
+        {
+            file->getName(filename, sizeof(filename));
+            audio_parent.open(filename, O_RDONLY);
+            single_bin_file = false;
+        }
+    }
+}
+
+uint64_t audio_get_file_position()
+{
+    return fpos;
+}
+
+void audio_set_file_position(uint8_t id, uint32_t lba)
+{
+    setup_playback(id, lba, 0, false);
+}
+
+
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF

+ 56 - 0
lib/ZuluSCSI_platform_RP2MCU/audio_i2s.h

@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2023 saybur
+ * Copyright (C) 2024-2025 Rabbit Hole Computing™
+ * 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/>.
+**/
+
+#pragma once
+#ifdef ENABLE_AUDIO_OUTPUT_I2S
+
+#include <stdint.h>
+
+
+// i2s PIO settings
+#define I2S_PIO_HW pio2_hw
+#define I2S_PIO_SM 0
+
+// audio subsystem DMA channels
+#define SOUND_DMA_CHA 6
+#define SOUND_DMA_CHB 7
+
+// size of the two audio sample buffers, in bytes
+// #define AUDIO_BUFFER_SIZE 8192 // reduce memory usage
+#define AUDIO_BUFFER_SIZE 2352 * 12
+/**
+ * Indicates if the audio subsystem is actively streaming, including if it is
+ * sending silent data during sample stall events.
+ *
+ * \return true if audio streaming is active, false otherwise.
+ */
+bool audio_is_active();
+
+/**
+ * Initializes the audio subsystem. Should be called only once, toward the end
+ * of platform_late_init().
+ */
+void audio_setup();
+
+/**
+ * Called from platform_poll() to fill sample buffer(s) if needed.
+ */
+void audio_poll();
+
+
+extern "C" void audio_dma_irq();
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF

+ 10 - 10
lib/ZuluSCSI_platform_RP2MCU/audio.cpp → lib/ZuluSCSI_platform_RP2MCU/audio_spdif.cpp

@@ -15,7 +15,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 **/
 
-#ifdef ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
 
 #include <SdFat.h>
 #include <stdbool.h>
@@ -23,7 +23,7 @@
 #include <hardware/irq.h>
 #include <hardware/spi.h>
 #include <pico/multicore.h>
-#include "audio.h"
+#include "audio_spdif.h"
 #include "ZuluSCSI_audio.h"
 #include "ZuluSCSI_config.h"
 #include "ZuluSCSI_log.h"
@@ -32,7 +32,7 @@
 extern SdFs SD;
 
 // Table with the number of '1' bits for each index.
-// Used for SP/DIF parity calculations.
+// Used for S/PDIF parity calculations.
 // Placed in SRAM5 for the second core to use with reduced contention.
 const uint8_t snd_parity[256] __attribute__((aligned(256), section(".scratch_y.snd_parity"))) = {
     0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
@@ -99,7 +99,7 @@ const uint16_t biphase[256] __attribute__((aligned(512), section(".scratch_y.bip
     0xCCAA, 0xB355, 0xD355, 0xACAA, 0xCB55, 0xB4AA, 0xD4AA, 0xAB55,
     0xCD55, 0xB2AA, 0xD2AA, 0xAD55, 0xCAAA, 0xB555, 0xD555, 0xAAAA };
 /*
- * Biphase frame headers for SP/DIF, including the special bit framing
+ * Biphase frame headers for S/PDIF, including the special bit framing
  * errors used to detect (sub)frame start conditions. See above table
  * for details.
  */
@@ -159,7 +159,7 @@ static uint8_t invert = 0; // biphase encode help: set if last wire bit was '1'
 
 /*
  * Translates 16-bit stereo sound samples to biphase wire patterns for the
- * SPI peripheral. Produces 8 patterns (128 bits, or 1 SP/DIF frame) per pair
+ * SPI peripheral. Produces 8 patterns (128 bits, or 1 S/PDIF frame) per pair
  * of input samples. Provided length is the total number of sample bytes present,
  * _twice_ the number of samples (little-endian order assumed)
  * 
@@ -355,7 +355,7 @@ bool audio_is_playing(uint8_t id) {
 }
 
 void audio_setup() {
-    // setup SPI to blast SP/DIF data over the TX pin
+    // setup SPI to blast S/PDIF data over the TX pin
     spi_set_baudrate(AUDIO_SPI, 5644800); // will be slightly wrong, ~0.03% slow
     hw_write_masked(&spi_get_hw(AUDIO_SPI)->cr0,
             0x1F, // TI mode with 16 bits
@@ -425,7 +425,7 @@ void audio_poll() {
     }
 }
 
-bool audio_play(uint8_t owner, ImageBackingStore* img, uint64_t start, uint64_t end, bool swap) {
+bool audio_play(uint8_t owner, image_config_t* img, uint64_t start, uint64_t end, bool swap) {
     // stop any existing playback first
     if (audio_is_active()) audio_stop(audio_owner);
 
@@ -441,7 +441,7 @@ bool audio_play(uint8_t owner, ImageBackingStore* img, uint64_t start, uint64_t
         return false;
     }
     platform_set_sd_callback(NULL, NULL);
-    audio_file = img;
+    audio_file = &img->file;
     if (!audio_file->isOpen()) {
         logmsg("File not open for audio playback, ", owner);
         return false;
@@ -590,9 +590,9 @@ uint64_t audio_get_file_position()
     return fpos;
 }
 
-void audio_set_file_position(uint32_t lba)
+void audio_set_file_position(uint8_t id, uint32_t lba)
 {
     fpos = 2352 * (uint64_t)lba;
 
 }
-#endif // ENABLE_AUDIO_OUTPUT
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF

+ 2 - 2
lib/ZuluSCSI_platform_RP2MCU/audio.h → lib/ZuluSCSI_platform_RP2MCU/audio_spdif.h

@@ -16,7 +16,7 @@
 **/
 
 #pragma once
-#ifdef ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
 
 #include <stdint.h>
 
@@ -60,4 +60,4 @@ void audio_setup();
  */
 void audio_poll();
 
-#endif // ENABLE_AUDIO_OUTPUT
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/bsp.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2016 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from bsp.h in SCSI2SD V6.

+ 16 - 6
lib/ZuluSCSI_platform_RP2MCU/custom_timings.cpp

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
  *
@@ -45,6 +45,7 @@ bool CustomTimings::set_timings_from_file()
     const char scsi_10_section[] = "scsi_10";
     const char scsi_5_section[] = "scsi_5";
     const char sdio_section[] = "sdio";
+    const char audio_section[] = "audio";
 
     // pll
     int32_t vco = ini_getl(pll_section, "vco_freq_hz", g_zuluscsi_timings->pll.vco_freq, CUSTOM_TIMINGS_FILE);
@@ -53,9 +54,9 @@ bool CustomTimings::set_timings_from_file()
 
     if (vco > 0 && post_div1 > 0 && post_div2 > 0)
     {
-        if (vco / post_div1 / post_div2 > 250000000)
+        if (vco / post_div1 / post_div2 > 252000000)
         {
-            logmsg("Reclocking over 250MHz with the PLL settings is not allowed using ", CUSTOM_TIMINGS_FILE);
+            logmsg("Reclocking over 252MHz with the PLL settings is not allowed using ", CUSTOM_TIMINGS_FILE);
             return false;
         }
     }
@@ -94,20 +95,26 @@ bool CustomTimings::set_timings_from_file()
     // scsi 20
     g_zuluscsi_timings->scsi_20.delay0 = ini_getl(scsi_20_section, "delay0_cc", g_zuluscsi_timings->scsi_20.delay0, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_20.delay1 = ini_getl(scsi_20_section, "delay1_cc", g_zuluscsi_timings->scsi_20.delay1, CUSTOM_TIMINGS_FILE);
-    g_zuluscsi_timings->scsi_20.total_delay_adjust = ini_getl(scsi_20_section, "total_delay_adjust_cc", g_zuluscsi_timings->scsi_20.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_20.total_period_adjust = ini_getl(scsi_20_section, "total_period_adjust_cc", g_zuluscsi_timings->scsi_20.total_period_adjust, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_20.max_sync = ini_getl(scsi_20_section, "max_sync", g_zuluscsi_timings->scsi_20.max_sync, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_20.rdelay1 = ini_getl(scsi_20_section, "read_delay1_cc", g_zuluscsi_timings->scsi_20.rdelay1, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_20.rtotal_period_adjust = ini_getl(scsi_20_section, "read_total_period_adjust_cc", g_zuluscsi_timings->scsi_20.rtotal_period_adjust, CUSTOM_TIMINGS_FILE);
 
     // scsi 10
     g_zuluscsi_timings->scsi_10.delay0 = ini_getl(scsi_10_section, "delay0_cc", g_zuluscsi_timings->scsi_10.delay0, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_10.delay1 = ini_getl(scsi_10_section, "delay1_cc", g_zuluscsi_timings->scsi_10.delay1, CUSTOM_TIMINGS_FILE);
-    g_zuluscsi_timings->scsi_10.total_delay_adjust = ini_getl(scsi_10_section, "total_delay_adjust_cc", g_zuluscsi_timings->scsi_10.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_10.total_period_adjust = ini_getl(scsi_10_section, "total_period_adjust_cc", g_zuluscsi_timings->scsi_10.total_period_adjust, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_10.max_sync = ini_getl(scsi_10_section, "max_sync", g_zuluscsi_timings->scsi_10.max_sync, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_10.rdelay1 = ini_getl(scsi_10_section, "read_delay1_cc", g_zuluscsi_timings->scsi_10.rdelay1, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_10.rtotal_period_adjust = ini_getl(scsi_10_section, "read_total_period_adjust_cc", g_zuluscsi_timings->scsi_10.rtotal_period_adjust, CUSTOM_TIMINGS_FILE);
 
     // scsi 5
     g_zuluscsi_timings->scsi_5.delay0 = ini_getl(scsi_5_section, "delay0_cc", g_zuluscsi_timings->scsi_5.delay0, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_5.delay1 = ini_getl(scsi_5_section, "delay1_cc", g_zuluscsi_timings->scsi_5.delay1, CUSTOM_TIMINGS_FILE);
-    g_zuluscsi_timings->scsi_5.total_delay_adjust = ini_getl(scsi_5_section, "total_delay_adjust_cc", g_zuluscsi_timings->scsi_5.total_delay_adjust, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_5.total_period_adjust = ini_getl(scsi_5_section, "total_period_adjust_cc", g_zuluscsi_timings->scsi_5.total_period_adjust, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->scsi_5.max_sync = ini_getl(scsi_5_section, "max_sync", g_zuluscsi_timings->scsi_5.max_sync, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_5.rdelay1 = ini_getl(scsi_5_section, "read_delay1_cc", g_zuluscsi_timings->scsi_5.rdelay1, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->scsi_5.rtotal_period_adjust = ini_getl(scsi_5_section, "read_total_period_adjust_cc", g_zuluscsi_timings->scsi_5.rtotal_period_adjust, CUSTOM_TIMINGS_FILE);
 
     // sdio
     g_zuluscsi_timings->sdio.clk_div_pio = ini_getl(sdio_section, "clk_div_pio", g_zuluscsi_timings->sdio.clk_div_pio, CUSTOM_TIMINGS_FILE);
@@ -115,5 +122,8 @@ bool CustomTimings::set_timings_from_file()
     g_zuluscsi_timings->sdio.delay0 = ini_getl(sdio_section, "delay0", g_zuluscsi_timings->sdio.delay0, CUSTOM_TIMINGS_FILE);
     g_zuluscsi_timings->sdio.delay1 = ini_getl(sdio_section, "delay1", g_zuluscsi_timings->sdio.delay1, CUSTOM_TIMINGS_FILE);
 
+    // audio
+    g_zuluscsi_timings->audio.clk_div_pio = ini_getl(audio_section, "clk_div_pio", g_zuluscsi_timings->audio.clk_div_pio, CUSTOM_TIMINGS_FILE);
+    g_zuluscsi_timings->audio.audio_clocked = ini_getbool(audio_section, "clk_for_audio", g_zuluscsi_timings->audio.audio_clocked, CUSTOM_TIMINGS_FILE);
     return true;
 }

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/custom_timings.h

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  *
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version.
  *

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/library.json

@@ -9,6 +9,6 @@
     "platforms": "*",
     "dependencies":
     {
-        "CUEParser": "https://github.com/rabbitholecomputing/CUEParser"
+        "CUEParser": "https://github.com/rabbitholecomputing/CUEParser#v2025.02.25"
     }
 }

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/program_flash.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 3 - 1
lib/ZuluSCSI_platform_RP2MCU/rp2040-template.ld

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -97,6 +97,8 @@ SECTIONS
         /* RP2040 breakpoints in RAM code don't always work very well
          * because the boot routine tends to overwrite them.
          * Uncommenting this line puts all code in flash.
+         * You may have to delete "firmware.elf" for the next build
+         * to use this linker file's changes
          */
         /* *(.text .text*) */
     } > FLASH

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/rp2040_btldr.ld

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 18 - 24
lib/ZuluSCSI_platform_RP2MCU/rp23xx-template.ld

@@ -73,7 +73,18 @@ SECTIONS
          * FLASH ... we will include any thing excluded here in .data below by default */
         *(.init)
         *libgcc.a:cmse_nonsecure_call.o
-        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
+
+/* =============================================================== */
+    /* Exclude as much code from flash as possible as the RP2350 series has twice as much SRAM */
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *libZuluIDE_platform_RPMCU.a:  *libSdFat.a: *libSCSI2SD.a: *CUEParser.a: *minIni.a: *.cpp.o) .text*)
+
+    /* ---------------------------------------------------------
+    Uncomment the EXCLUDE_FILE line below and comment the EXCLUDE_FILE line above for debug to work properly,
+    the initial break point to main seems to get clobbered when everything is in SRAM.
+    You may have to delete the "firmware.elf" file so the next build links with newly modified linker script
+    ------------------------------------------------------------*/
+        /* *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) */
+/* =============================================================== */
         *(.fini)
         /* Pull all c'tors into .text */
         *crtbegin.o(.ctors)
@@ -112,28 +123,6 @@ SECTIONS
         *(.eh_frame*)
         . = ALIGN(4);
 
-               /* Put only non-timecritical code in flash
-         * This includes e.g. floating point math routines.
-         */
-        .pio/build/$project_name/src/ZuluSCSI_log.cpp.o(.text .text*)
-        .pio/build/$project_name/src/ZuluSCSI_log_trace.cpp.o(.text .text*)
-        .pio/build/$project_name/src/ZuluSCSI_settings.cpp.o(.text .text*)
-        .pio/build/$project_name/src/QuirksCheck.cpp.o(.text .text*)
-        *libZuluSCSI_platform_RP2350.a:ZuluSCSI_platform.cpp.o(.text .text*)
-        *libm*:(.text .text*)
-        *libc*:(.text .text*)
-        *libgcc*:*df*(.text .text*)
-        *USB*(.text .text*)
-        *SPI*(.text .text*)
-        *Spi*(.text .text*)
-        *spi*(.text .text*)
-        *stdc*:(.text .text*)
-        *supc*:(.text .text*)
-        *nosys*:(.text .text*)
-        *libc*:*printf*(.text .text*)
-        *libc*:*toa*(.text .text*)
-        *libminIni.a:(.text .text*)
-        *libCUEParser.a:(.text .text*)
     } > FLASH
 
     /* Note the boot2 section is optional, and should be discarded if there is
@@ -158,7 +147,12 @@ SECTIONS
         "ERROR: Pico second stage bootloader must be no more than 256 bytes in size")
 
     .rodata : {
-        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
+        /* Exclude as many constants as possible from flash as corresponding to the code that has been removed from flash
+            to keep the MCU from having to hit flash while it is executing in SRAM */
+        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:  *libZuluIDE_platform_RPMCU.a:  *libSdFat.a: *libSCSI2SD.a: *CUEParser.a: *minIni.a: *.cpp.o) .rodata*)
+/* Uncomment below and comment above lines for debugging */
+        /* *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) */
+
         *(.srodata*)
         . = ALIGN(4);
         *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/rp23xx_btldr.ld

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 3 - 14
lib/ZuluSCSI_platform_RP2MCU/run_pioasm.sh

@@ -1,17 +1,6 @@
 #!/bin/bash
 
 # This script regenerates the .pio.h files from .pio
-pioasm sdio_RP2040.pio sdio_RP2040.pio.h
-pioasm sdio_Pico.pio sdio_Pico.pio.h
-pioasm sdio.RP2350.pio sdio.RP2350.pio.h
-pioasm sdio.Pico_2.pio sdio.Pico_2.h
-
-pioasm scsi_accel_target_RP2040.pio scsi_accel_target_RP2040.pio.h
-pioasm scsi_accel_target_Pico.pio scsi_accel_target_Pico.pio.h
-pioasm scsi_accel_target_RP2350A.pio scsi_accel_target_RP2350A.pio.h
-pioasm scsi_accel_target_Pico_2.pio scsi_accel_target_Pico_2.pio.h
-
-pioasm scsi_accel_host_RP2040.pio scsi_accel_host_RP2040.pio.h
-pioasm scsi_accel_host_Pico.pio scsi_accel_host_Pico.pio.h
-pioasm scsi_accel_host_RP2350A.pio scsi_accel_host_RP2350A.pio.h
-pioasm scsi_accel_host_Pico_2.pio scsi_accel_host_Pico_2.pio.h
+pioasm sdio_RP2MCU.pio sdio_RP2MCU.pio.h
+pioasm scsi_accel_target_RP2MCU.pio scsi_accel_target_RP2MCU.pio.h
+pioasm scsi_accel_host_RP2MCU.pio scsi_accel_host_RP2MCU.pio.h

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsi2sd_time.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from time.h in SCSI2SD V6.

+ 3 - 3
lib/ZuluSCSI_platform_RP2MCU/scsi2sd_timings.c

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2024 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2024-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -20,11 +20,11 @@
 **/
 #include "timings.h"
 #if defined(ZULUSCSI_MCU_RP23XX)
-uint8_t g_max_sync_20_period = 18;
+uint8_t g_max_sync_20_period = 12;
 uint8_t g_max_sync_10_period = 25;
 uint8_t g_max_sync_5_period  = 50; 
 #elif defined(ZULUSCSI_MCU_RP20XX)
-uint8_t g_max_sync_20_period = 25;
+uint8_t g_max_sync_20_period = 12;
 uint8_t g_max_sync_10_period = 25;
 uint8_t g_max_sync_5_period  = 50; 
 #endif

+ 2 - 2
lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 
@@ -212,7 +212,7 @@ static inline uint8_t scsiHostReadOneByte(int* parityError)
     SCSIHOST_WAIT_INACTIVE(REQ);
     SCSI_OUT(ACK, 0);
 
-    if (parityError && r != (g_scsi_parity_lookup[r & 0xFF] ^ SCSI_IO_DATA_MASK))
+    if (parityError && r != (g_scsi_parity_lookup[r & 0xFF] ^ (SCSI_IO_DATA_MASK >> SCSI_IO_SHIFT)))
     {
         logmsg("Parity error in scsiReadOneByte(): ", (uint32_t)r);
         *parityError = 1;

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 6 - 2
lib/ZuluSCSI_platform_RP2MCU/scsiPhy.cpp

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.c in SCSI2SD V6.
@@ -149,7 +149,11 @@ static void scsiPhyIRQ(uint gpio, uint32_t events)
     {
         // Note BSY / SEL interrupts only when we are not driving OUT_BSY low ourselves.
         // The BSY input pin may be shared with other signals.
+#if SCSI_OUT_BSY > 31
+        if (sio_hw->gpio_hi_out & (1 << (SCSI_OUT_BSY - 32)))
+#else
         if (sio_hw->gpio_out & (1 << SCSI_OUT_BSY))
+#endif
         {
             scsi_bsy_deassert_interrupt();
         }
@@ -374,7 +378,7 @@ static inline uint8_t scsiReadOneByte(int* parityError)
     SCSI_OUT(REQ, 0);
     SCSI_WAIT_INACTIVE(ACK);
 
-    if (parityError && r != (g_scsi_parity_lookup[r & 0xFF] ^ SCSI_IO_DATA_MASK))
+    if (parityError && r != (g_scsi_parity_lookup[r & 0xFF] ^ (SCSI_IO_DATA_MASK >> SCSI_IO_SHIFT)))
     {
         logmsg("Parity error in scsiReadOneByte(): ", (uint32_t)r);
         *parityError = 1;

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsiPhy.h

@@ -1,6 +1,6 @@
 /** 
  * SCSI2SD V6 - Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * This file is licensed under the GPL version 3 or any later version.  
  * It is derived from scsiPhy.h in SCSI2SD V6.

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.h

@@ -1,5 +1,5 @@
 /** 
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  * 
  * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  * 

+ 1 - 1
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host_RP2MCU.pio

@@ -1,4 +1,4 @@
-; ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+; ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
 ; 
 ; ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
 ; 

+ 53 - 40
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_target.cpp

@@ -1,5 +1,5 @@
 /**
- * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™
+ * ZuluSCSI™ - Copyright (c) 2022-2025 Rabbit Hole Computing™
  *
  * This work incorporates work from the following
  *  Copyright (c) 2023 joshua stein <jcs@jcs.org>
@@ -41,9 +41,9 @@
 #include <hardware/sync.h>
 #include <pico/multicore.h>
 
-#ifdef ENABLE_AUDIO_OUTPUT
-#include <audio.h>
-#endif // ENABLE_AUDIO_OUTPUT
+#ifdef ENABLE_AUDIO_OUTPUT_SPDIF
+#include "audio_spdif.h"
+#endif // ENABLE_AUDIO_OUTPUT_SPDIF
 
 #include "scsi_accel_target_RP2MCU.pio.h"
 
@@ -893,10 +893,10 @@ static int pio_add_scsi_sync_write_program()
 
 static void scsi_dma_irq()
 {
-#ifndef ENABLE_AUDIO_OUTPUT
+#ifndef ENABLE_AUDIO_OUTPUT_SPDIF
     dma_hw->ints0 = (1 << SCSI_DMA_CH_A);
 #else
-    // see audio.h for whats going on here
+    // see audio_spdif.h for whats going on here
     if (dma_hw->intr & (1 << SCSI_DMA_CH_A)) {
         dma_hw->ints0 = (1 << SCSI_DMA_CH_A);
     } else {
@@ -941,16 +941,16 @@ static void scsidma_config_gpio()
         pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, SCSI_IO_DB0, 9, true);
         pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_DATA_SM, SCSI_OUT_REQ, 1, true);
 
-        iobank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB2].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB3].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB4].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB5].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_PIO0;
-        iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB2].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB3].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB4].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB5].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
     }
     else if (g_scsi_dma_state == SCSIDMA_READ)
     {
@@ -970,16 +970,16 @@ static void scsidma_config_gpio()
             pio_sm_set_consecutive_pindirs(SCSI_DMA_PIO, SCSI_SYNC_SM, SCSI_OUT_REQ, 1, true);
         }
 
-        iobank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB2].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB3].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB4].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB5].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
-        iobank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
+        io_bank0_hw->io[SCSI_IO_DB0].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB1].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB2].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB3].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB4].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB5].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB6].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DB7].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_IO_DBP].ctrl  = GPIO_FUNC_SIO;
+        io_bank0_hw->io[SCSI_OUT_REQ].ctrl = GPIO_FUNC_PIO0;
     }
 }
 
@@ -1230,43 +1230,56 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
             // delay2: Delay from REQ deassert to data write (negation period)
             // see timings.c for delay periods in clock cycles
             int delay0, delay1, delay2;
+
+            // setup timings for sync_read_pacer
+            // total period = rdelay0 + redelay1
+            // rdelay0: wait for transfer period (total_period +  rtotal_period_adjust - rdelay1)
+            // rdelay1: req assert period
+            int rdelay1;
+
             uint32_t up_rounder = g_zuluscsi_timings->scsi.clk_period_ps / 2 + 1;
             uint32_t delay_in_ps = (syncPeriod * 4) * 1000;
-            // This is the delay in clock cycles rounded up
-            int totalDelay = (delay_in_ps + up_rounder) / g_zuluscsi_timings->scsi.clk_period_ps;
-
+            // This is the period in clock cycles rounded up
+            int totalPeriod = (delay_in_ps + up_rounder) / g_zuluscsi_timings->scsi.clk_period_ps;
+            int rtotalPeriod = totalPeriod;
             if (syncPeriod < 25)
             {
                 // Fast-20 SCSI timing: 15 ns assertion period
                 // The hardware rise and fall time require some extra delay,
                 // These delays are in addition to the 1 cycle that the PIO takes to execute the instruction
-                totalDelay += g_zuluscsi_timings->scsi_20.total_delay_adjust;
+                totalPeriod += g_zuluscsi_timings->scsi_20.total_period_adjust;
                 delay0 = g_zuluscsi_timings->scsi_20.delay0; //Data setup time, should be min 11.5ns according to the spec for FAST-20
                 delay1 = g_zuluscsi_timings->scsi_20.delay1; //pulse width, should be min 15ns according to the spec for FAST-20
-                delay2 = totalDelay - delay0 - delay1 - 3;  //Data hold time, should be min 16.5ns according to the spec for FAST-20
+                delay2 = totalPeriod - delay0 - delay1 - 3;  //Data hold time, should be min 16.5ns according to the spec for FAST-20
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
+                rdelay1 = g_zuluscsi_timings->scsi_20.rdelay1;
+                rtotalPeriod += g_zuluscsi_timings->scsi_20.rtotal_period_adjust;
             }
             else if (syncPeriod < 50 )
             {
                 // Fast-10 SCSI timing: 30 ns assertion period, 25 ns skew delay
                 // The hardware rise and fall time require some extra delay,
-                totalDelay += g_zuluscsi_timings->scsi_10.total_delay_adjust;
+                totalPeriod += g_zuluscsi_timings->scsi_10.total_period_adjust;
                 delay0 = g_zuluscsi_timings->scsi_10.delay0; // 4;
                 delay1 = g_zuluscsi_timings->scsi_10.delay1; // 6;
-                delay2 = totalDelay - delay0 - delay1 - 3;
+                delay2 = totalPeriod - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
+                rdelay1 = g_zuluscsi_timings->scsi_10.rdelay1;
+                rtotalPeriod += g_zuluscsi_timings->scsi_10.rtotal_period_adjust;
             }
             else
             {
                 // Slow SCSI timing: 90 ns assertion period, 55 ns skew delay
-                totalDelay += g_zuluscsi_timings->scsi_5.total_delay_adjust;
+                totalPeriod += g_zuluscsi_timings->scsi_5.total_period_adjust;
                 delay0 = g_zuluscsi_timings->scsi_5.delay0;
                 delay1 = g_zuluscsi_timings->scsi_5.delay1;
-                delay2 = totalDelay - delay0 - delay1 - 3;
+                delay2 = totalPeriod - delay0 - delay1 - 3;
                 if (delay2 < 0) delay2 = 0;
                 if (delay2 > 15) delay2 = 15;
+                rdelay1 = g_zuluscsi_timings->scsi_5.rdelay1;
+                rtotalPeriod += g_zuluscsi_timings->scsi_5.rtotal_period_adjust;
             }
 
             // Patch the delay values into the instructions in scsi_sync_write.
@@ -1279,11 +1292,11 @@ bool scsi_accel_rp2040_setSyncMode(int syncOffset, int syncPeriod)
             SCSI_DMA_PIO->instr_mem[g_scsi_dma.pio_offset_sync_write + 2] = instr2;
 
             // And similar patching for scsi_sync_read_pacer
-            int rdelay2 = totalDelay - delay1 - 2;
-            if (rdelay2 > 15) rdelay2 = 15;
-            if (rdelay2 < 5) rdelay2 = 5;
-            uint16_t rinstr0 = scsi_sync_read_pacer_program_instructions[0] | pio_encode_delay(rdelay2);
-            uint16_t rinstr1 = (scsi_sync_read_pacer_program_instructions[1] + g_scsi_dma.pio_offset_sync_read_pacer) | pio_encode_delay(delay1);
+            int rdelay0 = rtotalPeriod - rdelay1 - 2;
+            if (rdelay0 > 15) rdelay0 = 15;
+            if (rdelay0 < 0) rdelay0 = 0;
+            uint16_t rinstr0 = scsi_sync_read_pacer_program_instructions[0] | pio_encode_delay(rdelay0);
+            uint16_t rinstr1 = (scsi_sync_read_pacer_program_instructions[1] + g_scsi_dma.pio_offset_sync_read_pacer) | pio_encode_delay(rdelay1);
             SCSI_DMA_PIO->instr_mem[g_scsi_dma.pio_offset_sync_read_pacer + 0] = rinstr0;
             SCSI_DMA_PIO->instr_mem[g_scsi_dma.pio_offset_sync_read_pacer + 1] = rinstr1;
         }

Vissa filer visades inte eftersom för många filer har ändrats