cartreader/Cart_Reader/LJPRO.ino
2024-07-08 19:25:29 +02:00

555 lines
13 KiB
C++

//******************************************
// LITTLE JAMMER PRO MODULE
//******************************************
#ifdef ENABLE_LJPRO
// Little Jammer Pro
// Cartridge Pinout
// 48P 1.25mm pitch connector
//
// FORM FACTOR IS SAME AS BANDAI WONDERSWAN/BENESSE POCKET CHALLENGE V2/LITTLE JAMMER
// WIRING IS COMPLETELY DIFFERENT!
//
// LEFT SIDE
// 1 GND
// 2 GND
// 3 S1 (GND)
// 4 S2 (GND)
// 5 U1_WP#/ACC
// 6 U1_SCLK
// 7 U1_SCLK
// 8 U1_SI
// 9 U1_SI
// 10 U1_SO/PO7
// 11 U1_SO/PO7
// 12 U1_PO6
// 13 U1_PO5
// 14 U1_PO4
// 15 U1_PO3
// 16 U1_PO2
// 17 U1_PO1
// 18 U1_PO0
// 19 U1_CS#
// 20 U1_CS#
// 21 U1_HOLD#
// 22 U1_HOLD#
// 23 VCC (+3V)
// 24 VCC (+3V)
// 25 VCC (+3V)
// 26 VCC (+3V)
// 27 U2_SCLK
// 28 U2_SCLK
// 29 U2_SI
// 30 U2_SI
// 31 U2_SO/PO7
// 32 U2_SO/PO7
// 33 U2_PO6
// 34 U2_PO5
// 35 U2_PO4
// 36 U2_PO3
// 37 U2_PO2
// 38 U2_PO1
// 39 U2_PO0
// 40 U2_CS#
// 41 U2_CS#
// 42 U2_HOLD#
// 43 U2_HOLD#
// 44 U2_WP#/ACC
// 45 S3 (GND)
// 46 S4 (GND)
// 47 GND
// 48 GND
// RIGHT SIDE
// CONTROL PINS:
// U1_HOLD# (PH4) - SNES /IRQ
// U1_CS# (PK0) - SNES A8
// U1_SI (PK1) - SNES A9
// U1_WP#/ACC (PK2) - SNES A10
//
// U2_HOLD# (PH0) - SNES RESET
// U2_SI (PH3) - SNES /CS
// U2_WP#/ACC (PH5) - SNES /WR
// U2_CS# (PH6) - SNES /RD
//
// S1 (PK4) - SNES A12
// S2 (PK5) - SNES A13
// S3 (PK6) - SNES A14
// S4 (PK7) - SNES A15
// COMBINE U1_SCLK + U2_SCLK INTO SINGLE SCLK
// SCLK(PH1) - SNES CPUCLK
// DATA PINS:
// U1 D0-D7 (PORTF)
// U2 D0-D7 (PORTC)
// NOTES:
// HOLD# NOT USED FOR PARALLEL MODE - PULLED UP TO VCC ON CARTS
// WP#/ACC PULLED DOWN TO GND ON CARTS
//******************************************
// DEFINES
//******************************************
#define CS1_LOW PORTK &= ~(1 << 0)
#define CS1_HIGH PORTK |= (1 << 0)
#define CS2_LOW PORTH &= ~(1 << 6)
#define CS2_HIGH PORTH |= (1 << 6)
//******************************************
// VARIABLES
//******************************************
byte LJPRO[] = {2,4,6,8};
byte ljprolo = 0; // Lowest Entry
byte ljprohi = 3; // Highest Entry
byte ljprosize;
byte newljprosize;
char mnfID[3];
char deviceID[5];
boolean ljproflash1found = false;
boolean ljproflash2found = false;
byte ljproflash1size;
byte ljproflash2size;
// EEPROM MAPPING
// 08 ROM SIZE
//******************************************
// MENU
//******************************************
// Base Menu
static const char* const menuOptionsLJPRO[] PROGMEM = { FSTRING_READ_ROM, FSTRING_SET_SIZE, FSTRING_RESET };
// U1_HOLD#(PH4) - SNES /IRQ
// U1_CS# - SNES A8
// U1_WP#/ACC - SNES A9
// U1_SI - SNES A10
//
// U2_HOLD#(PH0) - SNES RESET
// U2_SI(PH3) - SNES /CS
// U2_WP#/ACC(PH5) - SNES /WR
// U2_CS#(PH6) - SNES /RD
void ljproMenu()
{
convertPgm(menuOptionsLJPRO, 3);
uint8_t mainMenu = question_box(F("LITTLE JAMMER PRO"), menuOptions, 3, 0);
switch (mainMenu)
{
case 0:
// Read ROM
sd.chdir("/");
readROM_LJPRO();
sd.chdir("/");
break;
case 1:
// Set Size
setROMSize_LJPRO();
break;
case 2:
// reset
resetArduino();
break;
}
}
//******************************************
// SETUP
//******************************************
void setup_LJPRO()
{
// Request 3.3V
setVoltage(VOLTS_SET_3V3);
// LITTLE JAMMER PRO uses Serial Flash
// Set Data Pins to Input
DDRF = 0x00; // U1 Data
DDRC = 0x00; // U2 Data
// Set Unused Address Pins to Output
DDRL = 0xFF;
// Set Control Pins to Output
// U2_HLD(PH0) SCLK(PH1) U2_SI(PH3) U1_HLD(PH4) U2_WP(PH5) U2_CS(PH6)
DDRH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// U1_CS(PK0) U1_SI(PK1) U1_WP(PK2) --------
DDRK |= (1 << 0) | (1 << 1) | (1 <<2) | (1 << 3);
// FLASH Configuration Pins to Input
// S1(PK4) S2(PK5) S3(PK6) S4(PK7)
DDRK &= ~((1 << 4) | (1 << 5) | (1 << 6) | (1 << 7));
// Set TIME(PJ0) to Output (UNUSED)
DDRJ |= (1 << 0);
// Setting Control Pins to HIGH
// U2_HLD(PH0) SCLK(PH1) U2_SI(PH3) U1_HLD(PH4) U2_WP(PH5) U2_CS(PH6)
PORTH |= (1 << 0) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// U1_CS(PK0) U1_SI(PK1) U1_WP(PK2) -------- S1(PK4) S2(PK5) S3(PK6) S4(PK7)
PORTK |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
// Set Unused Data Pins (PA0-PA7) to Output
DDRA = 0xFF;
// Set Unused Pins HIGH
PORTA = 0xFF;
PORTJ |= (1 << 0); // TIME(PJ0)
checkStatus_LJPRO();
strcpy(romName, "LJPRO");
mode = CORE_LJPRO;
}
//******************************************
// SERIAL MODE
//******************************************
// 25L1605/25L3205
// Default Serial Mode
void sendSerial_U1(uint8_t data)
{
for (int i = 0; i < 8; i++) {
PORTH &= ~(1 << 1); // SCLK LOW
if ((data >> 7) & 0x1) { // Bit is HIGH
PORTK |= (1 << 1); // U1_SI HIGH;
}
else {
PORTK &= ~(1 << 1); // U1_SI LOW;
}
PORTH |= (1 << 1); // SCLK HIGH
// rotate to the next bit
data <<= 1;
}
}
void sendSerial_U2(uint8_t data)
{
for (int i = 0; i < 8; i++) {
PORTH &= ~(1 << 1); // SCLK LOW
if ((data >> 7) & 0x1) { // Bit is HIGH
PORTH |= (1 << 3); // U2_SI HIGH;
}
else {
PORTH &= ~(1 << 3); // U2_SI LOW;
}
PORTH |= (1 << 1); // SCLK HIGH
// rotate to the next bit
data <<= 1;
}
}
uint8_t readSerial_U1()
{
bool serBits[9];
for (byte i = 0; i < 8; i++) {
pulseClock_LJPRO(1);
serBits[i] = (PINF >> 7) & 0x1;
}
byte tempdata = serBits[0] << 7 | serBits[1] << 6 | serBits[2] << 5 | serBits[3] << 4 | serBits[4] << 3 | serBits[5] << 2 | serBits[6] << 1 | serBits[7];
return tempdata;
}
uint8_t readSerial_U2()
{
bool serBits[9];
for (byte i = 0; i < 8; i++) {
pulseClock_LJPRO(1);
serBits[i] = (PINC >> 7) & 0x1;
}
byte tempdata = serBits[0] << 7 | serBits[1] << 6 | serBits[2] << 5 | serBits[3] << 4 | serBits[4] << 3 | serBits[5] << 2 | serBits[6] << 1 | serBits[7];
return tempdata;
}
//******************************************
// PARALLEL MODE
//******************************************
// 25L1605/25L3205
// Parallel Mode - Command 0x55
// SCLK Frequency 1.2MHz (Cycle 833.33ns)
// READ 0x03
// WRITE 0x02
void pulseClock_LJPRO(unsigned int times)
{
for (unsigned int i = 0; i < (times * 2); i++) {
// Switch the clock pin to 0 if it's 1 and 0 if it's 1
PORTH ^= (1 << 1);
// without the delay the clock pulse would be 1.5us and 666kHz
//__asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t"));
}
}
// Send one byte of data to Serial FLASH [Parallel Mode]
void sendData_U1(byte data)
{
DDRF = 0xFF; // U1 Data Output
PORTF = data;
pulseClock_LJPRO(8);
DDRF = 0x00; // U1 Data Input
}
void sendData_U2(byte data)
{
DDRC = 0xFF; // U2 Data Output
PORTC = data;
pulseClock_LJPRO(8);
DDRC = 0x00; // U2 Data Input
}
void readData_U1(uint32_t startaddr, uint32_t endaddr)
{
for (uint32_t addr = startaddr; addr < endaddr; addr += 512) {
for (int x = 0; x < 512; x++) {
pulseClock_LJPRO(1);
sdBuffer[x] = PINF;
}
myFile.write(sdBuffer, 512);
}
}
void readData_U2(uint32_t startaddr, uint32_t endaddr)
{
for (uint32_t addr = startaddr; addr < endaddr; addr += 512) {
for (int x = 0; x < 512; x++) {
pulseClock_LJPRO(1);
sdBuffer[x] = PINC;
}
myFile.write(sdBuffer, 512);
}
}
// RDID
// Manufacturer 0xC2
// Memory Density 0x20
// Device ID 0x15 [25L1605]/0x16 [25L3205]
// REMS
// Manufacturer 0xC2
// Device ID 0x14 [25L1605]/0x15 [25L3205]
void readID_U1() // Parallel Mode
{
CS1_LOW; // U1 LOW
sendSerial_U1(0x9F); // RDID Command
pulseClock_LJPRO(1);
byte id0 = PINF; // 0xC2
pulseClock_LJPRO(1);
byte id1 = PINF; // 0x20
pulseClock_LJPRO(1);
byte id2 = PINF; // 0x15 [MX25L1605]/0x16 [MX25L3205]
CS1_HIGH; // U1 HIGH
// Flash ID
sprintf(mnfID, "%02X", id0);
sprintf(deviceID, "%02X%02X", id1, id2);
// println_Msg(mnfID);
// println_Msg(deviceID);
// display_Update();
if(strcmp(deviceID, "2015") == 0) { // MX25L1605
ljproflash1found = 1;
ljproflash1size = 2;
display_Clear();
println_Msg(F("U1 MX25L1605 FOUND"));
display_Update();
}
else if (strcmp(deviceID, "2016") == 0) { // MX25L3205
ljproflash1found = 1;
ljproflash1size = 4;
display_Clear();
println_Msg(F("U1 MX25L3205 FOUND"));
display_Update();
}
}
void readID_U2() // Parallel Mode
{
CS2_LOW; // U2 LOW
sendSerial_U2(0x9F); // RDID Command
pulseClock_LJPRO(1);
byte id0 = PINC; // 0xC2
pulseClock_LJPRO(1);
byte id1 = PINC; // 0x20
pulseClock_LJPRO(1);
byte id2 = PINC; // 0x15 [MX25L1605]/0x16 [MX25L3205]
pulseClock_LJPRO(1);
CS2_HIGH; // U2 HIGH
// Flash ID
sprintf(mnfID, "%02X", id0);
sprintf(deviceID, "%02X%02X", id1, id2);
// println_Msg(mnfID);
// println_Msg(deviceID);
// display_Update();
if(strcmp(deviceID, "2015") == 0) { // MX25L1605
ljproflash2found = 1;
ljproflash2size = 2;
println_Msg(F("U2 MX25L1605 FOUND"));
display_Update();
}
else if (strcmp(deviceID, "2016") == 0) { // MX25L3205
ljproflash2found = 1;
ljproflash2size = 4;
println_Msg(F("U2 MX25L3205 FOUND"));
display_Update();
}
}
//******************************************
// READ ROM
//******************************************
void readROM_LJPRO()
{
createFolderAndOpenFile("LJPRO", "ROM", romName, "bin");
// Little Jammer Pro PCB B1043-02A
// Footprints for two 25L1605/25L3205 chips
// Test carts only have one 25L1605 (2MB) installed
// PCB could possibly install two 25L3205 chips (2x4MB = 8MB)
// Set U1 FLASH to Parallel Mode
CS1_LOW; // U1 LOW
sendSerial_U1(0x55); // Parallel Mode
CS1_HIGH; // U1 HIGH
// Read ID
readID_U1();
// Set U2 FLASH to Parallel Mode
CS2_LOW; // U2 LOW
sendSerial_U2(0x55); // Parallel Mode
CS2_HIGH; // U2 HIGH
// Read ID
readID_U2();
// Read U1
println_Msg(F("Reading U1..."));
display_Update();
CS1_LOW; // U1 LOW
DDRF = 0x00; // U1 Data Input
sendSerial_U1(0x03); // Read Array (Parallel)
sendSerial_U1(0x00); // Address A23-A16
sendSerial_U1(0x00); // Address A15-A8
sendSerial_U1(0x00); // Address A7-A0
readData_U1(0x000000, 0x200000);
if (ljproflash1size == 4) { // 4MB
readData_U1(0x200000, 0x400000);
}
CS1_HIGH; // U1 HIGH
if (ljproflash2found) {
// Read U2
println_Msg(F("Reading U2..."));
display_Update();
CS2_LOW; // U2 LOW
DDRC = 0x00; // U2 Data Input
sendSerial_U2(0x03); // Read Array (Parallel)
sendSerial_U2(0x00); // Address A23-A16
sendSerial_U2(0x00); // Address A15-A8
sendSerial_U2(0x00); // Address A7-A0
readData_U2(0x000000, 0x200000);
if (ljproflash2size == 4) { // 4MB
readData_U2(0x200000, 0x400000);
}
CS2_HIGH; // U2 HIGH
}
myFile.close();
printCRC(fileName, NULL, 0);
println_Msg(FS(FSTRING_EMPTY));
print_STR(press_button_STR, 1);
display_Update();
wait();
}
//******************************************
// ROM SIZE
//******************************************
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
void printRomSize_LJPRO(int index)
{
display_Clear();
print_Msg(FS(FSTRING_ROM_SIZE));
println_Msg(LJPRO[index]);
}
#endif
void setROMSize_LJPRO()
{
byte newljprosize;
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
if (ljprolo == ljprohi)
newljprosize = ljprolo;
else {
newljprosize = navigateMenu(ljprolo, ljprohi, &printRomSize_LJPRO);
display.setCursor(0, 56); // Display selection at bottom
}
print_Msg(FS(FSTRING_ROM_SIZE));
print_Msg(LJPRO[newljprosize]);
println_Msg(F("KB"));
display_Update();
delay(1000);
#else
if (ljprolo == ljprohi)
newljprosize = ljprolo;
else {
setrom:
String sizeROM;
for (int i = 0; i < (ljprohi - ljprolo + 1); i++) {
Serial.print(F("Select ROM Size: "));
Serial.print(i);
Serial.print(F(" = "));
Serial.print(LJPRO[i + ljprolo]);
Serial.println(F("KB"));
}
Serial.print(F("Enter ROM Size: "));
while (Serial.available() == 0) {}
sizeROM = Serial.readStringUntil('\n');
Serial.println(sizeROM);
newljprosize = sizeROM.toInt() + ljprolo;
if (newljprosize > ljprohi) {
Serial.println(F("SIZE NOT SUPPORTED"));
Serial.println(FS(FSTRING_EMPTY));
goto setrom;
}
}
Serial.print(F("ROM Size = "));
Serial.print(LJPRO[newljprosize]);
Serial.println(F("KB"));
#endif
EEPROM_writeAnything(8, newljprosize);
ljprosize = newljprosize;
}
void checkStatus_LJPRO()
{
EEPROM_readAnything(8, ljprosize);
if (ljprosize > ljprohi) {
ljprosize = 0; // default 2M
EEPROM_writeAnything(8, ljprosize);
}
#if (defined(ENABLE_OLED) || defined(ENABLE_LCD))
display_Clear();
println_Msg(F("LITTLE JAMMER PRO"));
println_Msg(FS(FSTRING_CURRENT_SETTINGS));
println_Msg(FS(FSTRING_EMPTY));
print_Msg(FS(FSTRING_ROM_SIZE));
print_Msg(LJPRO[ljprosize]);
println_Msg(F("MB"));
display_Update();
wait();
#else
Serial.print(FS(FSTRING_ROM_SIZE));
Serial.print(LJPRO[ljprosize]);
Serial.println(F("MB"));
Serial.println(FS(FSTRING_EMPTY));
#endif
}
#endif