From 2fdb5e638e023408697be8950e776e8f8ffd2590 Mon Sep 17 00:00:00 2001 From: FIX94 Date: Fri, 8 Apr 2016 23:10:13 +0200 Subject: added experimental save support (only tested with EEPROM) --- gba/source/libSave.c | 677 +++++++++++++++++++++++++++++++++++++++++++++++++++ gba/source/libSave.h | 27 ++ gba/source/main.c | 134 ++++++++-- 3 files changed, 818 insertions(+), 20 deletions(-) create mode 100644 gba/source/libSave.c create mode 100644 gba/source/libSave.h (limited to 'gba/source') diff --git a/gba/source/libSave.c b/gba/source/libSave.c new file mode 100644 index 0000000..1ae8162 --- /dev/null +++ b/gba/source/libSave.c @@ -0,0 +1,677 @@ +/* + libSave + Cartridge backup memory save routines. To use, call the required + routine with a pointer to an appropriately sized array of data to + be read from or written to the cartridge. + Data types are from wintermute's gba_types.h libgba library. +*/ + +#include +#include +#include +//--------------------------------------------------------------------------------- +#ifndef _gba_types_h_ +#define _gba_types_h_ +//--------------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------- +// Data types +//--------------------------------------------------------------------------------- +/** Unsigned 8 bit value + +*/ +typedef unsigned char u8; +/** Unsigned 16 bit value + +*/ +typedef unsigned short int u16; +/** Unsigned 32 bit value + +*/ +typedef unsigned int u32; + +/** signed 8 bit value + +*/ +typedef signed char s8; +/** Signed 16 bit value + +*/ +typedef signed short int s16; +/** Signed 32 bit value + +*/ +typedef signed int s32; + +/** Unsigned volatile 8 bit value + +*/ +typedef volatile u8 vu8; +/** Unsigned volatile 16 bit value + +*/ +typedef volatile u16 vu16; +/** Unsigned volatile 32 bit value + +*/ +typedef volatile u32 vu32; + +/** Unsigned volatile 8 bit value + +*/ +typedef volatile s8 vs8; +/** Signed volatile 16 bit value + +*/ +typedef volatile s16 vs16; +/** Signed volatile 32 bit value + +*/ +typedef volatile s32 vs32; + +#ifndef __cplusplus +/** C++ compatible bool for C + +*/ +typedef enum { false, true } bool; +#endif + +//--------------------------------------------------------------------------------- +#endif // data types +//--------------------------------------------------------------------------------- + + +#define EEPROM_ADDRESS (volatile u16*)0xDFFFF00 +#define SRAM_ADDRESS (volatile u16*)0x0E000000 +#define FLASH_1M_ADDRESS (volatile u16*)0x09FE0000 +#define REG_EEPROM (*(volatile u16*)0xDFFFF00) +#define REG_DM3SAD (*(volatile u32*)0x40000D4) +#define REG_DM3DAD (*(volatile u32*)0x40000D8) +#define REG_DM3CNT (*(volatile u32*)0x40000DC) + + +//----------------------------------------------------------------------- +// Common EEPROM Routines +//----------------------------------------------------------------------- + +void EEPROM_SendPacket( u16* packet, int size ) +{ + REG_DM3SAD = (u32)packet; + REG_DM3DAD = (u32)EEPROM_ADDRESS; + REG_DM3CNT = 0x80000000 + size; +} + +void EEPROM_ReceivePacket( u16* packet, int size ) +{ + REG_DM3SAD = (u32)EEPROM_ADDRESS; + REG_DM3DAD = (u32)packet; + REG_DM3CNT = 0x80000000 + size; +} + +//----------------------------------------------------------------------- +// Routines for 512B EEPROM +//----------------------------------------------------------------------- + +void EEPROM_Read_512B( volatile u8 offset, u8* dest ) // dest must point to 8 bytes +{ + u16 packet[68]; + u8* out_pos; + u16* in_pos; + u8 out_byte; + int byte, bit; + + memset( packet, 0, 68*2 ); + + // Read request + packet[0] = 1; + packet[1] = 1; + + // 6 bits eeprom address (MSB first) + packet[2] = (offset>>5)&1; + packet[3] = (offset>>4)&1; + packet[4] = (offset>>3)&1; + packet[5] = (offset>>2)&1; + packet[6] = (offset>>1)&1; + packet[7] = (offset)&1; + + // End of request + packet[8] = 0; + + // Do transfers + EEPROM_SendPacket( packet, 9 ); + memset( packet, 0, 68*2 ); + EEPROM_ReceivePacket( packet, 68 ); + + // Extract data + in_pos = &packet[4]; + out_pos = dest; + for( byte = 7; byte >= 0; --byte ) + { + out_byte = 0; + for( bit = 7; bit >= 0; --bit ) + { +// out_byte += (*in_pos++)<>5)&1; + packet[3] = (offset>>4)&1; + packet[4] = (offset>>3)&1; + packet[5] = (offset>>2)&1; + packet[6] = (offset>>1)&1; + packet[7] = (offset)&1; + + // Extract data + in_pos = source; + out_pos = &packet[8]; + for( byte = 7; byte >= 0; --byte ) + { + in_byte = *in_pos++; + for( bit = 7; bit >= 0; --bit ) + { + *out_pos++ = in_byte>>bit; + } + } + + // End of request + packet[72] = 0; + + // Do transfers + EEPROM_SendPacket( packet, 73 ); + + // Wait for EEPROM to finish (should timeout after 10 ms) + while( (REG_EEPROM & 1) == 0 ); +} + +//--------------------------------------------------------------------------------- +void GetSave_EEPROM_512B(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u8 x; + u32 sleep; + + // Set up waitstates for EEPROM access etc. + *(volatile unsigned short *)0x04000204 = 0x4317; + + for (x=0;x<64;++x){ + EEPROM_Read_512B(x,&data[x*8]); + for(sleep=0;sleep<512000;sleep++); + } +} + +//--------------------------------------------------------------------------------- +void PutSave_EEPROM_512B(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u8 x; + u32 sleep; + + // Set up waitstates for EEPROM access etc. + *(volatile unsigned short *)0x04000204 = 0x4317; + + for (x=0;x<64;x++){ + EEPROM_Write_512B(x,&data[x*8]); + for(sleep=0;sleep<512000;sleep++); + } +} +//----------------------------------------------------------------------- +// Routines for 8KB EEPROM +//----------------------------------------------------------------------- + +void EEPROM_Read_8KB( volatile u16 offset, u8* dest ) // dest must point to 8 bytes +{ + u16 packet[68]; + u8* out_pos; + u16* in_pos; + u8 out_byte; + int byte, bit; + + memset( packet, 0, 68*2 ); + + // Read request + packet[0] = 1; + packet[1] = 1; + + // 14 bits eeprom address (MSB first) + packet[2] = (offset>>13)&1; + packet[3] = (offset>>12)&1; + packet[4] = (offset>>11)&1; + packet[5] = (offset>>10)&1; + packet[6] = (offset>>9)&1; + packet[7] = (offset>>8)&1; + packet[8] = (offset>>7)&1; + packet[9] = (offset>>6)&1; + packet[10] = (offset>>5)&1; + packet[11] = (offset>>4)&1; + packet[12] = (offset>>3)&1; + packet[13] = (offset>>2)&1; + packet[14] = (offset>>1)&1; + packet[15] = (offset)&1; + + // End of request + packet[16] = 0; + + // Do transfers + EEPROM_SendPacket( packet, 17 ); + memset( packet, 0, 68*2 ); + EEPROM_ReceivePacket( packet, 68 ); + + // Extract data + in_pos = &packet[4]; + out_pos = dest; + for( byte = 7; byte >= 0; --byte ) + { + out_byte = 0; + for( bit = 7; bit >= 0; --bit ) + { +// out_byte += (*in_pos++)<>13)&1; + packet[3] = (offset>>12)&1; + packet[4] = (offset>>11)&1; + packet[5] = (offset>>10)&1; + packet[6] = (offset>>9)&1; + packet[7] = (offset>>8)&1; + packet[8] = (offset>>7)&1; + packet[9] = (offset>>6)&1; + packet[10] = (offset>>5)&1; + packet[11] = (offset>>4)&1; + packet[12] = (offset>>3)&1; + packet[13] = (offset>>2)&1; + packet[14] = (offset>>1)&1; + packet[15] = (offset)&1; + + // Extract data + in_pos = source; + out_pos = &packet[16]; + for( byte = 7; byte >= 0; --byte ) + { + in_byte = *in_pos++; + for( bit = 7; bit >= 0; --bit ) + { + *out_pos++ = (in_byte>>bit)&1; + } + } + + // End of request + packet[80] = 0; + + // Do transfers + EEPROM_SendPacket( packet, 81 ); + + // Wait for EEPROM to finish (should timeout after 10 ms) + while( (REG_EEPROM & 1) == 0 ); +} + +//--------------------------------------------------------------------------------- +void GetSave_EEPROM_8KB(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u16 x; + u32 sleep; + + // Set up waitstates for EEPROM access etc. + *(volatile unsigned short *)0x04000204 = 0x4317; + + for (x=0;x<1024;x++){ + EEPROM_Read_8KB(x,&data[x*8]); + for(sleep=0;sleep<512000;sleep++); + } + +} + +//--------------------------------------------------------------------------------- +void PutSave_EEPROM_8KB(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u16 x; + u32 sleep; + + // Set up waitstates for EEPROM access etc. + *(volatile unsigned short *)0x04000204 = 0x4317; + + for (x=0;x<1024;x++){ + EEPROM_Write_8KB(x,&data[x*8]); + for(sleep=0;sleep<512000;sleep++); + } +} + +//--------------------------------------------------------------------------------- +void GetSave_SRAM_32KB(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u8 *sram= (u8*) 0x0E000000; + volatile u16 x; + + for (x = 0; x < 0x8000; ++x) + { + data[x] = sram[x]; + } + +} + +//--------------------------------------------------------------------------------- +void PutSave_SRAM_32KB(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u8 *sram= (u8*) 0x0E000000; + volatile u16 x; + + for (x = 0; x < 0x8000; ++x) + { + sram[x] = data[x]; + } +} + +//--------------------------------------------------------------------------------- +void GetSave_FLASH_64KB(u8* data) +//--------------------------------------------------------------------------------- +{ + volatile u8 *sram= (u8*) 0x0E000000; + volatile u32 x; + + for (x = 0; x < 0x10000; ++x) + { + data[x] = sram[x]; + } +} + +//--------------------------------------------------------------------------------- +void PutSave_FLASH_64KB(u8* foo) +//--------------------------------------------------------------------------------- +{ + volatile u8 *fctrl0 = (u8*) 0xE005555; + volatile u8 *fctrl1 = (u8*) 0xE002AAA; + volatile u8 *fctrl2 = (u8*) 0xE000000; + + //init flash + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0x90; + *fctrl2 = 0xF0; + + //erase chip + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0x80; + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0x10; + + //wait for erase done + u8 val1; + u8 val2; + val1 = *fctrl2; + val2 = *fctrl2; + while (val1 != val2) { + val1 = *fctrl2; + val2 = *fctrl2; + } + val1 = *fctrl2; + val2 = *fctrl2; + while (val1 != val2) { + val1 = *fctrl2; + val2 = *fctrl2; + } + + volatile u8 *data = fctrl2; + u32 i; + //write data + for (i=0; i<65536; i++) { + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0xA0; + data [i] = foo [ i ]; + val1 = data [ i ]; + val2 = data [ i ]; + + while (val1 != val2) { + val1 = data [ i ]; + val2 = data [ i ]; + } + val1 = data [ i ]; + val2 = data [ i ]; + while (val1 != val2) { + val1 = data [ i ]; + val2 = data [ i ]; + } + val1 = data [ i ]; + val2 = data [ i ]; + while (val1 != val2) { + val1 = data [ i ]; + val2 = data [ i ]; + } + } +} + +//--------------------------------------------------------------------------------- +void GetSave_FLASH_128KB(u8* data) +//--------------------------------------------------------------------------------- +{ + const u32 size = 0x10000; + + volatile u8 *fctrl0 = (u8*) 0xE005555; + volatile u8 *fctrl1 = (u8*) 0xE002AAA; + volatile u8 *fctrl2 = (u8*) 0xE000000; + volatile u32 i; + volatile u8 *sram= (u8*) 0x0E000000; + + //init flash + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0x90; + *fctrl2 = 0xF0; + + // read first bank + *fctrl0 = 0xAA; + *fctrl1 = 0x55; + *fctrl0 = 0xB0; + *fctrl2 = 0x00; + + for (i=0; i=0;x--){ + switch (pak[x]) { + case 0x53414C46: + if (pak[x+1] == 0x5F4D3148){ + return 0x20000; // FLASH_128KB + } else if ((pak[x+1] & 0x0000FFFF) == 0x00005F48){ + return 0x10000; // FLASH_64KB + } else if (pak[x+1] == 0x32313548){ + return 0x10000; // FLASH_64KB + } + break; + case 0x52504545: + if ((pak[x+1] & 0x00FFFFFF) == 0x005F4D4F){ + GetSave_EEPROM_8KB(data); + if (memcmp(data,data+0x200,8) != 0 || + memcmp(data,data+0x400,8) != 0 || + memcmp(data,data+0x600,8) != 0){ + return 0x2000; //EEPROM_8KB + } + return 0x200; // EEPROM_512B + } + break; + case 0x4D415253: + if ((pak[x+1] & 0x000000FF) == 0x0000005F){ + return 0x8000; // SRAM_32KB + } + } + } + return 0; +} + diff --git a/gba/source/libSave.h b/gba/source/libSave.h new file mode 100644 index 0000000..5ecf822 --- /dev/null +++ b/gba/source/libSave.h @@ -0,0 +1,27 @@ + + +//--------------------------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif +//--------------------------------------------------------------------------------- + +void GetSave_EEPROM_512B(u8* data); +void PutSave_EEPROM_512B(u8* data); +void GetSave_EEPROM_8KB(u8* data); +void PutSave_EEPROM_8KB(u8* data); +void GetSave_SRAM_32KB(u8* data); +void PutSave_SRAM_32KB(u8* data); +void GetSave_FLASH_64KB(u8* data); +void PutSave_FLASH_64KB(u8* foo); +void GetSave_FLASH_128KB(u8* data); +void PutSave_FLASH_128KB(u8* foo); + +u32 SaveSize(u8* data, s32 gamesize); + + +//--------------------------------------------------------------------------------- +#ifdef __cplusplus +} // extern "C" +#endif +//--------------------------------------------------------------------------------- diff --git a/gba/source/main.c b/gba/source/main.c index 9d05189..567165a 100644 --- a/gba/source/main.c +++ b/gba/source/main.c @@ -7,12 +7,15 @@ #include #include #include +#include "libSave.h" -u32 getGameSize(void) +u8 save_data[0x20000] __attribute__ ((section (".sbss"))); + +s32 getGameSize(void) { if(*(vu32*)(0x08000004) != 0x51AEFF24) - return 0; - u32 i; + return -1; + s32 i; for(i = (1<<20); i < (1<<25); i<<=1) { vu16 *rompos = (vu16*)(0x08000000+i); @@ -30,6 +33,8 @@ u32 getGameSize(void) } return i; } +#define JOY_WRITE 2 +#define JOY_READ 4 //--------------------------------------------------------------------------------- // Program entry point //--------------------------------------------------------------------------------- @@ -49,43 +54,132 @@ int main(void) { u32 i; iprintf("\x1b[9;10HROM Dumper\n"); iprintf("\x1b[10;5HPlease look at the TV\n"); - REG_HS_CTRL |= 6; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + u32 prevIrqMask = REG_IME; while (1) { - if((REG_HS_CTRL&4)) + if((REG_HS_CTRL&JOY_READ)) { - REG_HS_CTRL |= 4; - u32 gamesize = getGameSize(); + irqDisable(IRQ_VBLANK); + REG_IME = 0; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + s32 gamesize = getGameSize(); + u32 savesize = SaveSize(save_data,gamesize); REG_JOYTR = gamesize; - while((REG_HS_CTRL&4) == 0) ; - REG_HS_CTRL |= 4; - if(gamesize == 0) + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + REG_JOYTR = savesize; + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + if(gamesize == -1) { REG_JOYTR = 0; + REG_IME = prevIrqMask; + irqEnable(IRQ_VBLANK); continue; //nothing to read } //game in, send header for(i = 0; i < 0xC0; i+=4) { REG_JOYTR = *(vu32*)(0x08000000+i); - while((REG_HS_CTRL&4) == 0) ; - REG_HS_CTRL |= 4; + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); } + REG_JOYTR = 0; //wait for other side to choose - while((REG_HS_CTRL&2) == 0) ; - REG_HS_CTRL |= 2; - if(REG_JOYRE == 0) + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + u32 choseval = REG_JOYRE; + if(choseval == 0) { REG_JOYTR = 0; + REG_IME = prevIrqMask; + irqEnable(IRQ_VBLANK); continue; //nothing to read } - //dump the game - for(i = 0; i < gamesize; i+=4) + else if(choseval == 1) { - REG_JOYTR = *(vu32*)(0x08000000+i); - while((REG_HS_CTRL&4) == 0) ; - REG_HS_CTRL |= 4; + //dump the game + for(i = 0; i < gamesize; i+=4) + { + REG_JOYTR = *(vu32*)(0x08000000+i); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + } + } + else if(choseval == 2) + { + //backup save + switch (savesize){ + case 0x200: + GetSave_EEPROM_512B(save_data); + break; + case 0x2000: + GetSave_EEPROM_8KB(save_data); + break; + case 0x8000: + GetSave_SRAM_32KB(save_data); + break; + case 0x10000: + GetSave_FLASH_64KB(save_data); + break; + case 0x20000: + GetSave_FLASH_128KB(save_data); + break; + default: + break; + } + REG_JOYTR = savesize; + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + //send the save + for(i = 0; i < savesize; i+=4) + { + REG_JOYTR = *(vu32*)(save_data+i); + while((REG_HS_CTRL&JOY_READ) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + } + } + else if(choseval == 3) + { + REG_JOYTR = savesize; + //receive the save + for(i = 0; i < savesize; i+=4) + { + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); + *(vu32*)(save_data+i) = REG_JOYRE; + } + //write it + switch (savesize){ + case 0x200: + PutSave_EEPROM_512B(save_data); + break; + case 0x2000: + PutSave_EEPROM_8KB(save_data); + break; + case 0x8000: + PutSave_SRAM_32KB(save_data); + break; + case 0x10000: + PutSave_FLASH_64KB(save_data); + break; + case 0x20000: + PutSave_FLASH_128KB(save_data); + break; + default: + break; + } + REG_JOYTR = 0; + //wait for a cmd receive for safety + while((REG_HS_CTRL&JOY_WRITE) == 0) ; + REG_HS_CTRL |= (JOY_WRITE|JOY_READ); } REG_JOYTR = 0; + REG_IME = prevIrqMask; + irqEnable(IRQ_VBLANK); } Halt(); } -- cgit 1.4.1