diff --git a/src/apu.h b/src/apu.h index 5e0f9da..3da9107 100644 --- a/src/apu.h +++ b/src/apu.h @@ -16,9 +16,7 @@ #define APU_ERR ERR_LOG -#define nes_apu_frame_period = 7450; - -typedef enum { +typedef enum __attribute__ ((__packed__)) { apu_Status_Square_0 = 0b00000001, apu_Status_Square_1 = 0b00000010, apu_Status_Triangle = 0b00000100, @@ -85,9 +83,15 @@ typedef enum { apu_Channel_Start = 0b00000010, } nes_apu_Channel_Flag; +#define nes_apu_reg_base (0x4000U) +#define nes_apu_chan_count (5U) +#define nes_apu_chan_size (4U) +#define nes_apu_reg_status (0x4015U) +#define nes_apu_reg_frame (0x4017U) + typedef struct nes_apu_Channel_t { uint8_t reg[4]; - void(*write)(struct nes_apu_Channel_t*, int reg, uint8_t); + int gain; int length; // Active counter int delay; // Cycles until next state change @@ -125,13 +129,11 @@ typedef struct nes_apu_Channel_t { uint16_t addr; }; }; + + void(*write)(struct nes_apu_Channel_t*, int reg, uint8_t); } nes_apu_Channel; typedef struct { - nes_apu_Channel channels[5]; - uint8_t (*mem_read)(void*, uint16_t); - void* arg_mem; - struct blip_t* blip; nes_apu_Status status; uint8_t frame_reg; int frame; @@ -139,6 +141,12 @@ typedef struct { int frame_period; int time; int frame_time_elapsed; + + nes_apu_Channel channels[nes_apu_chan_count]; + + uint8_t (*mem_read)(void*, uint16_t); + void* arg_mem; + struct blip_t* blip; } nes_apu; typedef enum { @@ -146,12 +154,6 @@ typedef enum { apu_Result_IRQ, } nes_apu_Result; -#define nes_apu_reg_base (0x4000U) -#define nes_apu_chan_count (5U) -#define nes_apu_chan_size (4U) -#define nes_apu_reg_status (0x4015U) -#define nes_apu_reg_frame (0x4017U) - int nes_apu_init(nes_apu*, int clock, int frequency, uint8_t(*)(void*, uint16_t), void*); void nes_apu_done(nes_apu*); diff --git a/src/cart.c b/src/cart.c index 043f937..4f912fa 100644 --- a/src/cart.c +++ b/src/cart.c @@ -90,7 +90,7 @@ int nes_cart_init_file(nes_cart* cart, FILE* file) { // Map file if (0 == status) { - mem = map_file(file, size); + mem = map_file(file, size, Filemap_Mode_Read); if (NULL == mem) { INES_ERR("Failed to map file (%d bytes)", size); status = -1; diff --git a/src/filemap.h b/src/filemap.h index 2a27222..e1349ef 100644 --- a/src/filemap.h +++ b/src/filemap.h @@ -2,7 +2,13 @@ #define NESE_FILEMAP_H_ -void* map_file(FILE* file, int size); +typedef enum { + Filemap_Mode_Read = 0, + Filemap_Mode_Write, +} Filemap_Mode; + + +void* map_file(FILE* file, int size, Filemap_Mode); void unmap_file(void* mem, int size); diff --git a/src/input.h b/src/input.h index 6b331a8..e954f34 100644 --- a/src/input.h +++ b/src/input.h @@ -12,6 +12,8 @@ typedef enum { input_Result_OK = 0, input_Result_Quit = 1, input_Result_Reset = 2, + input_Result_Save = 3, + input_Result_Load = 4, } nes_Input_Result; #define nes_controller_num_buttons (8U) diff --git a/src/linux/filemap.c b/src/linux/filemap.c index 7dd2e16..c88c31b 100644 --- a/src/linux/filemap.c +++ b/src/linux/filemap.c @@ -1,3 +1,4 @@ +#include #include #include @@ -5,9 +6,17 @@ #include "filemap.h" -void* map_file(FILE* file, int size) { - return mmap(NULL, size, PROT_READ, MAP_PRIVATE, - fileno(file), 0); +void* map_file(FILE* file, int size, Filemap_Mode map_mode) { + int prot = ( Filemap_Mode_Write == map_mode ? + PROT_WRITE : PROT_READ); + int flags = ( Filemap_Mode_Write == map_mode ? + MAP_SHARED : MAP_PRIVATE); + void* mem = mmap(NULL, size, prot, flags, fileno(file), 0); + if ((void*)-1 == mem) { + fprintf(stderr, "Failed to map file: %d\n", errno); + mem = NULL; + } + return mem; } void unmap_file(void* mem, int size) { diff --git a/src/map/cnrom.c b/src/map/cnrom.c index e9c63ba..d7ee707 100644 --- a/src/map/cnrom.c +++ b/src/map/cnrom.c @@ -1,4 +1,5 @@ #include +#include #include "map.h" @@ -9,15 +10,23 @@ typedef struct { uint8_t* chr_rom; int chr_rom_banks; uint8_t mirror; + + uint32_t bank; uint8_t vram[nes_vram_page_size * 2]; uint8_t* chr_bank; } cnrom_mapper; +static void cnrom_set_bank(cnrom_mapper* map, uint8_t bank) { + map->bank = (bank % map->chr_rom_banks); + map->chr_bank = &map->chr_rom[ + map->bank * (nes_chr_page_size * 2) + ]; +} static void cnrom_reset(nes_mapper* nes_map) { cnrom_mapper* map = (cnrom_mapper*)nes_map->data; - map->chr_bank = 0; + cnrom_set_bank(map, 0); } static int cnrom_init(nes_mapper* nes_map, nes_cart* cart) { @@ -60,8 +69,7 @@ static void cnrom_write(nes_mapper* nes_map, uint16_t addr, uint8_t val) { cnrom_mapper* map = (cnrom_mapper*)nes_map->data; if (addr >= nes_mem_rom_start) { - map->chr_bank = &map->chr_rom[(val % map->chr_rom_banks) * - (nes_chr_page_size * 2)]; + cnrom_set_bank(map, val); } } @@ -84,6 +92,41 @@ static void cnrom_chr_write(nes_mapper* nes_map, // ROM only. } + +/* Save State */ + +int cnrom_state_size(const nes_mapper* nes_map) { + cnrom_mapper* map = (cnrom_mapper*)nes_map->data; + return (sizeof(map->bank) + sizeof(map->vram)); +} + +int cnrom_state_read(nes_mapper* nes_map, const void* data) { + cnrom_mapper* map = (cnrom_mapper*)nes_map->data; + const void* ptr = data; + + memcpy(&map->bank, ptr, sizeof(map->bank)); + ptr += sizeof(map->bank); + + memcpy(map->vram, ptr, sizeof(map->vram)); + ptr += sizeof(map->vram); + + return (ptr - data); +} + +int cnrom_state_write(const nes_mapper* nes_map, void* data) { + cnrom_mapper* map = (cnrom_mapper*)nes_map->data; + void* ptr = data; + + memcpy(ptr, &map->bank, sizeof(map->bank)); + ptr += sizeof(map->bank); + + memcpy(ptr, map->vram, sizeof(map->vram)); + ptr += sizeof(map->vram); + + return (ptr - data); +} + + nes_mapper mapper_cnrom = { .name = "CNROM", .init = cnrom_init, @@ -94,4 +137,8 @@ nes_mapper mapper_cnrom = { .chr_addr = cnrom_chr_addr, .vram_addr = cnrom_vram_addr, .chr_write = cnrom_chr_write, + + .state_size = cnrom_state_size, + .state_read = cnrom_state_read, + .state_write = cnrom_state_write, }; diff --git a/src/map/mmc1.c b/src/map/mmc1.c index 8c9ee14..302a9f7 100644 --- a/src/map/mmc1.c +++ b/src/map/mmc1.c @@ -1,4 +1,5 @@ #include +#include #include "map.h" @@ -11,6 +12,12 @@ typedef struct { uint8_t battery; + uint8_t* chr_bank[2]; + uint8_t* vram_bank[4]; + uint8_t* prg_bank[2]; + uint8_t* chr_ram; + uint8_t* chr; + uint8_t reg_shift; uint8_t reg_n_shift; uint8_t reg_control; @@ -18,13 +25,6 @@ typedef struct { uint8_t reg_chr_1; uint8_t reg_prg; - uint8_t* chr_bank[2]; - uint8_t* vram_bank[4]; - uint8_t* prg_bank[2]; - - uint8_t* chr_ram; - uint8_t* chr; - uint8_t vram[2][nes_vram_page_size]; uint8_t wram[nes_mem_wram_size]; } mmc1_mapper; @@ -235,6 +235,30 @@ static int mmc1_sram_size(nes_mapper* nes_map) { } +/* Save State */ + +int mmc1_state_size(const nes_mapper* nes_map) { + mmc1_mapper* map = (mmc1_mapper*)nes_map->data; + return ( (void*)map + + sizeof(*map) - + (void*)&(map->reg_shift)); +} + +int mmc1_state_read(nes_mapper* nes_map, const void* data) { + mmc1_mapper* map = (mmc1_mapper*)nes_map->data; + int size = mmc1_state_size(nes_map); + memcpy(&(map->reg_shift), data, size); + return size; +} + +int mmc1_state_write(const nes_mapper* nes_map, void* data) { + mmc1_mapper* map = (mmc1_mapper*)nes_map->data; + int size = mmc1_state_size(nes_map); + memcpy(data, &(map->reg_shift), size); + return size; +} + + nes_mapper mapper_mmc1 = { .name = "MMC1", .init = mmc1_init, @@ -248,4 +272,8 @@ nes_mapper mapper_mmc1 = { .sram_size = mmc1_sram_size, .sram = mmc1_sram, + + .state_size = mmc1_state_size, + .state_read = mmc1_state_read, + .state_write = mmc1_state_write, }; diff --git a/src/map/mmc3.c b/src/map/mmc3.c index 397276d..be5a34d 100644 --- a/src/map/mmc3.c +++ b/src/map/mmc3.c @@ -1,4 +1,5 @@ #include +#include #include "map.h" @@ -29,7 +30,7 @@ typedef struct { uint8_t* vram_bank[4]; uint8_t r[8]; - mmc3_Flag flags; + uint8_t flags; uint8_t bank_select; uint8_t irq_count; uint8_t irq_latch; @@ -106,7 +107,7 @@ static inline void mmc3_update_chr(mmc3_mapper* map, } static inline void mmc3_update_rom_mode(mmc3_mapper* map, int val) { - uint8_t delta = map->bank_select ^ val; + uint8_t delta = (map->bank_select ^ val); map->bank_select = val; @@ -119,7 +120,6 @@ static inline void mmc3_update_rom_mode(mmc3_mapper* map, int val) { mmc3_map_prg(map, 0, map->prg_rom_banks - 2); mmc3_map_prg(map, 2, map->r[6]); } - } if (delta & mmc3_Bank_Select_CHR) { @@ -344,6 +344,30 @@ static int mmc3_sram_size(nes_mapper* nes_map) { } +/* Save State */ + +int mmc3_state_size(const nes_mapper* nes_map) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + return ( (map->wram - map->r) + + sizeof(map->wram) + + sizeof(map->vram)); +} + +int mmc3_state_read(nes_mapper* nes_map, const void* data) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + int size = mmc3_state_size(nes_map); + memcpy(map->r, data, size); + return size; +} + +int mmc3_state_write(const nes_mapper* nes_map, void* data) { + mmc3_mapper* map = (mmc3_mapper*)nes_map->data; + int size = mmc3_state_size(nes_map); + memcpy(data, map->r, size); + return size; +} + + nes_mapper mapper_mmc3 = { .name = "MMC3", .init = mmc3_init, @@ -358,4 +382,8 @@ nes_mapper mapper_mmc3 = { .sram_size = mmc3_sram_size, .sram = mmc3_sram, + + .state_size = mmc3_state_size, + .state_read = mmc3_state_read, + .state_write = mmc3_state_write, }; diff --git a/src/map/nrom.c b/src/map/nrom.c index d1aba8f..526d9ca 100644 --- a/src/map/nrom.c +++ b/src/map/nrom.c @@ -1,4 +1,5 @@ #include +#include #include "map.h" @@ -97,6 +98,29 @@ static void nrom_chr_write(nes_mapper* nes_map, // printf("CHR ROM Write: $%04x < %02x\n", addr, val); } + +/* Save State */ + +int nrom_state_size(const nes_mapper* nes_map) { + nrom_mapper* map = (nrom_mapper*)nes_map->data; + return (sizeof(map->vram) + sizeof(map->wram)); +} + +int nrom_state_read(nes_mapper* nes_map, const void* data) { + nrom_mapper* map = (nrom_mapper*)nes_map->data; + memcpy(map->vram, data, sizeof(map->vram)); + memcpy(map->wram, data + sizeof(map->vram), sizeof(map->wram)); + return (sizeof(map->vram) + sizeof(map->wram)); +} + +int nrom_state_write(const nes_mapper* nes_map, void* data) { + nrom_mapper* map = (nrom_mapper*)nes_map->data; + memcpy(data, map->vram, sizeof(map->vram)); + memcpy(data + sizeof(map->vram), map->wram, sizeof(map->wram)); + return (sizeof(map->vram) + sizeof(map->wram)); +} + + nes_mapper mapper_nrom = { .name = "NROM", .init = nrom_init, @@ -107,4 +131,8 @@ nes_mapper mapper_nrom = { .chr_addr = nrom_chr_addr, .vram_addr = nrom_vram_addr, .chr_write = nrom_chr_write, + + .state_size = nrom_state_size, + .state_read = nrom_state_read, + .state_write = nrom_state_write, }; diff --git a/src/map/uxrom.c b/src/map/uxrom.c index 3986a34..45c7de1 100644 --- a/src/map/uxrom.c +++ b/src/map/uxrom.c @@ -1,4 +1,5 @@ #include +#include #include "map.h" @@ -7,6 +8,8 @@ typedef struct { uint8_t* prg_rom; int prg_rom_banks; uint8_t mirror; + + uint8_t bank; uint8_t vram[nes_vram_page_size * 2]; uint8_t chr_ram[8 * 1024]; @@ -14,9 +17,16 @@ typedef struct { uint8_t* prg_bank_hi; } uxrom_mapper; +static void uxrom_set_bank(uxrom_mapper* map, uint8_t bank) { + map->bank = (bank % map->prg_rom_banks); + map->prg_bank_lo = &map->prg_rom[ + map->bank * nes_prg_rom_page_size + ]; +} static void uxrom_reset(nes_mapper* nes_map) { uxrom_mapper* map = (uxrom_mapper*)nes_map->data; + uxrom_set_bank(map, 0); map->prg_bank_lo = map->prg_rom; } @@ -58,10 +68,7 @@ static void uxrom_write(nes_mapper* nes_map, uint16_t addr, uint8_t val) { uxrom_mapper* map = (uxrom_mapper*)nes_map->data; if (addr >= nes_mem_rom_start) { - map->prg_bank_lo = &map->prg_rom[ - (val % map->prg_rom_banks) * - nes_prg_rom_page_size - ]; + uxrom_set_bank(map, val); } } @@ -84,6 +91,50 @@ static void uxrom_chr_write(nes_mapper* nes_map, ((uxrom_mapper*)nes_map->data)->chr_ram[addr] = val; } + +/* Save State */ + +int uxrom_state_size(const nes_mapper* nes_map) { + uxrom_mapper* map = (uxrom_mapper*)nes_map->data; + return ( sizeof(uint32_t) + + sizeof(map->vram) + + sizeof(map->chr_ram)); +} + +int uxrom_state_read(nes_mapper* nes_map, const void* data) { + uxrom_mapper* map = (uxrom_mapper*)nes_map->data; + const void* ptr = data; + + map->bank = *(uint32_t*)ptr; + ptr += sizeof(uint32_t); + + memcpy(map->vram, ptr, sizeof(map->vram)); + ptr += sizeof(map->vram); + + memcpy(map->chr_ram, ptr, sizeof(map->chr_ram)); + ptr += sizeof(map->chr_ram); + + return (ptr - data); +} + +int uxrom_state_write(const nes_mapper* nes_map, void* data) { + uxrom_mapper* map = (uxrom_mapper*)nes_map->data; + void* ptr = data; + + uint32_t bank = map->bank; + memcpy(ptr, &bank, sizeof(bank)); + ptr += sizeof(bank); + + memcpy(ptr, map->vram, sizeof(map->vram)); + ptr += sizeof(map->vram); + + memcpy(ptr, map->chr_ram, sizeof(map->chr_ram)); + ptr += sizeof(map->chr_ram); + + return (ptr - data); +} + + nes_mapper mapper_uxrom = { .name = "UxROM", .init = uxrom_init, @@ -94,4 +145,8 @@ nes_mapper mapper_uxrom = { .chr_addr = uxrom_chr_addr, .vram_addr = uxrom_vram_addr, .chr_write = uxrom_chr_write, + + .state_size = uxrom_state_size, + .state_read = uxrom_state_read, + .state_write = uxrom_state_write, }; diff --git a/src/mapper.h b/src/mapper.h index deef8e5..6d53c1e 100644 --- a/src/mapper.h +++ b/src/mapper.h @@ -34,8 +34,9 @@ typedef struct nes_mapper_t { void* (*sram)(struct nes_mapper_t*); int (*sram_size)(struct nes_mapper_t*); - void (*save)(struct nes_mapper_t**); - int (*save_size)(struct nes_mapper_t*); + int (*state_size)(const struct nes_mapper_t*); + int (*state_read)(struct nes_mapper_t*, const void*); + int (*state_write)(const struct nes_mapper_t*, void*); } nes_mapper; diff --git a/src/nese.c b/src/nese.c index a19c749..b282abf 100644 --- a/src/nese.c +++ b/src/nese.c @@ -99,7 +99,6 @@ int main(int argc, char* argv[]) { t_target += elapsed_us; time_us slept_us = time_sleep_until(t_target); - (void)slept_us; if (slept_us <= -elapsed_us) { // We're way out of sync. @@ -114,6 +113,14 @@ int main(int argc, char* argv[]) { if (input_Result_Reset == status) { nes_reset(&sys); status = 0; + + } else if (input_Result_Load == status) { + load_state(&sys, cart_filename); + status = 0; + + } else if (input_Result_Save == status) { + save_state(&sys, cart_filename); + status = 0; } if (status == 0) { diff --git a/src/ppu.h b/src/ppu.h index baef699..12a4acb 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -122,7 +122,6 @@ typedef enum { typedef struct { // Memory - struct nes_mapper_t* mapper; oam_sprite oam[nes_ppu_oam_sprite_count]; uint8_t palette[nes_ppu_mem_pal_size]; @@ -134,6 +133,9 @@ typedef struct { int hit_line; int hit_dot; + // Internal Registers + uint8_t latch; + // External registers uint8_t control; uint8_t mask; @@ -147,9 +149,8 @@ typedef struct { uint8_t data; uint8_t oam_addr; - // Internal Registers - uint8_t latch; - + // System Interface + struct nes_mapper_t* mapper; } nes_ppu; uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr); diff --git a/src/save.c b/src/save.c index a9760f5..c62a7cc 100644 --- a/src/save.c +++ b/src/save.c @@ -1,7 +1,13 @@ +#include +#include + #include "save.h" #include "file.h" #include "mapper.h" +#include "filemap.h" + +/* SRAM */ static int make_sram_filename(char* sram_filename, int max_len, const char* cart_filename) { @@ -45,3 +51,339 @@ int save_sram(const nes_cart* cart, const char* cart_filename) { return status; } + + +/* System State */ + +static int make_state_filename(char* sram_filename, int max_len, + const char* cart_filename) { + return make_filename( sram_filename, max_len, + basename(cart_filename), + "save", "nese"); +} + +int load_state(nes* sys, const char* cart_filename) { + int size = -1; + + char state_filename[FILENAME_MAX] = {0}; + make_state_filename(state_filename, FILENAME_MAX - 1, + cart_filename); + + FILE* file = fopen(state_filename, "rb"); + + if (NULL != file) { + int file_size = state_size(sys); + void* mem = map_file(file, file_size, Filemap_Mode_Read); + + if (NULL != mem) { + size = state_read(sys, mem, file_size); + unmap_file(mem, file_size); + } + + fclose(file); + } + + return size; +} + +int save_state(const nes* sys, const char* cart_filename) { + int size = -1; + + char state_filename[FILENAME_MAX] = {0}; + make_state_filename(state_filename, FILENAME_MAX - 1, + cart_filename); + + FILE* file = fopen(state_filename, "w+b"); + + if (NULL != file) { + int file_size = state_size(sys); + + fseek(file, file_size - 1, SEEK_SET); + fwrite("", 1, 1, file); + fflush(file); + + void* mem = map_file(file, file_size, Filemap_Mode_Write); + + if (NULL != mem) { + size = state_write(sys, mem, file_size); + unmap_file(mem, file_size); + } + + fclose(file); + } + + return size; +} + + +#define tag_def(X) ( \ + X[0] | (X[1] << 8) | (X[2] << 16) | (X[3] << 24) \ + ) + +#define tag_NESE tag_def("NESE") +#define tag_cpu tag_def("tCPU") +#define tag_ppu tag_def("tPPU") +#define tag_apu tag_def("tAPU") +#define tag_ram tag_def("WRAM") +#define tag_mapper tag_def("MAPP") + + +#define szChunkTag sizeof(uint32_t) +#define szChunkHeader (szChunkTag + sizeof(uint32_t)) + + +static int cpu_state_size(const e6502_Core* core) { + return (sizeof(core->registers) + sizeof(core->pins)); +} + +static int cpu_state_read(e6502_Core* core, const void* mem) { + memcpy(&core->registers, mem, sizeof(core->registers)); + memcpy(&core->pins, mem + sizeof(core->registers), + sizeof(core->pins)); + return (sizeof(core->registers) + sizeof(core->pins)); +} + +static int cpu_state_write(const e6502_Core* core, void* mem) { + memcpy(mem, &core->registers, sizeof(core->registers)); + memcpy(mem + sizeof(core->registers), &core->pins, + sizeof(core->pins)); + return (sizeof(core->registers) + sizeof(core->pins)); +} + + +static int ppu_state_size(const nes_ppu* ppu) { + return ((void*)&(ppu->mapper) - (void*)ppu); +} + +static int ppu_state_read(nes_ppu* ppu, const void* mem) { + int size = ppu_state_size(ppu); + memcpy(ppu, mem, size); + return size; +} + +static int ppu_state_write(const nes_ppu* ppu, void* mem) { + int size = ppu_state_size(ppu); + memcpy(mem, ppu, size); + return size; +} + + +static int apu_state_size(const nes_apu* apu) { + return ( ((void*)(apu->channels) - (void*)apu) + + (5 * ( (void*)&(apu->channels->write) - + (void*)(apu->channels) + ) ) + ); +} + +static int apu_state_read(nes_apu* apu, const void* mem) { + int size = ((void*)(apu->channels) - (void*)apu); + memcpy(apu, mem, size); + const void* ptr = (mem + size); + size = ( (void*)&(apu->channels->write) - + (void*)(apu->channels)); + for (int chan = 0; chan < nes_apu_chan_count; ++chan) { + memcpy(&apu->channels[chan], ptr, size); + ptr += size; + } + return (ptr - mem); +} + +static int apu_state_write(const nes_apu* apu, void* mem) { + int size = ((void*)(apu->channels) - (void*)apu); + memcpy(mem, apu, size); + void* ptr = (mem + size); + size = ( (void*)&(apu->channels->write) - + (void*)(apu->channels)); + for (int chan = 0; chan < nes_apu_chan_count; ++chan) { + memcpy(ptr, &apu->channels[chan], size); + ptr += size; + } + return (ptr - mem); +} + + +static int ram_state_size(const nes* sys) { + return sizeof(sys->ram); +} + +static int ram_state_read(nes* sys, const void* mem) { + memcpy(sys->ram, mem, sizeof(sys->ram)); + return sizeof(sys->ram); +} + +static int ram_state_write(const nes* sys, void* mem) { + memcpy(mem, sys->ram, sizeof(sys->ram)); + return sizeof(sys->ram); +} + + +int state_size(const nes* sys) { + int size = szChunkHeader; + + size += szChunkHeader + cpu_state_size(&sys->cpu); + size += szChunkHeader + ppu_state_size(&sys->ppu); + size += szChunkHeader + apu_state_size(&sys->apu); + // Ignoring input + size += szChunkHeader + ram_state_size(sys); + // Cart should already be loaded + size += szChunkHeader + + sys->cart.mapper->state_size(sys->cart.mapper); + + return size; +} + +static inline void* write_header(void* mem, + uint32_t tag, + uint32_t size) { + memcpy(mem, &tag, szChunkTag); + mem += szChunkTag; + memcpy(mem, &size, sizeof(size)); + mem += sizeof(size); + return mem; +} + +static inline const void* read_header(const void* mem, + uint32_t* tag, + uint32_t* size) { + memcpy(tag, mem, szChunkTag); + mem += szChunkTag; + memcpy(size, mem, sizeof(size[0])); + mem += sizeof(size[0]); + return mem; +} + +int state_write(const nes* sys, void* mem, int size) { + void* ptr = mem; + + ptr = write_header(ptr, tag_NESE, state_size(sys)); + + ptr = write_header(ptr, tag_cpu, cpu_state_size(&sys->cpu)); + ptr += cpu_state_write(&sys->cpu, ptr); + + ptr = write_header(ptr, tag_ppu, ppu_state_size(&sys->ppu)); + ptr += ppu_state_write(&sys->ppu, ptr); + + ptr = write_header(ptr, tag_apu, apu_state_size(&sys->apu)); + ptr += apu_state_write(&sys->apu, ptr); + + ptr = write_header(ptr, tag_ram, ram_state_size(sys)); + ptr += ram_state_write(sys, ptr); + + const nes_mapper* mapper = sys->cart.mapper; + ptr = write_header(ptr, tag_mapper, mapper->state_size(mapper)); + ptr += mapper->state_write(mapper, ptr); + + return (ptr - mem); +} + +typedef enum { + Component_CPU = 0b00000001, + Component_PPU = 0b00000010, + Component_APU = 0b00000100, + Component_RAM = 0b00001000, + Component_Mapper = 0b00010000, +} nes_Component; + +int state_read(nes* sys, const void* mem, int mem_size) { + int result = 0; + nes_Component loaded = 0; + + const void* ptr = mem; + const void* end = (mem + mem_size); + + uint32_t tag = 0; + uint32_t size = 0; + + ptr = read_header(mem, &tag, &size); + if (tag_NESE != tag) { + result = -1; + fprintf(stderr, + "Bad save state magic: %.4s\n", + (char*)&tag); + } + + while (0 == result && ptr < end) { + ptr = read_header(ptr, &tag, &size); + if ((ptr + size) > end) { + result = -1; + fprintf(stderr, + "Unusually large chunk: %.4s: +%"PRIu64"\n", + (char*)&tag, + ((ptr + size) - end)); + break; + } + + int n_read = 0; + + if (tag_cpu == tag) { + n_read = cpu_state_read(&sys->cpu, ptr); + loaded |= Component_CPU; + + } else if (tag_ppu == tag) { + n_read = ppu_state_read(&sys->ppu, ptr); + loaded |= Component_PPU; + + } else if (tag_apu == tag) { + n_read = apu_state_read(&sys->apu, ptr); + loaded |= Component_APU; + + } else if (tag_ram == tag) { + n_read = ram_state_read(sys, ptr); + loaded |= Component_RAM; + + } else if (tag_mapper == tag) { + n_read = sys->cart.mapper->state_read( + sys->cart.mapper, ptr + ); + loaded |= Component_Mapper; + + } else { + fprintf(stderr, + "Strange chunk: %.4s\n", + (char*)&tag); + } + + if (n_read != size) { + result = -1; + fprintf(stderr, + "Chunk %.4s: read %d, expected %d\n", + (char*)&tag, n_read, size); + } + + ptr += size; + } + + if (0 == result) { + if (!(loaded | Component_CPU)) { + result = -1; + fprintf(stderr, "Missing %s state\n", "CPU"); + } + + if (!(loaded | Component_PPU)) { + result = -1; + fprintf(stderr, "Missing %s state\n", "PPU"); + } + + if (!(loaded | Component_APU)) { + result = -1; + fprintf(stderr, "Missing %s state\n", "APU"); + } + + if (!(loaded | Component_RAM)) { + result = -1; + fprintf(stderr, "Missing %s state\n", "RAM"); + } + + if (!(loaded | Component_Mapper)) { + result = -1; + fprintf(stderr, "Missing %s state\n", "Mapper"); + } + } + + if (0 == result) { + result = (ptr - mem); + } + + return result; +} diff --git a/src/save.h b/src/save.h index 7f5b310..ffff4b0 100644 --- a/src/save.h +++ b/src/save.h @@ -1,11 +1,18 @@ #ifndef NESE_SAVE_H_ #define NESE_SAVE_H_ -#include "cart.h" +#include "nes.h" int load_sram(nes_cart* cart, const char* filename); int save_sram(const nes_cart* cart, const char* filename); +int state_size(const nes*); +int state_read(nes*, const void* mem, int size); +int state_write(const nes*, void* mem, int size); + +int load_state(nes*, const char* filename); +int save_state(const nes*, const char* filename); + #endif // NESE_SAVE_H_ diff --git a/src/sdl_input.c b/src/sdl_input.c index 53eb4b5..d852b94 100644 --- a/src/sdl_input.c +++ b/src/sdl_input.c @@ -5,7 +5,9 @@ static SDL_GameController* sdl_find_gamepad() { int i = SDL_NumJoysticks() - 1; + printf("Found %d joysticks\n", i + 1); for ( ; i >= 0 && !SDL_IsGameController(i); --i); + if (i >= 0) printf("Joystick %d is a gamepad\n", i); return (i < 0 ? NULL : SDL_GameControllerOpen(i)); } @@ -52,6 +54,8 @@ static void sdl_input_done(nes_Input_Reader* input) { } static const int sdl_reset_key = SDLK_ESCAPE; +static const int sdl_save_key = SDLK_F1; +static const int sdl_load_key = SDLK_F2; static const int sdl_keycodes[nes_controller_num_buttons] = { SDLK_a, @@ -102,9 +106,17 @@ static int sdl_input_update(nes_Input_Reader* reader, input->controllers[0].buttons &= ~mask; } - } else if ( event.key.keysym.sym == sdl_reset_key && + } else if ( sdl_reset_key == event.key.keysym.sym && SDL_KEYDOWN == event.type) { status = input_Result_Reset; + + } else if ( sdl_save_key == event.key.keysym.sym && + SDL_KEYDOWN == event.type) { + status = input_Result_Save; + + } else if ( sdl_load_key == event.key.keysym.sym && + SDL_KEYDOWN == event.type) { + status = input_Result_Load; } } else if ( SDL_CONTROLLERBUTTONDOWN == event.type || @@ -150,14 +162,22 @@ static int sdl_input_update(nes_Input_Reader* reader, } else if (SDL_CONTROLLERDEVICEADDED == event.type) { if (NULL == reader->data) { + printf("New gamepad connected\n"); reader->data = sdl_find_gamepad(); + if (reader->data) printf("Using new gamepad\n"); + } else { + printf("Redundant gamepad connected\n"); } } else if (SDL_CONTROLLERDEVICEREMOVED == event.type) { if (sdl_match_gamepad(event.cdevice.which, reader->data)) { + printf("Gamepad disconnected\n"); sdl_lose_gamepad(reader->data); reader->data = sdl_find_gamepad(); + if (reader->data) printf("Using another gamepad\n"); + } else { + printf("Redundant gamepad disconnected\n"); } } } diff --git a/src/win/filemap.c b/src/win/filemap.c index a5a0959..3164437 100644 --- a/src/win/filemap.c +++ b/src/win/filemap.c @@ -47,17 +47,22 @@ static void store_map_handle(const void* mem, HANDLE handle) { } -void* map_file(FILE* file, int size) { +void* map_file(FILE* file, int size, Filemap_Mode map_mode) { void* mem = NULL; + DWORD protect = ( Filemap_Mode_Write == map_mode ? + PAGE_READWRITE : PAGE_READONLY); + DWORD access = ( Filemap_Mode_Write == map_mode ? + FILE_MAP_WRITE : FILE_MAP_READ); + HANDLE hFile = (HANDLE)_get_osfhandle(fileno(file)); HANDLE hMap = CreateFileMappingA( - hFile, 0, PAGE_READONLY, 0, size, 0 + hFile, 0, protect, 0, size, 0 ); if (NULL != hMap) { - mem = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, size); + mem = MapViewOfFile(hMap, access, 0, 0, size); if (NULL == mem) { CloseHandle(hMap); } else {