Update to release v000r11.

byuu says:

Changelog:
- adds XML memory mapping to cartridge loading;
  nall/gameboy/cartridge.hpp will bullshit one for now
- still need to add proper MMM01 detection (menu code goes at end of ROM
  apparently)
- added save state support, which works here (but not inside bsnes, go
  figure), to aid in debugging. Expect constant changes for now a long
  while, it's way too early to add states
This commit is contained in:
Tim Allen 2011-01-07 22:11:56 +11:00
parent f0796e546e
commit d8a386031f
25 changed files with 516 additions and 89 deletions

View File

@ -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;
}

View File

@ -21,9 +21,7 @@ struct Cartridge : property<Cartridge> {
};
struct Information {
string name;
uint8 cgbflag;
uint8 sgbflag;
string xml;
Mapper mapper;
bool ram;
@ -43,7 +41,7 @@ struct Cartridge : property<Cartridge> {
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<Cartridge> {
void power();
void map();
void serialize(serializer&);
Cartridge();
~Cartridge();
};

View File

@ -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

View File

@ -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]++);

View File

@ -67,6 +67,8 @@ struct CPU : Processor, MMIO {
void interrupt_test();
void interrupt_exec(uint16 pc);
void power();
void serialize(serializer&);
CPU();
};

55
gameboy/cpu/serialization.cpp Executable file
View File

@ -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

View File

@ -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) {

View File

@ -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 <libco/libco.h>
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/property.hpp>
#include <nall/serializer.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
using namespace nall;

View File

@ -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() {
}
}

View File

@ -63,6 +63,9 @@ struct LCD : Processor, MMIO {
void render_obj();
void power();
void serialize(serializer&);
LCD();
};
extern LCD lcd;

38
gameboy/lcd/serialization.cpp Executable file
View File

@ -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

View File

@ -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() {
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -1,9 +1,13 @@
struct Scheduler {
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
enum class ExitReason : unsigned { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason;
cothread_t host_thread;
cothread_t active_thread;
void enter();
void exit();
void exit(ExitReason);
void swapto(Processor&);
void init();

View File

@ -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

View File

@ -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();
}
}

View File

@ -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 <gameboy/interface/interface.hpp>

104
nall/gameboy/cartridge.hpp Executable file
View File

@ -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 = "<?xml version='1.0' encoding='UTF-8'?>\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 << "<cartridge mapper='" << info.mapper << "'";
if(info.rtc) xml << " rtc='true'";
if(info.rumble) xml << " rumble='true'";
xml << ">\n";
xml << " <rom size='" << hex(romsize) << "'/>\n"; //TODO: trust/check info.romsize?
if(info.ramsize > 0)
xml << " <ram size='" << hex(info.ramsize) << "' battery='" << info.battery << "'/>\n";
xml << "</cartridge>\n";
}
}
#endif

View File

@ -2,6 +2,7 @@
#include <nall/foreach.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/gameboy/cartridge.hpp>
using namespace nall;
#include <ruby/ruby.hpp>

View File

@ -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();
};

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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;