/*--------------------------------------------------------------------
Sega CD Transfer Compatible Dumper / Backup RAM Cartridge Writer
for Arduino
Arduino用 Sega CD Transfer対応 ダンプ処理+バックアップRAMカートリッジ
書き込み処理 (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)
You can also use RAM_DUMP.BIN and RAM_WRTE.BIN to read and write to the
backup RAM cartridge.
Sega CD Transferを使ってメガCDのダンプデータをUSB経由で読み出します。
Teraterm等のターミナルソフトを使い、XMODEM通信でダンプファイルを受信します。
(COM通信設定: 115200BPS,8bit,none,0,1)
また、RAM_DUMP.BINとRAM_WRTE.BINを利用してバックアップRAMカートリッジの
読書きが可能です。
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)アクセスを利用しており、
他マイコンと互換性がありません。
[Cable Connection]
+---------+------------+
|Genesis |Pro Micro |
|MD(DSUB9)|(Atmega32U4)|
+---------+------------+
| 1(D0) |Pin3 (PD0) |
+---------+------------+
| 2(D1) |Pin2 (PD1) |
+---------+------------+
| 3(D2) |Pin1 (PD2) |
+---------+------------+
| 4(D3) |Pin0 (PD3) |
+---------+------------+
| 5(+5v) | NC |
+---------+------------+
| 6(ERR) |Pin4 (PD4) |
+---------+------------+
| 7(ALF) |Pin5 (PC6) |
+---------+------------+
| 8(GND) | GND |
+---------+------------+
| 9(SEL) |Pin6 (PD7) |
+---------+------------+
[Rsest button (Pro Micro)]
RST -- GND
[Dumper/Writer Select S/W (Pro Micro)]
Pin10 -- GND
OFF: Dumper
ON : Writer
note:
After selecting Dumper or Writer with this switch,
press the reset button to enable the setting.
このスイッチでDumperかWriterを選択後、リセットボタンを押す事で設定が
有効になります。
[Reference]
Sega CD Transfer Suite
https://www.retrodev.com/transfer.html
Extract the Mega CD BIOS using a backup RAM cartridge
http://blog.livedoor.jp/scrap_a/archives/24228164.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.11.14 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;}
// Dump/Write select Port
#define MD_SW 10 // DUMP/WRITE select swicth
// 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 // 通信タイムアウト(2秒)
#define _RETRY 60 // ACK/NAK受信リトライ回数
#define _BLKSIZE 128 // 通信パケットサイズ(128Byte)
#define _BLKRETRY 200 // ブロック送信リトライ回数
// 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]; // ダンプデータ用バッファ
bool dump_mode = true; // Dump/Write status
//////////////////////////////////////
// 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=0 ; i < _BLKSIZE ; i++ )
{
buf.data[i] = data[i];
check_sum += data[i];
}
buf.checksum = (char)(check_sum&0xff);
for( int i=0 ; 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;
}
//////////////////////////////////////
// Recive XMODEM packet
//////////////////////////////////////
int xmodem_recv(char *data)
{
if ( xmodem_phese >= 2 )
{
// finish
return EOT;
}
char code = ACK;
// first send NAK?
if ( !xmodem_phese )
{
code= NAK;
xmodem_phese = 1;
}
for ( int i = 0 ; i < _RETRY ; i++ )
{
// send packet request
Serial.write(code);
// wait SOH
Serial.readBytes(&code, 1);
if ( code == EOT )
{
// xmodem finish
xmodem_phese = 2;
// send ACK
Serial.write(ACK);
// finish
return EOT;
}
if ( code == CAN )
{
// send ACK
Serial.write(ACK);
// finish
return EOT;
}
if ( code == SOH )
{
code = 0;
// recive block number
Serial.readBytes(&buf.blk, 1);
Serial.readBytes(&buf._blk, 1);
// recive packet data
for ( int j = 0 ; j < _BLKSIZE ; j++ )
{
Serial.readBytes(&buf.data[j], 1);
// calc checksum
code += buf.data[j];
}
// recive checksum
Serial.readBytes(&buf.checksum, 1);
// block number OK?
if (( buf.blk == block )&&( buf._blk == ~block ))
{
// cheksum OK?
if (buf.checksum == code)
{
memcpy( data ,&buf.data[0] ,_BLKSIZE);
// renewal block number
block++;
return 0;
}
}
}
// send NAK
code= NAK;
}
// retry error
return EOT;
}
//////////////////////////////////////
// XMODEM:finish
//////////////////////////////////////
void xmodem_end(void)
{
if ( xmodem_phese == 3 )
{
return;
}
Serial.write(EOT); // Send EOT --bug fix--
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 1バイト書込み
//////////////////////////////////////
void byte_write(unsigned char data )
{
noInterrupts(); //Disable Interrupts
// Wait ERR=Lo
WAIT_ERRLo(ERRWAIT);
// write data(high)
PORTD = (data>>4);
// Auto Line Feed = Hi
digitalWrite(MD_ALF, HIGH );
// Wait ERR=Hi
WAIT_ERRHi(ERRWAIT);
// Auto Line Feed = Lo
digitalWrite(MD_ALF, LOW );
// Wait ERR=Lo
WAIT_ERRLo(ERRWAIT);
// write data(low)
PORTD = data & 0xf;
// Auto Line Feed = Hi
digitalWrite(MD_ALF, HIGH );
// Wait ERR=Hi
WAIT_ERRHi(ERRWAIT);
// Auto Line Feed = Lo
digitalWrite(MD_ALF, LOW );
interrupts(); //Enable Interrupts
}
//////////////////////////////////////
// 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 Dump/Write selector port(INPUT_PULLUP)
pinMode(MD_SW, INPUT_PULLUP);
delayMicroseconds(10);
if (digitalRead(MD_SW) == HIGH)
{
// initialize port for DUMP(INPUT_PULLUP)
pinMode(MD_D0, INPUT_PULLUP);
pinMode(MD_D1, INPUT_PULLUP);
pinMode(MD_D2, INPUT_PULLUP);
pinMode(MD_D3, INPUT_PULLUP);
dump_mode = true;
}
else
{
// initialize port for WRITE(OUTPUT)
pinMode(MD_D0, OUTPUT);
pinMode(MD_D1, OUTPUT);
pinMode(MD_D2, OUTPUT);
pinMode(MD_D3, OUTPUT);
dump_mode = false;
}
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:
if( dump_mode == true )
{
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+1)/2,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");
}
if( dump_mode == false )
{
Serial.print("Sega CD Transfer Backup RAM Writer for Arduino.\r\n");
Serial.print("\r\nPlease start to send file with XMODEM protocol.\r\n");
// Sega CD Transfer connect check
while (PIND & ERR){}
xmodem_init();
while(1)
{
if ( xmodem_recv(data_buf) != 0 )
{
break;
}
for (int i = 0 ; i < _BLKSIZE ; i++ )
{
byte_write(data_buf[i]);
}
}
Serial.print("Recive data write finished.\r\n\r\n");
}
}