/*--------------------------------------------------------------------
Sega CD Transfer Compatible Dumper for Arduino
Arduino用 Sega CD Transfer対応 ダンプ処理(XMODEM送信)
About this:
You can dump via USB serial communication using Sega CD Transfer.
Use the terminal software to receive the dump file via XMODEM communication.
(COM Setting: 115200BPS,8bit,none,0,1)
Sega CD Transferを使ってメガCDのダンプデータをUSB経由で読み出します。
Teraterm等のターミナルソフトを使い、XMODEM通信でダンプファイルを受信します。
(COM通信設定: 115200BPS,8bit,none,0,1)
NOTE:
This sketch file is for use with Arduino Leonardo/Arduino Pro Micro only.
Please refer to the official page of sparkfun for how to use Pro Micro.
https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/
注意:
このスケッチは、Arduino Leonardo/Pro Micro(ATmega 32U4搭載)専用です。
メガドライブ側の読み出しを最速で行うためポート(PinD)アクセスを利用しており、
他マイコンと互換性がありません。
[Reference]
Sega CD Transfer Suite
https://www.retrodev.com/transfer.html
UCON64
https://ucon64.sourceforge.io/
I/O ポートの制御
http://rio.la.coocan.jp/lab/driver24/004hardware.html
XMODEM通信
https://s-suzuki.hatenadiary.org/entry/20150104/1420390724
2020.8.2 BLUE( qze12045@nifty.com )
--------------------------------------------------------------------*/
//For Debug
#define __XmodemTest 0 // 1:XMODEM test(send dummy data)
// Sega CD Transfer Control
#define ERRWAIT 9000 // ERR WAIT
#define ERR 0x10 // Error bit mask
#define WAIT_ERRLo(x) for(int i=0 ; i<x ; i++ ){if(!(PIND & ERR)) break;}
#define WAIT_ERRHi(x) for(int i=0 ; i<x ; i++ ){if(PIND & ERR) break;}
// Sega CD Transfer Ports
#define MD_D0 3 // PD0/Pin3
#define MD_D1 2 // PD1/Pin2
#define MD_D2 1 // PD2/Pin1(RX1)
#define MD_D3 0 // PD3/Pin0(TX1)
#define MD_ERR 4 // PD4/Pin4 Error信号
#define MD_SEL 6 // PD7/Pin6 Select信号
#define MD_ALF 5 // PC6/Pin5 Auto Line Feed信号ポート番号
// XMODEM Parameter
#define _BAUDRATE 115200 // 通信速度 115200Bps
#define _TIMEOUT 1000 // 通信タイムアウト(1秒)
#define _RETRY 60 // ACK/NAK受信リトライ回数
#define _BLKSIZE 128 // 通信パケットサイズ(128Byte)
#define _BLKRETRY 100 // ブロック送信リトライ回数
// XMODEM code
#define SOH 0x01 // ブロック先頭
#define EOT 0x04 // 通信終了
#define ACK 0x06 // 通信正常
#define NAK 0x15 // 通信開始/パケット再送付要求
#define CAN 0x18 // 通信中断
// XMODEM packet
typedef struct xmodem {
char soh; // SOH
char blk; // Block Number
char _blk; // ~Block Number(BLock番号補数)
char data[_BLKSIZE]; // data / 通信データ
char checksum; // Checksum
} XMODEM;
XMODEM buf; // send pachet / 送信バッファ
int xmodem_phese = 0; // 通信フェーズ(0:初回NAK待ち、1:データ送信中、2:EOT送信前、3:完了)
char block; // ブロック番号
unsigned short check_sum; // チェックサム
// dump data buffer
char data_buf[_BLKSIZE]; // ダンプデータ用バッファ
//////////////////////////////////////
// serial read (1byte)
// Received code (Timeout:-1)
//////////////////////////////////////
char recv_c(void)
{
char code;
for (int i=0 ; i<_RETRY ;i++)
{
Serial.readBytes(&code, 1);
if ( (code != ACK) && (code!=NAK) )
{
// retry
continue;
}
else
{
if((!xmodem_phese)&&(code!=NAK))
{
// retry
continue;
}
return code;
}
}
return -1;
}
//////////////////////////////////////
// initialize XMODEM
//////////////////////////////////////
void xmodem_init(void)
{
// initial phase
xmodem_phese = 0;
// set first
block = 1;
// set timeout
Serial.setTimeout(_TIMEOUT);
}
//////////////////////////////////////
// send XMODEM
//////////////////////////////////////
int xmodem_send(char *data)
{
char status;
switch(xmodem_phese)
{
// first NAK
case 0:
if (recv_c()==NAK)
{
xmodem_phese = 1;
}
else
{
xmodem_phese = 2;
return 1;
}
// XMODEM:Send Packet
case 1:
buf.soh = SOH;
buf.blk = block;
buf._blk = ~block;
check_sum = 0;
for( int i ; i < _BLKSIZE ; i++ )
{
buf.data[i] = data[i];
check_sum += data[i];
}
buf.checksum = (char)(check_sum&0xff);
for( int i ; i < _BLKRETRY ; i++ )
{
Serial.write((const char*)&buf, sizeof(buf));
status = recv_c();
// ACK(normal?)
if (status == ACK)
{
block++;
return 0;
}
// NAK(retry?)
if (status == NAK)
{
continue;
}
// other(CAN?)
else
break;
}
// retry over(XMODEM:error)
xmodem_phese = 2;
return 1;
default:
break;
}
return 0;
}
//////////////////////////////////////
// XMODEM:finish
//////////////////////////////////////
void xmodem_end(void)
{
if ( xmodem_phese == 3 )
{
return;
}
for( int i ; i < 5 ; i++ )
{
// send EOT
Serial.write(EOT);
char status = recv_c();
// ACK(normal?)
if( status == ACK )
{
break;
}
// NAK(retry?)
if( status == NAK )
{
continue;
}
}
xmodem_phese = 3;
return;
}
//////////////////////////////////////
// Sega CD transfer:dump 1byte
//////////////////////////////////////
unsigned char byte_dump(void)
{
#if __XmodemTest
static unsigned char d = 0;
return d++;
#endif //__XmodemTest
unsigned char data=0;
noInterrupts(); //Disable Interrupts
// Auto Line Feed = Hi
digitalWrite(MD_ALF, HIGH );
// Wait ERR=Hi
WAIT_ERRHi(ERRWAIT);
data = PIND & 0xf;
// Auto Line Feed = Lo
digitalWrite(MD_ALF, LOW );
// Wait ERR=Lo
WAIT_ERRLo(ERRWAIT);
// Auto Line Feed = Hi
digitalWrite(MD_ALF, HIGH );
// Wait ERR=Hi
WAIT_ERRHi(ERRWAIT);
data |= (PIND & 0xf)<<4;
// Auto Line Feed = Lo
digitalWrite(MD_ALF, LOW );
// Wait ERR=Lo
WAIT_ERRLo(ERRWAIT);
interrupts(); //Enable Interrupts
return data;
}
//////////////////////////////////////
// read dump size
//////////////////////////////////////
unsigned char read_dump_size(void)
{
#if __XmodemTest
static unsigned char d = 0;
return d++;
#endif //__XmodemTest
// Sega CD Transfer connect check
while (PIND & ERR){}
return byte_dump();
}
//////////////////////////////////////
// initialize
//////////////////////////////////////
void setup() {
// put your setup code here, to run once:
// initialize port(INPUT_PULLUP)
pinMode(MD_D0, INPUT_PULLUP);
pinMode(MD_D1, INPUT_PULLUP);
pinMode(MD_D2, INPUT_PULLUP);
pinMode(MD_D3, INPUT_PULLUP);
pinMode(MD_ERR, INPUT_PULLUP);
pinMode(MD_SEL, INPUT_PULLUP);
// Auto Line Feed(OUTPUT)
pinMode(MD_ALF, OUTPUT);
// Auto Line Feed = Lo
digitalWrite(MD_ALF, LOW );
Serial.begin(_BAUDRATE);
// Arduino Leonardo/Arduino Pro Micro only.
while (!Serial){}
}
//////////////////////////////////////
// main loop
//////////////////////////////////////
void loop() {
// put your main code here, to run repeatedly:
long dump = 0; // count of dump data
long size = 0; // size of dump data
int index = 0; // index of data_buf[]
Serial.print("Sega CD Transfer Compatible Dumper for Arduino.\r\n");
Serial.print("Waiting for Respons from Genesis/MegaDrive...\r\n");
size = (long)read_dump_size();
Serial.print("Link Archved. Dumping ");
Serial.print(size,DEC);
Serial.print(" Megabits...\r\n");
size = (size+1)<<16;
Serial.print("\r\nPlease start receiving XMODEM protocol.\r\n");
xmodem_init();
while (dump < size)
{
data_buf[index++] = byte_dump();
if( index >= _BLKSIZE )
{
if( xmodem_send(data_buf)!=0 )
{
xmodem_end();
}
index = 0;
}
dump++;
}
xmodem_end();
Serial.print("Dump data send finished.\r\n\r\n");
}