|  | @@ -6,7 +6,7 @@
 | 
	
		
			
				|  |  |  #include "config.h"
 | 
	
		
			
				|  |  |  #include "scsi.h"
 | 
	
		
			
				|  |  |  // Log File
 | 
	
		
			
				|  |  | -#define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-03-20-F4"
 | 
	
		
			
				|  |  | +#define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-09-28-F4"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define LOG_FILENAME "LOG.txt"
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -114,6 +114,7 @@
 | 
	
		
			
				|  |  |  #define GPIOREG(VPIN)     ((VPIN) >= 16 ? ((VPIN) >= 32 ? ((VPIN) >= 48 ? ((VPIN) >= 48 ? PEREG : PDREG) : PCREG) : PBREG) : PAREG)
 | 
	
		
			
				|  |  |  #define BITMASK(VPIN)     (1 << ((VPIN % 16) & 15))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #define vATN       PB(14)     // SCSI:ATN
 | 
	
		
			
				|  |  |  #define vBSY       PB(6)      // SCSI:BSY
 | 
	
		
			
				|  |  |  #define vACK       PB(7)      // SCSI:ACK
 | 
	
	
		
			
				|  | @@ -159,4 +160,296 @@ void switchImage(void);
 | 
	
		
			
				|  |  |  void initFileLog(void);
 | 
	
		
			
				|  |  |  void finalizeFileLog(void);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// SCSI config
 | 
	
		
			
				|  |  | +#define MAX_SCSIID  7          // Maximum number of supported SCSI-IDs (The minimum is 0)
 | 
	
		
			
				|  |  | +#define MAX_SCSILUN 8          // Maximum number of LUNs supported     (The minimum is 0)
 | 
	
		
			
				|  |  | +#define NUM_SCSIID  MAX_SCSIID // Number of enabled SCSI IDs
 | 
	
		
			
				|  |  | +#define NUM_SCSILUN 1          // Number of enabled LUNs
 | 
	
		
			
				|  |  | +#define READ_PARITY_CHECK 0    // Perform read parity check (unverified)
 | 
	
		
			
				|  |  | +#define DEFAULT_SCSI_ID 1
 | 
	
		
			
				|  |  | +#define DEFAULT_SCSI_LUN 0
 | 
	
		
			
				|  |  | +#define SCSI_BUF_SIZE 512      // Size of the SCSI Buffer
 | 
	
		
			
				|  |  | +#define HDD_BLOCK_SIZE 512
 | 
	
		
			
				|  |  | +#define OPTICAL_BLOCK_SIZE 2048
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// HDD format
 | 
	
		
			
				|  |  | +#define MAX_BLOCKSIZE 4096     // Maximum BLOCK size
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// LED ERRORS
 | 
	
		
			
				|  |  | +#define ERROR_FALSE_INIT  3
 | 
	
		
			
				|  |  | +#define ERROR_NO_SDCARD   5
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +enum SCSI_DEVICE_TYPE
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  SCSI_DEVICE_HDD,
 | 
	
		
			
				|  |  | +  SCSI_DEVICE_OPTICAL,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define CDROM_RAW_SECTORSIZE    2352
 | 
	
		
			
				|  |  | +#define CDROM_COMMON_SECTORSIZE 2048
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define MAX_SCSI_COMMAND  0xff
 | 
	
		
			
				|  |  | +#define SCSI_COMMAND_HANDLER(x) static byte x(SCSI_DEVICE *dev, const byte *cdb)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define ATN       PB14      // SCSI:ATN
 | 
	
		
			
				|  |  | +#define BSY       PB6      // SCSI:BSY
 | 
	
		
			
				|  |  | +#define ACK       PB7     // SCSI:ACK
 | 
	
		
			
				|  |  | +#define RST       PA15     // SCSI:RST
 | 
	
		
			
				|  |  | +#define MSG       PE2      // SCSI:MSG
 | 
	
		
			
				|  |  | +#define SEL       PE3      // SCSI:SEL
 | 
	
		
			
				|  |  | +#define CD        PE4      // SCSI:C/D
 | 
	
		
			
				|  |  | +#define REQ       PE5      // SCSI:REQ
 | 
	
		
			
				|  |  | +#define IO        PB1      // SCSI:I/O
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define LED1      PA4     // LED
 | 
	
		
			
				|  |  | +#define LED2      PA5     // Driven LED
 | 
	
		
			
				|  |  | +#define LED3      PA6     // Driven LED
 | 
	
		
			
				|  |  | +// Image Set Selector
 | 
	
		
			
				|  |  | +#define IMAGE_SELECT1   PC4
 | 
	
		
			
				|  |  | +#define IMAGE_SELECT2   PC5
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define NOP(x) for(unsigned _nopcount = x; _nopcount; _nopcount--) { asm("NOP"); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* SCSI Timing delays */
 | 
	
		
			
				|  |  | +// Due to limitations in timing granularity all of these are "very" rough estimates
 | 
	
		
			
				|  |  | +#define SCSI_BUS_SETTLE() NOP(30);                            // spec 400ns ours ~420us
 | 
	
		
			
				|  |  | +#define SCSI_DATA_RELEASE() NOP(30);                          // spec 400ns ours ~420us
 | 
	
		
			
				|  |  | +#define SCSI_HOLD_TIME() asm("NOP"); asm("NOP"); asm("NOP");  // spec 45ns ours ~42ns
 | 
	
		
			
				|  |  | +#define SCSI_DESKEW() // asm("NOP"); asm("NOP"); asm("NOP");     // spec 45ns ours ~42ns
 | 
	
		
			
				|  |  | +#define SCSI_CABLE_SKEW() // asm("NOP");                         // spec 10ns ours ~14ns
 | 
	
		
			
				|  |  | +#define SCSI_RESET_HOLD() asm("NOP"); asm("NOP");             // spec 25ns ours ~28ns
 | 
	
		
			
				|  |  | +#define SCSI_DISCONNECTION_DELAY() NOP(15);                   // spec 200ns ours ~210ns
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* SCSI phases
 | 
	
		
			
				|  |  | ++=============-===============-==================================-============+
 | 
	
		
			
				|  |  | +|    Signal   |  Phase name   |       Direction of transfer      |  Comment   |
 | 
	
		
			
				|  |  | +|-------------|               |                                  |            |
 | 
	
		
			
				|  |  | +| MSG|C/D|I/O |               |                                  |            |
 | 
	
		
			
				|  |  | +|----+---+----+---------------+----------------------------------+------------|
 | 
	
		
			
				|  |  | +|  0 | 0 | 0  |  DATA OUT     |       Initiator to target     \  |  Data      |
 | 
	
		
			
				|  |  | +|  0 | 0 | 1  |  DATA IN      |       Initiator from target   /  |  phase     |
 | 
	
		
			
				|  |  | +|  0 | 1 | 0  |  COMMAND      |       Initiator to target        |            |
 | 
	
		
			
				|  |  | +|  0 | 1 | 1  |  STATUS       |       Initiator from target      |            |
 | 
	
		
			
				|  |  | +|  1 | 0 | 0  |  *            |                                  |            |
 | 
	
		
			
				|  |  | +|  1 | 0 | 1  |  *            |                                  |            |
 | 
	
		
			
				|  |  | +|  1 | 1 | 0  |  MESSAGE OUT  |       Initiator to target     \  |  Message   |
 | 
	
		
			
				|  |  | +|  1 | 1 | 1  |  MESSAGE IN   |       Initiator from target   /  |  phase     |
 | 
	
		
			
				|  |  | +|-----------------------------------------------------------------------------|
 | 
	
		
			
				|  |  | +| Key:  0 = False,  1 = True,  * = Reserved for future standardization        |
 | 
	
		
			
				|  |  | ++=============================================================================+ 
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +// SCSI phase change as single write to port B
 | 
	
		
			
				|  |  | +#define SCSIPHASEMASK(MSGACTIVE, CDACTIVE, IOACTIVE) ((BITMASK(vMSG)<<((MSGACTIVE)?16:0)) | (BITMASK(vCD)<<((CDACTIVE)?16:0)) | (BITMASK(vIO)<<((IOACTIVE)?16:0)))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_DATAOUT SCSIPHASEMASK(inactive, inactive, inactive)
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_DATAIN SCSIPHASEMASK(inactive, inactive, active)
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_COMMAND SCSIPHASEMASK(inactive, active, inactive)
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_STATUS SCSIPHASEMASK(inactive, active, active)
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_MESSAGEOUT SCSIPHASEMASK(active, active, inactive)
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_MESSAGEIN SCSIPHASEMASK(active, active, active)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define SCSI_PHASE_CHANGE(MASK) { PBREG->BSRR = (MASK); }
 | 
	
		
			
				|  |  | +//BLACKSASI clean this up and use defines
 | 
	
		
			
				|  |  | +// Data pins
 | 
	
		
			
				|  |  | +//                                                         5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbOutputRegOr_PDREG         = 0b00000000000000000101010101010101;
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbInputOutputAnd_PDREG      = 0b00000000000000000000000000000000;
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbInputOutputPullAnd_PDREG  = 0b00000000000000000101010101010101;
 | 
	
		
			
				|  |  | +// Control pins
 | 
	
		
			
				|  |  | +//                                                         5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbOutputRegOr_PEREG         = 0b00000000000000000000000000000001;
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbInputOutputAnd_PEREG      = 0b00000000000000000011111111110000;
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbInputOutputPullAnd_PEREG  = 0b00000000000000000101010101010001;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Put DB and DP in output mode and control buffers
 | 
	
		
			
				|  |  | +#define SCSI_DB_OUTPUT() { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG) | scsiDbOutputRegOr_PDREG; PEREG->MODER = (PEREG->MODER & scsiDbInputOutputAnd_PEREG) | scsiDbOutputRegOr_PEREG; SCSI_DATABUS_OUT() ;}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Put DB and DP in input mode and control buffers
 | 
	
		
			
				|  |  | +#define SCSI_DB_INPUT()  { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG); PEREG->MODER = (PEREG->MODER & scsiDbInputOutputAnd_PEREG); SCSI_DATABUS_IN();}
 | 
	
		
			
				|  |  | +#define SCSI_SET_PULL()  { PDREG->PUPDR |= scsiDbInputOutputPullAnd_PDREG; PEREG->PUPDR |= scsiDbInputOutputPullAnd_PEREG; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbOutputRegOr = 0x55150011;
 | 
	
		
			
				|  |  | +static const uint32_t scsiDbInputOutputAnd = 0x00C0FFCC;
 | 
	
		
			
				|  |  | +// Put DB and DP in output mode
 | 
	
		
			
				|  |  | +#define SCSI_DB_OUTPUT() { PBREG->MODER = (PBREG->MODER & scsiDbInputOutputAnd) | scsiDbOutputRegOr; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Put DB and DP in input mode
 | 
	
		
			
				|  |  | +#define SCSI_DB_INPUT()  { PBREG->MODER = (PBREG->MODER & scsiDbInputOutputAnd); }
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if XCVR == 1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define TR_TARGET        PC2   // Target Transceiver Control Pin
 | 
	
		
			
				|  |  | +#define TR_DBP           PC0   // Data Pins Transceiver Control Pin
 | 
	
		
			
				|  |  | +#define TR_INITIATOR     PC1   // Initiator Transciever Control Pin
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define vTR_TARGET       PC(2) // Target Transceiver Control Pin
 | 
	
		
			
				|  |  | +#define vTR_DBP          PC(0) // Data Pins Transceiver Control Pin
 | 
	
		
			
				|  |  | +#define vTR_INITIATOR    PC(1) // Initiator Transciever Control Pin
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define TR_INPUT 1
 | 
	
		
			
				|  |  | +#define TR_OUTPUT 0
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Transceiver control definitions
 | 
	
		
			
				|  |  | +#define TRANSCEIVER_IO_SET(VPIN,TR_INPUT) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((TR_INPUT) ? 16 : 0); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const uint32_t SCSI_TARGET_PORTA_AND = 0xFFF3FFFF;  // Sets input mode when AND-ed against MODER
 | 
	
		
			
				|  |  | +static const uint32_t SCSI_TARGET_PORTA_OR = 0x00040000;  // Sets output mode when AND+OR against MODER
 | 
	
		
			
				|  |  | +static const uint32_t SCSI_TARGET_PORTB_AND = 0xFFFF033F;  // Sets input mode when AND-ed against MODER
 | 
	
		
			
				|  |  | +static const uint32_t SCSI_TARGET_PORTB_OR = 0x00005440;  // Sets output mode when AND+OR against MODER
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Turn on the output only for BSY
 | 
	
		
			
				|  |  | +#define SCSI_BSY_ACTIVE()      { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND) | SCSI_TARGET_PORTA_OR; PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND) | SCSI_TARGET_PORTB_OR; SCSI_OUT(vBSY, active) }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
 | 
	
		
			
				|  |  | +#define SCSI_TARGET_INACTIVE() { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND); PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND); TRANSCEIVER_IO_SET(vTR_TARGET,TR_INPUT); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define SCSI_TARGET_ACTIVE()   { PAREG->MODER = (PAREG->MODER & SCSI_TARGET_PORTA_AND) | SCSI_TARGET_PORTA_OR; PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND) | SCSI_TARGET_PORTB_OR; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Turn on the output only for BSY
 | 
	
		
			
				|  |  | +#define SCSI_BSY_ACTIVE()      { pinMode(BSY, OUTPUT_OPEN_DRAIN); SCSI_OUT(vBSY,  active) }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input
 | 
	
		
			
				|  |  | +#define SCSI_TARGET_INACTIVE() { SCSI_OUT(vREQ,inactive); SCSI_PHASE_CHANGE(SCSI_PHASE_DATAOUT); SCSI_OUT(vBSY,inactive); pinMode(BSY, INPUT); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// BSY,REQ,MSG,CD,IO Turn on the output (no change required for OD)
 | 
	
		
			
				|  |  | +#define SCSI_TARGET_ACTIVE()   { }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// HDDimage file
 | 
	
		
			
				|  |  | +#define HDIMG_ID_POS  2                 // Position to embed ID number
 | 
	
		
			
				|  |  | +#define HDIMG_LUN_POS 3                 // Position to embed LUN numbers
 | 
	
		
			
				|  |  | +#define HDIMG_BLK_POS 5                 // Position to embed block size numbers
 | 
	
		
			
				|  |  | +#define MAX_FILE_PATH 64                // Maximum file name length
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + *  Data byte to BSRR register setting value and parity table
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * BSRR register generator
 | 
	
		
			
				|  |  | + * Totally configurable for which pin is each data bit, which pin is PTY, and which pin is REQ.
 | 
	
		
			
				|  |  | + * The only requirement is that data and parity pins are in the same GPIO block.
 | 
	
		
			
				|  |  | + * REQ can be specified as -1 to ignore, as it doesn't have to be in the same GPIO block.
 | 
	
		
			
				|  |  | + * This is dramatically slower than the original static array, but is easier to configure
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static uint32_t genBSRR(uint32_t data) {
 | 
	
		
			
				|  |  | +  uint8_t masks[] = {0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL};
 | 
	
		
			
				|  |  | +  // Positions array indicates which bit position each data bit goes in
 | 
	
		
			
				|  |  | +  // positions[0] is for data bit 0, position[1] for data bit 1, etc
 | 
	
		
			
				|  |  | +  // DB0, DB1, DB2, DB4, DB5, DB6, DB7 in order
 | 
	
		
			
				|  |  | +  uint8_t positions[] = {8UL, 9UL, 10UL, 2UL, 12UL, 13UL, 14UL, 15UL};
 | 
	
		
			
				|  |  | +  uint8_t dbpPosition = 0UL;
 | 
	
		
			
				|  |  | +  int reqPosition = 6;
 | 
	
		
			
				|  |  | +  uint8_t bitsAsserted = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  uint32_t output = 0x00000000;
 | 
	
		
			
				|  |  | +  for (int i = 0; i < 8; i++) {
 | 
	
		
			
				|  |  | +    if (data & (0x1 << masks[i])) {
 | 
	
		
			
				|  |  | +      // There's a one in this bit position, BSRR reset
 | 
	
		
			
				|  |  | +      output |= 0x1 << (positions[i] + 16);
 | 
	
		
			
				|  |  | +      bitsAsserted++;
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      // There's a 0 in this bit position, BSRR set high
 | 
	
		
			
				|  |  | +      output |= (0x1 << positions[i]);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Set the parity bit
 | 
	
		
			
				|  |  | +  if (bitsAsserted % 2 == 0) {
 | 
	
		
			
				|  |  | +    // Even number of bits asserted, Parity asserted (0, low, BSRR reset)
 | 
	
		
			
				|  |  | +    output |= 0x01 << (dbpPosition + 16);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    // Odd number of bits asserted, Parity deasserted (1, high, BSRR set)
 | 
	
		
			
				|  |  | +    output |= 0x01 << dbpPosition;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // BSRR set REQ if specified
 | 
	
		
			
				|  |  | +  // Only set > 0 if it's in the same GPIO block as DB and DBP
 | 
	
		
			
				|  |  | +  if (reqPosition >= 0) {
 | 
	
		
			
				|  |  | +    output |= 0x01 << reqPosition;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return output;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// BSRR register control value that simultaneously performs DB set, DP set, and REQ = H (inactrive)
 | 
	
		
			
				|  |  | +//uint32_t db_bsrr[256];
 | 
	
		
			
				|  |  | +// Parity bit acquisition
 | 
	
		
			
				|  |  | +#define PARITY(DB) (db_bsrr[DB]&1)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// #define GET_CDB6_LBA(x) ((x[2] & 01f) << 16) | (x[3] << 8) | x[4]
 | 
	
		
			
				|  |  | +#define READ_DATA_BUS() (byte)((~(uint32_t)GPIOB->regs->IDR)>>8)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct SCSI_INQUIRY_DATA
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  union
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +  struct {
 | 
	
		
			
				|  |  | +    // bitfields are in REVERSE order for ARM
 | 
	
		
			
				|  |  | +    // byte 0
 | 
	
		
			
				|  |  | +    byte peripheral_device_type:5;
 | 
	
		
			
				|  |  | +    byte peripheral_qualifier:3;
 | 
	
		
			
				|  |  | +    // byte 1
 | 
	
		
			
				|  |  | +    byte reserved_byte2:7;
 | 
	
		
			
				|  |  | +    byte rmb:1;
 | 
	
		
			
				|  |  | +    // byte 2
 | 
	
		
			
				|  |  | +    byte ansi_version:3;
 | 
	
		
			
				|  |  | +    byte always_zero_byte3:5;
 | 
	
		
			
				|  |  | +    // byte 3
 | 
	
		
			
				|  |  | +    byte response_format:4;
 | 
	
		
			
				|  |  | +    byte reserved_byte4:2;
 | 
	
		
			
				|  |  | +    byte tiop:1;
 | 
	
		
			
				|  |  | +    byte always_zero_byte4:1;
 | 
	
		
			
				|  |  | +    // byte 4
 | 
	
		
			
				|  |  | +    byte additional_length;
 | 
	
		
			
				|  |  | +    // byte 5-6
 | 
	
		
			
				|  |  | +    byte reserved_byte5;
 | 
	
		
			
				|  |  | +    byte reserved_byte6;
 | 
	
		
			
				|  |  | +    // byte 7
 | 
	
		
			
				|  |  | +    byte sync:1;
 | 
	
		
			
				|  |  | +    byte always_zero_byte7_more:4;
 | 
	
		
			
				|  |  | +    byte always_zero_byte7:3;
 | 
	
		
			
				|  |  | +    // byte 8-15
 | 
	
		
			
				|  |  | +    char vendor[8];
 | 
	
		
			
				|  |  | +    // byte 16-31
 | 
	
		
			
				|  |  | +    char product[16];
 | 
	
		
			
				|  |  | +    // byte 32-35
 | 
	
		
			
				|  |  | +    char revision[4];
 | 
	
		
			
				|  |  | +    // byte 36
 | 
	
		
			
				|  |  | +    byte release;
 | 
	
		
			
				|  |  | +    // 37-46
 | 
	
		
			
				|  |  | +    char revision_date[10];
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  // raw bytes
 | 
	
		
			
				|  |  | +  byte raw[64];
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// HDD image
 | 
	
		
			
				|  |  | +typedef __attribute__((aligned(4))) struct _SCSI_DEVICE
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	FsFile        *m_file;                  // File object
 | 
	
		
			
				|  |  | +	uint64_t      m_fileSize;               // File size
 | 
	
		
			
				|  |  | +	uint16_t      m_blocksize;              // SCSI BLOCK size
 | 
	
		
			
				|  |  | +  uint16_t      m_rawblocksize;           // OPTICAL raw sector size
 | 
	
		
			
				|  |  | +  uint8_t       m_type;                   // SCSI device type
 | 
	
		
			
				|  |  | +  uint32_t      m_blockcount;             // blockcount
 | 
	
		
			
				|  |  | +  bool          m_raw;                    // Raw disk
 | 
	
		
			
				|  |  | +  SCSI_INQUIRY_DATA *inquiry_block;       // SCSI information
 | 
	
		
			
				|  |  | +  uint8_t       m_senseKey;               // Sense key
 | 
	
		
			
				|  |  | +  uint16_t      m_additional_sense_code;  // ASC/ASCQ 
 | 
	
		
			
				|  |  | +  bool          m_mode2;                  // MODE2 CDROM
 | 
	
		
			
				|  |  | +  uint8_t       m_sector_offset;          // optical sector offset for missing sync header
 | 
	
		
			
				|  |  | +} SCSI_DEVICE;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #endif // __BLACKSASI_H__
 |