mirror of
https://github.com/sanni/cartreader.git
synced 2024-12-25 12:31:54 +01:00
V5.0: Add suport for NeoGeo Pocket
Many thanks to splash5
This commit is contained in:
parent
61ff6cff34
commit
615ef8d68c
@ -2,8 +2,8 @@
|
||||
Cartridge Reader for Arduino Mega2560
|
||||
|
||||
Author: sanni
|
||||
Date: 20.04.2020
|
||||
Version: 4.9
|
||||
Date: 12.05.2020
|
||||
Version: 5.0
|
||||
|
||||
SD lib: https://github.com/greiman/SdFat
|
||||
LCD lib: https://github.com/adafruit/Adafruit_SSD1306
|
||||
@ -38,12 +38,12 @@
|
||||
rama - code speedup & improvements
|
||||
Gens-gs - Megadrive checksum
|
||||
Modman - N64 checksum comparison fix
|
||||
splash5 - EMS GB Smart cart support, Wonderswan module
|
||||
splash5 - EMS GB Smart cart support, Wonderswan module, NGP module
|
||||
|
||||
**********************************************************************************/
|
||||
#include <SdFat.h>
|
||||
|
||||
char ver[5] = "4.9";
|
||||
char ver[5] = "5.0";
|
||||
|
||||
/******************************************
|
||||
Options
|
||||
@ -155,6 +155,7 @@ typedef enum COLOR_T {
|
||||
#define mode_GB_GBSmart_Flash 19
|
||||
#define mode_GB_GBSmart_Game 20
|
||||
#define mode_WS 21
|
||||
#define mode_NGP 22
|
||||
|
||||
// optimization-safe nop delay
|
||||
#define NOP __asm__ __volatile__ ("nop\n\t")
|
||||
@ -382,9 +383,10 @@ static const char addonsItem1[] PROGMEM = "NES/Famicom";
|
||||
static const char addonsItem2[] PROGMEM = "Flashrom Programmer";
|
||||
static const char addonsItem3[] PROGMEM = "PC Engine/TG16";
|
||||
static const char addonsItem4[] PROGMEM = "Sega Master System";
|
||||
static const char addonsItem6[] PROGMEM = "WonderSwan";
|
||||
static const char addonsItem5[] PROGMEM = "Reset";
|
||||
static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem6, addonsItem5};
|
||||
static const char addonsItem5[] PROGMEM = "WonderSwan";
|
||||
static const char addonsItem6[] PROGMEM = "NeoGeo Pocket";
|
||||
static const char addonsItem7[] PROGMEM = "Reset";
|
||||
static const char* const addonsOptions[] PROGMEM = {addonsItem1, addonsItem2, addonsItem3, addonsItem4, addonsItem5, addonsItem6, addonsItem7};
|
||||
|
||||
// Info Screen
|
||||
void aboutScreen() {
|
||||
@ -479,11 +481,11 @@ void mainMenu() {
|
||||
|
||||
// Everything that needs an adapter
|
||||
void addonsMenu() {
|
||||
// create menu with title and 5 options to choose from
|
||||
// create menu with title and 7 options to choose from
|
||||
unsigned char addonsMenu;
|
||||
// Copy menuOptions out of progmem
|
||||
convertPgm(addonsOptions, 6);
|
||||
addonsMenu = question_box(F("Choose Adapter"), menuOptions, 6, 0);
|
||||
convertPgm(addonsOptions, 7);
|
||||
addonsMenu = question_box(F("Choose Adapter"), menuOptions, 7, 0);
|
||||
|
||||
// wait for user choice to come back from the question box menu
|
||||
switch (addonsMenu)
|
||||
@ -505,10 +507,20 @@ void addonsMenu() {
|
||||
break;
|
||||
|
||||
case 4:
|
||||
display_Clear();
|
||||
display_Update();
|
||||
setup_WS();
|
||||
mode = mode_WS;
|
||||
break;
|
||||
|
||||
default:
|
||||
case 5:
|
||||
display_Clear();
|
||||
display_Update();
|
||||
setup_NGP();
|
||||
mode = mode_NGP;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
resetArduino();
|
||||
break;
|
||||
}
|
||||
@ -1509,6 +1521,9 @@ void loop() {
|
||||
else if (mode == mode_WS) {
|
||||
wsMenu();
|
||||
}
|
||||
else if (mode == mode_NGP) {
|
||||
ngpMenu();
|
||||
}
|
||||
else {
|
||||
display_Clear();
|
||||
println_Msg(F("Menu Error"));
|
||||
|
238
Cart_Reader/NGP.ino
Normal file
238
Cart_Reader/NGP.ino
Normal file
@ -0,0 +1,238 @@
|
||||
//******************************************
|
||||
// NGP MODULE
|
||||
//******************************************
|
||||
|
||||
static const char ngpMenuItem1[] PROGMEM = "Read Rom";
|
||||
static const char ngpMenuItemReset[] PROGMEM = "Reset";
|
||||
static const char* const menuOptionsNGP[] PROGMEM = {ngpMenuItem1, ngpMenuItemReset};
|
||||
|
||||
char ngpRomVersion[5];
|
||||
|
||||
void setup_NGP() {
|
||||
// A0 - A7
|
||||
DDRF = 0xff;
|
||||
// A8 - A15
|
||||
DDRK = 0xff;
|
||||
// A16 - A20
|
||||
DDRL = 0xff;
|
||||
|
||||
// D0 - D7
|
||||
DDRC = 0x00;
|
||||
|
||||
// controls
|
||||
// /CE0: PH3
|
||||
// /CE1: PH0
|
||||
// /OE: PH6
|
||||
// /WE: PH5
|
||||
// PWR: PH4
|
||||
DDRH |= ((1 << 0) | (1 << 3) | (1 << 5) | (1 << 6));
|
||||
DDRH &= ~(1 << 4);
|
||||
|
||||
PORTH |= ((1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6));
|
||||
|
||||
if (getCartInfo_NGP())
|
||||
{
|
||||
showCartInfo_NGP();
|
||||
}
|
||||
else
|
||||
{
|
||||
println_Msg(F("NeoGeo Pocket"));
|
||||
println_Msg(F(""));
|
||||
println_Msg(F(""));
|
||||
println_Msg(F("Rom Size Error"));
|
||||
println_Msg(F(""));
|
||||
println_Msg(F("Press Button..."));
|
||||
display_Update();
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
void ngpMenu() {
|
||||
|
||||
uint8_t mainMenu;
|
||||
|
||||
convertPgm(menuOptionsNGP, 2);
|
||||
mainMenu = question_box(F("NGP Menu"), menuOptions, 2, 0);
|
||||
|
||||
switch (mainMenu)
|
||||
{
|
||||
case 0:
|
||||
sd.chdir("/");
|
||||
readROM_NGP(filePath, FILEPATH_LENGTH);
|
||||
sd.chdir("/");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
resetArduino();
|
||||
break;
|
||||
}
|
||||
|
||||
println_Msg(F(""));
|
||||
println_Msg(F("Press Button..."));
|
||||
display_Update();
|
||||
wait();
|
||||
}
|
||||
|
||||
bool getCartInfo_NGP() {
|
||||
uint8_t *tmp;
|
||||
|
||||
// enter autoselect mode
|
||||
dataOut();
|
||||
writeByte_NGP(0x555, 0xaa);
|
||||
writeByte_NGP(0x2aa, 0x55);
|
||||
writeByte_NGP(0x555, 0x90);
|
||||
|
||||
dataIn();
|
||||
|
||||
cartSize = 0;
|
||||
tmp = (uint8_t*)&romSize;
|
||||
*(tmp + 0) = readByte_NGP(0);
|
||||
*(tmp + 1) = readByte_NGP(1);
|
||||
|
||||
switch (romSize)
|
||||
{
|
||||
case 0x2c98:
|
||||
cartSize = 1048576;
|
||||
break;
|
||||
}
|
||||
|
||||
// reset to read mode
|
||||
dataOut();
|
||||
writeByte_NGP(0x000000, 0xf0);
|
||||
|
||||
if (cartSize == 0)
|
||||
return false;
|
||||
|
||||
dataIn();
|
||||
|
||||
for (uint32_t addr = 0; addr < 28; addr++)
|
||||
sdBuffer[addr] = readByte_NGP(addr);
|
||||
|
||||
if (memcmp_P(sdBuffer, PSTR("COPYRIGHT BY SNK CORPORATION"), 28) != 0 && memcmp_P(sdBuffer, PSTR(" LICENSED BY SNK CORPORATION"), 28) != 0)
|
||||
return false;
|
||||
|
||||
snprintf(cartID, 5, "%02X%02X", readByte_NGP(0x000021), readByte_NGP(0x000020));
|
||||
snprintf(ngpRomVersion, 5, "%02X%02X", readByte_NGP(0x000023), readByte_NGP(0x000022));
|
||||
|
||||
for (uint32_t i = 0; i < 17; i++)
|
||||
romName[i] = readByte_NGP(0x24 + i);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void showCartInfo_NGP() {
|
||||
display_Clear();
|
||||
|
||||
println_Msg(F("NGP Cart Info"));
|
||||
|
||||
print_Msg(F("Game: "));
|
||||
println_Msg(romName);
|
||||
|
||||
print_Msg(F("GameID: "));
|
||||
println_Msg(cartID);
|
||||
|
||||
print_Msg(F("Rom Size: "));
|
||||
print_Msg((cartSize >> 17));
|
||||
println_Msg(F(" Mb"));
|
||||
|
||||
print_Msg(F("Version: "));
|
||||
println_Msg(ngpRomVersion);
|
||||
|
||||
println_Msg(F(""));
|
||||
println_Msg(F("Press Button..."));
|
||||
display_Update();
|
||||
wait();
|
||||
}
|
||||
|
||||
void readROM_NGP(char *outPathBuf, size_t bufferSize) {
|
||||
// generate fullname of rom file
|
||||
snprintf(fileName, FILENAME_LENGTH, "%s.ngp", romName);
|
||||
|
||||
// create a new folder for storing rom file
|
||||
EEPROM_readAnything(0, foldern);
|
||||
snprintf(folder, sizeof(folder), "NGP/ROM/%s/%d", romName, foldern);
|
||||
sd.mkdir(folder, true);
|
||||
sd.chdir(folder);
|
||||
|
||||
// filling output file path to buffer
|
||||
if (outPathBuf != NULL && bufferSize > 0)
|
||||
snprintf(outPathBuf, bufferSize, "%s/%s", folder, fileName);
|
||||
|
||||
display_Clear();
|
||||
print_Msg(F("Saving to "));
|
||||
print_Msg(folder);
|
||||
println_Msg(F("/..."));
|
||||
display_Update();
|
||||
|
||||
// open file on sdcard
|
||||
if (!myFile.open(fileName, O_RDWR | O_CREAT))
|
||||
print_Error(F("Can't create file on SD"), true);
|
||||
|
||||
// write new folder number back to EEPROM
|
||||
foldern++;
|
||||
EEPROM_writeAnything(0, foldern);
|
||||
|
||||
// back to read mode
|
||||
dataOut();
|
||||
writeByte_NGP(0x0, 0xf0);
|
||||
|
||||
dataIn();
|
||||
for (uint32_t addr = 0; addr < cartSize; addr += 512) {
|
||||
|
||||
// blink LED
|
||||
if ((addr & ((1 << 14) - 1)) == 0)
|
||||
PORTB ^= (1 << 4);
|
||||
|
||||
for (uint32_t i = 0; i < 512; i++)
|
||||
sdBuffer[i] = readByte_NGP(addr + i);
|
||||
|
||||
myFile.write(sdBuffer, 512);
|
||||
}
|
||||
|
||||
myFile.close();
|
||||
}
|
||||
|
||||
|
||||
void writeByte_NGP(uint32_t addr, uint8_t data) {
|
||||
PORTF = addr & 0xff;
|
||||
PORTK = (addr >> 8) & 0xff;
|
||||
PORTL = (addr >> 16) & 0x1f;
|
||||
PORTC = data;
|
||||
|
||||
// which chip to select
|
||||
// 0x000000 - 0x1fffff -> /CE0
|
||||
// 0x200000 - 0x3fffff -> /CE1
|
||||
data = (addr & 0x00200000 ? (1 << 0) : (1 << 3));
|
||||
|
||||
PORTH &= ~data;
|
||||
PORTH &= ~(1 << 5);
|
||||
NOP;
|
||||
|
||||
PORTH |= data;
|
||||
PORTH |= (1 << 5);
|
||||
NOP; NOP;
|
||||
}
|
||||
|
||||
uint8_t readByte_NGP(uint32_t addr) {
|
||||
uint8_t data;
|
||||
|
||||
PORTF = addr & 0xff;
|
||||
PORTK = (addr >> 8) & 0xff;
|
||||
PORTL = (addr >> 16) & 0x1f;
|
||||
|
||||
// which chip to select
|
||||
// 0x000000 - 0x1fffff -> /CE0
|
||||
// 0x200000 - 0x3fffff -> /CE1
|
||||
data = (addr & 0x00200000 ? (1 << 0) : (1 << 3));
|
||||
|
||||
PORTH &= ~data;
|
||||
PORTH &= ~(1 << 6);
|
||||
NOP; NOP; NOP;
|
||||
|
||||
data = PINC;
|
||||
|
||||
PORTH |= data;
|
||||
PORTH |= (1 << 6);
|
||||
|
||||
return data;
|
||||
}
|
@ -69,8 +69,6 @@ void setup_WS()
|
||||
DDRG &= ~(1 << 5);
|
||||
PORTG |= (1 << 5);
|
||||
|
||||
display_Clear();
|
||||
|
||||
// unlock MMC
|
||||
// if (!unlockMMC2003_WS())
|
||||
// print_Error(F("Can't initial MMC"), true);
|
||||
@ -78,8 +76,8 @@ void setup_WS()
|
||||
// if (getCartInfo_WS() != 0xea)
|
||||
// print_Error(F("Rom header read error"), true);
|
||||
|
||||
display.println("Initializing...");
|
||||
display.display();
|
||||
println_Msg(F("Initializing..."));
|
||||
display_Update();
|
||||
|
||||
do {
|
||||
unlockMMC2003_WS();
|
||||
@ -89,7 +87,6 @@ void setup_WS()
|
||||
getCartInfo_WS();
|
||||
|
||||
showCartInfo_WS();
|
||||
mode = mode_WS;
|
||||
}
|
||||
|
||||
boolean headerCheck() {
|
||||
|
@ -14,6 +14,10 @@
|
||||
|
||||
![image](https://dl.dropboxusercontent.com/s/755249v8smcuoft/wonderswan_adapter.png?dl=1)
|
||||
|
||||
#### ngp_adapter.zip is an add-on for reading NeoGeo Pocket carts. [PCB thickness needs to be changed to 1.2mm](https://dl.dropboxusercontent.com/s/755249v8smcuoft/wonderswan_adapter.png?dl=1), this is very important or else it won't fit into the SNES slot.
|
||||
|
||||
![image](https://dl.dropboxusercontent.com/s/yvutwme8n7d4tiy/ngp_adapter.png?dl=1)
|
||||
|
||||
#### flash_adapter.zip is an add-on for writing flashroms like the 29F032, 29L3211, 29LV160, [PCB thickness needs to be changed to 1.2mm](https://dl.dropboxusercontent.com/s/va1c72073cqfy90/pcb12.jpg?dl=1), this is very important or else it won't fit into the SNES slot.
|
||||
|
||||
![image](https://dl.dropboxusercontent.com/s/afrfmiuwvmvg9px/flash_adapter.png?dl=1)
|
||||
|
BIN
pcb/adapters/ngp_adapter.zip
Normal file
BIN
pcb/adapters/ngp_adapter.zip
Normal file
Binary file not shown.
BIN
pcb/adapters/ngp_adapter_schematic.png
Normal file
BIN
pcb/adapters/ngp_adapter_schematic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Loading…
Reference in New Issue
Block a user