Merge branch 'master' into master

This commit is contained in:
sanni 2017-09-19 17:21:14 +02:00 committed by GitHub
commit f2e2196ab9
36 changed files with 51549 additions and 40682 deletions

View File

@ -2,15 +2,15 @@
Cartridge Reader for Arduino Mega2560
Author: sanni
Date: 2017-02-11
Version: V22
Date: 2017-09-18
Version: V28E
SD lib: https://github.com/greiman/SdFat
LCD lib: https://github.com/adafruit/Adafruit_SSD1306
Clockgen: https://github.com/etherkit/Si5351Arduino
RGB Tools lib: https://github.com/joushx/Arduino-RGB-Tools
Compiled with Arduino 1.8.0
Compiled with Arduino 1.8.3
Thanks to:
MichlK - ROM-Reader for Super Nintendo
@ -34,12 +34,11 @@
YamaArashi - GBA flashrom bank switch command
**********************************************************************************/
char ver[5] = "V22";
char ver[5] = "V28E";
/******************************************
Define Output
******************************************/
// If you don't have an OLED screen change
// enable_OLED to 0 and enable_Serial to 1
#define enable_OLED 1
#define enable_Serial 0
@ -106,7 +105,6 @@ typedef enum COLOR_T {
// SD Card (Pin 50 = MISO, Pin 51 = MOSI, Pin 52 = SCK, Pin 53 = SS)
#include <SdFat.h>
#include <SdFatUtil.h>
#define chipSelectPin 53
SdFat sd;
SdFile myFile;
@ -118,13 +116,15 @@ SdFile myFile;
#define mode_N64_Cart 0
#define mode_N64_Controller 1
#define mode_SNES 2
#define mode_NP 3
#define mode_NPFlash 4
#define mode_NPGame 5
#define mode_SFM 3
#define mode_SFM_Flash 4
#define mode_SFM_Game 5
#define mode_GB 6
#define mode_FLASH8 7
#define mode_FLASH16 8
#define mode_GBA 9
#define mode_GBM 10
#define mode_MD 11
/******************************************
Variables
@ -166,10 +166,11 @@ int incomingByte;
int choice = 0;
// Temporary array that holds the menu option read out of progmem
char menuOptions[7][20];
boolean ignoreError = 0;
// File browser
char fileName[26];
char filePath[36];
char filePath[50];
byte currPage;
byte lastPage;
byte numPages;
@ -178,11 +179,11 @@ boolean filebrowse = 0;
char fileOptions[30][20];
// Common
char romName[10];
int sramSize = 0;
char romName[17];
unsigned long sramSize = 0;
int romType = 0;
byte saveType;
int romSize = 0;
word romSize = 0;
byte numBanks = 128;
char checksumStr[5];
bool errorLvl = 0;
@ -190,6 +191,8 @@ byte romVersion = 0;
char cartID[5];
unsigned long cartSize;
char flashid[5];
unsigned long fileSize;
unsigned long sramBase;
// Variable to count errors
unsigned long writeErrors;
@ -199,7 +202,7 @@ byte mode;
//remember folder number to create a new folder for every save
int foldern;
char folder[24];
char folder[36];
// Array that holds the data
byte sdBuffer[512];
@ -319,45 +322,52 @@ static const unsigned char PROGMEM sig [] = {
*****************************************/
// All menus and menu entries are stored in progmem to save on sram
// Main menu
const char modeItem1[] PROGMEM = "Nintendo 64";
const char modeItem2[] PROGMEM = "Super Nintendo";
const char modeItem3[] PROGMEM = "Nintendo Power";
const char modeItem4[] PROGMEM = "Game Boy";
const char modeItem5[] PROGMEM = "Flashrom Programmer";
const char modeItem6[] PROGMEM = "About";
const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6};
static const char modeItem1[] PROGMEM = "Nintendo 64";
static const char modeItem2[] PROGMEM = "Super Nintendo";
static const char modeItem3[] PROGMEM = "Nintendo Power";
static const char modeItem4[] PROGMEM = "Game Boy";
static const char modeItem5[] PROGMEM = "Mega Drive";
static const char modeItem6[] PROGMEM = "Flashrom Programmer";
static const char modeItem7[] PROGMEM = "About";
static const char* const modeOptions[] PROGMEM = {modeItem1, modeItem2, modeItem3, modeItem4, modeItem5, modeItem6, modeItem7};
// N64 Submenu
const char n64MenuItem1[] PROGMEM = "Cart Slot";
const char n64MenuItem2[] PROGMEM = "Controller";
const char* const menuOptionsN64[] PROGMEM = {n64MenuItem1, n64MenuItem2};
static const char n64MenuItem1[] PROGMEM = "Cart Slot";
static const char n64MenuItem2[] PROGMEM = "Controller";
static const char n64MenuItem3[] PROGMEM = "Flash Repro";
static const char* const menuOptionsN64[] PROGMEM = {n64MenuItem1, n64MenuItem2, n64MenuItem3};
// Nintendo Power Submenu
static const char npMenuItem1[] PROGMEM = "SF Memory";
static const char npMenuItem2[] PROGMEM = "GB Memory";
static const char* const menuOptionsNP[] PROGMEM = {npMenuItem1, npMenuItem2};
// Flash Submenu
const char flashMenuItem1[] PROGMEM = "8bit slot";
const char flashMenuItem2[] PROGMEM = "16bit slot";
const char* const menuOptionsFlash[] PROGMEM = {flashMenuItem1, flashMenuItem2};
static const char flashMenuItem1[] PROGMEM = "8bit slot";
static const char flashMenuItem2[] PROGMEM = "16bit slot";
static const char* const menuOptionsFlash[] PROGMEM = {flashMenuItem1, flashMenuItem2};
// GBx Submenu
const char gbxMenuItem1[] PROGMEM = "Game Boy (Color)";
const char gbxMenuItem2[] PROGMEM = "Game Boy Advance";
const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2};
static const char gbxMenuItem1[] PROGMEM = "Game Boy (Color)";
static const char gbxMenuItem2[] PROGMEM = "Game Boy Advance";
static const char* const menuOptionsGBx[] PROGMEM = {gbxMenuItem1, gbxMenuItem2};
void mainMenu() {
// create menu with title and 6 options to choose from
unsigned char modeMenu;
// Copy menuOptions out of progmem
convertPgm(modeOptions, 6);
modeMenu = question_box("Cartridge Reader", menuOptions, 6, 0);
convertPgm(modeOptions, 7);
modeMenu = question_box("Cartridge Reader", menuOptions, 7, 0);
// wait for user choice to come back from the question box menu
switch (modeMenu)
{
case 0:
// create menu with title and 2 options to choose from
// create menu with title and 3 options to choose from
unsigned char n64Dev;
// Copy menuOptions out of progmem
convertPgm(menuOptionsN64, 2);
n64Dev = question_box("Select N64 device", menuOptions, 2, 0);
convertPgm(menuOptionsN64, 3);
n64Dev = question_box("Select N64 device", menuOptions, 3, 0);
// wait for user choice to come back from the question box menu
switch (n64Dev)
@ -366,6 +376,7 @@ void mainMenu() {
display_Clear();
display_Update();
setup_N64_Cart();
printCartInfo_N64();
mode = mode_N64_Cart;
break;
@ -375,6 +386,16 @@ void mainMenu() {
setup_N64_Controller();
mode = mode_N64_Controller;
break;
case 2:
display_Clear();
display_Update();
setup_N64_Cart();
flashRepro_N64();
printCartInfo_N64();
mode = mode_N64_Cart;
break;
}
break;
@ -386,10 +407,29 @@ void mainMenu() {
break;
case 2:
display_Clear();
display_Update();
setup_NP();
mode = mode_NP;
// create menu with title and 2 options to choose from
unsigned char npCart;
// Copy menuOptions out of progmem
convertPgm(menuOptionsNP, 2);
npCart = question_box("Select NP Cart", menuOptions, 2, 0);
// wait for user choice to come back from the question box menu
switch (npCart)
{
case 0:
display_Clear();
display_Update();
setup_SFM();
mode = mode_SFM;
break;
case 1:
display_Clear();
display_Update();
setup_GBM();
mode = mode_GBM;
break;
}
break;
case 3:
@ -419,6 +459,13 @@ void mainMenu() {
break;
case 4:
display_Clear();
display_Update();
setup_MD();
mode = mode_MD;
break;
case 5:
// create menu with title and 2 options to choose from
unsigned char flashSlot;
// Copy menuOptions out of progmem
@ -444,7 +491,7 @@ void mainMenu() {
}
break;
case 5:
case 6:
display_Clear();
// Draw the Logo
display.drawBitmap(0, 0, sig, 128, 64, 1);
@ -481,7 +528,7 @@ void mainMenu() {
display_Update();
delay(2000);
foldern = 0;
EEPROM_writeAnything(0, foldern);
EEPROM_writeAnything(10, foldern);
asm volatile (" jmp 0");
}
}
@ -506,8 +553,11 @@ void setup() {
//PORTD |= (1 << 7);
//PORTG |= (1 << 2);
// Initialize LED
rgb.setColor(0, 0, 0);
// Read current folder number out of eeprom
EEPROM_readAnything(0, foldern);
EEPROM_readAnything(10, foldern);
if (enable_OLED) {
// GLCD
@ -532,11 +582,6 @@ void setup() {
Serial.println(F("Cartridge Reader"));
Serial.println(F("2017 sanni"));
Serial.println("");
// Print available RAM
Serial.print(F("Free Ram: "));
Serial.print(FreeRam());
Serial.println(F("Bytes"));
}
// Init SD card
@ -592,7 +637,19 @@ void print_Error(const __FlashStringHelper *errorMessage, boolean forceReset) {
println_Msg(F("Press Button..."));
display_Update();
wait();
asm volatile (" jmp 0");
if (ignoreError == 0) {
asm volatile (" jmp 0");
}
else {
ignoreError = 0;
display_Clear();
println_Msg(F(""));
println_Msg(F(""));
println_Msg(F(""));
println_Msg(F(" Error Overwrite"));
display_Update();
delay(2000);
}
}
}
@ -894,12 +951,9 @@ int checkButton2() {
// Wait for user to push button
void wait_btn() {
// Change led to green
if (errorLvl == 0)
rgbLed(green_color);
else
errorLvl = 0;
while (1)
{
@ -907,16 +961,22 @@ void wait_btn() {
int b = checkButton();
// Send some clock pulses to the Eeprom in case it locked up
if (mode == mode_N64_Cart) {
if ((mode == mode_N64_Cart) && ((saveType == 5) || (saveType == 6))) {
pulseClock_N64(1);
}
// if the cart readers input button is pressed shortly
if (b == 1) {
errorLvl = 0;
break;
}
// if the cart readers input button is pressed long
if (b == 3) {
if (errorLvl) {
// Debug
//ignoreError = 1;
errorLvl = 0;
}
break;
}
}
@ -1240,8 +1300,8 @@ void loop() {
else if (mode == mode_FLASH16) {
flashromMenu16();
}
else if (mode == mode_NP) {
npMenu();
else if (mode == mode_SFM) {
sfmMenu();
}
else if (mode == mode_GB) {
gbMenu();
@ -1249,11 +1309,17 @@ void loop() {
else if (mode == mode_GBA) {
gbaMenu();
}
else if (mode == mode_NPFlash) {
NPFlashMenu();
else if (mode == mode_SFM_Flash) {
sfmFlashMenu();
}
else if (mode == mode_NPGame) {
NPGameOptions();
else if (mode == mode_SFM_Game) {
sfmGameOptions();
}
else if (mode == mode_GBM) {
gbmMenu();
}
else if (mode == mode_MD) {
mdMenu();
}
else {
display_Clear();

View File

@ -16,24 +16,24 @@ unsigned long blank;
Menu
*****************************************/
// 8bit Flash menu items
const char flash8MenuItem1[] PROGMEM = "Blankcheck";
const char flash8MenuItem2[] PROGMEM = "Erase";
const char flash8MenuItem3[] PROGMEM = "Read";
const char flash8MenuItem4[] PROGMEM = "Write";
const char flash8MenuItem5[] PROGMEM = "ID";
const char flash8MenuItem6[] PROGMEM = "Print";
const char flash8MenuItem7[] PROGMEM = "Reset";
const char* const menuOptionsFLASH8[] PROGMEM = {flash8MenuItem1, flash8MenuItem2, flash8MenuItem3, flash8MenuItem4, flash8MenuItem5, flash8MenuItem6, flash8MenuItem7};
static const char flash8MenuItem1[] PROGMEM = "Blankcheck";
static const char flash8MenuItem2[] PROGMEM = "Erase";
static const char flash8MenuItem3[] PROGMEM = "Read";
static const char flash8MenuItem4[] PROGMEM = "Write";
static const char flash8MenuItem5[] PROGMEM = "ID";
static const char flash8MenuItem6[] PROGMEM = "Print";
static const char flash8MenuItem7[] PROGMEM = "Reset";
static const char* const menuOptionsFLASH8[] PROGMEM = {flash8MenuItem1, flash8MenuItem2, flash8MenuItem3, flash8MenuItem4, flash8MenuItem5, flash8MenuItem6, flash8MenuItem7};
// 16bit Flash menu items
const char flash16MenuItem1[] PROGMEM = "Blankcheck";
const char flash16MenuItem2[] PROGMEM = "Erase";
const char flash16MenuItem3[] PROGMEM = "Read";
const char flash16MenuItem4[] PROGMEM = "Write";
const char flash16MenuItem5[] PROGMEM = "ID";
const char flash16MenuItem6[] PROGMEM = "Print";
const char flash16MenuItem7[] PROGMEM = "Reset";
const char* const menuOptionsFLASH16[] PROGMEM = {flash16MenuItem1, flash16MenuItem2, flash16MenuItem3, flash16MenuItem4, flash16MenuItem5, flash16MenuItem6, flash16MenuItem7};
static const char flash16MenuItem1[] PROGMEM = "Blankcheck";
static const char flash16MenuItem2[] PROGMEM = "Erase";
static const char flash16MenuItem3[] PROGMEM = "Read";
static const char flash16MenuItem4[] PROGMEM = "Write";
static const char flash16MenuItem5[] PROGMEM = "ID";
static const char flash16MenuItem6[] PROGMEM = "Print";
static const char flash16MenuItem7[] PROGMEM = "Reset";
static const char* const menuOptionsFLASH16[] PROGMEM = {flash16MenuItem1, flash16MenuItem2, flash16MenuItem3, flash16MenuItem4, flash16MenuItem5, flash16MenuItem6, flash16MenuItem7};
void flashromMenu8() {
// create menu with title and 7 options to choose from
@ -68,8 +68,6 @@ void flashromMenu8() {
}
else {
eraseFlash29F1610();
delay(1000);
busyCheck29F1610();
}
println_Msg(F("Flashrom erased"));
display_Update();
@ -96,21 +94,26 @@ void flashromMenu8() {
time = millis();
if (flashromType == 1)
writeFlash29F032();
else if (flashromType == 2)
writeFlash29F1610();
else if (flashromType == 2) {
if (strcmp(flashid, "C2F3") == 0)
writeFlash29F1601();
else if (strcmp(flashid, "C2A8") == 0)
writeFlash29LV320();
else if (strcmp(flashid, "C2C9") == 0)
writeFlash29LV640();
else
writeFlash29F1610();
}
delay(100);
// Reset twice just to be sure
delay(1000);
if (flashromType == 1)
resetFlash29F032();
else
resetFlash29F1610();
delay(1000);
if (flashromType == 1)
resetFlash29F032();
else
resetFlash29F1610();
delay(1000);
verifyFlash();
break;
@ -150,7 +153,6 @@ void flashromMenu8() {
resetFlash29F032();
else
resetFlash29F1610();
delay(500);
asm volatile (" jmp 0");
break;
}
@ -189,12 +191,10 @@ void flashromMenu16() {
println_Msg(F("Erase Flashrom"));
display_Update();
time = millis();
resetFlash16();
eraseFlash16();
delay(1000);
busyCheck16();
println_Msg(F("Flashrom erased."));
display_Update();
resetFlash16();
break;
case 2:
@ -210,7 +210,12 @@ void flashromMenu16() {
fileBrowser("Select file");
display_Clear();
time = millis();
writeFlash16();
if (strcmp(flashid, "C2F3") == 0) {
writeFlash16_29F1601();
}
else {
writeFlash16();
}
delay(100);
resetFlash16();
delay(100);
@ -241,7 +246,6 @@ void flashromMenu16() {
display_Clear();
display_Update();
resetFlash16();
delay(500);
asm volatile (" jmp 0");
break;
}
@ -296,12 +300,27 @@ idtheflash:
flashSize = 2097152;
flashromType = 2;
}
else if (strcmp(flashid, "C2F3") == 0) {
println_Msg(F("MX29F1601 detected"));
flashSize = 2097152;
flashromType = 2;
}
else if (strcmp(flashid, "C2F9") == 0) {
println_Msg(F("MX29L3211 detected"));
println_Msg(F("ATTENTION 3.3V"));
flashSize = 4194304;
flashromType = 2;
}
else if (strcmp(flashid, "C2A8") == 0) {
println_Msg(F("MX29LV320"));
flashSize = 4194304;
flashromType = 2;
}
else if (strcmp(flashid, "C2C9") == 0) {
println_Msg(F("MX29LV640"));
flashSize = 8388608;
flashromType = 2;
}
else if (strcmp(flashid, "0141") == 0) {
println_Msg(F("AM29F032B"));
flashSize = 4194304;
@ -359,7 +378,7 @@ void setup_Flash16() {
// Setting CE(PH6) LOW
PORTH &= ~(1 << 6);
delay(10);
delay(100);
// ID flash
idFlash16();
@ -375,6 +394,11 @@ void setup_Flash16() {
flashSize = 2097152;
flashromType = 2;
}
else if (strcmp(flashid, "C2F3") == 0) {
println_Msg(F("MX29F1601 detected"));
flashSize = 2097152;
flashromType = 2;
}
else if (strcmp(flashid, "C2F9") == 0) {
println_Msg(F("MX29L3211 detected"));
println_Msg(F("ATTENTION 3.3V"));
@ -522,6 +546,8 @@ void resetFlash29F032() {
// Set data pins to input again
dataIn8();
delay(500);
}
void idFlash29F032() {
@ -578,12 +604,16 @@ void writeFlash29F032() {
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut();
// Fill sdBuffer
for (unsigned long currByte = 0; currByte < flashSize; currByte += 512) {
for (unsigned long currByte = 0; currByte < fileSize; currByte += 512) {
myFile.read(sdBuffer, 512);
// Blink led
if (currByte % 2048 == 0)
@ -644,6 +674,8 @@ void resetFlash29F1610() {
// Set data pins to input again
dataIn8();
delay(500);
}
void writeFlash29F1610() {
@ -655,10 +687,15 @@ void writeFlash29F1610() {
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut();
for (unsigned long currByte = 0; currByte < flashSize; currByte += 128) {
for (unsigned long currByte = 0; currByte < fileSize; currByte += 128) {
// Fill sdBuffer with 1 page at a time then write it repeat until all bytes are written
myFile.read(sdBuffer, 128);
@ -681,6 +718,69 @@ void writeFlash29F1610() {
}
}
// Check if write is complete
busyCheck29F1610();
// Set data pins to input again
dataIn8();
// Close the file:
myFile.close();
}
else {
println_Msg(F("Can't open file on SD"));
display_Update();
}
}
void writeFlash29F1601() {
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Flashing file "));
println_Msg(filePath);
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut();
for (unsigned long currByte = 0; currByte < fileSize; currByte += 128) {
// Fill sdBuffer with 1 page at a time then write it repeat until all bytes are written
myFile.read(sdBuffer, 128);
// Blink led
if (currByte % 3072 == 0)
PORTB ^= (1 << 4);
// Check if write is complete
delayMicroseconds(100);
busyCheck29F1610();
// Write command sequence
writeByte_Flash(0x5555 << 1, 0xaa);
writeByte_Flash(0x2aaa << 1, 0x55);
writeByte_Flash(0x5555 << 1, 0xa0);
// Write one full page at a time
for (byte c = 0; c < 128; c++) {
writeByte_Flash(currByte + c, sdBuffer[c]);
if (c == 127) {
// Write the last byte twice or else it won't write at all
writeByte_Flash(currByte + c, sdBuffer[c]);
}
}
}
// Check if write is complete
busyCheck29F1610();
// Set data pins to input again
dataIn8();
@ -740,6 +840,8 @@ void eraseFlash29F1610() {
// Set data pins to input again
dataIn8();
busyCheck29F1610();
}
// Delay between write operations based on status register
@ -758,6 +860,112 @@ void busyCheck29F1610() {
dataOut();
}
/******************************************
MX29LV flashrom functions
*****************************************/
void busyCheck29LV640(unsigned long myAddress, byte myData) {
// Set data pins to input
dataIn8();
// Read the status register
byte statusReg = readByte_Flash(myAddress);
while ((statusReg & 0x80) != (myData & 0x80)) {
statusReg = readByte_Flash(myAddress);
}
// Set data pins to output
dataOut();
}
void writeFlash29LV320() {
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Flashing file "));
println_Msg(filePath);
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut();
for (unsigned long currByte = 0; currByte < fileSize; currByte += 512) {
// Fill sdBuffer
myFile.read(sdBuffer, 512);
// Blink led
if (currByte % 2048 == 0)
PORTB ^= (1 << 4);
for (int c = 0; c < 512; c++) {
// Write command sequence
writeByte_Flash(0x555 << 1, 0xaa);
writeByte_Flash(0x2aa << 1, 0x55);
writeByte_Flash(0x555 << 1, 0xa0);
// Write current byte
writeByte_Flash(currByte + c, sdBuffer[c]);
// Check if write is complete
busyCheck29F032(sdBuffer[c]);
}
}
// Set data pins to input again
dataIn8();
// Close the file:
myFile.close();
}
else {
println_Msg(F("Can't open file on SD"));
display_Update();
}
}
void writeFlash29LV640() {
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Flashing file "));
println_Msg(filePath);
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut();
for (unsigned long currByte = 0; currByte < fileSize; currByte += 512) {
// Fill sdBuffer
myFile.read(sdBuffer, 512);
// Blink led
if (currByte % 2048 == 0)
PORTB ^= (1 << 4);
for (int c = 0; c < 512; c++) {
// Write command sequence
writeByte_Flash(0x555 << 1, 0xaa);
writeByte_Flash(0x2aa << 1, 0x55);
writeByte_Flash(0x555 << 1, 0xa0);
// Write current byte
writeByte_Flash(currByte + c, sdBuffer[c]);
// Check if write is complete
busyCheck29LV640(currByte + c, sdBuffer[c]);
}
}
// Set data pins to input again
dataIn8();
// Close the file:
myFile.close();
}
else {
println_Msg(F("Can't open file on SD"));
display_Update();
}
}
/******************************************
Common flashrom functions
*****************************************/
@ -783,15 +991,18 @@ void blankcheck_Flash() {
}
void verifyFlash() {
println_Msg(F("Verifying against"));
println_Msg(filePath);
println_Msg(F("Verifying..."));
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
blank = 0;
for (unsigned long currByte = 0; currByte < flashSize; currByte += 512) {
for (unsigned long currByte = 0; currByte < fileSize; currByte += 512) {
//fill sdBuffer
myFile.read(sdBuffer, 512);
for (int c = 0; c < 512; c++) {
@ -824,14 +1035,14 @@ void readFlash() {
sd.chdir("/");
// Get name, add extension and convert to char array for sd lib
EEPROM_readAnything(0, foldern);
EEPROM_readAnything(10, foldern);
sd.mkdir("FLASH", true);
sd.chdir("FLASH");
sprintf(fileName, "FL%d", foldern);
strcat(fileName, ".bin");
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
EEPROM_writeAnything(10, foldern);
display_Clear();
print_Msg(F("Saving as "));
@ -887,6 +1098,8 @@ void resetFlash16() {
// Set data pins to input again
dataIn16();
delay(500);
}
void writeFlash16() {
@ -898,12 +1111,17 @@ void writeFlash16() {
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut16();
// Fill sdBuffer with 1 page at a time then write it repeat until all bytes are written
int d = 0;
for (unsigned long currByte = 0; currByte < flashSize / 2; currByte += 64) {
for (unsigned long currByte = 0; currByte < fileSize / 2; currByte += 64) {
myFile.read(sdBuffer, 128);
// Blink led
@ -921,13 +1139,80 @@ void writeFlash16() {
// Write one full page at a time
for (byte c = 0; c < 64; c++) {
word currWord = ((sdBuffer[d + 1] << 8) | sdBuffer[d]);
word currWord = ( ( sdBuffer[d + 1] & 0xFF ) << 8 ) | ( sdBuffer[d] & 0xFF );
writeWord_Flash(currByte + c, currWord);
d += 2;
}
d = 0;
}
// Check if write is complete
busyCheck16();
// Set data pins to input again
dataIn16();
// Close the file:
myFile.close();
}
else {
println_Msg(F("Can't open file on SD."));
display_Update();
}
}
void writeFlash16_29F1601() {
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Flashing file "));
println_Msg(filePath);
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize)
print_Error(F("File size exceeds flash size."), true);
// Set data pins to output
dataOut16();
// Fill sdBuffer with 1 page at a time then write it repeat until all bytes are written
int d = 0;
for (unsigned long currByte = 0; currByte < fileSize / 2; currByte += 64) {
myFile.read(sdBuffer, 128);
// Blink led
if (currByte % 2048 == 0)
PORTB ^= (1 << 4);
// Check if write is complete
delayMicroseconds(100);
busyCheck16();
// Write command sequence
writeWord_Flash(0x5555, 0xaa);
writeWord_Flash(0x2aaa, 0x55);
writeWord_Flash(0x5555, 0xa0);
// Write one full page at a time
for (byte c = 0; c < 64; c++) {
word currWord = ( ( sdBuffer[d + 1] & 0xFF ) << 8 ) | ( sdBuffer[d] & 0xFF );
writeWord_Flash(currByte + c, currWord);
if (c == 63) {
// Write the last byte twice or else it won't write at all
writeWord_Flash(currByte + c, sdBuffer[d + 1]);
}
d += 2;
}
d = 0;
}
// Check if write is complete
busyCheck16();
// Set data pins to input again
dataIn16();
@ -987,6 +1272,8 @@ void eraseFlash16() {
// Set data pins to input again
dataIn16();
busyCheck16();
}
void blankcheck16() {
@ -1006,29 +1293,30 @@ void blankcheck16() {
display_Update();
}
else {
println_Msg(F("Error: Not blank!"));
display_Update();
print_Error(F("Error: Not blank"), false);
}
}
void verifyFlash16() {
println_Msg(F("Verifying against"));
println_Msg(filePath);
println_Msg(F("Verifying..."));
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Get rom size from file
fileSize = myFile.fileSize();
if (fileSize > flashSize) {
print_Error(F("File size exceeds flash size."), true);
}
blank = 0;
word d = 0;
for (unsigned long currByte = 0; currByte < flashSize / 2; currByte += 256) {
for (unsigned long currByte = 0; currByte < fileSize / 2; currByte += 256) {
//fill sdBuffer
myFile.read(sdBuffer, 512);
for (int c = 0; c < 256; c++) {
word currWord = ((sdBuffer[d] << 8) | sdBuffer[d + 1]);
// Swap bytes in word
currWord = ((currWord >> 8) & 0x00FF00FF) | ((currWord & 0x00FF00FF) << 8);
word currWord = ((sdBuffer[d + 1] << 8) | sdBuffer[d]);
if (readWord_Flash(currByte + c) != currWord) {
blank++;
}
@ -1060,14 +1348,14 @@ void readFlash16() {
sd.chdir("/");
// Get name, add extension and convert to char array for sd lib
EEPROM_readAnything(0, foldern);
EEPROM_readAnything(10, foldern);
sd.mkdir("FLASH", true);
sd.chdir("FLASH");
sprintf(fileName, "FL%d", foldern);
strcat(fileName, ".bin");
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
EEPROM_writeAnything(10, foldern);
display_Clear();
print_Msg(F("Saving as "));
@ -1085,13 +1373,11 @@ void readFlash16() {
for (unsigned long currByte = 0; currByte < flashSize / 2; currByte += 256) {
for (word c = 0; c < 256; c++) {
word currWord = readWord_Flash(currByte + c);
// Swap bytes in word
currWord = ((currWord >> 8) & 0x00FF00FF) | ((currWord & 0x00FF00FF) << 8);
// Split word into two bytes
// Left
sdBuffer[d] = (( currWord >> 8 ) & 0xFF);
// Right
sdBuffer[d + 1] = (currWord & 0xFF);
sdBuffer[d + 1] = (( currWord >> 8 ) & 0xFF);
// Left
sdBuffer[d] = (currWord & 0xFF);
d += 2;
}
myFile.write(sdBuffer, 512);
@ -1111,33 +1397,31 @@ void printFlash16(int numBytes) {
short_val = ( ( left_byte & 0xFF ) << 8 ) | ( right_byte & 0xFF );
*/
char buffer[3];
char buf[3];
for (int currByte = 0; currByte < numBytes / 2; currByte += 5) {
// 5 words per line
for (int c = 0; c < 5; c++) {
word currWord = readWord_Flash(currByte + c);
// Swap bytes in word
currWord = ((currWord >> 8) & 0x00FF00FF) | ((currWord & 0x00FF00FF) << 8);
// Split word into two bytes
byte right_byte = currWord & 0xFF;
byte left_byte = ( currWord >> 8 ) & 0xFF;
byte left_byte = currWord & 0xFF;
byte right_byte = ( currWord >> 8 ) & 0xFF;
itoa (left_byte, buffer, 16);
for (int i = 0; i < 2 - strlen(buffer); i++) {
print_Msg('0');
sprintf (buf, "%x", left_byte);
for (int i = 0; i < 2 - strlen(buf); i++) {
print_Msg("0");
}
// Now print the significant bits
print_Msg(buffer);
print_Msg(buf);
itoa (right_byte, buffer, 16);
for (int i = 0; i < 2 - strlen(buffer); i++) {
print_Msg('0');
sprintf (buf, "%x", right_byte);
for (int i = 0; i < 2 - strlen(buf); i++) {
print_Msg("0");
}
// Now print the significant bits
print_Msg(buffer);
print_Msg(buf);
}
println_Msg("");
}

View File

@ -14,12 +14,12 @@ uint16_t sramEndAddress = 0;
Menu
*****************************************/
// GB menu items
const char GBMenuItem1[] PROGMEM = "Read Rom";
const char GBMenuItem2[] PROGMEM = "Read Save";
const char GBMenuItem3[] PROGMEM = "Write Save";
const char GBMenuItem4[] PROGMEM = "Write Flashcart";
const char GBMenuItem5[] PROGMEM = "Reset";
const char* const menuOptionsGB[] PROGMEM = {GBMenuItem1, GBMenuItem2, GBMenuItem3, GBMenuItem4, GBMenuItem5};
static const char GBMenuItem1[] PROGMEM = "Read Rom";
static const char GBMenuItem2[] PROGMEM = "Read Save";
static const char GBMenuItem3[] PROGMEM = "Write Save";
static const char GBMenuItem4[] PROGMEM = "Write Flashcart";
static const char GBMenuItem5[] PROGMEM = "Reset";
static const char* const menuOptionsGB[] PROGMEM = {GBMenuItem1, GBMenuItem2, GBMenuItem3, GBMenuItem4, GBMenuItem5};
void gbMenu() {
// create menu with title and 5 options to choose from
@ -128,7 +128,7 @@ void setup_GB() {
display_Clear();
if (strcmp(checksumStr, "00") != 0) {
println_Msg(F("GB Cart Info"));
print_Msg(F("Rom Name: "));
print_Msg(F("Name: "));
println_Msg(romName);
print_Msg(F("Rom Type: "));
switch (romType) {
@ -329,13 +329,13 @@ void getCartInfo_GB() {
// Get Checksum as string
sprintf(checksumStr, "%02X%02X", readByte_GB(0x014E), readByte_GB(0x014F));
// Dump name into 8.3 compatible format
// Get name
byte myByte = 0;
byte myLength = 0;
for (int addr = 0x0134; addr <= 0x13C; addr++) {
myByte = readByte_GB(addr);
if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 8) {
if (((char(myByte) >= 48 && char(myByte) <= 57) || (char(myByte) >= 65 && char(myByte) <= 122)) && myLength < 15) {
romName[myLength] = char(myByte);
myLength++;
}
@ -345,25 +345,24 @@ void getCartInfo_GB() {
// Dump ROM
void readROM_GB() {
// Get name, add extension and convert to char array for sd lib
char fileName[26];
strcpy(fileName, romName);
strcat(fileName, ".GB");
// create a new folder for the rom file
EEPROM_readAnything(0, foldern);
sprintf(folder, "ROM/%s/%d", romName, foldern);
EEPROM_readAnything(10, foldern);
sprintf(folder, "GB/ROM/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
//clear the screen
display_Clear();
println_Msg(F("Creating folder: "));
println_Msg(folder);
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
EEPROM_writeAnything(10, foldern);
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
@ -406,10 +405,6 @@ void readROM_GB() {
// Close the file:
myFile.close();
// Signal end of process
print_Msg(F("Saved as "));
println_Msg(fileName);
}
unsigned int calc_checksum_GB (char* fileName, char* folder) {
@ -451,13 +446,12 @@ boolean compare_checksum_GB() {
println_Msg(F("Calculating Checksum"));
display_Update();
char fileName[26];
strcpy(fileName, romName);
strcat(fileName, ".GB");
// last used rom folder
EEPROM_readAnything(0, foldern);
sprintf(folder, "ROM/%s/%d", romName, foldern - 1);
EEPROM_readAnything(10, foldern);
sprintf(folder, "GB/ROM/%s/%d", romName, foldern - 1);
char calcsumStr[5];
sprintf(calcsumStr, "%04X", calc_checksum_GB(fileName, folder));
@ -483,19 +477,18 @@ void readSRAM_GB() {
if (sramEndAddress > 0) {
// Get name, add extension and convert to char array for sd lib
char fileName[26];
strcpy(fileName, romName);
strcat(fileName, ".sav");
// create a new folder for the save file
EEPROM_readAnything(0, foldern);
sprintf(folder, "SAVE/%s/%d", romName, foldern);
EEPROM_readAnything(10, foldern);
sprintf(folder, "GB/SAVE/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(0, foldern);
EEPROM_writeAnything(10, foldern);
//open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
@ -540,12 +533,9 @@ void readSRAM_GB() {
myFile.close();
// Signal end of process
print_Msg(F("Saved to SAVE/"));
print_Msg(romName);
print_Msg(F("/"));
print_Msg(foldern - 1);
print_Msg(F("/"));
println_Msg(fileName);
print_Msg(F("Saved to "));
print_Msg(folder);
println_Msg(F("/"));
display_Update();
}
else {

File diff suppressed because it is too large Load Diff

487
Cart_Reader/MD.ino Normal file
View File

@ -0,0 +1,487 @@
//******************************************
// SEGA MEGA DRIVE
//******************************************
/******************************************
Variables
*****************************************/
unsigned long sramEnd;
/******************************************
Menu
*****************************************/
// MD menu items
static const char MDMenuItem1[] PROGMEM = "Read Rom";
static const char MDMenuItem2[] PROGMEM = "Read Save";
static const char MDMenuItem3[] PROGMEM = "Write Save";
static const char MDMenuItem4[] PROGMEM = "Write Flashcart";
static const char MDMenuItem5[] PROGMEM = "Reset";
static const char* const menuOptionsMD[] PROGMEM = {MDMenuItem1, MDMenuItem2, MDMenuItem3, MDMenuItem4, MDMenuItem5};
void mdMenu() {
// create menu with title and 5 options to choose from
unsigned char mainMenu;
// Copy menuOptions out of progmem
convertPgm(menuOptionsMD, 5);
mainMenu = question_box("MEGA DRIVE Reader", menuOptions, 3, 0);
// wait for user choice to come back from the question box menu
switch (mainMenu)
{
case 0:
display_Clear();
// Change working dir to root
sd.chdir("/");
readROM_MD();
//compare_checksum_MD();
break;
case 1:
display_Clear();
// Does cartridge have SRAM
if ((saveType == 1) || (saveType == 2)) {
// Change working dir to root
sd.chdir("/");
println_Msg(F("Reading Sram..."));
display_Update();
enableSram_MD(1);
readSram_MD();
enableSram_MD(0);
}
else {
print_Error(F("Cart has no Sram"), false);
}
break;
case 2:
display_Clear();
// Does cartridge have SRAM
if ((saveType == 1) || (saveType == 2)) {
// Change working dir to root
sd.chdir("/");
// Launch file browser
fileBrowser("Select srm file");
display_Clear();
enableSram_MD(1);
writeSram_MD();
writeErrors = verifySram_MD();
enableSram_MD(0);
if (writeErrors == 0) {
println_Msg(F("Sram verified OK"));
display_Update();
}
else {
print_Msg(F("Error: "));
print_Msg(writeErrors);
println_Msg(F(" bytes "));
print_Error(F("did not verify."), false);
}
}
else {
print_Error(F("Cart has no Sram"), false);
}
break;
/*case 3:
// Change working dir to root
sd.chdir("/");
writeFlash_MD();
// Reset
wait();
asm volatile (" jmp 0");
break;
case 4:
asm volatile (" jmp 0");
break;*/
}
println_Msg(F(""));
println_Msg(F("Press Button..."));
display_Update();
wait();
}
/******************************************
Setup
*****************************************/
void setup_MD() {
// Set Address Pins to Output
//A0-A7
DDRF = 0xFF;
//A8-A15
DDRK = 0xFF;
//A16-A23
DDRL = 0xFF;
// Set Control Pins to Output RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6)
DDRH |= (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Set TIME(PJ0) to Output
DDRJ |= (1 << 0);
// Set Data Pins (D0-D15) to Input
DDRC = 0x00;
DDRA = 0x00;
// Setting RST(PH0) CS(PH3) WRH(PH4) WRL(PH5) OE(PH6) HIGH
PORTH |= (1 << 0) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Setting TIME(PJ0) HIGH
PORTJ |= (1 << 0);
delay(200);
// Print all the info
getCartInfo_MD();
}
/******************************************
I/O Functions
*****************************************/
/******************************************
Low level functions
*****************************************/
void writeWord_MD(unsigned long myAddress, word myData) {
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
PORTL = (myAddress >> 16) & 0xFF;
PORTC = myData;
PORTA = (myData >> 8) & 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns
// Wait till output is stable
__asm__("nop\n\t""nop\n\t");
// Switch WR(PH5) to LOW
PORTH &= ~(1 << 5);
// Setting CS(PH3) LOW
PORTH &= ~(1 << 3);
// Leave WR low for at least 200ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Setting CS(PH3) HIGH
PORTH |= (1 << 3);
// Switch WR(PH5) to HIGH
PORTH |= (1 << 5);
// Leave WR high for at least 50ns
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
}
word readWord_MD(unsigned long myAddress) {
PORTF = myAddress & 0xFF;
PORTK = (myAddress >> 8) & 0xFF;
PORTL = (myAddress >> 16) & 0xFF;
// Arduino running at 16Mhz -> one nop = 62.5ns
__asm__("nop\n\t");
// Setting CS(PH3) LOW
PORTH &= ~(1 << 3);
// Setting OE(PH6) LOW
PORTH &= ~(1 << 6);
// Long delay here or there will be read errors
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Read
word tempWord = ( ( PINA & 0xFF ) << 8 ) | ( PINC & 0xFF );
// Setting CS(PH3) HIGH
PORTH |= (1 << 3);
// Setting OE(PH6) HIGH
PORTH |= (1 << 6);
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
return tempWord;
}
// Switch data pins to write
void dataOut_MD() {
DDRC = 0xFF;
DDRA = 0xFF;
}
// Switch data pins to read
void dataIn_MD() {
DDRC = 0x00;
DDRA = 0x00;
}
/******************************************
MEGA DRIVE functions
*****************************************/
void getCartInfo_MD() {
// Set control
dataIn_MD();
cartSize = ((long(readWord_MD(0xD2)) << 16) | readWord_MD(0xD3)) + 1;
// Check if cart has sram
if ((readWord_MD(0xD8) == 0x5241) && (readWord_MD(0xD9) == 0xF820)) {
// Get sram start and end
sramBase = ((long(readWord_MD(0xDA)) << 16) | readWord_MD(0xDB));
sramEnd = ((long(readWord_MD(0xDC)) << 16) | readWord_MD(0xDD));
// Check alignment of sram
if (sramBase == 0x200001) {
// low byte
saveType = 1;
sramSize = (sramEnd - sramBase + 2) / 2;
// Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000
sramBase = sramBase >> 1;
}
else if (sramBase == 0x200000) {
// high byte
saveType = 2;
sramSize = (sramEnd - sramBase + 1) / 2;
// Right shift sram base address so [A21] is set to high 0x200000 = 0b001[0]00000000000000000000
sramBase = sramBase / 2;
}
else
print_Error(F("Unknown Sram Base"), true);
}
else {
// Either no save or eeprom save
saveType = 0;
sramSize = 0;
}
// Get name
for (byte c = 0; c < 48; c += 2) {
// split word
word myWord = readWord_MD((0x150 + c) / 2);
byte loByte = myWord & 0xFF;
byte hiByte = myWord >> 8;
// write to buffer
sdBuffer[c] = hiByte;
sdBuffer[c + 1] = loByte;
}
byte myLength = 0;
for (unsigned int i = 0; i < 48; i++) {
if (((char(sdBuffer[i]) >= 48 && char(sdBuffer[i]) <= 57) || (char(sdBuffer[i]) >= 65 && char(sdBuffer[i]) <= 122)) && myLength < 15) {
romName[myLength] = char(sdBuffer[i]);
myLength++;
}
}
display_Clear();
println_Msg(F("Cart Info"));
println_Msg(F(" "));
print_Msg(F("Name: "));
println_Msg(romName);
print_Msg(F("Size: "));
print_Msg(cartSize * 8 / 1024 / 1024 );
println_Msg(F(" MBit"));
print_Msg(F("Sram: "));
if (sramSize > 0) {
print_Msg(sramSize * 8 / 1024);
println_Msg(F(" KBit"));
}
else
println_Msg(F("None"));
println_Msg(F(" "));
// Wait for user input
if (enable_OLED) {
println_Msg(F("Press Button..."));
display_Update();
wait();
}
}
// Read rom and save to the SD card
void readROM_MD() {
// Set control
dataIn_MD();
// Get name, add extension and convert to char array for sd lib
strcpy(fileName, romName);
strcat(fileName, ".MD");
// create a new folder
EEPROM_readAnything(10, foldern);
sprintf(folder, "MD/ROM/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
display_Clear();
print_Msg(F("Saving to "));
print_Msg(folder);
println_Msg(F("/..."));
display_Update();
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(10, foldern);
// Open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_Error(F("SD Error"), true);
}
word d = 0;
for (unsigned long currBuffer = 0; currBuffer < cartSize / 2; currBuffer += 256) {
// Blink led
if (currBuffer % 16384 == 0)
PORTB ^= (1 << 4);
for (int currWord = 0; currWord < 256; currWord++) {
word myWord = readWord_MD(currBuffer + currWord);
// Split word into two bytes
// Left
sdBuffer[d] = (( myWord >> 8 ) & 0xFF);
// Right
sdBuffer[d + 1] = (myWord & 0xFF);
d += 2;
}
myFile.write(sdBuffer, 512);
d = 0;
}
// Close the file:
myFile.close();
}
/******************************************
SRAM functions
*****************************************/
// Sonic 3 sram enable
void enableSram_MD(boolean enableSram) {
dataOut_MD();
// Set D0 to either 1(enable SRAM) or 0(enable ROM)
PORTC = enableSram;
// Strobe TIME(PJ0) LOW to latch the data
PORTJ &= ~(1 << 0);
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
// Set TIME(PJ0) HIGH
PORTJ |= (1 << 0);
__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");
dataIn_MD();
}
// Write sram to cartridge
void writeSram_MD() {
dataOut_MD();
// Create filepath
sprintf(filePath, "%s/%s", filePath, fileName);
println_Msg(F("Writing..."));
println_Msg(filePath);
display_Update();
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
// Write to the lower byte
if (saveType == 1) {
for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) {
writeWord_MD(currByte, (myFile.read() & 0xFF));
}
}
// Write to the upper byte
else if (saveType == 2) {
for (unsigned long currByte = sramBase; currByte < sramBase + sramSize; currByte++) {
writeWord_MD(currByte, ((myFile.read() << 8 ) & 0xFF));
}
}
else
print_Error(F("Unknown save type"), false);
// Close the file:
myFile.close();
println_Msg(F("Done"));
display_Update();
}
else {
print_Error(F("SD Error"), true);
}
dataIn_MD();
}
// Read sram and save to the SD card
void readSram_MD() {
dataIn_MD();
// Get name, add extension and convert to char array for sd lib
strcpy(fileName, romName);
strcat(fileName, ".srm");
// create a new folder for the save file
EEPROM_readAnything(10, foldern);
sprintf(folder, "MD/SAVE/%s/%d", romName, foldern);
sd.mkdir(folder, true);
sd.chdir(folder);
// write new folder number back to eeprom
foldern = foldern + 1;
EEPROM_writeAnything(10, foldern);
// Open file on sd card
if (!myFile.open(fileName, O_RDWR | O_CREAT)) {
print_Error(F("SD Error"), true);
}
for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) {
for (int currWord = 0; currWord < 512; currWord++) {
word myWord = readWord_MD(currBuffer + currWord);
if (saveType == 2) {
// Only use the upper byte
sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF);
}
else if (saveType == 1) {
// Only use the lower byte
sdBuffer[currWord] = (myWord & 0xFF);
}
}
myFile.write(sdBuffer, 512);
}
// Close the file:
myFile.close();
print_Msg(F("Saved to "));
print_Msg(folder);
println_Msg(F("/"));
display_Update();
}
unsigned long verifySram_MD() {
dataIn_MD();
writeErrors = 0;
// Open file on sd card
if (myFile.open(filePath, O_READ)) {
for (unsigned long currBuffer = sramBase; currBuffer < sramBase + sramSize; currBuffer += 512) {
for (int currWord = 0; currWord < 512; currWord++) {
word myWord = readWord_MD(currBuffer + currWord);
if (saveType == 2) {
// Only use the upper byte
sdBuffer[currWord] = (( myWord >> 8 ) & 0xFF);
}
else if (saveType == 1) {
// Only use the lower byte
sdBuffer[currWord] = (myWord & 0xFF);
}
}
// Check sdBuffer content against file on sd card
for (int i = 0; i < 512; i++) {
if (myFile.read() != sdBuffer[i]) {
writeErrors++;
}
}
}
// Close the file:
myFile.close();
}
else {
print_Error(F("SD Error"), true);
}
// Return 0 if verified ok, or number of errors
return writeErrors;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,68 @@
Double click on Cart_Reader.ino to open the program in the Arduino IDE.
And don't forget to copy the files from the sd folder to the root of your sd card.
This Arduino code is written by a beginner for beginners, therefore I tried to comment every line of code. To an experienced coder this probably will seem very wrong but it really helps if you're just starting out.
Source code files:
- Cart_Reader.ino is the main file that contains functions and variables being used by multiple submodules and also the main menu
- EEPROMAnything.h helps with reading and writing to the Atmega's internal eeprom
- FLASH.ino is for reading and writing 29F016, 29F032, 29F033, 29F1610 and 29L3211 flashroms
- GB.ino includes everything Game Boy and Game Boy Color
- GBA.ino includes everything Game Boy Advance
- MD.ino includes everything for the SEGA Mega Drive
- N64.ino includes everything Nintendo 64
- NP.ino includes everything for Nintendo Power SF Memory and GB Memory cartridges
- SNES.ino includes everything Super Nintendo
Every submodule has it's own setup_XX() function that configures the needed pins and a submenu that lets you choose what you want to do.
The code directly addresses the pins via the DDR, PIN and PORT registers.
Please also refer to the [pinout Excel sheet](https://github.com/sanni/cartreader/blob/master/pinout.xls).
```
void setup_N64_Controller() {
// Output a low signal
PORTH &= ~(1 << 4);
// Set Controller Data Pin(PH4) to Input
DDRH &= ~(1 << 4);
}
```
Would be the same as this in a more traditional Arduino sketch:
```
int dataPin = 7;
void setup(){
// Output a low signal
digitalWrite(dataPin, LOW);
// Set controller data pin to input
pinMode(dataPin, INPUT);
}
```
To preserve memory every string is saved into the flash of the Arduino, also called progmem. This is done by using the F() macro.
```
println_Msg(F("Press Button."));
```
Also all the menus are stored in progmem and are only recalled to sram when needed.
```
// N64 controller menu items
const char N64ContMenuItem1[] PROGMEM = "Test Controller";
const char N64ContMenuItem2[] PROGMEM = "Read ControllerPak";
const char N64ContMenuItem3[] PROGMEM = "Write ControllerPak";
const char N64ContMenuItem4[] PROGMEM = "Reset";
const char* const menuOptionsN64Controller[] PROGMEM = {N64ContMenuItem1, N64ContMenuItem2, N64ContMenuItem3, N64ContMenuItem4};
```
In an effort to keep the codebase as portable as possible instead of using the functions supplied by the OLED library directly to print out text helper functions like `println_Msg` are being used. So if you want to change to another display library you don't need to change all the code but only the helper functions.
```
void print_Msg(long unsigned int message) {
if (enable_OLED)
display.print(message);
if (enable_Serial)
Serial.print(message);
}
```
For development purposes you can route all the text output to the Arduino's Serial Monitor instead of to the OLED screen. In this case you control the cart reader by typing numbers into the serial monitor corresponding to the action you want to perform. If you are asked to press the button just send a random number.
```
// If you don't have an OLED screen change
// enable_OLED to 0 and enable_Serial to 1
#define enable_OLED 0
#define enable_Serial 1
```
To compile and upload the code please have a look at [this wiki article](https://github.com/sanni/cartreader/wiki/How-to-flash-the-Arduino-Code-to-your-Cart-Reader).

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,28 @@
# Cartridge Reader for Arduino Mega2560
# Cartridge Reader Shield for Arduino Mega 2560
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/v9/cartreaders.JPG)
![image](https://dl.dropboxusercontent.com/s/o26c0yey8onuha2/v11_front_diagonal.jpg?dl=1)
####Features:
#### Features:
- Reads SNES roms and reads/writes save games from and to the SNES cartridge
Supported cartridge types so far: LoRom, HiRom, ExHiRom, SuperFX, SuperFX2, SDD1, SA1(needs Adafruit Clock Generator)
- Reads and writes SFC Nintendo Power Cartridges (also known as SF Memory, needs Adafruit Clock Generator for best result)
- Reads and writes SFC Nintendo Power Flash Cartridges (also known as SF Memory, needs Adafruit Clock Generator for best result)
- Reads N64 roms and reads/writes save games(4K/16K Eeprom + Sram + all 3 types of Flashram)
- Reads and writes N64 controller paks and also can test a N64 controller's buttons and thumbstick
- Reads Game Boy (Color) roms and reads/writes save games(only tested with MBC5)
- Programs custom made Game Boy Color flashcarts
- Reads Game Boy Advance roms and reads/writes save games(4K Eeprom, 64K Eeprom, Sram/Fram, SST39VF512 512K flash + MX29L010 1M flash)
- Programs Flashroms like 29F016, 29F032, 29F033, 29F1610 and 29L3211(needs 3.3V)
- Reflashes N64 Repros with Spansion S29GL256N or Intel 4400L0ZDQ0 flashroms
- Reads Game Boy (Color) roms and reads/writes save games
- Programs custom made Game Boy Color flashcarts with MX29F033 flashrom
- Reads Game Boy Advance roms and reads/writes most of the save games(4K Eeprom, 64K Eeprom, Sram/Fram, SST39VF512 512K flash + MX29L010 1M flash)
- Reflashes GBA Repros with Intel 4000L0YBQ0, Macronix MX29GL128E or Fujitsu MSP55LV128 flashroms
- Reads Sega Mega Drive roms and reads/writes save games(Sram/Fram)
- Programs Flashroms like 29F016, 29F032, 29F033, 29F1610 and 29L3211
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/v9/cartreader_bottom.JPG)
![image](https://dl.dropboxusercontent.com/s/goc70t6cg9v7sfa/v11_switches.jpg?dl=1)
####Be sure to check the guides in the wiki too.
#### Be sure to check the guides in the [Wiki](https://github.com/sanni/cartreader/wiki) too.
[![](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/youtubevideo.jpg)](https://www.youtube.com/watch?v=1BviR53_6NQ)
[![](https://dl.dropboxusercontent.com/s/950svg0i21syq3j/youtube_preview.jpg?dl=1)](https://www.youtube.com/watch?v=AD_o7J85bCg)  
####Thanks to:
#### Thanks to:
MichlK - ROM-Reader for Super Nintendo
Jeff Saltzman - 4-Way Button
Wayne and Layne - Video-Game-Shield menu
@ -39,7 +42,8 @@
insidegadgets - GBCartRead
RobinTheHood - GameboyAdvanceRomDumper
YamaArashi - GBA flashrom bank switch command
![image](https://dl.dropboxusercontent.com/s/0be9rp9ss37vq2c/v11_rear.jpg?dl=1)
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/v82_components.jpg)
####Please join the discussion at: http://forum.arduino.cc/index.php?topic=158974.0
#### Please join the discussion at: http://forum.arduino.cc/index.php?topic=158974.0
And also feel free to send me a private message over the Arduino Forum in case you need help with ordering the parts or soldering or really anything else.

Binary file not shown.

Binary file not shown.

View File

@ -1,2 +1 @@
A simple tool used to byteswap or wordswap N64 roms.
Made by Skelux.
Here are two tools that can byteswap a N64 rom for you.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,41 +1,40 @@
###Here you will find all the printed circuit boards needed to build the Arduino Cartridge Reader
### Here you will find all the printed circuit boards needed to build the Arduino Cartridge Reader
####cartreader.zip is the main PCB, PCB thickness is 1.6mm and size is 10x10cm
#### cartreader.zip is the main PCB, PCB thickness is 1.6mm and size is 10x10cm
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/cartreader.png)
![image](https://dl.dropboxusercontent.com/s/b21houxm5bvdds1/cartreader.png?dl=01)
####adapter.zip are various adapter PCBs, PCB thickness is 1.2mm and size is 10x10cm
#### gbaadapter.zip is an adapter for Game Boy and Game Boy Advance cartridges, PCB thickness is 1.2mm
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/adapter.png)
![image](https://dl.dropboxusercontent.com/s/rnnnstkn3ynv10g/gbaadapter_pcb.png?dl=1)
Contains:
- Game Boy (Color)/ Game Boy Advance cartridge adapter
- small pcb for soldering a 29F032 flashrom into a Mission Impossible Game Boy Color cartridge
- flash adapter for flashing 29F016, 29F032, 29F033, 29F1610 and 29L3211 flashroms
- adapter for soldering a 29F032 into a SNES cartridge
#### flash_adapter.zip is an adapter for 8bit and 16bit flashroms like the 29F032 or the 29L3211, PCB thickness is 1.2mm
####backplate.dxf are the acrylic pieces for laser cutting, the thickness is 4mm and size is 10x10cm, you need to tell the manufacturer that you need all the cut-outs too
![image](https://dl.dropboxusercontent.com/s/3oovzhcgdt3a3x9/flashadapter.png?dl=1)
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/backplate.png)
#### snes_spacer.stl is a small 3d printed spacer to relieve pressure from the pins of SNES clone console slot
Contains:
- backplate
- 2x SD card spacer
- display protector
![image](https://dl.dropboxusercontent.com/s/07slhy8pi9ujiri/snes_spacer.png?dl=1)
####snes_spacer.stl is a small 3d printed spacer to relieve pressure from the pins of SNES clone console slot
#### n64sleeve.stl helps with aligning N64 cartridges if the clone slot is not long/wide enough
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/snes_spacer.png)
![image](https://dl.dropboxusercontent.com/s/s6jxobazww3sm81/n64sleeve.png?dl=1)
####gbslot.stl is a 3d printed case for the Game Boy (Advance) adapter pcb
#### gbaadapter.stl is a 3d printed case for the Game Boy (Advance) adapter pcb
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/gbslot.png)
![image](https://dl.dropboxusercontent.com/s/ainwnoclzzq24ez/gbaadapter.png?dl=1)
####backplate_3dprint.stl is an alternative backplate for 3d printing
#### backplate.stl is the backplate that holds the Arduino
![image](https://dl.dropboxusercontent.com/u/20912715/snes/Snes_Cart_Reader_Mega_Shield/wiki/backplate_3dprint.png)
![image](https://dl.dropboxusercontent.com/s/0qixf0n33emi1ed/backplate.png?dl=1)
####For the needed parts list see here: https://github.com/sanni/cartreader/wiki/PCB-Needed-Parts
####How to order a PCB: https://github.com/sanni/cartreader/wiki/PCB-How-to-order-printed-circuit-boards
#### sd_spacer.stl holds the RGB led and fits between the SD card and the Cart Reader pcb
###Attention: While I have tested all the parts I can not guarantee flawless operation, so use at own risk and always check the boards for short circuits
![image](https://dl.dropboxusercontent.com/s/fn6g1pm88puq8h6/sd_spacer.png?dl=1)
#### sidewall.stl secures the Arduino Mega to the backplate with three M2x10 screws
![image](https://dl.dropboxusercontent.com/s/3wqdbc4p9au7dqw/sidewall.png?dl=1)
#### For the needed parts list see here: https://github.com/sanni/cartreader/wiki/Needed-Parts
#### How to order a PCB: https://github.com/sanni/cartreader/wiki/How-to-order-printed-circuit-boards

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

BIN
pcb/flash_adapter.zip Normal file

Binary file not shown.

11958
pcb/gbaadapter.stl Normal file

File diff suppressed because it is too large Load Diff

BIN
pcb/gbaadapter.zip Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

226
pcb/n64sleeve.stl Normal file
View File

@ -0,0 +1,226 @@
solid n64slotsleeve
facet normal 0.0 -1.0 0.0
outer loop
vertex 70.5 -0.30000000000000004 15.0
vertex -0.5 -0.30000000000000004 0.0
vertex 70.5 -0.30000000000000004 0.0
endloop
endfacet
facet normal 0.0 -1.0 0.0
outer loop
vertex -0.5 -0.30000000000000004 0.0
vertex 70.5 -0.30000000000000004 15.0
vertex -0.5 -0.30000000000000004 15.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex -0.5 10.299999999999999 0.0
vertex 0.0 9.8 0.0
vertex -0.5 -0.30000000000000004 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 0.0 9.8 0.0
vertex -0.5 10.299999999999999 0.0
vertex 70.5 10.299999999999999 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 0.0 9.8 0.0
vertex 70.5 10.299999999999999 0.0
vertex 70.0 9.8 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 70.0 9.8 0.0
vertex 70.5 10.299999999999999 0.0
vertex 70.0 0.20000000000000004 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex -0.5 -0.30000000000000004 0.0
vertex 0.0 0.20000000000000004 0.0
vertex 70.5 -0.30000000000000004 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 0.0 0.20000000000000004 0.0
vertex -0.5 -0.30000000000000004 0.0
vertex 0.0 9.8 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 70.5 -0.30000000000000004 0.0
vertex 0.0 0.20000000000000004 0.0
vertex 70.0 0.20000000000000004 0.0
endloop
endfacet
facet normal 0.0 0.0 -1.0
outer loop
vertex 70.5 -0.30000000000000004 0.0
vertex 70.0 0.20000000000000004 0.0
vertex 70.5 10.299999999999999 0.0
endloop
endfacet
facet normal 0.0 1.0 0.0
outer loop
vertex -0.5 10.299999999999999 15.0
vertex 70.5 10.299999999999999 0.0
vertex -0.5 10.299999999999999 0.0
endloop
endfacet
facet normal 0.0 1.0 0.0
outer loop
vertex 70.5 10.299999999999999 0.0
vertex -0.5 10.299999999999999 15.0
vertex 70.5 10.299999999999999 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex -0.5 -0.30000000000000004 15.0
vertex 0.0 0.20000000000000004 15.0
vertex -0.5 10.299999999999999 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 0.0 0.20000000000000004 15.0
vertex -0.5 -0.30000000000000004 15.0
vertex 70.5 -0.30000000000000004 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 0.0 0.20000000000000004 15.0
vertex 70.5 -0.30000000000000004 15.0
vertex 70.0 0.20000000000000004 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 70.0 0.20000000000000004 15.0
vertex 70.5 -0.30000000000000004 15.0
vertex 70.0 9.8 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex -0.5 10.299999999999999 15.0
vertex 0.0 9.8 15.0
vertex 70.5 10.299999999999999 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 0.0 9.8 15.0
vertex -0.5 10.299999999999999 15.0
vertex 0.0 0.20000000000000004 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 70.5 10.299999999999999 15.0
vertex 0.0 9.8 15.0
vertex 70.0 9.8 15.0
endloop
endfacet
facet normal -0.0 -0.0 1.0
outer loop
vertex 70.5 10.299999999999999 15.0
vertex 70.0 9.8 15.0
vertex 70.5 -0.30000000000000004 15.0
endloop
endfacet
facet normal 1.0 0.0 0.0
outer loop
vertex 70.5 10.299999999999999 0.0
vertex 70.5 -0.30000000000000004 15.0
vertex 70.5 -0.30000000000000004 0.0
endloop
endfacet
facet normal 1.0 0.0 0.0
outer loop
vertex 70.5 -0.30000000000000004 15.0
vertex 70.5 10.299999999999999 0.0
vertex 70.5 10.299999999999999 15.0
endloop
endfacet
facet normal -1.0 0.0 0.0
outer loop
vertex -0.5 10.299999999999999 15.0
vertex -0.5 -0.30000000000000004 0.0
vertex -0.5 -0.30000000000000004 15.0
endloop
endfacet
facet normal -1.0 0.0 0.0
outer loop
vertex -0.5 -0.30000000000000004 0.0
vertex -0.5 10.299999999999999 15.0
vertex -0.5 10.299999999999999 0.0
endloop
endfacet
facet normal 0.0 -1.0 0.0
outer loop
vertex 70.0 9.8 15.0
vertex 0.0 9.8 0.0
vertex 70.0 9.8 0.0
endloop
endfacet
facet normal 0.0 -1.0 0.0
outer loop
vertex 0.0 9.8 0.0
vertex 70.0 9.8 15.0
vertex 0.0 9.8 15.0
endloop
endfacet
facet normal 1.0 -0.0 -0.0
outer loop
vertex 0.0 9.8 0.0
vertex 0.0 0.20000000000000004 15.0
vertex 0.0 0.20000000000000004 0.0
endloop
endfacet
facet normal 1.0 -0.0 -0.0
outer loop
vertex 0.0 0.20000000000000004 15.0
vertex 0.0 9.8 0.0
vertex 0.0 9.8 15.0
endloop
endfacet
facet normal -1.0 0.0 0.0
outer loop
vertex 70.0 9.8 15.0
vertex 70.0 0.20000000000000004 0.0
vertex 70.0 0.20000000000000004 15.0
endloop
endfacet
facet normal -1.0 0.0 0.0
outer loop
vertex 70.0 0.20000000000000004 0.0
vertex 70.0 9.8 15.0
vertex 70.0 9.8 0.0
endloop
endfacet
facet normal 0.0 1.0 0.0
outer loop
vertex 0.0 0.20000000000000004 15.0
vertex 70.0 0.20000000000000004 0.0
vertex 0.0 0.20000000000000004 0.0
endloop
endfacet
facet normal 0.0 1.0 0.0
outer loop
vertex 70.0 0.20000000000000004 0.0
vertex 0.0 0.20000000000000004 15.0
vertex 70.0 0.20000000000000004 15.0
endloop
endfacet
endsolid n64slotsleeve

1892
pcb/sd_spacer.stl Normal file

File diff suppressed because it is too large Load Diff

16165
pcb/sidewall.stl Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,6 +1,6 @@
####Copy these files to the root of your SD card.
#### Copy these files to the root of your SD card.
####gba.txt
#### gba.txt
This file stores the GBA database which is needed because the save type and rom size are not stored inside the rom.
Example:
@ -16,7 +16,7 @@ Savetypes:
5 = 1024K Flash
6 = 512K Sram
####n64.txt
#### n64.txt
This file stores the N64 database which is needed because the save type and rom size are not stored inside the rom.
The CRC32 checksum is used to verify a good dump.
@ -31,14 +31,14 @@ Savetypes:
5 = 4K Eeprom
6 = 16K Eeprom
####snes.txt
#### snes.txt
This file is needed for odd sized SNES games like Final Fantasy (JAP), Super Metroid(US/JAP) or Tales of Symphonia. Without this file you will get overdumps and the checksum calculation will fail. There are still a lot games missing from this list.
Example:
A172,24,48
checksum, size in Mbit, number of banks (lorom needs twice as many banks as hirom for the same rom size)
####hirom64.map
#### hirom64.map
This is a Nintendo Power mapping file that changes the mapping to a single 4MB HiRom game with 64Kbit/8KByte save. The first byte is used to specify the mapping.
Bit0-1 SRAM Size (0=2K, 1=8K, 2=32K, 3=None) ;ie. 2K SHL (N*2)
@ -54,7 +54,7 @@ Example:
111 -> 4M
01 -> 8K
####lorom256.map
#### lorom256.map
This is a Nintendo Power mapping file that changes the mapping to a single 4MB LoRom game with 256Kbit/32 KByte save.
Example:

View File

@ -159,12 +159,12 @@ a9417994,NDAJ,32,1
b1e87639,NDYP,12,5
eb759206,NDYE,12,5
5acca298,NDYE,12,5
7fdec270,NDQE,20,5
7fdec270,NDQE,20,0
7737ed9e,NTAP,16,0
99c7649d,NTAF,16,0
0b0954c5,NTAD,16,0
c38ca641,NTAE,16,0
c4b0d9ea,NDQP,20,5
c4b0d9ea,NDQP,20,0
a28c71c6,NDOP,32,6
919f7e74,NDOJ,32,6
c83dfa15,NDPE,32,6