commit 3ee2b14821386ae3d70e3ac5527a20b007cc19e5 Author: ztto <39242973+ztto@users.noreply.github.com> Date: Sun Dec 30 03:07:30 2018 +0900 新規作成 diff --git a/ArdSCSino.ino b/ArdSCSino.ino new file mode 100644 index 0000000..00a904f --- /dev/null +++ b/ArdSCSino.ino @@ -0,0 +1,724 @@ +/* + * SCSI-HDデバイスエミュレータ + */ +#include +#include + +//ENABLE_EXTENDED_TRANSFER_CLASSを1に設定する +//libraries/SdFat/SdFatConfig.h +SPIClass SPI_2(2); +//SdFat SD(2); +SdFatEX SD(&SPI_2); + +//#define SPI_SPEED SD_SCK_MHZ(18) + +#define LOG(XX) //Serial.println(XX) +#define LOGHEX(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) +{ + byte bret = 0x00; + + GPIOA->regs->CRL = 0x88888888; // Configure GPIOA[7:0] + uint32 ret = GPIOA->regs->IDR; + 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) +{ +// 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(); +// afio_cfg_debug_ports(AFIO_DEBUG_NONE); + + //シリアル初期化 + 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_READ | O_WRITE); + 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))) { + LOG("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) +{ + LOG("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) +{ + LOG("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) +{ + LOG("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) +{ + LOG("-R"); + LOGHEX(adds); + LOGHEX(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) +{ + LOG("-W"); + LOGHEX(adds); + LOGHEX(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) +{ + LOG("MsgIn2"); + gpio_write(MSG, high); + gpio_write(CD, high); + gpio_write(IO, high); + writeHandshake(msg); +} + +/* + * MsgOut2. + */ +void MsgOut2() +{ + LOG("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; + } + + LOG("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; + } + } + } + + LOG("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]); + } + + switch(cmd[0]) { + case 0x00: + LOG("[Test Unit]"); + break; + case 0x01: + LOG("[Rezero Unit]"); + break; + case 0x03: + LOG("[RequestSense]"); + onRequestSenseCommand(cmd[4]); + break; + case 0x04: + LOG("[FormatUnit]"); + break; + case 0x06: + LOG("[FormatUnit]"); + break; + case 0x07: + LOG("[ReassignBlocks]"); + break; + case 0x08: + LOG("[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: + LOG("[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: + LOG("[Seek6]"); + break; + case 0x12: + LOG("[Inquiry]"); + onInquiryCommand(cmd[4]); + break; + case 0x1A: + LOG("[ModeSense6]"); + onModeSenseCommand(cmd[1]&0x80, cmd[2] & 0x3F, cmd[4]); + break; + case 0x1B: + LOG("[StartStopUnit]"); + break; + case 0x1E: + LOG("[PreAllowMed.Removal]"); + break; + case 0x25: + LOG("[ReadCapacity]"); + onReadCapacityCommand(cmd[8]); + break; + case 0x28: + LOG("[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: + LOG("[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: + LOG("[Seek10]"); + break; + case 0x5A: + LOG("[ModeSense10]"); + onModeSenseCommand(cmd[1] & 0x80, cmd[2] & 0x3F, ((uint32_t)cmd[7] << 8) | cmd[8]); + break; + default: + LOG("[*Unknown]"); + sts = 2; + m_senseKey = 5; + break; + } + if(m_isBusReset) { + goto BusFree; + } + + LOG("Sts"); + gpio_write(MSG, low); + gpio_write(CD, high); + gpio_write(IO, high); + writeHandshake(sts); + if(m_isBusReset) { + goto BusFree; + } + + LOG("MsgIn"); + gpio_write(MSG, high); + gpio_write(CD, high); + gpio_write(IO, high); + writeHandshake(msg); + +BusFree: + LOG("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); +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..72f75ab --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# ArdSCSino-stm32 diff --git a/pin.png b/pin.png new file mode 100644 index 0000000..727d99e Binary files /dev/null and b/pin.png differ