mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-27 19:10:44 +00:00
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:
parent
f0796e546e
commit
d8a386031f
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
52
gameboy/cartridge/serialization.cpp
Executable file
52
gameboy/cartridge/serialization.cpp
Executable 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
|
@ -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]++);
|
||||
|
@ -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
55
gameboy/cpu/serialization.cpp
Executable 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
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
38
gameboy/lcd/serialization.cpp
Executable 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
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
62
gameboy/system/serialization.cpp
Executable file
62
gameboy/system/serialization.cpp
Executable 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
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
104
nall/gameboy/cartridge.hpp
Executable 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
|
@ -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>
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user