diff --git a/bsnes/gameboy/cartridge/cartridge.cpp b/bsnes/gameboy/cartridge/cartridge.cpp index 7939d68c..81862973 100755 --- a/bsnes/gameboy/cartridge/cartridge.cpp +++ b/bsnes/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/bsnes/gameboy/cartridge/cartridge.hpp b/bsnes/gameboy/cartridge/cartridge.hpp index fc0cb15f..2ce22a90 100755 --- a/bsnes/gameboy/cartridge/cartridge.hpp +++ b/bsnes/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/bsnes/gameboy/cartridge/serialization.cpp b/bsnes/gameboy/cartridge/serialization.cpp new file mode 100755 index 00000000..2e9f8e05 --- /dev/null +++ b/bsnes/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/bsnes/gameboy/cpu/cpu.cpp b/bsnes/gameboy/cpu/cpu.cpp index 5e00802f..e2348577 100755 --- a/bsnes/gameboy/cpu/cpu.cpp +++ b/bsnes/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/bsnes/gameboy/cpu/cpu.hpp b/bsnes/gameboy/cpu/cpu.hpp index 50ad7264..d84568b2 100755 --- a/bsnes/gameboy/cpu/cpu.hpp +++ b/bsnes/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/bsnes/gameboy/cpu/serialization.cpp b/bsnes/gameboy/cpu/serialization.cpp new file mode 100755 index 00000000..b289bdc9 --- /dev/null +++ b/bsnes/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/bsnes/gameboy/cpu/timing/timing.cpp b/bsnes/gameboy/cpu/timing/timing.cpp index 3a400d06..b272c430 100755 --- a/bsnes/gameboy/cpu/timing/timing.cpp +++ b/bsnes/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/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index 4a5e6773..b4f96e9b 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/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/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index 33143362..7d6ce882 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/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/bsnes/gameboy/lcd/lcd.hpp b/bsnes/gameboy/lcd/lcd.hpp index a3c72525..8692d9c4 100755 --- a/bsnes/gameboy/lcd/lcd.hpp +++ b/bsnes/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/bsnes/gameboy/lcd/serialization.cpp b/bsnes/gameboy/lcd/serialization.cpp new file mode 100755 index 00000000..1274fdd9 --- /dev/null +++ b/bsnes/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/bsnes/gameboy/memory/memory.cpp b/bsnes/gameboy/memory/memory.cpp index 62ffe162..c46a424c 100755 --- a/bsnes/gameboy/memory/memory.cpp +++ b/bsnes/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/bsnes/gameboy/memory/memory.hpp b/bsnes/gameboy/memory/memory.hpp index 0b9ca99c..3dfb8df9 100755 --- a/bsnes/gameboy/memory/memory.hpp +++ b/bsnes/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/bsnes/gameboy/scheduler/scheduler.cpp b/bsnes/gameboy/scheduler/scheduler.cpp index 2f32cea5..14bd7331 100755 --- a/bsnes/gameboy/scheduler/scheduler.cpp +++ b/bsnes/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/bsnes/gameboy/scheduler/scheduler.hpp b/bsnes/gameboy/scheduler/scheduler.hpp index 69337acf..ccdb78ec 100755 --- a/bsnes/gameboy/scheduler/scheduler.hpp +++ b/bsnes/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/bsnes/gameboy/system/serialization.cpp b/bsnes/gameboy/system/serialization.cpp new file mode 100755 index 00000000..438f3374 --- /dev/null +++ b/bsnes/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/bsnes/gameboy/system/system.cpp b/bsnes/gameboy/system/system.cpp index 4e959d4b..29ac155f 100755 --- a/bsnes/gameboy/system/system.cpp +++ b/bsnes/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/bsnes/gameboy/system/system.hpp b/bsnes/gameboy/system/system.hpp index 7d54d529..40ff657b 100755 --- a/bsnes/gameboy/system/system.hpp +++ b/bsnes/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/bsnes/nall/gameboy/cartridge.hpp b/bsnes/nall/gameboy/cartridge.hpp new file mode 100755 index 00000000..d4da133a --- /dev/null +++ b/bsnes/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/bsnes/nall/snes/cartridge.hpp b/bsnes/nall/snes/cartridge.hpp index 485e91eb..2ecc36b1 100755 --- a/bsnes/nall/snes/cartridge.hpp +++ b/bsnes/nall/snes/cartridge.hpp @@ -145,23 +145,23 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) { xml << " \n"; xml << " \n"; xml << " \n"; - xml << " \n"; + xml << " \n"; xml << " \n"; xml << " \n"; xml << " \n"; xml << " \n"; - xml << " \n"; + xml << " \n"; } else if(type == TypeSuperGameBoy2Bios) { xml << " \n"; xml << " \n"; xml << " \n"; xml << " \n"; - xml << " \n"; + xml << " \n"; xml << " \n"; xml << " \n"; xml << " \n"; xml << " \n"; - xml << " \n"; + xml << " \n"; } else if(has_spc7110) { xml << " \n"; xml << " \n"; diff --git a/bsnes/snes/cartridge/cartridge.cpp b/bsnes/snes/cartridge/cartridge.cpp index 7f2b3af9..83965ec7 100755 --- a/bsnes/snes/cartridge/cartridge.cpp +++ b/bsnes/snes/cartridge/cartridge.cpp @@ -14,7 +14,6 @@ namespace memory { MappedRAM bsxflash, bsxram, bsxpram; MappedRAM stArom, stAram; MappedRAM stBrom, stBram; - MappedRAM gbrom, gbram, gbrtc; }; Cartridge cartridge; @@ -25,8 +24,6 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { ram_size = 0; spc7110_data_rom_offset = 0x100000; supergameboy_version = SuperGameBoyVersion::Version1; - supergameboy_ram_size = 0; - supergameboy_rtc_size = 0; has_bsx_slot = false; has_superfx = false; @@ -45,7 +42,7 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { has_serial = false; parse_xml(xml_list); -//print(xml_list[0], "\n"); +//foreach(xml_item, xml_list) print(xml_item, "\n\n"); if(ram_size > 0) { memory::cartram.map(allocate(ram_size, 0xff), ram_size); @@ -65,13 +62,6 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { if(memory::stBrom.data()) memory::stBram.map(allocate(128 * 1024, 0xff), 128 * 1024); } - if(mode == Mode::SuperGameBoy) { - if(memory::gbrom.data()) { - if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); - if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); - } - } - memory::cartrom.write_protect(true); memory::cartram.write_protect(false); memory::cartrtc.write_protect(false); @@ -82,15 +72,11 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { memory::stAram.write_protect(false); memory::stBrom.write_protect(true); memory::stBram.write_protect(false); - memory::gbrom.write_protect(true); - memory::gbram.write_protect(false); - memory::gbrtc.write_protect(false); unsigned checksum = ~0; foreach(n, memory::cartrom ) checksum = crc32_adjust(checksum, n); if(memory::bsxflash.size() != 0 && memory::bsxflash.size() != ~0) foreach(n, memory::bsxflash) checksum = crc32_adjust(checksum, n); if(memory::stArom.size() != 0 && memory::stArom.size() != ~0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); if(memory::stBrom.size() != 0 && memory::stBrom.size() != ~0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); - if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); crc32 = ~checksum; sha256_ctx sha; @@ -105,7 +91,6 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { sha256 = hash; bus.load_cart(); - system.serialize_init(); loaded = true; } @@ -120,9 +105,6 @@ void Cartridge::unload() { memory::stAram.reset(); memory::stBrom.reset(); memory::stBram.reset(); - memory::gbrom.reset(); - memory::gbram.reset(); - memory::gbrtc.reset(); if(loaded == false) return; bus.unload_cart(); diff --git a/bsnes/snes/cartridge/cartridge.hpp b/bsnes/snes/cartridge/cartridge.hpp index fe0b7d08..950ae302 100755 --- a/bsnes/snes/cartridge/cartridge.hpp +++ b/bsnes/snes/cartridge/cartridge.hpp @@ -31,8 +31,6 @@ public: readonly ram_size; readonly spc7110_data_rom_offset; readonly supergameboy_version; - readonly supergameboy_ram_size; - readonly supergameboy_rtc_size; readonly has_bsx_slot; readonly has_superfx; @@ -83,6 +81,7 @@ private: void xml_parse_rom(xml_element&); void xml_parse_ram(xml_element&); + void xml_parse_icd2(xml_element&); void xml_parse_superfx(xml_element&); void xml_parse_sa1(xml_element&); void xml_parse_upd77c25(xml_element&); @@ -109,7 +108,6 @@ namespace memory { extern MappedRAM bsxflash, bsxram, bsxpram; extern MappedRAM stArom, stAram; extern MappedRAM stBrom, stBram; - extern MappedRAM gbrom, gbram, gbrtc; }; extern Cartridge cartridge; diff --git a/bsnes/snes/cartridge/serialization.cpp b/bsnes/snes/cartridge/serialization.cpp index 847b2354..9bf56367 100755 --- a/bsnes/snes/cartridge/serialization.cpp +++ b/bsnes/snes/cartridge/serialization.cpp @@ -24,14 +24,6 @@ void Cartridge::serialize(serializer &s) { if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) { s.array(memory::stBram.data(), memory::stBram.size()); } - - if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) { - s.array(memory::gbram.data(), memory::gbram.size()); - } - - if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) { - s.array(memory::gbrtc.data(), memory::gbrtc.size()); - } } #endif diff --git a/bsnes/snes/cartridge/xml.cpp b/bsnes/snes/cartridge/xml.cpp index 680f1911..c6795cb1 100755 --- a/bsnes/snes/cartridge/xml.cpp +++ b/bsnes/snes/cartridge/xml.cpp @@ -32,12 +32,12 @@ void Cartridge::parse_xml_cartridge(const char *data) { foreach(node, head.element) { if(node.name == "rom") xml_parse_rom(node); if(node.name == "ram") xml_parse_ram(node); + if(node.name == "icd2") xml_parse_icd2(node); if(node.name == "superfx") xml_parse_superfx(node); if(node.name == "sa1") xml_parse_sa1(node); if(node.name == "upd77c25") xml_parse_upd77c25(node); if(node.name == "bsx") xml_parse_bsx(node); if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); - if(node.name == "supergameboy") xml_parse_supergameboy(node); if(node.name == "srtc") xml_parse_srtc(node); if(node.name == "sdd1") xml_parse_sdd1(node); if(node.name == "spc7110") xml_parse_spc7110(node); @@ -59,28 +59,6 @@ void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { } void Cartridge::parse_xml_gameboy(const char *data) { - xml_element document = xml_parse(data); - if(document.element.size() == 0) return; - - foreach(head, document.element) { - if(head.name == "cartridge") { - foreach(attr, head.attribute) { - if(attr.name == "rtc") { - supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; - } - } - - foreach(leaf, head.element) { - if(leaf.name == "ram") { - foreach(attr, leaf.attribute) { - if(attr.name == "size") { - supergameboy_ram_size = hex(attr.content); - } - } - } - } - } - } } void Cartridge::xml_parse_rom(xml_element &root) { @@ -117,6 +95,31 @@ void Cartridge::xml_parse_ram(xml_element &root) { } } +void Cartridge::xml_parse_icd2(xml_element &root) { + if(mode != Mode::SuperGameBoy) return; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; + if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; + } + } + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((Memory&)icd2); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + void Cartridge::xml_parse_superfx(xml_element &root) { has_superfx = true; @@ -382,31 +385,6 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) { } } -void Cartridge::xml_parse_supergameboy(xml_element &root) { - if(mode != Mode::SuperGameBoy) return; - - foreach(attr, root.attribute) { - if(attr.name == "revision") { - if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; - if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; - } - } - - foreach(node, root.element) { - if(node.name == "mmio") { - foreach(leaf, node.element) { - if(leaf.name == "map") { - Mapping m((Memory&)icd2); - foreach(attr, leaf.attribute) { - if(attr.name == "address") xml_parse_address(m, attr.content); - } - mapping.append(m); - } - } - } - } -} - void Cartridge::xml_parse_srtc(xml_element &root) { has_srtc = true; diff --git a/bsnes/snes/chip/icd2/icd2.cpp b/bsnes/snes/chip/icd2/icd2.cpp index 347222d7..27eab8ca 100755 --- a/bsnes/snes/chip/icd2/icd2.cpp +++ b/bsnes/snes/chip/icd2/icd2.cpp @@ -5,12 +5,17 @@ namespace SNES { #include "interface/interface.cpp" #include "mmio/mmio.cpp" +#include "serialization.cpp" ICD2 icd2; void ICD2::Enter() { icd2.enter(); } void ICD2::enter() { while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + if(r6003 & 0x80) { GameBoy::system.run(); step(GameBoy::system.clocks_executed); @@ -63,7 +68,6 @@ void ICD2::reset() { pulselock = true; GameBoy::system.init(this); - GameBoy::cartridge.load(memory::gbrom.data(), memory::gbrom.size()); GameBoy::system.power(); } diff --git a/bsnes/snes/chip/icd2/icd2.hpp b/bsnes/snes/chip/icd2/icd2.hpp index 76bbb10f..179dff68 100755 --- a/bsnes/snes/chip/icd2/icd2.hpp +++ b/bsnes/snes/chip/icd2/icd2.hpp @@ -8,6 +8,8 @@ public: void power(); void reset(); + void serialize(serializer&); + private: #include "interface/interface.hpp" #include "mmio/mmio.hpp" diff --git a/bsnes/snes/chip/icd2/serialization.cpp b/bsnes/snes/chip/icd2/serialization.cpp new file mode 100755 index 00000000..f97260ff --- /dev/null +++ b/bsnes/snes/chip/icd2/serialization.cpp @@ -0,0 +1,39 @@ +#ifdef ICD2_CPP + +void ICD2::serialize(serializer &s) { + Processor::serialize(s); + + GameBoy::system.runtosave(); + GameBoy::system.serialize_all(s); + + for(unsigned n = 0; n < 64; n++) s.array(packet[n].data); + s.integer(packetsize); + + s.integer(joyp_id); + s.integer(joyp15lock); + s.integer(joyp14lock); + s.integer(pulselock); + s.integer(strobelock); + s.integer(packetlock); + s.array(joyp_packet.data); + s.integer(packetoffset); + s.integer(bitdata); + s.integer(bitoffset); + + s.integer(r2181); + s.integer(r2182); + + s.integer(r6000); + s.integer(r6003); + s.integer(r6004); + s.integer(r6005); + s.integer(r6006); + s.integer(r6007); + s.array(r7000); + s.integer(r7800); + s.integer(mlt_req); + + s.array(vram); +} + +#endif diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 7d627a6a..909b7f52 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "073.02"; + static const char Version[] = "073.03"; static const unsigned SerializerVersion = 16; } } @@ -30,6 +30,7 @@ namespace SNES { #include #include #include +#include using namespace nall; #include diff --git a/bsnes/snes/system/serialization.cpp b/bsnes/snes/system/serialization.cpp index 5671406b..ed40fff9 100755 --- a/bsnes/snes/system/serialization.cpp +++ b/bsnes/snes/system/serialization.cpp @@ -57,7 +57,7 @@ void System::serialize_all(serializer &s) { ppu.serialize(s); dsp.serialize(s); -//if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.serialize(s); + if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.serialize(s); if(cartridge.has_superfx()) superfx.serialize(s); if(cartridge.has_sa1()) sa1.serialize(s); if(cartridge.has_upd77c25()) upd77c25.serialize(s); @@ -71,7 +71,7 @@ void System::serialize_all(serializer &s) { if(cartridge.has_serial()) serial.serialize(s); } -//called once upon cartridge load event: perform dry-run state save. +//perform dry-run state save: //determines exactly how many bytes are needed to save state for this cartridge, //as amount varies per game (eg different RAM sizes, special chips, etc.) void System::serialize_init() { diff --git a/bsnes/snes/system/system.cpp b/bsnes/snes/system/system.cpp index 4395d199..9df2fbf7 100755 --- a/bsnes/snes/system/system.cpp +++ b/bsnes/snes/system/system.cpp @@ -167,6 +167,7 @@ void System::power() { if(cartridge.has_serial()) cpu.coprocessors.append(&serial); scheduler.init(); + serialize_init(); input.update(); //video.update(); @@ -206,6 +207,7 @@ void System::reset() { if(cartridge.has_serial()) cpu.coprocessors.append(&serial); scheduler.init(); + serialize_init(); input.port_set_device(0, config.controller_port1); input.port_set_device(1, config.controller_port2); @@ -214,7 +216,6 @@ void System::reset() { } void System::unload() { -//if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.unload(); } void System::scanline() { diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index b8b1d0b0..e9dfe143 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -7,6 +7,7 @@ #include #include #include +#include using namespace nall; #include diff --git a/bsnes/ui/cartridge/cartridge.cpp b/bsnes/ui/cartridge/cartridge.cpp index de6a1732..37b00d64 100755 --- a/bsnes/ui/cartridge/cartridge.cpp +++ b/bsnes/ui/cartridge/cartridge.cpp @@ -56,12 +56,28 @@ bool Cartridge::loadSufamiTurbo(const char *basename, const char *slotAname, con bool Cartridge::loadSuperGameBoy(const char *basename, const char *slotname) { unload(); if(loadCartridge(SNES::memory::cartrom, baseXML, basename) == false) return false; - loadCartridge(SNES::memory::gbrom, slotAXML, slotname); + + file fp; + if(fp.open(slotname, file::mode::read)) { + unsigned size = fp.size(); + uint8_t *data = new uint8_t[size]; + fp.read(data, size); + fp.close(); + + GameBoyCartridge info(data, size); + GameBoy::cartridge.load(info.xml, data, size); + delete[] data; + } + SNES::cartridge.basename = baseName = nall::basename(basename); slotAName = nall::basename(slotname); - SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { baseXML, slotAXML }); - loadMemory(SNES::memory::gbram, slotAName, ".sav"); - loadMemory(SNES::memory::gbrtc, slotAName, ".rtc"); + SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { baseXML, "" }); + + if(GameBoy::cartridge.info.battery && fp.open(string(slotAName, ".sav"), file::mode::read)) { + fp.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, fp.size())); + fp.close(); + } + utility.cartridgeLoaded(); return true; } @@ -75,8 +91,15 @@ void Cartridge::unload() { saveMemory(SNES::memory::bsxpram, baseName, ".psr"); saveMemory(SNES::memory::stAram, slotAName, ".srm"); saveMemory(SNES::memory::stBram, slotBName, ".srm"); - saveMemory(SNES::memory::gbram, slotAName, ".sav"); - saveMemory(SNES::memory::gbrtc, slotAName, ".rtc"); + + if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) { + file fp; + if(GameBoy::cartridge.info.battery && fp.open(string(slotAName, ".sav"), file::mode::write)) { + fp.write(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize); + fp.close(); + } + } + utility.cartridgeUnloaded(); baseName = slotAName = slotBName = ""; }