| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266 | 
							- /*  
 
-  *  AzulSCSI
 
-  *  Copyright (c) 2022 Rabbit Hole Computing
 
-  * 
 
-  * This project is based on BlueSCSI:
 
-  *
 
-  *  BlueSCSI
 
-  *  Copyright (c) 2021  Eric Helgeson, Androda
 
-  *  
 
-  *  This file is free software: you may copy, redistribute and/or modify it  
 
-  *  under the terms of the GNU General Public License as published by the  
 
-  *  Free Software Foundation, either version 2 of the License, or (at your  
 
-  *  option) any later version.  
 
-  *  
 
-  *  This file 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://github.com/erichelgeson/bluescsi.  
 
-  *  
 
-  * This file incorporates work covered by the following copyright and  
 
-  * permission notice:  
 
-  *  
 
-  *     Copyright (c) 2019 komatsu   
 
-  *  
 
-  *     Permission to use, copy, modify, and/or distribute this software  
 
-  *     for any purpose with or without fee is hereby granted, provided  
 
-  *     that the above copyright notice and this permission notice appear  
 
-  *     in all copies.  
 
-  *  
 
-  *     THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL  
 
-  *     WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  
 
-  *     WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE  
 
-  *     AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR  
 
-  *     CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS  
 
-  *     OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,  
 
-  *     NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN  
 
-  *     CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  
 
-  */
 
- #include <SdFat.h>
 
- #include <minIni.h>
 
- #include <string.h>
 
- #include <ctype.h>
 
- #include "AzulSCSI_config.h"
 
- #include "AzulSCSI_platform.h"
 
- #include "AzulSCSI_log.h"
 
- SdFs SD;
 
- FsFile g_logfile;
 
- /***********************************/
 
- /* Error reporting by blinking led */
 
- /***********************************/
 
- #define BLINK_ERROR_NO_IMAGES  3 
 
- #define BLINK_ERROR_NO_SD_CARD 5
 
- void blinkStatus(int count)
 
- {
 
-   for (int i = 0; i < count; i++)
 
-   {
 
-     LED_ON();
 
-     delay(250);
 
-     LED_OFF();
 
-     delay(250);
 
-   }
 
- }
 
- /**************/
 
- /* Log saving */
 
- /**************/
 
- void save_logfile(bool always = false)
 
- {
 
-   static uint32_t prev_log_pos = 0;
 
-   static uint32_t prev_log_len = 0;
 
-   static uint32_t prev_log_save = 0;
 
-   uint32_t loglen = azlog_get_buffer_len();
 
-   if (loglen != prev_log_len)
 
-   {
 
-     // When debug is off, save log at most every LOG_SAVE_INTERVAL_MS
 
-     // When debug is on, save after every SCSI command.
 
-     if (always || g_azlog_debug || (LOG_SAVE_INTERVAL_MS > 0 && (uint32_t)(millis() - prev_log_save) > LOG_SAVE_INTERVAL_MS))
 
-     {
 
-       g_logfile.write(azlog_get_buffer(&prev_log_pos));
 
-       g_logfile.flush();
 
-       
 
-       prev_log_len = loglen;
 
-       prev_log_save = millis();
 
-     }
 
-   }
 
- }
 
- void init_logfile()
 
- {
 
-   static bool first_open_after_boot = true;
 
-   bool truncate = first_open_after_boot;
 
-   int flags = O_WRONLY | O_CREAT | (truncate ? O_TRUNC : O_APPEND);
 
-   g_logfile = SD.open(LOGFILE, flags);
 
-   save_logfile(true);
 
-   first_open_after_boot = false;
 
- }
 
- /*********************************/
 
- /* Global state for SCSI code    */
 
- /*********************************/
 
- volatile bool g_busreset = false;
 
- uint8_t   g_sensekey = 0;    // Error information
 
- uint8_t   g_sense_asc = 0;   // Additional error code
 
- uint8_t   g_sense_ascq = 0;  // Additional error code qualifier
 
- uint8_t   g_scsi_id_mask;    // Mask list of responding SCSI IDs
 
- uint8_t   g_scsi_id;         // Currently responding SCSI-ID
 
- uint8_t   g_scsi_lun;        // Logical unit number currently responding
 
- uint8_t   g_scsi_sts;        // Status byte
 
- uint8_t   g_scsi_buffer[READBUFFER_SIZE] __attribute__((aligned(4)));
 
- /*********************************/
 
- /* SCSI Drive Vendor information */
 
- /*********************************/
 
- // Quirks for specific SCSI hosts
 
- enum scsi_quirks_t {
 
-   SCSI_QUIRKS_STANDARD = 0,
 
-   SCSI_QUIRKS_SHARP = 1,
 
-   SCSI_QUIRKS_NEC_PC98 = 2
 
- } g_scsi_quirks;
 
- struct {
 
-   uint32_t ARBITRATION_DELAY_US;
 
-   uint32_t SELECTION_DELAY_US;
 
-   uint32_t COMMAND_DELAY_US;
 
-   uint32_t DATA_DELAY_US;
 
-   uint32_t STATUS_DELAY_US;
 
-   uint32_t MESSAGE_DELAY_US;
 
-   uint32_t REQ_TYPE_SETUP_NS;
 
- } g_scsi_timing;
 
- uint8_t SCSI_INFO_BUF[36] = {
 
-   0x00, //device type
 
-   0x00, //RMB = 0
 
-   0x01, //ISO, ECMA, ANSI version
 
-   0x01, //Response data format
 
-   35 - 4, //Additional data length
 
-   0, 0, //Reserve
 
-   0x00, //Support function
 
-   'Q', 'U', 'A', 'N', 'T', 'U', 'M', ' ', // vendor 8
 
-   'F', 'I', 'R', 'E', 'B', 'A', 'L', 'L', '1', ' ', ' ',' ', ' ', ' ', ' ', ' ', // product 16
 
-   '1', '.', '0', ' ' // version 4
 
- };
 
- void readSCSIDeviceConfig()
 
- {
 
-   char tmp[32];
 
-   g_scsi_quirks = (scsi_quirks_t)ini_getl("SCSI", "Quirks", SCSI_QUIRKS_STANDARD, CONFIGFILE);
 
-   const char *default_vendor = DEFAULT_VENDOR;
 
-   const char *default_product = DEFAULT_PRODUCT;
 
-   const char *default_version = DEFAULT_VERSION;
 
-   if (g_scsi_quirks == SCSI_QUIRKS_NEC_PC98)
 
-   {
 
-     default_vendor = "NECITSU ";
 
-     default_product = "ArdSCSino       ";
 
-     default_version = "0010";
 
-   }
 
-   memset(tmp, 0, sizeof(tmp));
 
-   ini_gets("SCSI", "Vendor", default_vendor, tmp, sizeof(tmp), CONFIGFILE);
 
-   memcpy(&(SCSI_INFO_BUF[8]), tmp, 8);
 
-   memset(tmp, 0, sizeof(tmp));
 
-   ini_gets("SCSI", "Product", default_product, tmp, sizeof(tmp), CONFIGFILE);
 
-   memcpy(&(SCSI_INFO_BUF[16]), tmp, 16);
 
-   memset(tmp, 0, sizeof(tmp));
 
-   ini_gets("SCSI", "Version", default_version, tmp, sizeof(tmp), CONFIGFILE);
 
-   memcpy(&(SCSI_INFO_BUF[32]), tmp, 4);
 
-   
 
-   if (ini_getbool("SCSI", "Debug", 0, CONFIGFILE))
 
-   {
 
-     g_azlog_debug = true;
 
-   }
 
-   g_scsi_timing.ARBITRATION_DELAY_US = ini_getl("SCSI", "ARBITRATION_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.SELECTION_DELAY_US   = ini_getl("SCSI", "SELECTION_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.COMMAND_DELAY_US     = ini_getl("SCSI", "COMMAND_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.DATA_DELAY_US        = ini_getl("SCSI", "DATA_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.STATUS_DELAY_US      = ini_getl("SCSI", "STATUS_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.MESSAGE_DELAY_US     = ini_getl("SCSI", "MESSAGE_DELAY_US", DEFAULT_SCSI_DELAY_US, CONFIGFILE);
 
-   g_scsi_timing.REQ_TYPE_SETUP_NS    = ini_getl("SCSI", "REQ_TYPE_SETUP_NS", DEFAULT_REQ_TYPE_SETUP_NS, CONFIGFILE);
 
- }
 
- /*********************************/
 
- /* Harddisk image file handling  */
 
- /*********************************/
 
- // Information about active HDD image
 
- typedef struct hddimg_struct
 
- {
 
- 	FsFile      m_file;                 // File object
 
- 	uint64_t    m_fileSize;             // File size
 
- 	size_t      m_blocksize;            // SCSI BLOCK size
 
- } HDDIMG;
 
- HDDIMG g_hddimg[NUM_SCSIID][NUM_SCSILUN]; // Allocate storage for all images
 
- static HDDIMG *g_currentimg = NULL; // Pointer to active image based on LUN/ID
 
- bool hddimageOpen(HDDIMG *h,const char *image_name,int id,int lun,int blocksize)
 
- {
 
-   h->m_fileSize = 0;
 
-   h->m_blocksize = blocksize;
 
-   h->m_file = SD.open(image_name, O_RDWR);
 
-   if(h->m_file.isOpen())
 
-   {
 
-     h->m_fileSize = h->m_file.size();
 
-     azlog("Opened image file ", image_name, " fileSize: ", (int)(h->m_fileSize / 1024), " kB");
 
-     
 
-     if (h->m_file.contiguousRange(NULL, NULL))
 
-     {
 
-       azlog("Image file is contiguous.");
 
-     }
 
-     else
 
-     {
 
-       azlog("WARNING: file ", image_name, " is not contiguous. This will increase read latency.");
 
-     }
 
-     if(h->m_fileSize > 0)
 
-     {
 
-       return true; // File opened
 
-     }
 
-     else
 
-     {
 
-       h->m_file.close();
 
-       h->m_fileSize = h->m_blocksize = 0; // no file
 
-       azlog("Error: image is empty");
 
-     }
 
-   }
 
-   return false;
 
- }
 
- // Iterate over the root path in the SD card looking for candidate image files.
 
- void findHDDImages()
 
- {
 
-   g_scsi_id_mask = 0x00;
 
-   SdFile root;
 
-   root.open("/");
 
-   SdFile file;
 
-   bool imageReady;
 
-   int usedDefaultId = 0;
 
-   while (1) {
 
-     if (!file.openNext(&root, O_READ)) break;
 
-     char name[MAX_FILE_PATH+1];
 
-     if(!file.isDir()) {
 
-       file.getName(name, MAX_FILE_PATH+1);
 
-       file.close();
 
-       if (tolower(name[0]) == 'h' && tolower(name[1]) == 'd') {
 
-         // Defaults for Hard Disks
 
-         int id  = 1; // 0 and 3 are common in Macs for physical HD and CD, so avoid them.
 
-         int lun = 0;
 
-         int blk = 512;
 
-         // Positionally read in and coerase the chars to integers.
 
-         // We only require the minimum and read in the next if provided.
 
-         int file_name_length = strlen(name);
 
-         if(file_name_length > 2) { // HD[N]
 
-           int tmp_id = name[HDIMG_ID_POS] - '0';
 
-           if(tmp_id > -1 && tmp_id < 8)
 
-           {
 
-             id = tmp_id; // If valid id, set it, else use default
 
-           }
 
-           else
 
-           {
 
-             id = usedDefaultId++;
 
-           }
 
-         }
 
-         if(file_name_length > 3) { // HD0[N]
 
-           int tmp_lun = name[HDIMG_LUN_POS] - '0';
 
-           if(tmp_lun > -1 && tmp_lun < 2) {
 
-             lun = tmp_lun; // If valid id, set it, else use default
 
-           }
 
-         }
 
-         int blk1 = 0, blk2 = 0, blk3 = 0, blk4 = 0;
 
-         if(file_name_length > 8) { // HD00_[111]
 
-           blk1 = name[HDIMG_BLK_POS] - '0';
 
-           blk2 = name[HDIMG_BLK_POS+1] - '0';
 
-           blk3 = name[HDIMG_BLK_POS+2] - '0';
 
-           if(file_name_length > 9) // HD00_NNN[1]
 
-             blk4 = name[HDIMG_BLK_POS+3] - '0';
 
-         }
 
-         if(blk1 == 2 && blk2 == 5 && blk3 == 6) {
 
-           blk = 256;
 
-         } else if(blk1 == 1 && blk2 == 0 && blk3 == 2 && blk4 == 4) {
 
-           blk = 1024;
 
-         } else if(blk1 == 2 && blk2 == 0 && blk3 == 4 && blk4 == 8) {
 
-           blk  = 2048;
 
-         }
 
-         if(id < NUM_SCSIID && lun < NUM_SCSILUN) {
 
-           HDDIMG *h = &g_hddimg[id][lun];
 
-           azlog("Trying to open ", name, " for id:", id, " lun:", lun);
 
-           imageReady = hddimageOpen(h,name,id,lun,blk);
 
-           if(imageReady) { // Marked as a responsive ID
 
-             g_scsi_id_mask |= 1<<id;
 
-           }
 
-         } else {
 
-           azlog("Invalid lun or id for image ", name);
 
-         }
 
-       } else {
 
-         azlog("Skipping file ", name);
 
-       }
 
-     }
 
-   }
 
-   if(usedDefaultId > 0) {
 
-     azlog("Some images did not specify a SCSI ID. Last file will be used at ID ", usedDefaultId);
 
-   }
 
-   root.close();
 
-   // Error if there are 0 image files
 
-   if(g_scsi_id_mask==0) {
 
-     azlog("ERROR: No valid images found!");
 
-     blinkStatus(BLINK_ERROR_NO_IMAGES);
 
-   }
 
-   // Print SCSI drive map
 
-   azlog("SCSI drive map:");
 
-   azlog_raw("ID");
 
-   for (int lun = 0; lun < NUM_SCSILUN; lun++)
 
-   {
 
-     azlog_raw(":LUN", lun);
 
-   }
 
-   azlog_raw(":\n");
 
-   for (int id = 0; id < NUM_SCSIID; id++)
 
-   {
 
-     azlog_raw(" ", id);
 
-     for (int lun = 0; lun < NUM_SCSILUN; lun++)
 
-     {
 
-       HDDIMG *h = &g_hddimg[id][lun];
 
-       if (h->m_file)
 
-       {
 
-         azlog_raw((h->m_blocksize<1000) ? ": " : ":");
 
-         azlog_raw((int)h->m_blocksize);
 
-       }
 
-       else
 
-       {
 
-         azlog_raw(":----");
 
-       }
 
-     }
 
-     azlog_raw(":\n");
 
-   }
 
- }
 
- /*********************************/
 
- /* SCSI bus communication        */
 
- /*********************************/
 
- #define active   1
 
- #define inactive 0
 
- #define SCSI_WAIT_ACTIVE(pin) \
 
-   if (!SCSI_IN(pin)) { \
 
-     if (!SCSI_IN(pin)) { \
 
-       while(!SCSI_IN(pin) && !g_busreset); \
 
-     } \
 
-   }
 
- #define SCSI_WAIT_INACTIVE(pin) \
 
-   if (SCSI_IN(pin)) { \
 
-     if (SCSI_IN(pin)) { \
 
-       while(SCSI_IN(pin) && !g_busreset); \
 
-     } \
 
-   }
 
- /*
 
-  * Read by handshake.
 
-  */
 
- inline uint8_t readHandshake(void)
 
- {
 
-   SCSI_OUT(REQ,active);
 
-   SCSI_WAIT_ACTIVE(ACK);
 
-   delay_100ns(); // ACK.Fall to DB output delay 100ns(MAX)  (DTC-510B)
 
-   uint8_t r = SCSI_IN_DATA();
 
-   SCSI_OUT(REQ, inactive);
 
-   SCSI_WAIT_INACTIVE(ACK);
 
-   return r;  
 
- }
 
- /*
 
-  * Write with a handshake.
 
-  */
 
- inline void writeHandshake(uint8_t d)
 
- {
 
-   SCSI_OUT_DATA(d);
 
-   delay_100ns(); // DB hold time before REQ (DTC-510B)
 
-   SCSI_OUT(REQ, active);
 
-   SCSI_WAIT_ACTIVE(ACK);
 
-   SCSI_RELEASE_DATA_REQ(); // Release data and REQ
 
-   SCSI_WAIT_INACTIVE(ACK);
 
- }
 
- /*
 
-  * Data in phase.
 
-  *  Send len uint8_ts of data array p.
 
-  */
 
- void writeDataPhase(int len, const uint8_t* p)
 
- {
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,inactive);
 
-   SCSI_OUT(IO ,  active);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   for (int i = 0; i < len; i++) {
 
-     if (g_busreset) break;
 
-     writeHandshake(p[i]);
 
-   }
 
- }
 
- /* 
 
-  * Data in phase.
 
-  *  Send len blocks while reading from SD card.
 
-  */
 
- void writeDataPhase_FromSD(uint32_t adds, uint32_t len)
 
- {
 
-   uint32_t pos = adds * g_currentimg->m_blocksize;
 
-   g_currentimg->m_file.seek(pos);
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,inactive);
 
-   SCSI_OUT(IO ,  active);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   while (len > 0)
 
-   {
 
-     uint32_t max_transfer_len = READBUFFER_SIZE / g_currentimg->m_blocksize;
 
-     uint32_t transfer_len = (len < max_transfer_len) ? len : max_transfer_len;
 
-     len -= transfer_len;
 
-     transfer_len *= g_currentimg->m_blocksize;
 
- #if STREAM_SD_TRANSFERS
 
-     azplatform_prepare_stream(g_scsi_buffer);
 
-     g_currentimg->m_file.read(g_scsi_buffer, transfer_len);
 
-     
 
-     if (g_busreset) return;
 
-     size_t status = azplatform_finish_stream();
 
-     if (status == 0)
 
-     {
 
-       // Streaming did not happen, send data now
 
-       azdbg("Streaming from SD failed, using fallback");
 
-       writeDataPhase(transfer_len, g_scsi_buffer);
 
-     }
 
-     else if (status != transfer_len)
 
-     {
 
-       azlog("Streaming failed halfway: ", (int)status, "/", (int)transfer_len, " bytes, data may be corrupt, aborting!");
 
-       g_scsi_sts |= 2;
 
-       return;
 
-     }
 
- #else
 
-     g_currentimg->m_file.read(g_scsi_buffer, transfer_len);
 
-     writeDataPhase(transfer_len, g_scsi_buffer);
 
- #endif
 
-   }
 
- }
 
- /*
 
-  * Data out phase.
 
-  *  len block read
 
-  */
 
- void readDataPhase(int len, uint8_t* p)
 
- {
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,inactive);
 
-   SCSI_OUT(IO ,inactive);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   for(int i = 0; i < len; i++)
 
-   {
 
-     if (g_busreset) break;
 
-     p[i] = readHandshake();
 
-   }
 
- }
 
- /*
 
-  * Data out phase.
 
-  *  Write to SD card while reading len block.
 
-  */
 
- void readDataPhase_ToSD(uint32_t adds, uint32_t len)
 
- {
 
-   uint32_t pos = adds * g_currentimg->m_blocksize;
 
-   g_currentimg->m_file.seek(pos);
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,inactive);
 
-   SCSI_OUT(IO ,inactive);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   while (len > 0)
 
-   {
 
-     uint32_t max_transfer_len = READBUFFER_SIZE / g_currentimg->m_blocksize;
 
-     uint32_t transfer_len = (len < max_transfer_len) ? len : max_transfer_len;
 
-     len -= transfer_len;
 
-     transfer_len *= g_currentimg->m_blocksize;
 
- #if STREAM_SD_TRANSFERS
 
-     azplatform_prepare_stream(g_scsi_buffer);
 
-     g_currentimg->m_file.write(g_scsi_buffer, transfer_len);
 
-     pos += transfer_len;
 
-     if (g_busreset) return;
 
-     size_t status = azplatform_finish_stream();
 
-     if (status == 0)
 
-     {
 
-       // Streaming did not happen, rewrite
 
-       azdbg("Streaming to SD failed, using fallback");
 
-       g_currentimg->m_file.seek(pos - transfer_len);
 
-       readDataPhase(transfer_len, g_scsi_buffer);
 
-       g_currentimg->m_file.write(g_scsi_buffer, transfer_len);
 
-     }
 
-     else if (status != transfer_len)
 
-     {
 
-       azlog("Streaming to SD failed halfway, data may be corrupt, aborting!");
 
-       g_scsi_sts |= 2;
 
-       return;
 
-     }
 
- #else
 
-     readDataPhase(transfer_len, g_scsi_buffer);
 
-     g_currentimg->m_file.write(g_scsi_buffer, transfer_len);
 
- #endif
 
-   }
 
-   g_currentimg->m_file.flush();
 
- }
 
- /*********************************/
 
- /* SCSI commands                 */
 
- /*********************************/
 
- // https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-08.html#8.2.16
 
- uint8_t onTestUnitReady()
 
- {
 
-   if(!g_currentimg)
 
-   {
 
-     g_sensekey = 2; // Not ready
 
-     g_sense_asc = 0x3A; // Medium not present
 
-     g_sense_ascq = 0;
 
-     return 0x02; // Check condition
 
-   }
 
-   return 0x00;
 
- }
 
- // INQUIRY command processing.
 
- uint8_t onInquiryCommand(uint8_t len)
 
- {
 
-   uint8_t response[sizeof(SCSI_INFO_BUF)];
 
-   int responselen = sizeof(SCSI_INFO_BUF);
 
-   memcpy(response, SCSI_INFO_BUF, responselen);
 
-   // Select device type based on LUN
 
-   if (g_scsi_lun >= NUM_SCSILUN)
 
-   {
 
-     response[0] = 0x7F; // Unsupported LUN
 
-   }
 
-   else if (!g_currentimg)
 
-   {
 
-     response[0] = 0x3F; // Unconnected LUN
 
-   }
 
-   writeDataPhase(len < responselen ? len : responselen, response);
 
-   return 0x00;
 
- }
 
- // REQUEST SENSE command processing.
 
- // Refer to https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-08.html#8.2.14
 
- void onRequestSenseCommand(uint8_t len)
 
- {
 
-   uint8_t buf[18] = {
 
-     0xF0,   //CheckCondition
 
-     0,      //Segment number
 
-     g_sensekey,   //Sense key
 
-     0, 0, 0, 0,  //information
 
-     17 - 7 ,   //Additional data length
 
-     0, 0, 0, 0, // Command specific
 
-     g_sense_asc,
 
-     g_sense_ascq,
 
-     0,
 
-     0, 0, 0
 
-   };
 
-   g_sensekey = 0;
 
-   g_sense_asc = 0;
 
-   g_sense_ascq = 0;
 
-   writeDataPhase(len < 18 ? len : 18, buf);  
 
- }
 
- // READ CAPACITY command processing.
 
- uint8_t onReadCapacityCommand(uint8_t pmi)
 
- {
 
-   if(!g_currentimg) return 0x02; // Image file absent
 
-   
 
-   uint32_t bl = g_currentimg->m_blocksize;
 
-   uint32_t bc = g_currentimg->m_fileSize / bl;
 
-   uint8_t buf[8] = {
 
-     (uint8_t)(bc >> 24), (uint8_t)(bc >> 16), (uint8_t)(bc >> 8), (uint8_t)(bc),
 
-     (uint8_t)(bl >> 24), (uint8_t)(bl >> 16), (uint8_t)(bl >> 8), (uint8_t)(bl)    
 
-   };
 
-   writeDataPhase(8, buf);
 
-   return 0x00;
 
- }
 
- // READ6 / 10 Command processing.
 
- uint8_t onReadCommand(uint32_t adds, uint32_t len)
 
- {
 
-   azdbg("Read at ", adds, " ", (int)len, " blocks");
 
-   
 
-   if(!g_currentimg) return 0x02; // Image file absent
 
-   
 
-   LED_ON();
 
-   writeDataPhase_FromSD(adds, len);
 
-   LED_OFF();
 
-   return 0x00;
 
- }
 
- // WRITE6 / 10 Command processing.
 
- uint8_t onWriteCommand(uint32_t adds, uint32_t len)
 
- {
 
-   azdbg("Write at ", adds, " ", (int)len, " blocks");
 
-   
 
-   if(!g_currentimg) return 0x02; // Image file absent
 
-   
 
-   LED_ON();
 
-   readDataPhase_ToSD(adds, len);
 
-   LED_OFF();
 
-   return 0;
 
- }
 
- // MODE SENSE command processing for NEC_PC98
 
- uint8_t onModeSenseCommand_NEC_PC98(uint8_t dbd, int cmd2, uint32_t len)
 
- {
 
-   if(!g_currentimg) return 0x02; // Image file absent
 
-   uint8_t buf[512];
 
-   int pageCode = cmd2 & 0x3F;
 
-   // Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
 
-   int size = g_currentimg->m_fileSize;
 
-   int cylinders = (int)(size >> 9);
 
-   cylinders >>= 3;
 
-   cylinders /= 25;
 
-   int sectorsize = 512;
 
-   int sectors = 25;
 
-   int heads = 8;
 
-   // Sector size
 
-   int disksize = 0;
 
-   for(disksize = 16; disksize > 0; --(disksize)) {
 
-     if ((1 << disksize) == sectorsize)
 
-       break;
 
-   }
 
-   // Number of blocks
 
-   // uint32_t diskblocks = (uint32_t)(size >> disksize);
 
-   memset(buf, 0, sizeof(buf)); 
 
-   uint32_t a = 4;
 
-   if(dbd == 0) {
 
-     uint32_t bl = g_currentimg->m_blocksize;
 
-     uint32_t bc = g_currentimg->m_fileSize / bl;
 
-     uint8_t c[8] = {
 
-       0,// Density code
 
-       (uint8_t)(bc >> 16), (uint8_t)(bc >> 8), (uint8_t)bc,
 
-       0, //Reserve
 
-       (uint8_t)(bl >> 16), (uint8_t)(bl >> 8), (uint8_t)(bl)
 
-     };
 
-     memcpy(&buf[4], c, 8);
 
-     a += 8;
 
-     buf[3] = 0x08;
 
-   }
 
-   switch(pageCode) {
 
-   case 0x3F:
 
-   {
 
-     buf[a + 0] = 0x01;
 
-     buf[a + 1] = 0x06;
 
-     a += 8;
 
-   }
 
-   case 0x03:  // drive parameters
 
-   {
 
-     buf[a + 0] = 0x80 | 0x03; // Page code
 
-     buf[a + 1] = 0x16; // Page length
 
-     buf[a + 2] = (uint8_t)(heads >> 8);// number of sectors / track
 
-     buf[a + 3] = (uint8_t)(heads);// number of sectors / track
 
-     buf[a + 10] = (uint8_t)(sectors >> 8);// number of sectors / track
 
-     buf[a + 11] = (uint8_t)(sectors);// number of sectors / track
 
-     int size = 1 << disksize;
 
-     buf[a + 12] = (uint8_t)(size >> 8);// number of sectors / track
 
-     buf[a + 13] = (uint8_t)(size);// number of sectors / track
 
-     a += 24;
 
-     if(pageCode != 0x3F) {
 
-       break;
 
-     }
 
-   }
 
-   case 0x04:  // drive parameters
 
-   {
 
-       azdbg("onModeSenseCommand_NEC_PC98: AddDrive");
 
-       buf[a + 0] = 0x04; // Page code
 
-       buf[a + 1] = 0x12; // Page length
 
-       buf[a + 2] = (cylinders >> 16);// Cylinder length
 
-       buf[a + 3] = (cylinders >> 8);
 
-       buf[a + 4] = cylinders;
 
-       buf[a + 5] = heads;   // Number of heads
 
-       a += 20;
 
-     if(pageCode != 0x3F) {
 
-       break;
 
-     }
 
-   }
 
-   default:
 
-     break;
 
-   }
 
-   buf[0] = a - 1;
 
-   writeDataPhase(len < a ? len : a, buf);
 
-   return 0x00;
 
- }
 
- uint8_t onModeSenseCommand(uint8_t dbd, int cmd2, uint32_t len)
 
- {
 
-   if (g_scsi_quirks == SCSI_QUIRKS_NEC_PC98)
 
-   {
 
-     return onModeSenseCommand_NEC_PC98(dbd, cmd2, len);
 
-   }
 
-   if(!g_currentimg) return 0x02; // No image file
 
-   uint8_t buf[512];
 
-   memset(buf, 0, sizeof(buf));
 
-   int pageCode = cmd2 & 0x3F;
 
-   uint32_t a = 4;
 
-   if(dbd == 0) {
 
-     uint32_t bl =  g_currentimg->m_blocksize;
 
-     uint32_t bc = g_currentimg->m_fileSize / bl;
 
-     uint8_t c[8] = {
 
-       0,//Density code
 
-       (uint8_t)(bc >> 16), (uint8_t)(bc >> 8), (uint8_t)bc,
 
-       0, //Reserve
 
-       (uint8_t)(bl >> 16), (uint8_t)(bl >> 8), (uint8_t)bl    
 
-     };
 
-     memcpy(&buf[4], c, 8);
 
-     a += 8;
 
-     buf[3] = 0x08;
 
-   }
 
-   switch(pageCode) {
 
-   case 0x3F:
 
-   case 0x03:  //Drive parameters
 
-     buf[a + 0] = 0x03; //Page code
 
-     buf[a + 1] = 0x16; // Page length
 
-     buf[a + 11] = 0x3F;//Number of sectors / track
 
-     a += 24;
 
-     if(pageCode != 0x3F) {
 
-       break;
 
-     }
 
-   case 0x04:  //Drive parameters
 
-     {
 
-       uint32_t bc = g_currentimg->m_fileSize / g_currentimg->m_file;
 
-       buf[a + 0] = 0x04; //Page code
 
-       buf[a + 1] = 0x16; // Page length
 
-       buf[a + 2] = bc >> 16;// Cylinder length
 
-       buf[a + 3] = bc >> 8;
 
-       buf[a + 4] = bc;
 
-       buf[a + 5] = 1;   //Number of heads
 
-       a += 24;
 
-     }
 
-     if(pageCode != 0x3F) {
 
-       break;
 
-     }
 
-   default:
 
-     break;
 
-   }
 
-   buf[0] = a - 1;
 
-   writeDataPhase(len < a ? len : a, buf);
 
-   return 0x00;
 
- }
 
- /*
 
-  * dtc510b_setDriveparameter for SCSI_QUIRKS_SHARP
 
-  */
 
- typedef struct __attribute__((packed)) dtc500_cmd_c2_param_struct
 
- {
 
-   uint8_t StepPlusWidth;        // Default is 13.6usec (11)
 
-   uint8_t StepPeriod;         // Default is  3  msec.(60)
 
-   uint8_t StepMode;         // Default is  Bufferd (0)
 
-   uint8_t MaximumHeadAdress;      // Default is 4 heads (3)
 
-   uint8_t HighCylinderAddressuint8_t;  // Default set to 0   (0)
 
-   uint8_t LowCylinderAddressuint8_t;   // Default is 153 cylinders (152)
 
-   uint8_t ReduceWrietCurrent;     // Default is above Cylinder 128 (127)
 
-   uint8_t DriveType_SeekCompleteOption;// (0)
 
-   uint8_t Reserved8;          // (0)
 
-   uint8_t Reserved9;          // (0)
 
- } DTC510_CMD_C2_PARAM;
 
- static uint8_t dtc510b_setDriveparameter(void)
 
- {
 
-   DTC510_CMD_C2_PARAM DriveParameter;
 
-   uint16_t maxCylinder;
 
-   uint16_t numLAD;
 
-   // int StepPeriodMsec;
 
-   // receive paramter
 
-   writeDataPhase(sizeof(DriveParameter),(uint8_t *)(&DriveParameter));
 
-  
 
-   maxCylinder =
 
-     (((uint16_t)DriveParameter.HighCylinderAddressuint8_t)<<8) |
 
-     (DriveParameter.LowCylinderAddressuint8_t);
 
-   numLAD = maxCylinder * (DriveParameter.MaximumHeadAdress+1);
 
-   // StepPeriodMsec = DriveParameter.StepPeriod*50;
 
-   azdbg(" StepPlusWidth      : ",DriveParameter.StepPlusWidth);
 
-   azdbg(" StepPeriod         : ",DriveParameter.StepPeriod   );
 
-   azdbg(" StepMode           : ",DriveParameter.StepMode     );
 
-   azdbg(" MaximumHeadAdress  : ",DriveParameter.MaximumHeadAdress);
 
-   azdbg(" CylinderAddress    : ",maxCylinder);
 
-   azdbg(" ReduceWriteCurrent : ",DriveParameter.ReduceWrietCurrent);
 
-   azdbg(" DriveType/SeekCompleteOption : ",DriveParameter.DriveType_SeekCompleteOption);
 
-   azdbg(" Maximum LAD        : ",numLAD-1);
 
-   return  0;
 
- }
 
- // Read the command, returns 0 on bus reset.
 
- int readSCSICommand(uint8_t cmd[12])
 
- {
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,  active);
 
-   SCSI_OUT(IO ,inactive);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   cmd[0] = readHandshake();
 
-   if (g_busreset) return 0;
 
-   static const int cmd_class_len[8]={6,10,10,6,6,12,6,6};
 
-   int cmdlen = cmd_class_len[cmd[0] >> 5];
 
-   for (int i = 1; i < cmdlen; i++)
 
-   {
 
-     cmd[i] = readHandshake();
 
-     if (g_busreset) return 0;
 
-   }
 
-   azdbg("Got command (", cmdlen, " bytes): ", bytearray(cmd, cmdlen));
 
-   return cmdlen;
 
- }
 
- // ATN message handling, returns false on abort/reset
 
- bool onATNMessage()
 
- {
 
-   bool syncenable = false;
 
-   int syncperiod = 50;
 
-   int syncoffset = 0;
 
-   SCSI_OUT(MSG,  active);
 
-   SCSI_OUT(CD ,  active);
 
-   SCSI_OUT(IO ,inactive);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   uint8_t msg[256];
 
-   memset(msg, 0x00, sizeof(msg));
 
-   uint8_t response[64];
 
-   size_t responselen = 0;
 
-   memset(response, 0x00, sizeof(response));
 
-   int msg_bytes = 0;
 
-   while(SCSI_IN(ATN) && msg_bytes < (int)sizeof(msg)) {
 
-     if (g_busreset)
 
-     {
 
-       return false;
 
-     }
 
-     
 
-     msg[msg_bytes] = readHandshake();
 
-     msg_bytes++;
 
-   }
 
-   if (msg_bytes == 0)
 
-   {
 
-     return false;
 
-   }
 
-   azdbg("Received MSG ", (int)msg_bytes, " bytes: ", bytearray(msg, msg_bytes));
 
-   for (int i = 0; i < msg_bytes; i++)
 
-   {
 
-     // ABORT
 
-     if (msg[i] == 0x06) {
 
-       azdbg("MSG_ABORT");
 
-       return false;
 
-     }
 
-     // BUS DEVICE RESET
 
-     if (msg[i] == 0x0C) {
 
-       azdbg("MSG_BUS_DEVICE_RESET");
 
-       return false;
 
-     }
 
-     // IDENTIFY
 
-     if (msg[i] >= 0x80) {
 
-       azdbg("MSG_IDENTIFY");
 
-     }
 
-     // Extended message
 
-     if (msg[i] == 0x01) {
 
-       azdbg("MSG_EXTENDED");
 
-       // Check only when synchronous transfer is possible
 
-       if (!syncenable || msg[i + 2] != 0x01) {
 
-         response[0] = 0x07;
 
-         responselen = 1;
 
-         break;
 
-       }
 
-       // Transfer period factor(50 x 4 = Limited to 200ns)
 
-       syncperiod = msg[i + 3];
 
-       if (syncperiod > 50) {
 
-         syncperiod = 50;
 
-       }
 
-       // REQ/ACK offset(Limited to 16)
 
-       syncoffset = msg[i + 4];
 
-       if (syncoffset > 16) {
 
-         syncoffset = 16;
 
-       }
 
-       // STDR response message generation
 
-       response[0] = 0x01;
 
-       response[1] = 0x03;
 
-       response[2] = 0x01;
 
-       response[3] = syncperiod;
 
-       response[4] = syncoffset;
 
-       responselen = 5;
 
-       break;
 
-     }
 
-   }
 
-   if (responselen > 0)
 
-   {
 
-     azdbg("Sending MSG response, ", (int)responselen, " bytes: ", bytearray(response, responselen));
 
-     SCSI_OUT(MSG,  active);
 
-     SCSI_OUT(CD ,  active);
 
-     SCSI_OUT(IO ,  active);
 
-     delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-     for (int i = 0; i < responselen; i++)
 
-     {
 
-       writeHandshake(response[i]);
 
-     }
 
-   }
 
-   return true;
 
- }
 
- /*********************************/
 
- /* Main SCSI handling loop       */
 
- /*********************************/
 
- void scsi_loop()
 
- {
 
-   SCSI_RELEASE_OUTPUTS();
 
-   if (g_busreset)
 
-   {
 
-     g_busreset = false;
 
-   }
 
-   // Wait until RST = H, BSY = H, SEL = L
 
-   uint32_t start = millis();
 
-   while (SCSI_IN(BSY) || !SCSI_IN(SEL) || SCSI_IN(RST))
 
-   {
 
-     if ((uint32_t)(millis() - start) > 1000)
 
-     {
 
-       // Service main loop while waiting for request
 
-       return;
 
-     }
 
-   }
 
-   // BSY+ SEL-
 
-   // If the ID to respond is not driven, wait for the next
 
-   uint8_t scsiid = SCSI_IN_DATA() & g_scsi_id_mask;
 
-   if (scsiid == 0) {
 
-     return; // Not for us
 
-   }
 
-   g_busreset = false;
 
-   
 
-   delay_us(g_scsi_timing.ARBITRATION_DELAY_US);
 
-   // Set BSY to-when selected
 
-   SCSI_OUT(BSY, active);
 
-   azdbg("------------ SCSI device selected");
 
-   
 
-   // Ask for a TARGET-ID to respond
 
-   g_scsi_id = 0;
 
-   for(int i = 7; i >= 0; i--)
 
-   {
 
-     if (scsiid & (1<<i))
 
-     {
 
-       g_scsi_id = i;
 
-       break;
 
-     }
 
-   }
 
-   // Wait until SEL becomes inactive
 
-   while (SCSI_IN(SEL))
 
-   {
 
-     if (g_busreset)
 
-     {
 
-       SCSI_RELEASE_OUTPUTS();
 
-       return;
 
-     }
 
-   }
 
-   
 
-   delay_us(g_scsi_timing.SELECTION_DELAY_US);
 
-   if(SCSI_IN(ATN))
 
-   {
 
-     if (!onATNMessage())
 
-     {
 
-       // Abort/reset message
 
-       SCSI_RELEASE_OUTPUTS();
 
-       return;
 
-     }
 
-   }
 
-   delay_us(g_scsi_timing.COMMAND_DELAY_US);
 
-   uint8_t cmd[12];
 
-   int cmdlen = readSCSICommand(cmd);
 
-   delay_us(g_scsi_timing.DATA_DELAY_US);
 
-   if (cmdlen == 0)
 
-   {
 
-     SCSI_RELEASE_OUTPUTS();
 
-     return;
 
-   }
 
-   
 
-   // LUN selection
 
-   g_scsi_lun = cmd[1] >> 5;
 
-   // HDD Image selection
 
-   g_currentimg = (HDDIMG *)0; // None
 
-   if( (g_scsi_lun <= NUM_SCSILUN) )
 
-   {
 
-     g_currentimg = &(g_hddimg[g_scsi_id][g_scsi_lun]); // There is an image
 
-     if(!(g_currentimg->m_file.isOpen()))
 
-       g_currentimg = (HDDIMG *)0;       // Image absent
 
-   }
 
-   g_scsi_sts = 0;
 
-   
 
-   azdbg("CMD ", cmd[0], " (", cmdlen, " bytes): ", "ID", (int)g_scsi_id, ", LUN", (int)g_scsi_lun);
 
-   
 
-   switch(cmd[0]) {
 
-     case 0x00:
 
-       azdbg("[Test Unit Ready]");
 
-       g_scsi_sts |= onTestUnitReady();
 
-       break;
 
-     case 0x01: azdbg("[Rezero Unit]"); break;
 
-     case 0x03:
 
-       azdbg("[RequestSense]");
 
-       onRequestSenseCommand(cmd[4]);
 
-       break;
 
-     case 0x04: azdbg("[FormatUnit]"); break;
 
-     case 0x06: azdbg("[FormatUnit]"); break;
 
-     case 0x07: azdbg("[ReassignBlocks]"); break;
 
-     case 0x08:
 
-       azdbg("[Read6]");
 
-       g_scsi_sts |= onReadCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]);
 
-       break;
 
-     case 0x0A:
 
-       azdbg("[Write6]");
 
-       g_scsi_sts |= onWriteCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]);
 
-       break;
 
-     case 0x0B: azdbg("[Seek6]"); break;
 
-     case 0x12:
 
-       azdbg("[Inquiry]");
 
-       g_scsi_sts |= onInquiryCommand(cmd[4]);
 
-       break;
 
-     case 0x1A:
 
-       azdbg("[ModeSense6]");
 
-       g_scsi_sts |= onModeSenseCommand(cmd[1]&0x80, cmd[2], cmd[4]);
 
-       break;
 
-     case 0x1B: azdbg("[StartStopUnit]"); break;
 
-     case 0x1E: azdbg("[PreAllowMed.Removal]"); break;
 
-     case 0x25:
 
-       azdbg("[ReadCapacity]");
 
-       g_scsi_sts |= onReadCapacityCommand(cmd[8]);
 
-       break;
 
-     case 0x28:
 
-       azdbg("[Read10]");
 
-       g_scsi_sts |= onReadCommand(((uint32_t)cmd[2] << 24) | ((uint32_t)cmd[3] << 16) | ((uint32_t)cmd[4] << 8) | cmd[5], ((uint32_t)cmd[7] << 8) | cmd[8]);
 
-       break;
 
-     case 0x2A:
 
-       azdbg("[Write10]");
 
-       g_scsi_sts |= onWriteCommand(((uint32_t)cmd[2] << 24) | ((uint32_t)cmd[3] << 16) | ((uint32_t)cmd[4] << 8) | cmd[5], ((uint32_t)cmd[7] << 8) | cmd[8]);
 
-       break;
 
-     case 0x2B: azdbg("[Seek10]"); break;
 
-     case 0x5A:
 
-       azdbg("[ModeSense10]");
 
-       onModeSenseCommand(cmd[1] & 0x80, cmd[2], ((uint32_t)cmd[7] << 8) | cmd[8]);
 
-       break;
 
-     case 0xc2:
 
-       if (g_scsi_quirks == SCSI_QUIRKS_SHARP)
 
-       {
 
-         azdbg("[DTC510B setDriveParameter]");
 
-         g_scsi_sts |= dtc510b_setDriveparameter();
 
-       }
 
-       else
 
-       {
 
-         g_scsi_sts |= 0x02;
 
-         g_sensekey = 5;
 
-       }
 
-       break;
 
-       
 
-     default:
 
-       azdbg("[Unknown CMD: ", cmd[0], "]");
 
-       g_scsi_sts |= 0x02;
 
-       g_sensekey = 5;
 
-       break;
 
-   }
 
-   if (g_busreset) {
 
-      SCSI_RELEASE_OUTPUTS();
 
-      return;
 
-   }
 
-   delay_us(g_scsi_timing.STATUS_DELAY_US);
 
-   azdbg("Status: ", g_scsi_sts);
 
-   SCSI_OUT(MSG,inactive);
 
-   SCSI_OUT(CD ,  active);
 
-   SCSI_OUT(IO ,  active);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   writeHandshake(g_scsi_sts);
 
-   
 
-   if(g_busreset) {
 
-      SCSI_RELEASE_OUTPUTS();
 
-      return;
 
-   }
 
-   delay_us(g_scsi_timing.MESSAGE_DELAY_US);
 
-   SCSI_OUT(MSG,  active);
 
-   SCSI_OUT(CD ,  active);
 
-   SCSI_OUT(IO ,  active);
 
-   delay_ns(g_scsi_timing.REQ_TYPE_SETUP_NS);
 
-   writeHandshake(0);
 
-   save_logfile();
 
-   azdbg("------------ Command complete");
 
-   SCSI_RELEASE_OUTPUTS();
 
- }
 
- void onBusReset(void)
 
- {
 
-   if (g_busreset)
 
-   {
 
-     // Previous reset is not yet handled
 
-     return;
 
-   }
 
-   int filterlen = 100;
 
-   if (g_scsi_quirks == SCSI_QUIRKS_SHARP)
 
-   {
 
-     // SASI I / F for X1 turbo has RST pulse write cycle +2 clock
 
-     // Active about 1.25 us
 
-     filterlen = 2;
 
-   }
 
-   while (filterlen > 0)
 
-   {
 
-     delay_100ns();
 
-     if (!SCSI_IN(RST)) return;
 
-     filterlen--;
 
-   }
 
-   SCSI_RELEASE_OUTPUTS();
 
-   azdbg("BUSRESET");
 
-   g_busreset = true;
 
- }
 
- int main(void)
 
- {
 
-   azplatform_init();
 
-   azplatform_set_rst_callback(&onBusReset);
 
-   if(!SD.begin(SD_CONFIG))
 
-   {
 
-     azlog("SD card init failed, sdErrorCode: ", (int)SD.sdErrorCode(),
 
-            " sdErrorData: ", (int)SD.sdErrorData());
 
-     
 
-     do
 
-     {
 
-       blinkStatus(BLINK_ERROR_NO_SD_CARD);
 
-       delay(1000);
 
-     } while (!SD.begin(SD_CONFIG));
 
-     azlog("SD card init succeeded after retry");
 
-   }
 
-   uint64_t size = (uint64_t)SD.vol()->clusterCount() * SD.vol()->bytesPerCluster();
 
-   azlog("SD card init succeeded, FAT", (int)SD.vol()->fatType(),
 
-           " volume size: ", (int)(size / 1024 / 1024), " MB");
 
-   readSCSIDeviceConfig();
 
-   findHDDImages();
 
-   azlog("Initialization complete!");
 
-   azlog("Platform: ", g_azplatform_name);
 
-   azlog("FW Version: ", g_azlog_firmwareversion);
 
-   init_logfile();
 
-   if (g_scsi_id_mask != 0)
 
-   {
 
-     // Ok, there is an image
 
-     blinkStatus(1);
 
-   }
 
-   while (1)
 
-   {
 
-     scsi_loop();
 
-     // Check SD card status for hotplug
 
-     uint32_t ocr;
 
-     if (!SD.card()->readOCR(&ocr))
 
-     {
 
-       if (!SD.card()->readOCR(&ocr))
 
-       {
 
-         azlog("SD card removed, trying to reinit");
 
-         do
 
-         {
 
-           blinkStatus(BLINK_ERROR_NO_SD_CARD);
 
-           delay(1000);
 
-         } while (!SD.begin(SD_CONFIG));
 
-         azlog("SD card reinit succeeded");
 
-         readSCSIDeviceConfig();
 
-         findHDDImages();
 
-         if (g_scsi_id_mask != 0)
 
-         {
 
-           blinkStatus(1);
 
-         }
 
-       }
 
-     }
 
-   }
 
- }
 
 
  |