greenscsi/ArdSCSinoV2.ino

735 lines
16 KiB
Arduino
Raw Normal View History

2019-06-12 13:11:36 +00:00
/*
* SCSI-HDデバイスエミュレータ
*/
#include <SPI.h>
#include "SdFat.h"
//ENABLE_EXTENDED_TRANSFER_CLASSを1に設定する
//libraries/SdFat/SdFatConfig.h
SPIClass SPI_1(1);
SdFatEX SD(&SPI_1);
#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 PB8 // SCSI:DB0
//#define DB1 PB9 // SCSI:DB1
//#define DB2 PB10 // SCSI:DB2
//#define DB3 PB11 // SCSI:DB3
//#define DB4 PB12 // SCSI:DB4
//#define DB5 PB13 // SCSI:DB5
//#define DB6 PB14 // SCSI:DB6
//#define DB7 PB15 // SCSI:DB7
//#define DBP PB0 // SCSI:DBP
#define ATN PA8 // SCSI:ATN
#define BSY PA9 // SCSI:BSY
#define ACK PA10 // SCSI:ACK
#define RST PA15 // SCSI:RST
#define MSG PB3 // SCSI:MSG
#define SEL PB4 // SCSI:SEL
#define CD PB5 // SCSI:C/D
#define REQ PB6 // SCSI:REQ
#define IO PB7 // SCSI:I/O
#define SD_CS PA4 // 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)初期化
//ポート設定レジスタ(下位)
// GPIOB->regs->CRL |= 0x000000008; // SET INPUT W/ PUPD on PAB-PB0
//ポート設定レジスタ(上位)
GPIOB->regs->CRH = 0x88888888; // SET INPUT W/ PUPD on PB15-PB8
// GPIOB->regs->ODR = 0x0000FF00; // SET PULL-UPs on PB15-PB8
//ポート入力データレジスタ
uint32 ret = GPIOB->regs->IDR;
byte bret = 0x00;
bret |= (!(ret & (1<<15))) << 7;
bret |= (!(ret & (1<<14))) << 6;
bret |= (!(ret & (1<<13))) << 5;
bret |= (!(ret & (1<<12))) << 4;
bret |= (!(ret & (1<<11))) << 3;
bret |= (!(ret & (1<<10))) << 2;
bret |= (!(ret & (1<<9))) << 1;
bret |= (!(ret & (1<<8))) << 0;
return bret;
}
/*
* IO書き込み.
*/
inline void writeIO(byte v)
{
//GPIO(SCSI BUS)初期化
//ポート設定レジスタ(下位)
GPIOB->regs->CRL |= 0x00000003; // SET OUTPUT W/ PUPD on PA7-PB0 50MHz
//ポート設定レジスタ(上位)
GPIOB->regs->CRH = 0x33333333; // SET OUTPUT W/ PUPD on PB15-PB8 50MHz
// GPIOB->regs->ODR != 0x0000FF00; // SET PULL-UPs on PB15-PB8
uint32 retL = 0x00;
uint32 retH = 0x00;
if(!parity(v)) {
retL |= (1<<0);
} else {
retH |= (1<<0);
}
if(v & ( 1 << 7 )) {
retL |= (1<<15);
} else {
retH |= (1<<15);
}
if(v & ( 1 << 6 )) {
retL |= (1<<14);
} else {
retH |= (1<<14);
}
if(v & ( 1 << 5 )) {
retL |= (1<<13);
} else {
retH |= (1<<13);
}
if(v & ( 1 << 4 )) {
retL |= (1<<12);
} else {
retH |= (1<<12);
}
if(v & ( 1 << 3 )) {
retL |= (1<<11);
} else {
retH |= (1<<11);
}
if(v & ( 1 << 2 )) {
retL |= (1<<10);
} else {
retH |= (1<<10);
}
if(v & ( 1 << 1 )) {
retL |= (1<<9);
} else {
retH |= (1<<9);
}
if(v & ( 1 << 0 )) {
retL |= (1<<8);
} else {
retH |= (1<<8);
}
//ビットがLOWに設定される
GPIOB->regs->BRR = retL ;
// ビットがHIGHに設定される
GPIOB->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)初期化
//ポート設定レジスタ(下位)
// GPIOB->regs->CRL |= 0x000000008; // SET INPUT W/ PUPD on PAB-PB0
//ポート設定レジスタ(上位)
GPIOB->regs->CRH = 0x88888888; // SET INPUT W/ PUPD on PB15-PB8
// GPIOB->regs->ODR = 0x0000FF00; // SET PULL-UPs on PB15-PB8
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);
}