diff --git a/gameboy/cartridge/cartridge.cpp b/gameboy/cartridge/cartridge.cpp index 7939d68c..81862973 100755 --- a/gameboy/cartridge/cartridge.cpp +++ b/gameboy/cartridge/cartridge.cpp @@ -13,84 +13,63 @@ namespace GameBoy { #include "mmm01/mmm01.cpp" #include "huc1/huc1.cpp" #include "huc3/huc3.cpp" +#include "serialization.cpp" Cartridge cartridge; -void Cartridge::load(uint8_t *data, unsigned size) { +void Cartridge::load(const string &xml, uint8_t *data, unsigned size) { //uint32_t crc = crc32_calculate(data, size); //print("CRC32 = ", hex<4>(crc), "\n"); romdata = new uint8[romsize = size]; memcpy(romdata, data, size); - char name[17]; - memcpy(name, romdata + 0x0134, 16); - name[16] = 0; - info.name = name; - info.name.rtrim(); - - info.cgbflag = romdata[0x0143]; - info.sgbflag = romdata[0x0146]; - info.mapper = Mapper::Unknown; info.ram = false; info.battery = false; info.rtc = false; + info.rumble = false; - switch(romdata[0x0147]) { - case 0x00: info.mapper = Mapper::MBC0; break; - case 0x01: info.mapper = Mapper::MBC1; break; - case 0x02: info.mapper = Mapper::MBC1; info.ram = true; break; - case 0x03: info.mapper = Mapper::MBC1; info.ram = true; info.battery = true; break; - case 0x05: info.mapper = Mapper::MBC2; info.ram = true; break; - case 0x06: info.mapper = Mapper::MBC2; info.ram = true; info.battery = true; break; - case 0x08: info.mapper = Mapper::MBC0; info.ram = true; break; - case 0x09: info.mapper = Mapper::MBC0; info.ram = true; info.battery = true; break; - case 0x0b: info.mapper = Mapper::MMM01; break; - case 0x0c: info.mapper = Mapper::MMM01; info.ram = true; break; - case 0x0d: info.mapper = Mapper::MMM01; info.ram = true; info.battery = true; break; - case 0x0f: info.mapper = Mapper::MBC3; info.rtc = true; info.battery = true; break; - case 0x10: info.mapper = Mapper::MBC3; info.rtc = true; info.ram = true; info.battery = true; break; - case 0x11: info.mapper = Mapper::MBC3; break; - case 0x12: info.mapper = Mapper::MBC3; info.ram = true; break; - case 0x13: info.mapper = Mapper::MBC3; info.ram = true; info.battery = true; break; - case 0x19: info.mapper = Mapper::MBC5; break; - case 0x1a: info.mapper = Mapper::MBC5; info.ram = true; break; - case 0x1b: info.mapper = Mapper::MBC5; info.ram = true; info.battery = true; break; - case 0x1c: info.mapper = Mapper::MBC5; info.rumble = true; break; - case 0x1d: info.mapper = Mapper::MBC5; info.rumble = true; info.ram = true; break; - case 0x1e: info.mapper = Mapper::MBC5; info.rumble = true; info.ram = true; info.battery = true; break; - case 0xfc: break; //Pocket Camera - case 0xfd: break; //Bandai TAMA5 - case 0xfe: info.mapper = Mapper::HuC3; break; - case 0xff: info.mapper = Mapper::HuC1; info.ram = true; info.battery = true; break; + info.romsize = 0; + info.ramsize = 0; + + xml_element document = xml_parse(xml); + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "mapper") { + if(attr.content == "none") info.mapper = Mapper::MBC0; + if(attr.content == "MBC1") info.mapper = Mapper::MBC1; + if(attr.content == "MBC2") info.mapper = Mapper::MBC2; + if(attr.content == "MBC3") info.mapper = Mapper::MBC3; + if(attr.content == "MBC5") info.mapper = Mapper::MBC5; + if(attr.content == "MMM01") info.mapper = Mapper::MMM01; + if(attr.content == "HuC1") info.mapper = Mapper::HuC1; + if(attr.content == "HuC3") info.mapper = Mapper::HuC3; + } + + if(attr.name == "rtc") info.rtc = (attr.content == "true" ? true : false); + if(attr.name == "rumble") info.rumble = (attr.content == "true" ? true : false); + } + + foreach(elem, head.element) { + if(elem.name == "rom") { + foreach(attr, elem.attribute) { + if(attr.name == "size") info.romsize = hex(attr.content); + } + } + + if(elem.name == "ram") { + info.ram = true; + foreach(attr, elem.attribute) { + if(attr.name == "size") info.ramsize = hex(attr.content); + if(attr.name == "battery") info.battery = (attr.content == "true" ? true : false); + } + } + } + } } -//print("Mapper: ", hex<2>(romdata[0x0147]), "\n"); - - switch(romdata[0x0148]) { default: - case 0x00: info.romsize = 2 * 16 * 1024; break; - case 0x01: info.romsize = 4 * 16 * 1024; break; - case 0x02: info.romsize = 8 * 16 * 1024; break; - case 0x03: info.romsize = 16 * 16 * 1024; break; - case 0x04: info.romsize = 32 * 16 * 1024; break; - case 0x05: info.romsize = 64 * 16 * 1024; break; - case 0x06: info.romsize = 128 * 16 * 1024; break; - case 0x07: info.romsize = 256 * 16 * 1024; break; - case 0x52: info.romsize = 72 * 16 * 1024; break; - case 0x53: info.romsize = 80 * 16 * 1024; break; - case 0x54: info.romsize = 96 * 16 * 1024; break; - } - - switch(romdata[0x0149]) { default: - case 0x00: info.ramsize = 0 * 1024; break; - case 0x01: info.ramsize = 2 * 1024; break; - case 0x02: info.ramsize = 8 * 1024; break; - case 0x03: info.ramsize = 32 * 1024; break; - } - - if(info.mapper == Mapper::MBC2) info.ramsize = 512; //512 x 4-bit ramdata = new uint8_t[ramsize = info.ramsize](); - loaded = true; } diff --git a/gameboy/cartridge/cartridge.hpp b/gameboy/cartridge/cartridge.hpp index fc0cb15f..2ce22a90 100755 --- a/gameboy/cartridge/cartridge.hpp +++ b/gameboy/cartridge/cartridge.hpp @@ -21,9 +21,7 @@ struct Cartridge : property { }; struct Information { - string name; - uint8 cgbflag; - uint8 sgbflag; + string xml; Mapper mapper; bool ram; @@ -43,7 +41,7 @@ struct Cartridge : property { uint8_t *ramdata; unsigned ramsize; - void load(uint8_t *data, unsigned size); + void load(const string &xml, uint8_t *data, unsigned size); void unload(); uint8 rom_read(unsigned addr); @@ -54,6 +52,7 @@ struct Cartridge : property { void power(); void map(); + void serialize(serializer&); Cartridge(); ~Cartridge(); }; diff --git a/gameboy/cartridge/serialization.cpp b/gameboy/cartridge/serialization.cpp new file mode 100755 index 00000000..2e9f8e05 --- /dev/null +++ b/gameboy/cartridge/serialization.cpp @@ -0,0 +1,52 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + if(info.battery) s.array(ramdata, ramsize); + + s.integer(mbc1.ram_enable); + s.integer(mbc1.rom_select); + s.integer(mbc1.ram_select); + s.integer(mbc1.mode_select); + + s.integer(mbc2.ram_enable); + s.integer(mbc2.rom_select); + + s.integer(mbc3.ram_enable); + s.integer(mbc3.rom_select); + s.integer(mbc3.ram_select); + s.integer(mbc3.rtc_latch); + + s.integer(mbc3.rtc_halt); + s.integer(mbc3.rtc_second); + s.integer(mbc3.rtc_minute); + s.integer(mbc3.rtc_hour); + s.integer(mbc3.rtc_day); + s.integer(mbc3.rtc_day_carry); + + s.integer(mbc3.rtc_latch_second); + s.integer(mbc3.rtc_latch_minute); + s.integer(mbc3.rtc_latch_hour); + s.integer(mbc3.rtc_latch_day); + s.integer(mbc3.rtc_latch_day_carry); + + s.integer(mbc5.ram_enable); + s.integer(mbc5.rom_select); + s.integer(mbc5.ram_select); + + s.integer(mmm01.rom_mode); + s.integer(mmm01.rom_base); + + s.integer(mmm01.ram_enable); + s.integer(mmm01.rom_select); + s.integer(mmm01.ram_select); + + s.integer(huc1.ram_enable); + s.integer(huc1.rom_select); + s.integer(huc1.ram_select); + + s.integer(huc3.ram_enable); + s.integer(huc3.rom_select); + s.integer(huc3.ram_select); +} + +#endif diff --git a/gameboy/cpu/cpu.cpp b/gameboy/cpu/cpu.cpp index 5e00802f..e2348577 100755 --- a/gameboy/cpu/cpu.cpp +++ b/gameboy/cpu/cpu.cpp @@ -6,6 +6,7 @@ namespace GameBoy { #include "core/core.cpp" #include "mmio/mmio.cpp" #include "timing/timing.cpp" +#include "serialization.cpp" CPU cpu; void CPU::Main() { @@ -14,6 +15,11 @@ void CPU::Main() { void CPU::main() { while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::CPU) { + scheduler.sync = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + if(trace) print(disassemble(r[PC]), "\n"); interrupt_test(); uint8 opcode = op_read(r[PC]++); diff --git a/gameboy/cpu/cpu.hpp b/gameboy/cpu/cpu.hpp index 50ad7264..d84568b2 100755 --- a/gameboy/cpu/cpu.hpp +++ b/gameboy/cpu/cpu.hpp @@ -67,6 +67,8 @@ struct CPU : Processor, MMIO { void interrupt_test(); void interrupt_exec(uint16 pc); void power(); + + void serialize(serializer&); CPU(); }; diff --git a/gameboy/cpu/serialization.cpp b/gameboy/cpu/serialization.cpp new file mode 100755 index 00000000..b289bdc9 --- /dev/null +++ b/gameboy/cpu/serialization.cpp @@ -0,0 +1,55 @@ +#ifdef CPU_CPP + +void CPU::serialize(serializer &s) { + s.array(wram); + s.array(hram); + + s.integer(r.a.data); + s.integer(r.f.z); + s.integer(r.f.n); + s.integer(r.f.h); + s.integer(r.f.c); + s.integer(r.b.data); + s.integer(r.c.data); + s.integer(r.d.data); + s.integer(r.e.data); + s.integer(r.h.data); + s.integer(r.l.data); + s.integer(r.sp.data); + s.integer(r.pc.data); + + s.integer(status.clock); + s.integer(status.halt); + s.integer(status.stop); + + s.integer(status.ime); + s.integer(status.timer0); + s.integer(status.timer1); + s.integer(status.timer2); + s.integer(status.timer3); + + s.integer(status.p15); + s.integer(status.p14); + s.integer(status.joyp); + s.integer(status.mlt_req); + + s.integer(status.div); + s.integer(status.tima); + s.integer(status.tma); + s.integer(status.timer_enable); + s.integer(status.timer_clock); + + s.integer(status.interrupt_request_joypad); + s.integer(status.interrupt_request_serial); + s.integer(status.interrupt_request_timer); + s.integer(status.interrupt_request_stat); + s.integer(status.interrupt_request_vblank); + + s.integer(status.interrupt_enable_joypad); + s.integer(status.interrupt_enable_serial); + s.integer(status.interrupt_enable_timer); + s.integer(status.interrupt_enable_stat); + s.integer(status.interrupt_enable_vblank); +} + +#endif diff --git a/gameboy/cpu/timing/timing.cpp b/gameboy/cpu/timing/timing.cpp index 3a400d06..b272c430 100755 --- a/gameboy/cpu/timing/timing.cpp +++ b/gameboy/cpu/timing/timing.cpp @@ -14,7 +14,7 @@ void CPU::add_clocks(unsigned clocks) { system.clocks_executed += clocks; - scheduler.exit(); + scheduler.exit(Scheduler::ExitReason::StepEvent); status.clock += clocks; if(status.clock >= 4 * 1024 * 1024) { diff --git a/gameboy/gameboy.hpp b/gameboy/gameboy.hpp index 4a5e6773..b4f96e9b 100755 --- a/gameboy/gameboy.hpp +++ b/gameboy/gameboy.hpp @@ -5,14 +5,17 @@ namespace GameBoy { namespace Info { static const char Name[] = "bgameboy"; - static const char Version[] = "000.10"; + static const char Version[] = "000.11"; + static unsigned SerializerVersion = 1; } } #include #include +#include #include +#include #include #include using namespace nall; diff --git a/gameboy/lcd/lcd.cpp b/gameboy/lcd/lcd.cpp index 33143362..7d6ce882 100755 --- a/gameboy/lcd/lcd.cpp +++ b/gameboy/lcd/lcd.cpp @@ -4,6 +4,7 @@ namespace GameBoy { #include "mmio/mmio.cpp" +#include "serialization.cpp" LCD lcd; void LCD::Main() { @@ -12,6 +13,10 @@ void LCD::Main() { void LCD::main() { while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + add_clocks(4); if(status.lx == 320) { @@ -25,7 +30,9 @@ void LCD::add_clocks(unsigned clocks) { if(status.lx >= 456) scanline(); cpu.clock -= clocks; - if(cpu.clock <= 0) co_switch(scheduler.active_thread = cpu.thread); + if(cpu.clock <= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) { + co_switch(scheduler.active_thread = cpu.thread); + } } void LCD::scanline() { @@ -50,7 +57,7 @@ void LCD::frame() { cpu.mmio_joyp_poll(); status.ly = 0; - scheduler.exit(); + scheduler.exit(Scheduler::ExitReason::FrameEvent); } void LCD::render() { @@ -221,4 +228,7 @@ void LCD::power() { status.wx = 0; } +LCD::LCD() { +} + } diff --git a/gameboy/lcd/lcd.hpp b/gameboy/lcd/lcd.hpp index a3c72525..8692d9c4 100755 --- a/gameboy/lcd/lcd.hpp +++ b/gameboy/lcd/lcd.hpp @@ -63,6 +63,9 @@ struct LCD : Processor, MMIO { void render_obj(); void power(); + + void serialize(serializer&); + LCD(); }; extern LCD lcd; diff --git a/gameboy/lcd/serialization.cpp b/gameboy/lcd/serialization.cpp new file mode 100755 index 00000000..1274fdd9 --- /dev/null +++ b/gameboy/lcd/serialization.cpp @@ -0,0 +1,38 @@ +#ifdef LCD_CPP + +void LCD::serialize(serializer &s) { + s.integer(status.lx); + + s.integer(status.display_enable); + s.integer(status.window_tilemap_select); + s.integer(status.window_display_enable); + s.integer(status.bg_tiledata_select); + s.integer(status.bg_tilemap_select); + s.integer(status.obj_size); + s.integer(status.obj_enable); + s.integer(status.bg_enable); + + s.integer(status.interrupt_lyc); + s.integer(status.interrupt_oam); + s.integer(status.interrupt_vblank); + s.integer(status.interrupt_hblank); + + s.integer(status.scy); + s.integer(status.scx); + s.integer(status.ly); + s.integer(status.lyc); + + s.array(status.bgp); + s.array(status.obp[0]); + s.array(status.obp[1]); + + s.integer(status.wy); + s.integer(status.wx); + + s.array(screen); + s.array(vram); + s.array(oam); + s.array(line); +} + +#endif diff --git a/gameboy/memory/memory.cpp b/gameboy/memory/memory.cpp index 62ffe162..c46a424c 100755 --- a/gameboy/memory/memory.cpp +++ b/gameboy/memory/memory.cpp @@ -51,10 +51,6 @@ void Bus::write(uint16 addr, uint8 data) { void Bus::power() { for(unsigned n = 0; n < 65536; n++) mmio[n] = &unmapped; - reset(); -} - -void Bus::reset() { } } diff --git a/gameboy/memory/memory.hpp b/gameboy/memory/memory.hpp index 0b9ca99c..3dfb8df9 100755 --- a/gameboy/memory/memory.hpp +++ b/gameboy/memory/memory.hpp @@ -21,15 +21,11 @@ struct Unmapped : MMIO { }; struct Bus { - Memory cartrom; - Memory cartram; - MMIO *mmio[65536]; uint8 read(uint16 addr); void write(uint16 addr, uint8 data); void power(); - void reset(); }; extern Unmapped unmapped; diff --git a/gameboy/scheduler/scheduler.cpp b/gameboy/scheduler/scheduler.cpp index 2f32cea5..14bd7331 100755 --- a/gameboy/scheduler/scheduler.cpp +++ b/gameboy/scheduler/scheduler.cpp @@ -10,7 +10,8 @@ void Scheduler::enter() { co_switch(active_thread); } -void Scheduler::exit() { +void Scheduler::exit(ExitReason reason) { + exit_reason = reason; active_thread = co_active(); co_switch(host_thread); } @@ -26,6 +27,7 @@ void Scheduler::init() { } Scheduler::Scheduler() { + exit_reason = ExitReason::UnknownEvent; host_thread = 0; active_thread = 0; } diff --git a/gameboy/scheduler/scheduler.hpp b/gameboy/scheduler/scheduler.hpp index 69337acf..ccdb78ec 100755 --- a/gameboy/scheduler/scheduler.hpp +++ b/gameboy/scheduler/scheduler.hpp @@ -1,9 +1,13 @@ -struct Scheduler { +struct Scheduler : property { + enum class SynchronizeMode : unsigned { None, CPU, All } sync; + enum class ExitReason : unsigned { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent }; + readonly exit_reason; + cothread_t host_thread; cothread_t active_thread; void enter(); - void exit(); + void exit(ExitReason); void swapto(Processor&); void init(); diff --git a/gameboy/system/serialization.cpp b/gameboy/system/serialization.cpp new file mode 100755 index 00000000..438f3374 --- /dev/null +++ b/gameboy/system/serialization.cpp @@ -0,0 +1,62 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0; + char description[512]; + memset(&description, 0, sizeof description); + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + return s; +} + +bool System::unserialize(serializer &s) { + unsigned signature, version, crc32; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + if(signature != 0x31545342) return false; + if(version != Info::SerializerVersion) return false; +//if(crc32 != 0) return false; + + serialize_all(s); + return true; +} + +void System::serialize(serializer &s) { + s.integer(clocks_executed); +} + +void System::serialize_all(serializer &s) { + cartridge.serialize(s); + system.serialize(s); + cpu.serialize(s); + lcd.serialize(s); +} + +void System::serialize_init() { + serializer s; + + unsigned signature = 0, version = 0, crc32 = 0; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + serialize_size = s.size(); +} + +#endif diff --git a/gameboy/system/system.cpp b/gameboy/system/system.cpp index 4e959d4b..29ac155f 100755 --- a/gameboy/system/system.cpp +++ b/gameboy/system/system.cpp @@ -5,8 +5,34 @@ namespace GameBoy { #include "bootrom-dmg.cpp" #include "bootrom-sgb.cpp" +#include "serialization.cpp" System system; +void System::run() { + scheduler.sync = Scheduler::SynchronizeMode::None; + + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { + } +} + +void System::runtosave() { + scheduler.sync = Scheduler::SynchronizeMode::CPU; + runthreadtosave(); + + scheduler.active_thread = lcd.thread; + runthreadtosave(); +} + +void System::runthreadtosave() { + while(true) { + scheduler.enter(); + if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break; + if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) { + } + } +} + uint8 System::mmio_read(uint16 addr) { if((addr & 0xff00) == 0x0000) { return BootROM::sgb[addr]; @@ -34,11 +60,8 @@ void System::power() { for(unsigned n = 0x0000; n <= 0x00ff; n++) bus.mmio[n] = this; bus.mmio[0xff50] = this; - system.clocks_executed = 0; -} - -void System::run() { - scheduler.enter(); + clocks_executed = 0; + serialize_init(); } } diff --git a/gameboy/system/system.hpp b/gameboy/system/system.hpp index 7d54d529..40ff657b 100755 --- a/gameboy/system/system.hpp +++ b/gameboy/system/system.hpp @@ -10,15 +10,28 @@ struct System : MMIO { static const uint8 sgb[256]; } bootROM; + void run(); + void runtosave(); + void runthreadtosave(); + uint8 mmio_read(uint16 addr); void mmio_write(uint16 addr, uint8 data); void init(Interface*); void power(); - void run(); Interface *interface; unsigned clocks_executed; + + //serialization.cpp + unsigned serialize_size; + + serializer serialize(); + bool unserialize(serializer&); + + void serialize(serializer&); + void serialize_all(serializer&); + void serialize_init(); }; #include diff --git a/nall/gameboy/cartridge.hpp b/nall/gameboy/cartridge.hpp new file mode 100755 index 00000000..d4da133a --- /dev/null +++ b/nall/gameboy/cartridge.hpp @@ -0,0 +1,104 @@ +#ifndef NALL_GAMEBOY_CARTRIDGE_HPP +#define NALL_GAMEBOY_CARTRIDGE_HPP + +namespace nall { + +class GameBoyCartridge { +public: + string xml; + inline GameBoyCartridge(const uint8_t *data, unsigned size); + +//private: + struct Information { + string mapper; + bool ram; + bool battery; + bool rtc; + bool rumble; + + unsigned romsize; + unsigned ramsize; + } info; +}; + +GameBoyCartridge::GameBoyCartridge(const uint8_t *romdata, unsigned romsize) { + xml = "\n"; + if(romsize < 0x4000) return; + + info.mapper = "unknown"; + info.ram = false; + info.battery = false; + info.rtc = false; + info.rumble = false; + + info.romsize = 0; + info.ramsize = 0; + + switch(romdata[0x0147]) { + case 0x00: info.mapper = "none"; break; + case 0x01: info.mapper = "MBC1"; break; + case 0x02: info.mapper = "MBC1"; info.ram = true; break; + case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break; + case 0x05: info.mapper = "MBC2"; info.ram = true; break; + case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break; + case 0x08: info.mapper = "none"; info.ram = true; break; + case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break; + case 0x0b: info.mapper = "MMM01"; break; + case 0x0c: info.mapper = "MMM01"; info.ram = true; break; + case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break; + case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break; + case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break; + case 0x11: info.mapper = "MBC3"; break; + case 0x12: info.mapper = "MBC3"; info.ram = true; break; + case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break; + case 0x19: info.mapper = "MBC5"; break; + case 0x1a: info.mapper = "MBC5"; info.ram = true; break; + case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break; + case 0x1c: info.mapper = "MBC5"; info.rumble = true; break; + case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break; + case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break; + case 0xfc: break; //Pocket Camera + case 0xfd: break; //Bandai TAMA5 + case 0xfe: info.mapper = "HuC3"; break; + case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break; + } + + switch(romdata[0x0148]) { default: + case 0x00: info.romsize = 2 * 16 * 1024; break; + case 0x01: info.romsize = 4 * 16 * 1024; break; + case 0x02: info.romsize = 8 * 16 * 1024; break; + case 0x03: info.romsize = 16 * 16 * 1024; break; + case 0x04: info.romsize = 32 * 16 * 1024; break; + case 0x05: info.romsize = 64 * 16 * 1024; break; + case 0x06: info.romsize = 128 * 16 * 1024; break; + case 0x07: info.romsize = 256 * 16 * 1024; break; + case 0x52: info.romsize = 72 * 16 * 1024; break; + case 0x53: info.romsize = 80 * 16 * 1024; break; + case 0x54: info.romsize = 96 * 16 * 1024; break; + } + + switch(romdata[0x0149]) { default: + case 0x00: info.ramsize = 0 * 1024; break; + case 0x01: info.ramsize = 2 * 1024; break; + case 0x02: info.ramsize = 8 * 1024; break; + case 0x03: info.ramsize = 32 * 1024; break; + } + + if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit + + xml << "\n"; + + xml << " \n"; //TODO: trust/check info.romsize? + + if(info.ramsize > 0) + xml << " \n"; + + xml << "\n"; +} + +} + +#endif diff --git a/ui/base.hpp b/ui/base.hpp index 1024ef22..34892c9e 100755 --- a/ui/base.hpp +++ b/ui/base.hpp @@ -2,6 +2,7 @@ #include #include #include +#include using namespace nall; #include diff --git a/ui/general/main-window.cpp b/ui/general/main-window.cpp index d20d9d11..a93fa165 100755 --- a/ui/general/main-window.cpp +++ b/ui/general/main-window.cpp @@ -15,6 +15,19 @@ void MainWindow::create() { settingsVideoSync.setChecked(true); tools.create(*this, "Tools"); + toolsSaveState.create(tools, "Save State"); + toolsSaveState1.create(toolsSaveState, "Slot 1"); + toolsSaveState2.create(toolsSaveState, "Slot 2"); + toolsSaveState3.create(toolsSaveState, "Slot 3"); + toolsSaveState4.create(toolsSaveState, "Slot 4"); + toolsSaveState5.create(toolsSaveState, "Slot 5"); + toolsLoadState.create(tools, "Load State"); + toolsLoadState1.create(toolsLoadState, "Slot 1"); + toolsLoadState2.create(toolsLoadState, "Slot 2"); + toolsLoadState3.create(toolsLoadState, "Slot 3"); + toolsLoadState4.create(toolsLoadState, "Slot 4"); + toolsLoadState5.create(toolsLoadState, "Slot 5"); + toolsSeparator1.create(tools); toolsTraceCPU.create(tools, "Trace CPU"); help.create(*this, "Help"); @@ -43,6 +56,18 @@ void MainWindow::create() { video.set(Video::Synchronize, mainWindow.settingsVideoSync.checked()); }; + toolsSaveState1.onTick = []() { utility.saveState(1); }; + toolsSaveState2.onTick = []() { utility.saveState(2); }; + toolsSaveState3.onTick = []() { utility.saveState(3); }; + toolsSaveState4.onTick = []() { utility.saveState(4); }; + toolsSaveState5.onTick = []() { utility.saveState(5); }; + + toolsLoadState1.onTick = []() { utility.loadState(1); }; + toolsLoadState2.onTick = []() { utility.loadState(2); }; + toolsLoadState3.onTick = []() { utility.loadState(3); }; + toolsLoadState4.onTick = []() { utility.loadState(4); }; + toolsLoadState5.onTick = []() { utility.loadState(5); }; + toolsTraceCPU.onTick = []() { GameBoy::cpu.trace = mainWindow.toolsTraceCPU.checked(); }; diff --git a/ui/general/main-window.hpp b/ui/general/main-window.hpp index 57b8084f..6d752ba1 100755 --- a/ui/general/main-window.hpp +++ b/ui/general/main-window.hpp @@ -8,6 +8,19 @@ struct MainWindow : Window { MenuCheckItem settingsVideoSync; Menu tools; + Menu toolsSaveState; + MenuItem toolsSaveState1; + MenuItem toolsSaveState2; + MenuItem toolsSaveState3; + MenuItem toolsSaveState4; + MenuItem toolsSaveState5; + Menu toolsLoadState; + MenuItem toolsLoadState1; + MenuItem toolsLoadState2; + MenuItem toolsLoadState3; + MenuItem toolsLoadState4; + MenuItem toolsLoadState5; + MenuSeparator toolsSeparator1; MenuCheckItem toolsTraceCPU; Menu help; diff --git a/ui/main.cpp b/ui/main.cpp index 5aebd270..b0162ff6 100755 --- a/ui/main.cpp +++ b/ui/main.cpp @@ -46,7 +46,9 @@ void Application::main(int argc, char **argv) { OS::run(); if(GameBoy::cartridge.loaded()) { - for(unsigned n = 0; n < 1024 * 1024; n++) GameBoy::system.run(); + do { + GameBoy::system.run(); + } while(GameBoy::scheduler.exit_reason() != GameBoy::Scheduler::ExitReason::FrameEvent); } } } diff --git a/ui/utility/utility.cpp b/ui/utility/utility.cpp index 864a1393..39875d72 100755 --- a/ui/utility/utility.cpp +++ b/ui/utility/utility.cpp @@ -8,8 +8,41 @@ void Utility::loadCartridge(const char *filename) { uint8_t *data = new uint8_t[size]; fp.read(data, size); fp.close(); - GameBoy::cartridge.load(data, size); + + cartridge.basename = nall::basename(filename); + print(cartridge.basename, "\n"); + + GameBoyCartridge info(data, size); + GameBoy::cartridge.load(info.xml, data, size); delete[] data; GameBoy::system.power(); } } + +bool Utility::saveState(unsigned slot) { + GameBoy::system.runtosave(); + serializer s = GameBoy::system.serialize(); + + file fp; + if(fp.open(string(cartridge.basename, "-", slot, ".bst"), file::mode::write)) { + fp.write(s.data(), s.size()); + fp.close(); + return true; + } + + return false; +} + +bool Utility::loadState(unsigned slot) { + file fp; + if(fp.open(string(cartridge.basename, "-", slot, ".bst"), file::mode::read)) { + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + serializer s(data, size); + return GameBoy::system.unserialize(s); + } + + return false; +} diff --git a/ui/utility/utility.hpp b/ui/utility/utility.hpp index d6e1afaa..f8f39a31 100755 --- a/ui/utility/utility.hpp +++ b/ui/utility/utility.hpp @@ -1,5 +1,11 @@ struct Utility { + struct Cartridge { + string basename; + } cartridge; + void loadCartridge(const char *filename); + bool saveState(unsigned slot); + bool loadState(unsigned slot); }; extern Utility utility;