/* * SCSI-HDデバイスエミュレータ */ #include #include "SdFat.h" //ENABLE_EXTENDED_TRANSFER_CLASSを1に設定する //libraries/SdFat/SdFatConfig.h SPIClass SPI_2(2); SdFatEX SD(&SPI_2); //#define SPI_SPEED SD_SCK_MHZ(18) #define LOG(XX) //Serial.print(XX) #define LOGHEX(XX) //Serial.print(XX, HEX) #define LOGN(XX) //Serial.println(XX) #define LOGHEXN(XX) //Serial.println(XX, HEX) #define high 0 #define low 1 #define isHigh(XX) ((XX) == high) #define isLow(XX) ((XX) != high) #define gpio_mode(pin,val) gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val); #define gpio_write(pin,val) gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val) #define gpio_read(pin) gpio_read_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit) //#define DB0 PA0 // SCSI:DB0 //#define DB1 PA1 // SCSI:DB1 //#define DB2 PA2 // SCSI:DB2 //#define DB3 PA3 // SCSI:DB3 //#define DB4 PA4 // SCSI:DB4 //#define DB5 PA5 // SCSI:DB5 //#define DB6 PA6 // SCSI:DB6 //#define DB7 PA7 // SCSI:DB7 //#define DBP PA8 // SCSI:DBP #define ATN PB0 // SCSI:ATN #define BSY PB1 // SCSI:BSY #define ACK PB10 // SCSI:ACK #define RST PB11 // SCSI:RST #define MSG PB5 // SCSI:MSG #define SEL PB6 // SCSI:SEL #define CD PB7 // SCSI:C/D #define REQ PB8 // SCSI:REQ #define IO PB9 // SCSI:I/O #define SD_CS PB12 // SDCARD:CS #define LED PC13 // LED #define SCSIID 0 // SCSI-ID #define BLOCKSIZE 512 // 1BLOCKサイズ uint8_t m_senseKey = 0; //センスキー volatile bool m_isBusReset = false; //バスリセット #define HDIMG_FILE "HD.HDS" // HDイメージファイル名 File m_file; // ファイルオブジェクト uint32_t m_fileSize; // ファイルサイズ byte m_buf[BLOCKSIZE]; // 汎用バッファ int m_msc; bool m_msb[256]; /* * IO読み込み. */ inline byte readIO(void) { //GPIO(SCSI BUS)初期化 //ポート設定レジスタ(下位 GPIOA->regs->CRL = 0x88888888; // Configure GPIOA[7:0] uint32 ret = GPIOA->regs->IDR; byte bret = 0x00; bret |= ((!bitRead(ret,7)) << 7); bret |= ((!bitRead(ret,6)) << 6); bret |= ((!bitRead(ret,5)) << 5); bret |= ((!bitRead(ret,4)) << 4); bret |= ((!bitRead(ret,3)) << 3); bret |= ((!bitRead(ret,2)) << 2); bret |= ((!bitRead(ret,1)) << 1); bret |= ((!bitRead(ret,0)) << 0); return bret; } /* * IO書き込み. */ inline void writeIO(byte v) { //GPIO(SCSI BUS)初期化 //ポート設定レジスタ(下位) // GPIOA->regs->CRL = 0x11111111; // Configure GPIOA PP[7:0]10MHz GPIOA->regs->CRL = 0x33333333; // Configure GPIOA PP[7:0]50MHz //ポート設定レジスタ(上位) GPIOA->regs->CRH = 0x00000003; // Configure GPIOA PP[16:8]50MHz uint32 retL = 0x00; uint32 retH = 0x00; if(!parity(v)) { bitWrite(retL, 8, 1); } else { bitWrite(retH, 8, 1); } if(v & ( 1 << 7 )) { bitWrite(retL, 7, 1); } else { bitWrite(retH, 7, 1); } if(v & ( 1 << 6 )) { bitWrite(retL, 6, 1); } else { bitWrite(retH, 6, 1); } if(v & ( 1 << 5 )) { bitWrite(retL, 5, 1); } else { bitWrite(retH, 5, 1); } if(v & ( 1 << 4 )) { bitWrite(retL, 4, 1); } else { bitWrite(retH, 4, 1); } if(v & ( 1 << 3 )) { bitWrite(retL, 3, 1); } else { bitWrite(retH, 3, 1); } if(v & ( 1 << 2 )) { bitWrite(retL, 2, 1); } else { bitWrite(retH, 2, 1); } if(v & ( 1 << 1 )) { bitWrite(retL, 1, 1); } else { bitWrite(retH, 1, 1); } if(v & ( 1 << 0 )) { bitWrite(retL, 0, 1); } else { bitWrite(retH, 0, 1); } //ビットがLOWに設定される GPIOA->regs->BRR = retL ; // ビットがHIGHに設定される GPIOA->regs->BSRR = retH ; } /* * 初期化. * パリティチェック */ inline int parity(byte val) { val ^= val >> 16; val ^= val >> 8; val ^= val >> 4; val ^= val >> 2; val ^= val >> 1; return val & 0x00000001; } /* * 初期化. * バスの初期化、PINの向きの設定を行う */ void setup() { // PA15 / PB3 / PB4 が使えない // JTAG デバッグ用に使われているからです。 disableDebugPorts(); //シリアル初期化 //Serial.begin(9600); //while (!Serial); //PINの初期化 gpio_mode(LED, GPIO_OUTPUT_OD); gpio_write(LED, low); //GPIO(SCSI BUS)初期化 //ポート設定レジスタ(下位) GPIOA->regs->CRL = 0x888888888; // Configure GPIOA[8:0] gpio_mode(ATN, GPIO_INPUT_PU); gpio_mode(BSY, GPIO_INPUT_PU); gpio_mode(ACK, GPIO_INPUT_PU); gpio_mode(RST, GPIO_INPUT_PU); gpio_mode(SEL, GPIO_INPUT_PU); gpio_mode(MSG, GPIO_OUTPUT_PP); gpio_mode(CD, GPIO_OUTPUT_PP); gpio_mode(REQ, GPIO_OUTPUT_PP); gpio_mode(IO, GPIO_OUTPUT_PP); gpio_write(MSG, low); gpio_write(CD, low); gpio_write(REQ, low); gpio_write(IO, low); //RSTピンの状態がHIGHからLOWに変わったときに発生 attachInterrupt(PIN_MAP[RST].gpio_bit, onBusReset, FALLING); if(!SD.begin(SD_CS,SPI_FULL_SPEED)) { Serial.println("SD initialization failed!"); onFalseInit(); } //HDイメージファイル m_file = SD.open(HDIMG_FILE, O_RDWR); if(!m_file) { Serial.println("Error: open hdimg"); onFalseInit(); } m_fileSize = m_file.size(); Serial.println("Found Valid HD Image File."); Serial.print(m_fileSize); Serial.println("byte"); Serial.print(m_fileSize / 1024); Serial.println("KB"); Serial.print(m_fileSize / 1024 / 1024); Serial.println("MB"); } /* * 初期化失敗. */ void onFalseInit(void) { while(true) { gpio_write(LED, high); delay(500); gpio_write(LED, low); delay(500); } } /* * バスリセット割り込み. */ void onBusReset(void) { if(isHigh(gpio_read(RST))) { delayMicroseconds(20); if(isHigh(gpio_read(RST))) { LOGN("BusReset!"); m_isBusReset = true; } } } /* * ハンドシェイクで読み込む. */ byte readHandshake(void) { gpio_write(REQ, high); while(isLow(gpio_read(ACK))) { if(m_isBusReset) { return 0; } } byte r = readIO(); gpio_write(REQ, low); while(isHigh(gpio_read(ACK))) { if(m_isBusReset) { return 0; } } return r; } /* * ハンドシェイクで書込み. */ void writeHandshake(byte d) { writeIO(d); gpio_write(REQ, high); while(isLow(gpio_read(ACK))) { if(m_isBusReset) { return; } } gpio_write(REQ, low); while(isHigh(gpio_read(ACK))) { if(m_isBusReset) { return; } } } /* * データインフェーズ. * データ配列 p を len バイト送信する。 */ void writeDataPhase(int len, byte* p) { LOGN("DATAIN PHASE"); gpio_write(MSG, low); gpio_write(CD, low); gpio_write(IO, high); for (int i = 0; i < len; i++) { if(m_isBusReset) { return; } writeHandshake(p[i]); } } /* * データインフェーズ. * SDカードからの読み込みながら len ブロック送信する。 */ void writeDataPhaseSD(uint32_t adds, uint32_t len) { LOGN("DATAIN PHASE(SD)"); uint32_t pos = adds * BLOCKSIZE; m_file.seek(pos); gpio_write(MSG, low); gpio_write(CD, low); gpio_write(IO, high); for(uint32_t i = 0; i < len; i++) { m_file.read(m_buf, BLOCKSIZE); for(int j = 0; j < BLOCKSIZE; j++) { if(m_isBusReset) { return; } writeHandshake(m_buf[j]); } } } /* * データアウトフェーズ. * len ブロック読み込みながら SDカードへ書き込む。 */ void readDataPhaseSD(uint32_t adds, uint32_t len) { LOGN("DATAOUT PHASE(SD)"); uint32_t pos = adds * BLOCKSIZE; m_file.seek(pos); gpio_write(MSG, low); gpio_write(CD, low); gpio_write(IO, low); for(uint32_t i = 0; i < len; i++) { for(int j = 0; j < BLOCKSIZE; j++) { if(m_isBusReset) { return; } m_buf[j] = readHandshake(); } m_file.write(m_buf, BLOCKSIZE); } m_file.flush(); } /* * INQUIRY コマンド処理. */ void onInquiryCommand(byte len) { byte buf[36] = { 0x00, //デバイスタイプ 0x00, //RMB = 0 0x01, //ISO,ECMA,ANSIバージョン 0x01, //レスポンスデータ形式 35 - 4, //追加データ長 0, 0, //Reserve 0x00, //サポート機能 'T', 'N', 'B', ' ', ' ', ' ', ' ', ' ', 'A', 'r', 'd', 'S', 'C', 'S', 'i', 'n', 'o', ' ', ' ',' ', ' ', ' ', ' ', ' ', '0', '0', '1', '0', }; writeDataPhase(len < 36 ? len : 36, buf); } /* * REQUEST SENSE コマンド処理. */ void onRequestSenseCommand(byte len) { byte buf[18] = { 0x70, //CheckCondition 0, //セグメント番号 0x00, //センスキー 0, 0, 0, 0, //インフォメーション 17 - 7 , //追加データ長 0, }; buf[2] = m_senseKey; m_senseKey = 0; writeDataPhase(len < 18 ? len : 18, buf); } /* * READ CAPACITY コマンド処理. */ void onReadCapacityCommand(byte pmi) { uint32_t bc = m_fileSize / BLOCKSIZE; uint32_t bl = BLOCKSIZE; uint8_t buf[8] = { bc >> 24, bc >> 16, bc >> 8, bc, bl >> 24, bl >> 16, bl >> 8, bl }; writeDataPhase(8, buf); } /* * READ6/10 コマンド処理. */ byte onReadCommand(uint32_t adds, uint32_t len) { LOGN("-R"); LOGHEXN(adds); LOGHEXN(len); gpio_write(LED, high); writeDataPhaseSD(adds, len); gpio_write(LED, low); return 0; //sts } /* * WRITE6/10 コマンド処理. */ byte onWriteCommand(uint32_t adds, uint32_t len) { LOGN("-W"); LOGHEXN(adds); LOGHEXN(len); gpio_write(LED, high); readDataPhaseSD(adds, len); gpio_write(LED, low); return 0; //sts } /* * MODE SENSE コマンド処理. */ void onModeSenseCommand(byte dbd, int pageCode, uint32_t len) { memset(m_buf, 0, sizeof(m_buf)); int a = 4; if(dbd == 0) { uint32_t bc = m_fileSize / BLOCKSIZE; uint32_t bl = BLOCKSIZE; byte c[8] = { 0,//デンシティコード bc >> 16, bc >> 8, bc, 0, //Reserve bl >> 16, bl >> 8, bl }; memcpy(&m_buf[4], c, 8); a += 8; m_buf[3] = 0x08; } switch(pageCode) { case 0x3F: case 0x03: //ドライブパラメータ m_buf[a + 0] = 0x03; //ページコード m_buf[a + 1] = 0x16; // ページ長 m_buf[a + 11] = 0x3F;//セクタ数/トラック a += 24; if(pageCode != 0x3F) { break; } case 0x04: //ドライブパラメータ { uint32_t bc = m_fileSize / BLOCKSIZE; m_buf[a + 0] = 0x04; //ページコード m_buf[a + 1] = 0x16; // ページ長 m_buf[a + 2] = bc >> 16;// シリンダ長 m_buf[a + 3] = bc >> 8; m_buf[a + 4] = bc; m_buf[a + 5] = 1; //ヘッド数 a += 24; } if(pageCode != 0x3F) { break; } default: break; } m_buf[0] = a - 1; writeDataPhase(len < a ? len : a, m_buf); } /* * MsgIn2. */ void MsgIn2(int msg) { LOGN("MsgIn2"); gpio_write(MSG, high); gpio_write(CD, high); gpio_write(IO, high); writeHandshake(msg); } /* * MsgOut2. */ void MsgOut2() { LOGN("MsgOut2"); gpio_write(MSG, high); gpio_write(CD, high); gpio_write(IO, low); m_msb[m_msc] = readHandshake(); m_msc++; m_msc %= 256; } /* * メインループ. */ void loop() { int sts = 0; int msg = 0; //BSY,SELが+はバスフリー // セレクションチェック // BSYが-の間ループ if(isHigh(gpio_read(BSY))) { return; } // SELが+の間ループ if(isLow(gpio_read(SEL))) { return; } // BSY+ SEL- byte db = readIO(); if((db & (1 << SCSIID)) == 0) { return; } LOGN("Selection"); m_isBusReset = false; // セレクトされたらBSYを-にする gpio_mode(BSY, GPIO_OUTPUT_PP); gpio_write(BSY, high); while(isHigh(gpio_read(SEL))) { if(m_isBusReset) { goto BusFree; } } if(isHigh(gpio_read(ATN))) { bool syncenable = false; int syncperiod = 50; int syncoffset = 0; m_msc = 0; memset(m_msb, 0x00, sizeof(m_msb)); while(isHigh(gpio_read(ATN))) { MsgOut2(); } for(int i = 0; i < m_msc; i++) { // ABORT if (m_msb[i] == 0x06) { goto BusFree; } // BUS DEVICE RESET if (m_msb[i] == 0x0C) { syncoffset = 0; goto BusFree; } // IDENTIFY if (m_msb[i] >= 0x80) { } // 拡張メッセージ if (m_msb[i] == 0x01) { // 同期転送が可能な時だけチェック if (!syncenable || m_msb[i + 2] != 0x01) { MsgIn2(0x07); break; } // Transfer period factor(50 x 4 = 200nsに制限) syncperiod = m_msb[i + 3]; if (syncperiod > 50) { syncoffset = 50; } // REQ/ACK offset(16に制限) syncoffset = m_msb[i + 4]; if (syncoffset > 16) { syncoffset = 16; } // STDR応答メッセージ生成 MsgIn2(0x01); MsgIn2(0x03); MsgIn2(0x01); MsgIn2(syncperiod); MsgIn2(syncoffset); break; } } } LOGN("Command"); gpio_write(MSG, low); gpio_write(CD, high); gpio_write(IO, low); int len; byte cmd[12]; cmd[0] = readHandshake(); LOGHEX(cmd[0]); len = 1; switch(cmd[0] >> 5) { case 0b000: len = 6; break; case 0b001: len = 10; break; case 0b010: len = 10; break; case 0b101: len = 12; break; default: break; } for(int i = 1; i < len; i++ ) { cmd[i] = readHandshake(); LOGHEX(cmd[i]); } LOGN(""); switch(cmd[0]) { case 0x00: LOGN("[Test Unit]"); break; case 0x01: LOGN("[Rezero Unit]"); break; case 0x03: LOGN("[RequestSense]"); onRequestSenseCommand(cmd[4]); break; case 0x04: LOGN("[FormatUnit]"); break; case 0x06: LOGN("[FormatUnit]"); break; case 0x07: LOGN("[ReassignBlocks]"); break; case 0x08: LOGN("[Read6]"); sts = onReadCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]); break; case 0x0A: LOGN("[Write6]"); sts = onWriteCommand((((uint32_t)cmd[1] & 0x1F) << 16) | ((uint32_t)cmd[2] << 8) | cmd[3], (cmd[4] == 0) ? 0x100 : cmd[4]); break; case 0x0B: LOGN("[Seek6]"); break; case 0x12: LOGN("[Inquiry]"); onInquiryCommand(cmd[4]); break; case 0x1A: LOGN("[ModeSense6]"); onModeSenseCommand(cmd[1]&0x80, cmd[2] & 0x3F, cmd[4]); break; case 0x1B: LOGN("[StartStopUnit]"); break; case 0x1E: LOGN("[PreAllowMed.Removal]"); break; case 0x25: LOGN("[ReadCapacity]"); onReadCapacityCommand(cmd[8]); break; case 0x28: LOGN("[Read10]"); 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: LOGN("[Write10]"); 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: LOGN("[Seek10]"); break; case 0x5A: LOGN("[ModeSense10]"); onModeSenseCommand(cmd[1] & 0x80, cmd[2] & 0x3F, ((uint32_t)cmd[7] << 8) | cmd[8]); break; default: LOGN("[*Unknown]"); sts = 2; m_senseKey = 5; break; } if(m_isBusReset) { goto BusFree; } LOGN("Sts"); gpio_write(MSG, low); gpio_write(CD, high); gpio_write(IO, high); writeHandshake(sts); if(m_isBusReset) { goto BusFree; } LOGN("MsgIn"); gpio_write(MSG, high); gpio_write(CD, high); gpio_write(IO, high); writeHandshake(msg); BusFree: LOGN("BusFree"); m_isBusReset = false; gpio_write(REQ, low); gpio_write(MSG, low); gpio_write(CD, low); gpio_write(IO, low); // gpio_write(BSY, low); gpio_mode(BSY, GPIO_INPUT_PU); }