#ifndef __BLACKSASI_H__ #define __BLACKSASI_H__ #include "sdcard.h" #include "gpio.h" #include "log.h" #include "config.h" #include "scsi.h" // Log File #define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-09-28" #define LOG_FILENAME "LOG.txt" #define DEBUG 0 // 0:No debug information output // 1: Debug information output to USB Serial // 2: Debug information output to LOG.txt (slow) #define SCSI_SELECT 0 // 0 for STANDARD // 1 for SHARP X1turbo // 2 for NEC PC98 /* #define READ_SPEED_OPTIMIZE 1 // Faster reads #define WRITE_SPEED_OPTIMIZE 1 // Speeding up writes #define USE_DB2ID_TABLE 1 // Use table to get ID from SEL-DB */ // SCSI config #define NUM_SCSIID 7 // Maximum number of supported SCSI-IDs (The minimum is 0) #define NUM_SCSILUN 2 // Maximum number of LUNs supported (The minimum is 0) #define READ_PARITY_CHECK 0 // Perform read parity check (unverified) // HDD format #define MAX_BLOCKSIZE 2048 // Maximum BLOCK size #if DEBUG == 1 #define serial Serial #define LOG(XX) serial.print(XX) #define LOGHEX(XX) serial.print(XX, HEX) #define LOGDEC(XX) serial.print(XX, DEC) #define LOGBIN(XX) serial.print(XX, BIN) #define LOGN(XX) serial.println(XX) #define LOGHEXN(XX) serial.println(XX, HEX) #define LOGDECN(XX) serial.println(XX, DEC) #define LOGBIN_N(XX) serial.println(XX, BIN) #elif DEBUG == 2 #define LOG(XX) LOG_FILE.print(XX); LOG_FILE.sync(); #define LOGHEX(XX) LOG_FILE.print(XX, HEX); LOG_FILE.sync(); #define LOGDEC(XX) LOG_FILE.print(XX, DEC); LOG_FILE.sync(); #define LOGBIN(XX) LOG_FILE.print(XX, BIN); LOG_FILE.sync(); #define LOGN(XX) LOG_FILE.println(XX); LOG_FILE.sync(); #define LOGHEXN(XX) LOG_FILE.println(XX, HEX); LOG_FILE.sync(); #define LOGDECN(XX) LOG_FILE.println(XX, DEC); LOG_FILE.sync(); #define LOGBIN_N(XX) LOG_FILE.println(XX, BIN); LOG_FILE.sync(); #else #define LOG(XX) //serial.print(XX) #define LOGHEX(XX) //serial.print(XX, HEX) #define LOGDEC(XX) //serial.print(XX, DEC) #define LOGBIN(XX) //serial.print(XX, BIN) #define LOGN(XX) //serial.println(XX) #define LOGHEXN(XX) //serial.println(XX, HEX) #define LOGDECN(XX) //serial.println(XX, DEC) #define LOGBIN_N(XX) //serial.println(XX, BIN) #endif #define active 1 #define inactive 0 #define high 0 #define low 1 #define isHigh(XX) ((XX) == high) #define isLow(XX) ((XX) != high) #define TR_INPUT 1 #define TR_OUTPUT 0 #define DB_INPUT 0 #define DB_OUTPUT 1 // GPIO register port #define PAREG GPIOA->regs #define PBREG GPIOB->regs #define PCREG GPIOC->regs #define PDREG GPIOD->regs #define PEREG GPIOE->regs // Termination control (LOW is active) #define TERMINATION_HIGH() GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_HIGH % 16) << 16 | (1 << BOARD_SCSI_TERM_LOW % 16); #define TERMINATION_LOW() GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_LOW % 16) << 16 | (1 << BOARD_SCSI_TERM_HIGH % 16); #define TERMINATION_OFF() GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_HIGH % 16) | (1 << BOARD_SCSI_TERM_LOW % 16); // Enable SCSI buffers #define SCSI_OUTPUT_DISABLE() GPIOREG(BOARD_TRANS_OE)->BSRR = (1 << (BOARD_TRANS_OE & 15)) << 16; #define SCSI_OUTPUT_ENABLE() GPIOREG(BOARD_TRANS_OE)->BSRR = (1 << (BOARD_TRANS_OE & 15)); // SCSI Data Direction #define SCSI_DATABUS_OUT() GPIOREG(BOARD_SCSI_DTD)->BSRR = (1 << (BOARD_SCSI_DTD & 15)) << 16; #define SCSI_DATABUS_IN() GPIOREG(BOARD_SCSI_DTD)->BSRR = (1 << (BOARD_SCSI_DTD & 15)) // Virtual pin (Arduio compatibility is slow, so make it MCU-dependent) #define PA(BIT) (BIT) #define PB(BIT) (BIT + 16) #define PC(BIT) (BIT + 32) #define PD(BIT) (BIT + 48) #define PE(BIT) (BIT + 64) // Virtual pin decoding #define GPIOREG(VPIN) ((VPIN) >= 16 ? ((VPIN) >= 32 ? ((VPIN) >= 48 ? ((VPIN) >= 64 ? PEREG : PDREG) : PCREG) : PBREG) : PAREG) #define BITMASK(VPIN) (1 << ((VPIN) & 15)) #define vATN PB(14) // SCSI:ATN #define vBSY PB(6) // SCSI:BSY #define vACK PB(7) // SCSI:ACK #define vRST PA(15) // SCSI:RST #define vMSG PE(2) // SCSI:MSG #define vSEL PE(3) // SCSI:SEL #define vCD PE(4) // SCSI:C/D #define vREQ PE(5) // SCSI:REQ #define vIO PE(6) // SCSI:I/O #define vSD_CS PB(1) // SDCARD:CS #define vDTD PC(0) // SCSI:DTD #define vIND PC(1) // SCSI:IND #define vTAD PC(2) // SCSI:TAD #define vTRANS_OE PB(12) // SCSI:TRANS_OE // SCSI output pin control: active LOW (direct pin drive) #define SCSI_OUT(VPIN,ACTIVE) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((ACTIVE) ? 16 : 0); } // SCSI input pin check (inactive=0,active=1) #define SCSI_IN(VPIN) ((~GPIOREG(VPIN)->IDR >> ((VPIN) & 15)) & 1) // HDDiamge 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 32 // Maximum file name length // SCSI #define SCSI_INFO_BUF_SIZE 36 #define SCSI_INFO_VENDOR_SIZE 9 #define SCSI_INFO_PRODUCT_SIZE 17 #define SCSI_INFO_VERSION_SIZE 5 typedef struct hddimg_struct { FsFile m_file; // File object uint64_t m_fileSize; // File size size_t m_blocksize; // SCSI BLOCK size }HDDIMG; // Declare functions void onFalseInit(void); void onBusReset(void); void switchImage(void); void initFileLog(int); 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 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) { PEREG->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 = 0b00000000000000010101010101010101; static const uint32_t scsiDbInputOutputAnd_PDREG = 0b00000000000000000000000000000000; static const uint32_t scsiDbInputOutputPullAnd_PDREG = 0b00000000000000010101010101010101; // Control pins // 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 static const uint32_t scsiDbInputOutputPullAnd_PEREG = 0b00000000000000000001010101010001; static const uint32_t scsiDbInputOutputPullAnd_PBREG = 0b00010001000100000101000000000000; // Put DB and DP in output mode and control buffers #define SCSI_DB_OUTPUT() { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG) | scsiDbOutputRegOr_PDREG; SCSI_DATABUS_OUT() ;} // Put DB and DP in input mode and control buffers #define SCSI_DB_INPUT() { PDREG->MODER = (PDREG->MODER & scsiDbInputOutputAnd_PDREG); SCSI_DATABUS_IN();} #define SCSI_SET_PULL() { PDREG->PUPDR |= scsiDbInputOutputPullAnd_PDREG; PEREG->PUPDR = PEREG->PUPDR | scsiDbInputOutputPullAnd_PEREG; PBREG->PUPDR = PBREG->PUPDR | scsiDbInputOutputPullAnd_PBREG; } // Transceiver control definitions #define TRANSCEIVER_IO_SET(VPIN,TRX_INPUT) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((TRX_INPUT) ? 16 : 0); } #define PTY(V) (1^((V)^((V)>>1)^((V)>>2)^((V)>>3)^((V)>>4)^((V)>>5)^((V)>>6)^((V)>>7))&1) // 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 static const uint32_t SCSI_TARGET_PORTB_AND = 0b11001111111111110000111111111111; static const uint32_t SCSI_TARGET_PORTB_OR = 0b00010000000000000101000000000000; // 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 static const uint32_t SCSI_TARGET_PORTE_AND = 0b11111111111111111100000011000011; static const uint32_t SCSI_TARGET_PORTE_OR = 0b00000000000000000001010100010000; // Turn on the output only for BSY //#define SCSI_BSY_ACTIVE() { PEREG->MODER = (PEREG->MODER & SCSI_TARGET_PORTE_AND) | SCSI_TARGET_PORTE_OR; PDREG->MODER = (PDREG->MODER & SCSI_TARGET_PORTD_AND) | SCSI_TARGET_PORTD_OR; SCSI_OUT(vBSY, active) } #define SCSI_BSY_ACTIVE() { pinMode(BOARD_SCSI_BSY, OUTPUT); SCSI_OUT(vBSY, active) } // BSY,REQ,MSG,CD,IO Turn off output, BSY is the last input #define SCSI_TARGET_INACTIVE() { PEREG->MODER = (PEREG->MODER & SCSI_TARGET_PORTE_AND); PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND); TRANSCEIVER_IO_SET(vTAD,TR_INPUT); } #define SCSI_TARGET_ACTIVE() { PEREG->MODER = (PEREG->MODER & SCSI_TARGET_PORTE_AND) | SCSI_TARGET_PORTE_OR; PBREG->MODER = (PBREG->MODER & SCSI_TARGET_PORTB_AND) | SCSI_TARGET_PORTB_OR; } // 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[] = {0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL, 7UL}; uint8_t dbpPosition = 8UL; int reqPosition = 0; uint8_t bitsAsserted = 0; //PM2022 Fix this in better way since we now have the bits correct uint32_t output = 0x00000000; //output |=(~data) | (data << 16); 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 READ_DATA_BUS() (byte)((~(uint32_t)GPIOD->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__