diff --git a/Makefile b/Makefile index e61a45d..04d6c58 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ GRAPHICS := graphics ARCH := -mthumb -mthumb-interwork CFLAGS := -g -Wall -O2\ - -mcpu=arm7tdmi -mtune=arm7tdmi\ + -mcpu=arm7tdmi -mtune=arm7tdmi -masm-syntax-unified\ $(ARCH) CFLAGS += $(INCLUDE) @@ -52,7 +52,7 @@ LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lmm -ltonc +LIBS := -lmm -ltonc -lgba #--------------------------------------------------------------------------------- @@ -60,7 +60,7 @@ LIBS := -lmm -ltonc # include and lib. # the LIBGBA path should remain in this list if you want to use maxmod #--------------------------------------------------------------------------------- -LIBDIRS := $(LIBGBA) $(LIBTONC) +LIBDIRS := $(LIBGBA) $(LIBTONC) $(LIBGBA) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional diff --git a/include/base_include.h b/include/base_include.h new file mode 100644 index 0000000..44b1ddb --- /dev/null +++ b/include/base_include.h @@ -0,0 +1,37 @@ +//Created by Lorenzooone https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X + +#ifndef BASE_INCLUDE__ +#define BASE_INCLUDE__ + +// GBA defines and all +#include +#include "useful_qualifiers.h" +#define SCANLINES 0xE4 +#define ROM 0x8000000 +ALWAYS_INLINE MAX_OPTIMIZE void __set_next_vcount_interrupt_gba(int scanline) { + REG_DISPSTAT = (REG_DISPSTAT & 0xFF) | (scanline<<8); +} +ALWAYS_INLINE MAX_OPTIMIZE int __get_next_vcount_interrupt(void) { + u16 reg_val = REG_DISPSTAT; + return reg_val >> 8; +} +#define __set_next_vcount_interrupt(x) __set_next_vcount_interrupt_gba(x) +#define SCANLINE_IRQ_BIT LCDC_VCNT +#define REG_WAITCNT *(vu16*)(REG_BASE + 0x204) // Wait state Control +#define SRAM_READING_VALID_WAITCYCLES 3 +#define SRAM_ACCESS_CYCLES 9 +#define NON_SRAM_MASK 0xFFFC +#define BASE_WAITCNT_VAL 0x4314 +#define OVRAM_START ((uintptr_t)OBJ_BASE_ADR) +#define TILE_1D_MAP 0 +#define ACTIVATE_SCREEN_HW 0 +#define EWRAM_SIZE 0x0003FF40 +#define DIV_SWI_VAL "0x06" +#define VRAM_0 VRAM +#define HAS_SIO +#define CLOCK_SPEED 16777216 +#define SAME_ON_BOTH_SCREENS 0 +#define CONSOLE_LETTER 'G' +#define VBLANK_SCANLINES SCREEN_HEIGHT + +#endif diff --git a/include/debug_mode.h b/include/debug_mode.h index 836c247..e490899 100644 --- a/include/debug_mode.h +++ b/include/debug_mode.h @@ -1,8 +1,10 @@ +//Created by Lorenzooone https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X + #ifndef DEBUG_MODE_H #define DEBUG_MODE_H #define DEBUG_MODE true -#define IGNORE_GAME_PAK false +#define IGNORE_GAME_PAK true #define IGNORE_LINK_CABLE true #define DEBUG_GAME SAPPHIRE_ID #define DEBUG_VERS VERS_1_0 diff --git a/include/delays.h b/include/delays.h new file mode 100644 index 0000000..6fd54a9 --- /dev/null +++ b/include/delays.h @@ -0,0 +1,12 @@ +//Created by Lorenzooone https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X + +#ifndef DELAYS__ +#define DELAYS__ + +#define CLOCK_CYCLES_PER_MS(x) ((((x) * ((uint64_t)CLOCK_SPEED)) + 999) / 1000) +#define CLOCK_CYCLES_PER_MUS(x) ((((x) * ((uint64_t)CLOCK_SPEED)) + 999999) / 1000000) + +void delay_cycles(u32); +void delay_cycles_until(u32, vu8*, u8, u8); + +#endif diff --git a/include/save.h b/include/save.h new file mode 100644 index 0000000..db8b1d0 --- /dev/null +++ b/include/save.h @@ -0,0 +1,22 @@ +//Created by Lorenzooone https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X + +#ifndef SAVE__ +#define SAVE__ + +#include +#define SECTOR_SIZE_BITS 12 +#define SECTOR_SIZE (1< +#include "useful_qualifiers.h" +#include "timing_basic.h" +#include "delays.h" + +#define NUM_CYCLES_PER_ITER 4 +#define NUM_CYCLES_PER_ITER_CHECK 8 + +NO_INLINE IWRAM_CODE void delay_cycles(u32 num_cycles) { + num_cycles = (num_cycles + NUM_CYCLES_PER_ITER - 1 + (NUM_CYCLES_PER_ITER / 2)) / NUM_CYCLES_PER_ITER; + __asm__ volatile ( + "L1%=: subs %[wait], #1;" + "bne L1%=;" + : [wait] "+l" (num_cycles) + ); +} + +NO_INLINE IWRAM_CODE void delay_cycles_until(u32 num_cycles, vu8* data, u8 wanted, u8 cycles_per_load) { + num_cycles = (num_cycles + (NUM_CYCLES_PER_ITER_CHECK + cycles_per_load) - 1 + ((NUM_CYCLES_PER_ITER_CHECK + cycles_per_load) / 2)) / (NUM_CYCLES_PER_ITER_CHECK + cycles_per_load); + __asm__ volatile ( + "L1%=: ldrb r3, [%[data], #0];" + "cmp r3, %[wanted];" + "beq L2%=;" + "subs %[wait], #1;" + "bne L1%=;" + "L2%=:;" + : [wait] "+l" (num_cycles) : [data]"l"(data), [wanted]"l"(wanted) : "r3" + ); +} diff --git a/source/main.cpp b/source/main.cpp index a69f527..e88c03b 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -29,6 +29,7 @@ #include "mystery_gift_injector.h" #include "mystery_gift_builder.h" #include "rom_data.h" +#include "save.h" /* TODO: @@ -211,7 +212,7 @@ int main(void) } // Initalize memory and save data after loading the game - flash_init(FLASH_SIZE_128KB); + init_bank(); initalize_memory_locations(); load_custom_save_data(); diff --git a/source/save.cpp b/source/save.cpp new file mode 100644 index 0000000..4fcb2d5 --- /dev/null +++ b/source/save.cpp @@ -0,0 +1,228 @@ +//Created by Lorenzooone https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X + +#include "base_include.h" +#include "save.h" +#include +#include "useful_qualifiers.h" +#include "timing_basic.h" +#include "delays.h" + +#define IS_FLASH 1 +#define SAVE_POS SRAM + +#define BASE_FLASH_CMD *((vu8*)(SAVE_POS+0x5555)) = 0xAA; *((vu8*)(SAVE_POS+0x2AAA)) = 0x55; +#define FLASH_WRITE_CMD BASE_FLASH_CMD *((vu8*)(SAVE_POS+0x5555)) = 0xA0; +#define FLASH_BANK_CMD BASE_FLASH_CMD *((vu8*)(SAVE_POS+0x5555)) = 0xB0; +#define FLASH_ERASE_SECTOR_BASE_CMD BASE_FLASH_CMD *((vu8*)(SAVE_POS+0x5555)) = 0x80; BASE_FLASH_CMD +#define FLASH_TERM_CMD *((vu8*)(SAVE_POS+0x5555)) = 0xF0; +#define FLASH_ENTER_MAN_CMD BASE_FLASH_CMD *((vu8*)(SAVE_POS+0x5555)) = 0x90; +#define FLASH_EXIT_MAN_CMD BASE_FLASH_CMD FLASH_TERM_CMD FLASH_TERM_CMD +#define ID_TIMEOUT_CYCLES CLOCK_CYCLES_PER_MS(28) +#define TIMEOUT_CYCLES CLOCK_CYCLES_PER_MS(10) +#define ERASE_TIMEOUT_CYCLES CLOCK_CYCLES_PER_MS(2000) +#define ERASED_BYTE 0xFF +#define BANK_SIZE 0x10000 +#define NUM_BANKS 2 + +#define MACRONIX_MAN_ID 0xC2 +#define SANYO_MAN_ID 0x62 +#define DEFAULT_MAN_ID 0 +#define ERASE_SECTOR_FINAL_CMD 0x30 + +#if IS_FLASH +#define BANK_LIMIT BANK_SIZE +#else +#define BANK_LIMIT (NUM_BANKS * BANK_SIZE) +#endif + +static uintptr_t bank_check(uintptr_t); +static size_t sanitize_save_size(uintptr_t, size_t); +static u8 read_direct_single_byte_save(uintptr_t); +static void write_direct_single_byte_save(uintptr_t, u8); + +u8 current_bank; +u8 is_macronix; + +IWRAM_CODE void init_bank() { + REG_WAITCNT &= NON_SRAM_MASK; + REG_WAITCNT |= SRAM_READING_VALID_WAITCYCLES; + current_bank = NUM_BANKS; + is_macronix = 0; + #if IS_FLASH + FLASH_ENTER_MAN_CMD + delay_cycles(ID_TIMEOUT_CYCLES); + u8 man_id = *((vu8*)SAVE_POS); + if((man_id == MACRONIX_MAN_ID) || (man_id == SANYO_MAN_ID) || (man_id == DEFAULT_MAN_ID)) + is_macronix = 1; + FLASH_EXIT_MAN_CMD + delay_cycles(ID_TIMEOUT_CYCLES); + #endif +} + +IWRAM_CODE uintptr_t bank_check(uintptr_t address) { + address %= (NUM_BANKS * BANK_SIZE); + #if IS_FLASH + u8 bank = address / BANK_SIZE; + address %= BANK_SIZE; + + if(bank != current_bank) { + FLASH_BANK_CMD + *((vu8*)SAVE_POS) = bank; + current_bank = bank; + } + #endif + return address; +} + +IWRAM_CODE void erase_sector(uintptr_t address) { + address = bank_check(address); + address >>= SECTOR_SIZE_BITS; + address <<= SECTOR_SIZE_BITS; + u8 failed = 1; + vu8* save_data = (vu8*)SAVE_POS; + for(size_t i = 0; failed && (i < 3); i++) { + #if IS_FLASH + FLASH_ERASE_SECTOR_BASE_CMD + save_data[address] = ERASE_SECTOR_FINAL_CMD; + #else + for(size_t j = 0; j < SECTOR_SIZE; j++) + save_data[address+(SECTOR_SIZE-1)-j] = ERASED_BYTE; + #endif + + delay_cycles_until(ERASE_TIMEOUT_CYCLES, &save_data[address], 0xFF, SRAM_ACCESS_CYCLES); + + failed = 0; + for(size_t j = 0; j < SECTOR_SIZE; j++) + if(read_direct_single_byte_save(address+j) != ERASED_BYTE) { + failed = 1; + break; + } + if(is_macronix && failed) + FLASH_TERM_CMD + } +} + +IWRAM_CODE u8 read_direct_single_byte_save(uintptr_t address) { + return *(vu8*)(SAVE_POS+address); +} + +IWRAM_CODE void write_direct_single_byte_save(uintptr_t address, u8 data) { + vu8* save_data = (vu8*)SAVE_POS; + for(int i = 0; (i < 3) && save_data[address] != data; i++) { + #if IS_FLASH + FLASH_WRITE_CMD + #endif + save_data[address] = data; + delay_cycles_until(TIMEOUT_CYCLES, &save_data[address], data, SRAM_ACCESS_CYCLES); + if(is_macronix && (save_data[address] != data)) + FLASH_TERM_CMD + } +} + +IWRAM_CODE u8 read_byte_save(uintptr_t address) { + address = bank_check(address); + return read_direct_single_byte_save(address); +} + +IWRAM_CODE u16 read_short_save(uintptr_t address) { + u16 data_out = 0; + copy_save_to_ram(address, (u8*)&data_out, sizeof(u16)); + return data_out; +} + +IWRAM_CODE u32 read_int_save(uintptr_t address) { + u32 data_out = 0; + copy_save_to_ram(address, (u8*)&data_out, sizeof(u32)); + return data_out; +} + +IWRAM_CODE void write_byte_save(uintptr_t address, u8 data) { + address = bank_check(address); + write_direct_single_byte_save(address, data); +} + +IWRAM_CODE void write_short_save(uintptr_t address, u16 data) { + u16 data_in = data; + copy_ram_to_save((u8*)&data_in, address, sizeof(u16)); +} + +IWRAM_CODE void write_int_save(uintptr_t address, u32 data) { + u32 data_in = data; + copy_ram_to_save((u8*)&data_in, address, sizeof(u32)); +} + +IWRAM_CODE size_t sanitize_save_size(uintptr_t address, size_t size) { + address %= NUM_BANKS * BANK_SIZE; + if((address + size) > (NUM_BANKS * BANK_SIZE)) + size = (NUM_BANKS * BANK_SIZE) - address; + return size; +} + +IWRAM_CODE void copy_save_to_ram(uintptr_t address, u8* destination, size_t size) { + // Sanitize size + size = sanitize_save_size(address, size); + + address = bank_check(address); + size_t num_banks = (address + size + BANK_SIZE - 1) / BANK_SIZE; + for(size_t n = 0; n < num_banks; n++) { + size_t inner_size = size; + if(n != (num_banks - 1)) + inner_size = BANK_SIZE - address; + + for(size_t i = 0; i < inner_size; i++) + destination[i] = read_direct_single_byte_save(address + i); + + if(n != (num_banks - 1)) { + address = bank_check(address + inner_size); + destination += inner_size; + size -= inner_size; + } + } +} + +IWRAM_CODE void copy_ram_to_save(u8* base_address, uintptr_t save_address, size_t size) { + // Sanitize size + size = sanitize_save_size(save_address, size); + + save_address = bank_check(save_address); + size_t num_banks = (save_address + size + BANK_SIZE - 1) / BANK_SIZE; + for(size_t n = 0; n < num_banks; n++) { + size_t inner_size = size; + if(n != (num_banks - 1)) + inner_size = BANK_SIZE - save_address; + + for(size_t i = 0; i < inner_size; i++) + write_direct_single_byte_save(save_address + i, base_address[i]); + + if(n != (num_banks - 1)) { + save_address = bank_check(save_address + inner_size); + base_address += inner_size; + size -= inner_size; + } + } +} + +IWRAM_CODE u8 is_save_correct(u8* base_address, uintptr_t save_address, size_t size) { + // Sanitize size + size = sanitize_save_size(save_address, size); + + save_address = bank_check(save_address); + size_t num_banks = (save_address + size + BANK_SIZE - 1) / BANK_SIZE; + for(size_t n = 0; n < num_banks; n++) { + size_t inner_size = size; + if(n != (num_banks - 1)) + inner_size = BANK_SIZE - save_address; + + for(size_t i = 0; i < inner_size; i++) + if(read_direct_single_byte_save(save_address + i) != base_address[i]) + return 0; + + if(n != (num_banks - 1)) { + save_address = bank_check(save_address + inner_size); + base_address += inner_size; + size -= inner_size; + } + } + + return 1; +}