mirror of https://github.com/bsnes-emu/bsnes.git
Update to v103r21 release.
byuu says: Changelog: - gb: added TAMA emulation [thanks to endrift for the initial notes] - gb: save RTC memory to disk (MBC3 doesn't write to said memory yet; TAMA doesn't emulate it yet) - gb: expect MMM01 boot loader to be at end of ROM instead of start - gb: store MBC2 save RAM as 256-bytes (512x4-bit) instead of 512-bytes (with padding) - gb: major cleanups to every cartridge mapper; moved to Mapper class instead of MMIO class - gb: don't serialize all mapper states with every save state; only serialize the active mapper - gb: serialize RAM even if a battery isn't present¹ - gb/cartridge: removed unnecessary code; refactored other code to eliminate duplication of functions - icarus: improve GB(C) heuristics generation to not include filenames for cartridges without battery backup - icarus: remove incorrect rearrangement of MMM01 ROM data - md/vdp: fix CRAM reads -- fixes Sonic Spinball colors [hex\_usr] - tomoko: hide the main higan window when entering fullscreen exclusive mode; helps with multi-monitor setups - tomoko: destroy ruby drivers before calling Application::quit() [Screwtape] - libco: add settings.h and defines to fiber, ucontext [Screwtape] ¹: this is one of those crystal clear indications that nobody's actually playing the higan DMG/CGB cores, or at least not with save states. This was a major mistake. Note: I can't find any official documentation that `GL_ALPHA_TEST` was removed from OpenGL 3.2. Since it's not hurting anything except showing some warnings in debug mode, I'm just going to leave it there for now.
This commit is contained in:
parent
d5c09c9ab1
commit
80841deaa5
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "103.20";
|
||||
static const string Version = "103.21";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace GameBoy {
|
||||
|
||||
Cartridge cartridge;
|
||||
#include "mbc0/mbc0.cpp"
|
||||
#include "mbc1/mbc1.cpp"
|
||||
#include "mbc1m/mbc1m.cpp"
|
||||
|
@ -11,8 +12,8 @@ namespace GameBoy {
|
|||
#include "mmm01/mmm01.cpp"
|
||||
#include "huc1/huc1.cpp"
|
||||
#include "huc3/huc3.cpp"
|
||||
#include "tama/tama.cpp"
|
||||
#include "serialization.cpp"
|
||||
Cartridge cartridge;
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
|
@ -43,49 +44,40 @@ auto Cartridge::load() -> bool {
|
|||
auto board = document["board"];
|
||||
information.title = document["information/title"].text();
|
||||
|
||||
auto mapperid = document["board/mapper"].text();
|
||||
if(mapperid == "none" ) information.mapper = Mapper::MBC0;
|
||||
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1;
|
||||
if(mapperid == "MBC1M") information.mapper = Mapper::MBC1M;
|
||||
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2;
|
||||
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3;
|
||||
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5;
|
||||
if(mapperid == "MMM01") information.mapper = Mapper::MMM01;
|
||||
if(mapperid == "HuC1" ) information.mapper = Mapper::HuC1;
|
||||
if(mapperid == "HuC3" ) information.mapper = Mapper::HuC3;
|
||||
auto mapperID = document["board/mapper"].text();
|
||||
if(mapperID == "MBC0" ) mapper = &mbc0;
|
||||
if(mapperID == "MBC1" ) mapper = &mbc1;
|
||||
if(mapperID == "MBC1M") mapper = &mbc1m;
|
||||
if(mapperID == "MBC2" ) mapper = &mbc2;
|
||||
if(mapperID == "MBC3" ) mapper = &mbc3;
|
||||
if(mapperID == "MBC5" ) mapper = &mbc5;
|
||||
if(mapperID == "MMM01") mapper = &mmm01;
|
||||
if(mapperID == "HuC1" ) mapper = &huc1;
|
||||
if(mapperID == "HuC3" ) mapper = &huc3;
|
||||
if(mapperID == "TAMA" ) mapper = &tama;
|
||||
|
||||
information.rtc = false;
|
||||
information.rumble = false;
|
||||
|
||||
rom.size = max(32768u, board["rom/size"].natural());
|
||||
rom.size = max(0x4000, document["board/rom/size"].natural());
|
||||
rom.data = (uint8*)memory::allocate(rom.size, 0xff);
|
||||
|
||||
ram.size = board["ram/size"].natural();
|
||||
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
|
||||
|
||||
if(auto name = board["rom/name"].text()) {
|
||||
if(auto name = document["board/rom/name"].text()) {
|
||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
|
||||
fp->read(rom.data, min(rom.size, fp->size()));
|
||||
}
|
||||
}
|
||||
if(auto name = board["ram/name"].text()) {
|
||||
|
||||
ram.size = document["board/ram/size"].natural();
|
||||
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
|
||||
if(auto name = document["board/ram/name"].text()) {
|
||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
|
||||
fp->read(ram.data, min(ram.size, fp->size()));
|
||||
}
|
||||
}
|
||||
|
||||
information.battery = (bool)board["ram/name"];
|
||||
|
||||
switch(information.mapper) { default:
|
||||
case Mapper::MBC0: mapper = &mbc0; break;
|
||||
case Mapper::MBC1: mapper = &mbc1; break;
|
||||
case Mapper::MBC1M: mapper = &mbc1m; break;
|
||||
case Mapper::MBC2: mapper = &mbc2; break;
|
||||
case Mapper::MBC3: mapper = &mbc3; break;
|
||||
case Mapper::MBC5: mapper = &mbc5; break;
|
||||
case Mapper::MMM01: mapper = &mmm01; break;
|
||||
case Mapper::HuC1: mapper = &huc1; break;
|
||||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
rtc.size = document["board/rtc/size"].natural();
|
||||
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
|
||||
if(auto name = document["board/rtc/name"].text()) {
|
||||
if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
|
||||
fp->read(rtc.data, min(rtc.size, fp->size()));
|
||||
}
|
||||
}
|
||||
|
||||
information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
|
||||
|
@ -100,35 +92,21 @@ auto Cartridge::save() -> void {
|
|||
fp->write(ram.data, ram.size);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto name = document["board/rtc/name"].text()) {
|
||||
if(auto fp = platform->open(pathID(), name, File::Write)) {
|
||||
fp->write(rtc.data, rtc.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::unload() -> void {
|
||||
delete[] rom.data;
|
||||
delete[] ram.data;
|
||||
delete[] rtc.data;
|
||||
rom = {};
|
||||
ram = {};
|
||||
}
|
||||
|
||||
auto Cartridge::readROM(uint addr) -> uint8 {
|
||||
if(addr >= rom.size) addr %= rom.size;
|
||||
return rom.data[addr];
|
||||
}
|
||||
|
||||
auto Cartridge::writeROM(uint addr, uint8 data) -> void {
|
||||
if(addr >= rom.size) addr %= rom.size;
|
||||
rom.data[addr] = data;
|
||||
}
|
||||
|
||||
auto Cartridge::readRAM(uint addr) -> uint8 {
|
||||
if(ram.size == 0) return 0xff;
|
||||
if(addr >= ram.size) addr %= ram.size;
|
||||
return ram.data[addr];
|
||||
}
|
||||
|
||||
auto Cartridge::writeRAM(uint addr, uint8 data) -> void {
|
||||
if(ram.size == 0) return;
|
||||
if(addr >= ram.size) addr %= ram.size;
|
||||
ram.data[addr] = data;
|
||||
rtc = {};
|
||||
}
|
||||
|
||||
auto Cartridge::readIO(uint16 addr) -> uint8 {
|
||||
|
@ -140,10 +118,10 @@ auto Cartridge::readIO(uint16 addr) -> uint8 {
|
|||
if(Model::GameBoyColor()) data = system.bootROM.cgb;
|
||||
if(Model::SuperGameBoy()) data = system.bootROM.sgb;
|
||||
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr];
|
||||
if(addr >= 0x0200 && addr <= 0x08ff && Model::GameBoyColor()) return data[addr - 256];
|
||||
if(addr >= 0x0200 && addr <= 0x08ff && Model::GameBoyColor()) return data[addr - 0x100];
|
||||
}
|
||||
|
||||
return mapper->readIO(addr);
|
||||
return mapper->read(addr);
|
||||
}
|
||||
|
||||
auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
|
||||
|
@ -152,25 +130,33 @@ auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
|
|||
return;
|
||||
}
|
||||
|
||||
mapper->writeIO(addr, data);
|
||||
mapper->write(addr, data);
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
bootromEnable = true;
|
||||
|
||||
mbc0.power();
|
||||
mbc1.power();
|
||||
mbc1m.power();
|
||||
mbc2.power();
|
||||
mbc3.power();
|
||||
mbc5.power();
|
||||
mmm01.power();
|
||||
huc1.power();
|
||||
huc3.power();
|
||||
|
||||
for(uint n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
|
||||
for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
|
||||
bus.mmio[0xff50] = this;
|
||||
|
||||
bootromEnable = true;
|
||||
|
||||
mapper->power();
|
||||
}
|
||||
|
||||
auto Cartridge::second() -> void {
|
||||
mapper->second();
|
||||
}
|
||||
|
||||
auto Cartridge::Memory::read(uint address) const -> uint8 {
|
||||
if(!size) return 0xff;
|
||||
if(address >= size) address %= size;
|
||||
return data[address];
|
||||
}
|
||||
|
||||
auto Cartridge::Memory::write(uint address, uint8 byte) -> void {
|
||||
if(!size) return;
|
||||
if(address >= size) address %= size;
|
||||
data[address] = byte;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,19 +8,41 @@ struct Cartridge : MMIO {
|
|||
auto save() -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto readROM(uint addr) -> uint8;
|
||||
auto writeROM(uint addr, uint8 data) -> void;
|
||||
|
||||
auto readRAM(uint addr) -> uint8;
|
||||
auto writeRAM(uint addr, uint8 data) -> void;
|
||||
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
auto readIO(uint16 address) -> uint8;
|
||||
auto writeIO(uint16 address, uint8 data) -> void;
|
||||
|
||||
auto power() -> void;
|
||||
auto second() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string sha256;
|
||||
string manifest;
|
||||
string title;
|
||||
} information;
|
||||
|
||||
struct Memory {
|
||||
auto read(uint address) const -> uint8;
|
||||
auto write(uint address, uint8 data) -> void;
|
||||
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
} rom, ram, rtc;
|
||||
|
||||
bool bootromEnable = true;
|
||||
|
||||
private:
|
||||
struct Mapper {
|
||||
virtual auto second() -> void {}
|
||||
virtual auto read(uint16 address) -> uint8 = 0;
|
||||
virtual auto write(uint16 address, uint8 data) -> void = 0;
|
||||
virtual auto power() -> void = 0;
|
||||
virtual auto serialize(serializer&) -> void = 0;
|
||||
};
|
||||
Mapper* mapper = nullptr;
|
||||
|
||||
#include "mbc0/mbc0.hpp"
|
||||
#include "mbc1/mbc1.hpp"
|
||||
#include "mbc1m/mbc1m.hpp"
|
||||
|
@ -30,40 +52,7 @@ struct Cartridge : MMIO {
|
|||
#include "mmm01/mmm01.hpp"
|
||||
#include "huc1/huc1.hpp"
|
||||
#include "huc3/huc3.hpp"
|
||||
|
||||
enum Mapper : uint {
|
||||
MBC0,
|
||||
MBC1,
|
||||
MBC1M,
|
||||
MBC2,
|
||||
MBC3,
|
||||
MBC5,
|
||||
MMM01,
|
||||
HuC1,
|
||||
HuC3,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string sha256;
|
||||
string manifest;
|
||||
string title;
|
||||
|
||||
Mapper mapper = Mapper::Unknown;
|
||||
boolean ram;
|
||||
boolean battery;
|
||||
boolean rtc;
|
||||
boolean rumble;
|
||||
} information;
|
||||
|
||||
struct Memory {
|
||||
uint8* data = nullptr;
|
||||
uint size = 0;
|
||||
} rom, ram;
|
||||
|
||||
MMIO* mapper = nullptr;
|
||||
bool bootromEnable = true;
|
||||
#include "tama/tama.hpp"
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
||||
|
|
|
@ -1,49 +1,54 @@
|
|||
auto Cartridge::HuC1::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::HuC1::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.select << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::HuC1::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
ram.writable = data.bits(0,3) == 0x0a;
|
||||
auto Cartridge::HuC1::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.writable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
rom.select = data + (data == 0);
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank = data;
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
ram.select = data;
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
model = data.bit(0);
|
||||
if((address & 0xe000) == 0x6000) { //$6000-7fff
|
||||
io.model = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!ram.writable) return;
|
||||
return cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.writable) return;
|
||||
return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::HuC1::power() -> void {
|
||||
rom.select = 0x01;
|
||||
ram.writable = false;
|
||||
ram.select = 0x00;
|
||||
model = 0;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::HuC1::serialize(serializer& s) -> void {
|
||||
s.integer(io.model);
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.writable);
|
||||
s.integer(io.ram.bank);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
struct HuC1 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct HuC1 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool writable;
|
||||
uint8 select;
|
||||
} ram;
|
||||
bool model;
|
||||
struct IO {
|
||||
uint1 model;
|
||||
struct ROM {
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 writable;
|
||||
uint8 bank;
|
||||
} ram;
|
||||
} io;
|
||||
} huc1;
|
||||
|
|
|
@ -1,49 +1,48 @@
|
|||
auto Cartridge::HuC3::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::HuC3::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.select << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
return 0x01; //does not return open collection
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return 0x01; //does not return open collection
|
||||
return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::HuC3::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
ram.enable = data.bits(0,3) == 0x0a;
|
||||
auto Cartridge::HuC3::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
rom.select = data;
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
ram.select = data;
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
//unknown purpose
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
return;
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return;
|
||||
return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::HuC3::power() -> void {
|
||||
rom.select = 0x01;
|
||||
ram.enable = false;
|
||||
ram.select = 0x00;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::HuC3::serialize(serializer& s) -> void {
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
s.integer(io.ram.bank);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
struct HuC3 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct HuC3 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
uint8 select;
|
||||
} ram;
|
||||
struct IO {
|
||||
struct ROM {
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable;
|
||||
uint8 bank;
|
||||
} ram;
|
||||
} io;
|
||||
} huc3;
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
auto Cartridge::MBC0::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0x8000) == 0x0000) { //$0000-7fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::MBC0::read(uint16 address) -> uint8 {
|
||||
if((address & 0x8000) == 0x0000) { //$0000-7fff
|
||||
return cartridge.rom.read(address.bits(0,14));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.readRAM((uint13)addr);
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.ram.read(address.bits(0,12));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC0::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
cartridge.writeRAM((uint13)addr, data);
|
||||
auto Cartridge::MBC0::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
cartridge.ram.write(address.bits(0,12), data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC0::power() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::MBC0::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
struct MBC0 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MBC0 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
} mbc0;
|
||||
|
|
|
@ -1,66 +1,67 @@
|
|||
auto Cartridge::MBC1::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::MBC1::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
if(mode == 0) {
|
||||
return cartridge.readROM(ram.select << 19 | rom.select << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
if(io.mode == 0) {
|
||||
return cartridge.rom.read(io.ram.bank << 19 | io.rom.bank << 14 | address.bits(0,13));
|
||||
} else {
|
||||
return cartridge.readROM(rom.select << 14 | (uint14)addr);
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) {
|
||||
if(mode == 0) {
|
||||
return cartridge.readRAM((uint13)addr);
|
||||
} else {
|
||||
return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
}
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return 0xff;
|
||||
if(io.mode == 0) {
|
||||
return cartridge.ram.read(address.bits(0,12));
|
||||
} else {
|
||||
return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
ram.enable = (data & 0x0f) == 0x0a;
|
||||
auto Cartridge::MBC1::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
rom.select = (data & 0x1f) + ((data & 0x1f) == 0);
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank = data.bits(0,4);
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
ram.select = data & 0x03;
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data.bits(0,1);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
mode = data & 0x01;
|
||||
if((address & 0xe000) == 0x6000) { //$6000-7fff
|
||||
io.mode = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) {
|
||||
if(mode == 0) {
|
||||
cartridge.writeRAM(addr & 0x1fff, data);
|
||||
} else {
|
||||
cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
}
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return;
|
||||
if(io.mode == 0) {
|
||||
return cartridge.ram.write(address.bits(0,12), data);
|
||||
} else {
|
||||
return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1::power() -> void {
|
||||
rom.select = 0x01;
|
||||
ram.enable = false;
|
||||
ram.select = 0x00;
|
||||
mode = 0;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1::serialize(serializer& s) -> void {
|
||||
s.integer(io.mode);
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
s.integer(io.ram.bank);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
struct MBC1 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MBC1 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer& s) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
uint8 select;
|
||||
} ram;
|
||||
bool mode;
|
||||
struct IO {
|
||||
uint1 mode;
|
||||
struct ROM {
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable;
|
||||
uint8 bank;
|
||||
} ram;
|
||||
} io;
|
||||
} mbc1;
|
||||
|
|
|
@ -1,40 +1,43 @@
|
|||
auto Cartridge::MBC1M::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
if(mode == 0) return cartridge.readROM((uint14)addr);
|
||||
return cartridge.readROM(rom.hi << 18 | (uint14)addr);
|
||||
auto Cartridge::MBC1M::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
if(io.mode == 0) return cartridge.rom.read(address.bits(0,13));
|
||||
return cartridge.rom.read(io.rom.bank.bits(4,5) << 18 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.hi << 18 | rom.lo << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.readRAM((uint13)addr);
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
return cartridge.ram.read(address.bits(0,12));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1M::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
rom.lo = data.bits(0,3);
|
||||
auto Cartridge::MBC1M::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank.bits(0,3) = data.bits(0,3);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
rom.hi = data.bits(0,1);
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.rom.bank.bits(4,5) = data.bits(0,1);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
mode = data.bit(0);
|
||||
if((address & 0xe000) == 0x6000) { //$6000-7fff
|
||||
io.mode = data.bit(0);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
cartridge.writeRAM((uint13)addr, data);
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
cartridge.ram.write(address.bits(0,13), data);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1M::power() -> void {
|
||||
rom.lo = 1;
|
||||
rom.hi = 0;
|
||||
mode = 0;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC1M::serialize(serializer& s) -> void {
|
||||
s.integer(io.mode);
|
||||
s.integer(io.rom.bank);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
struct MBC1M : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MBC1M : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint4 lo;
|
||||
uint2 hi;
|
||||
} rom;
|
||||
uint1 mode;
|
||||
struct IO {
|
||||
uint1 mode;
|
||||
struct ROM {
|
||||
uint6 bank = 0x01;
|
||||
} rom;
|
||||
} io;
|
||||
} mbc1m;
|
||||
|
|
|
@ -1,38 +1,61 @@
|
|||
auto Cartridge::MBC2::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::MBC2::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.select << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
|
||||
if(ram.enable) return cartridge.readRAM((uint9)addr);
|
||||
return 0xff;
|
||||
if((address & 0xee01) == 0xa000) { //$a000-a1ff (even)
|
||||
if(!io.ram.enable) return 0xff;
|
||||
auto ram = cartridge.ram.read(address.bits(1,8));
|
||||
return 0xf0 | ram.bits(0,3);
|
||||
}
|
||||
|
||||
if((address & 0xee01) == 0xa001) { //$a000-a1ff (odd)
|
||||
if(!io.ram.enable) return 0xff;
|
||||
auto ram = cartridge.ram.read(address.bits(1,8));
|
||||
return 0xf0 | ram.bits(4,7);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC2::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
if(!addr.bit(8)) ram.enable = data.bits(0,3) == 0x0a;
|
||||
auto Cartridge::MBC2::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
if(!address.bit(8)) io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
if( addr.bit(8)) rom.select = data.bits(0,3) + (data.bits(0,3) == 0);
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
if(address.bit(8)) io.rom.bank = data.bits(0,3);
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xee00) == 0xa000) { //$a000-a1ff
|
||||
if(ram.enable) cartridge.writeRAM((uint9)addr, data.bits(0,3));
|
||||
if((address & 0xee01) == 0xa000) { //$a000-a1ff (even)
|
||||
if(!io.ram.enable) return;
|
||||
auto ram = cartridge.ram.read(address.bits(1,8));
|
||||
ram.bits(0,3) = data.bits(0,3);
|
||||
cartridge.ram.write(address.bits(1,8), ram);
|
||||
return;
|
||||
}
|
||||
|
||||
if((address & 0xee01) == 0xa001) { //$a000-a1ff (odd)
|
||||
if(!io.ram.enable) return;
|
||||
auto ram = cartridge.ram.read(address.bits(1,8));
|
||||
ram.bits(4,7) = data.bits(0,3);
|
||||
cartridge.ram.write(address.bits(1,8), ram);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC2::power() -> void {
|
||||
rom.select = 0x01;
|
||||
ram.enable = false;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC2::serialize(serializer& s) -> void {
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
struct MBC2 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MBC2 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
} ram;
|
||||
struct IO {
|
||||
struct ROM {
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable = 0;
|
||||
} ram;
|
||||
} io;
|
||||
} mbc2;
|
||||
|
|
|
@ -1,118 +1,113 @@
|
|||
auto Cartridge::MBC3::second() -> void {
|
||||
if(!rtc.halt) {
|
||||
if(++rtc.second >= 60) {
|
||||
rtc.second = 0;
|
||||
if(++rtc.minute >= 60) {
|
||||
rtc.minute = 0;
|
||||
if(++rtc.hour >= 24) {
|
||||
rtc.hour = 0;
|
||||
if(++rtc.day >= 512) {
|
||||
rtc.day = 0;
|
||||
rtc.dayCarry = true;
|
||||
}
|
||||
if(io.rtc.halt) return;
|
||||
if(++io.rtc.second >= 60) {
|
||||
io.rtc.second = 0;
|
||||
if(++io.rtc.minute >= 60) {
|
||||
io.rtc.minute = 0;
|
||||
if(++io.rtc.hour >= 24) {
|
||||
io.rtc.hour = 0;
|
||||
if(++io.rtc.day == 0) {
|
||||
io.rtc.dayCarry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC3::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::MBC3::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.select<< 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) {
|
||||
if(ram.select <= 0x03) {
|
||||
return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
}
|
||||
if(ram.select == 0x08) return rtc.latchSecond;
|
||||
if(ram.select == 0x09) return rtc.latchMinute;
|
||||
if(ram.select == 0x0a) return rtc.latchHour;
|
||||
if(ram.select == 0x0b) return rtc.latchDay;
|
||||
if(ram.select == 0x0c) return rtc.latchDayCarry << 7 | rtc.latchDay >> 8;
|
||||
}
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return 0xff;
|
||||
if(io.ram.bank <= 0x03) return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
if(io.ram.bank == 0x08) return io.rtc.latchSecond;
|
||||
if(io.ram.bank == 0x09) return io.rtc.latchMinute;
|
||||
if(io.ram.bank == 0x0a) return io.rtc.latchHour;
|
||||
if(io.ram.bank == 0x0b) return io.rtc.latchDay;
|
||||
if(io.ram.bank == 0x0c) return io.rtc.latchDayCarry << 7 | io.rtc.latchDay >> 8;
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC3::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
ram.enable = (data & 0x0f) == 0x0a;
|
||||
auto Cartridge::MBC3::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
rom.select = (data & 0x7f) + ((data & 0x7f) == 0);
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank = data.bits(0,6);
|
||||
if(!io.rom.bank) io.rom.bank = 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
ram.select = data;
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(rtc.latch == 0 && data == 1) {
|
||||
rtc.latchSecond = rtc.second;
|
||||
rtc.latchMinute = rtc.minute;
|
||||
rtc.latchHour = rtc.hour;
|
||||
rtc.latchDay = rtc.day;
|
||||
rtc.latchDayCarry = rtc.dayCarry;
|
||||
if((address & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(io.rtc.latch == 0 && data == 1) {
|
||||
io.rtc.latchSecond = io.rtc.second;
|
||||
io.rtc.latchMinute = io.rtc.minute;
|
||||
io.rtc.latchHour = io.rtc.hour;
|
||||
io.rtc.latchDay = io.rtc.day;
|
||||
io.rtc.latchDayCarry = io.rtc.dayCarry;
|
||||
}
|
||||
rtc.latch = data;
|
||||
io.rtc.latch = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) {
|
||||
if(ram.select <= 0x03) {
|
||||
cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
} else if(ram.select == 0x08) {
|
||||
if(data >= 60) data = 0;
|
||||
rtc.second = data;
|
||||
} else if(ram.select == 0x09) {
|
||||
if(data >= 60) data = 0;
|
||||
rtc.minute = data;
|
||||
} else if(ram.select == 0x0a) {
|
||||
if(data >= 24) data = 0;
|
||||
rtc.hour = data;
|
||||
} else if(ram.select == 0x0b) {
|
||||
rtc.day = (rtc.day & 0x0100) | data;
|
||||
} else if(ram.select == 0x0c) {
|
||||
rtc.day = ((data & 1) << 8) | (rtc.day & 0xff);
|
||||
rtc.halt = data & 0x40;
|
||||
rtc.dayCarry = data & 0x80;
|
||||
}
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return;
|
||||
if(io.ram.bank <= 0x03) {
|
||||
cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
} else if(io.ram.bank == 0x08) {
|
||||
if(data >= 60) data = 0;
|
||||
io.rtc.second = data;
|
||||
} else if(io.ram.bank == 0x09) {
|
||||
if(data >= 60) data = 0;
|
||||
io.rtc.minute = data;
|
||||
} else if(io.ram.bank == 0x0a) {
|
||||
if(data >= 24) data = 0;
|
||||
io.rtc.hour = data;
|
||||
} else if(io.ram.bank == 0x0b) {
|
||||
io.rtc.day.bits(0,7) = data.bits(0,7);
|
||||
} else if(io.ram.bank == 0x0c) {
|
||||
io.rtc.day.bit(8) = data.bit(0);
|
||||
io.rtc.halt = data.bit(6);
|
||||
io.rtc.dayCarry = data.bit(7);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC3::power() -> void {
|
||||
rom.select = 0x01;
|
||||
|
||||
ram.enable = false;
|
||||
ram.select = 0x00;
|
||||
|
||||
rtc.latch = 0;
|
||||
|
||||
rtc.halt = true;
|
||||
rtc.second = 0;
|
||||
rtc.minute = 0;
|
||||
rtc.hour = 0;
|
||||
rtc.day = 0;
|
||||
rtc.dayCarry = false;
|
||||
|
||||
rtc.latchSecond = 0;
|
||||
rtc.latchMinute = 0;
|
||||
rtc.latchHour = 0;
|
||||
rtc.latchDay = 0;
|
||||
rtc.latchDayCarry = false;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC3::serialize(serializer& s) -> void {
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
s.integer(io.ram.bank);
|
||||
s.integer(io.rtc.halt);
|
||||
s.integer(io.rtc.latch);
|
||||
s.integer(io.rtc.second);
|
||||
s.integer(io.rtc.minute);
|
||||
s.integer(io.rtc.hour);
|
||||
s.integer(io.rtc.day);
|
||||
s.integer(io.rtc.dayCarry);
|
||||
s.integer(io.rtc.latchSecond);
|
||||
s.integer(io.rtc.latchMinute);
|
||||
s.integer(io.rtc.latchHour);
|
||||
s.integer(io.rtc.latchDay);
|
||||
s.integer(io.rtc.latchDayCarry);
|
||||
}
|
||||
|
|
|
@ -1,30 +1,33 @@
|
|||
struct MBC3 : MMIO {
|
||||
struct MBC3 : Mapper {
|
||||
auto second() -> void;
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer& s) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
uint8 select;
|
||||
} ram;
|
||||
struct RTC {
|
||||
bool latch;
|
||||
struct IO {
|
||||
struct ROM {
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable;
|
||||
uint8 bank;
|
||||
} ram;
|
||||
struct RTC {
|
||||
uint1 halt = true;
|
||||
uint1 latch;
|
||||
|
||||
bool halt;
|
||||
uint second;
|
||||
uint minute;
|
||||
uint hour;
|
||||
uint day;
|
||||
bool dayCarry;
|
||||
uint8 second;
|
||||
uint8 minute;
|
||||
uint8 hour;
|
||||
uint9 day;
|
||||
uint1 dayCarry;
|
||||
|
||||
uint latchSecond;
|
||||
uint latchMinute;
|
||||
uint latchHour;
|
||||
uint latchDay;
|
||||
uint latchDayCarry;
|
||||
} rtc;
|
||||
uint8 latchSecond;
|
||||
uint8 latchMinute;
|
||||
uint8 latchHour;
|
||||
uint9 latchDay;
|
||||
uint1 latchDayCarry;
|
||||
} rtc;
|
||||
} io;
|
||||
} mbc3;
|
||||
|
|
|
@ -1,49 +1,53 @@
|
|||
auto Cartridge::MBC5::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(addr);
|
||||
auto Cartridge::MBC5::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(rom.select << 14 | (uint14)addr);
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
return 0xff;
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return 0xff;
|
||||
return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MBC5::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
ram.enable = data.bits(0,3) == 0x0a;
|
||||
auto Cartridge::MBC5::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xf000) == 0x2000) { //$2000-2fff
|
||||
rom.select.byte(0) = data;
|
||||
if((address & 0xf000) == 0x2000) { //$2000-2fff
|
||||
io.rom.bank.bits(0,7) = data.bits(0,7);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xf000) == 0x3000) { //$3000-3fff
|
||||
rom.select.byte(1) = data.bit(0);
|
||||
if((address & 0xf000) == 0x3000) { //$3000-3fff
|
||||
io.rom.bank.bit(8) = data.bit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
ram.select = data.bits(0,3);
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data.bits(0,3);
|
||||
return;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
return;
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return;
|
||||
return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MBC5::power() -> void {
|
||||
rom.select = 0x001;
|
||||
ram.enable = false;
|
||||
ram.select = 0x00;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MBC5::serialize(serializer& s) -> void {
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
s.integer(io.ram.bank);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
struct MBC5 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MBC5 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint9 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
uint4 select;
|
||||
} ram;
|
||||
struct IO {
|
||||
struct ROM {
|
||||
uint9 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable;
|
||||
uint4 bank;
|
||||
} ram;
|
||||
} io;
|
||||
} mbc5;
|
||||
|
|
|
@ -1,60 +1,65 @@
|
|||
auto Cartridge::MMM01::readIO(uint16 addr) -> uint8 {
|
||||
if((addr & 0x8000) == 0x0000) { //$0000-7fff
|
||||
if(mode == 0) return cartridge.readROM(addr);
|
||||
}
|
||||
auto Cartridge::MMM01::read(uint16 address) -> uint8 {
|
||||
if(io.mode == 0) {
|
||||
if((address & 0x8000) == 0x0000) { //$0000-7fff
|
||||
return cartridge.rom.read(cartridge.rom.size - 0x8000 + address.bits(0,14));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.readROM(0x8000 + (rom.base << 14) + (uint14)addr);
|
||||
}
|
||||
return 0xff;
|
||||
} else {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read((io.rom.base << 14) + address.bits(0,13));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.readROM(0x8000 + (rom.base << 14) + (rom.select<< 14) + (uint14)addr);
|
||||
}
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read((io.rom.base << 14) + (io.rom.bank << 14) + address.bits(0,13));
|
||||
}
|
||||
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return 0xff;
|
||||
return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) return cartridge.readRAM(ram.select << 13 | (uint13)addr);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::MMM01::writeIO(uint16 addr, uint8 data) -> void {
|
||||
if((addr & 0xe000) == 0x0000) { //$0000-1fff
|
||||
if(mode == 0) {
|
||||
mode = 1;
|
||||
} else {
|
||||
ram.enable= data.bits(0,3) == 0x0a;
|
||||
auto Cartridge::MMM01::write(uint16 address, uint8 data) -> void {
|
||||
if(io.mode == 0) {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x2000) { //$2000-3fff
|
||||
if(mode == 0) {
|
||||
rom.base = data.bits(0,5);
|
||||
} else {
|
||||
rom.select = data;
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.base = data.bits(0,5);
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x4000) { //$4000-5fff
|
||||
if(mode == 1) {
|
||||
ram.select = data;
|
||||
} else {
|
||||
if((address & 0xe000) == 0x0000) { //$0000-1fff
|
||||
io.ram.enable = data.bits(0,3) == 0x0a;
|
||||
}
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
//unknown purpose
|
||||
}
|
||||
if((address & 0xe000) == 0x2000) { //$2000-3fff
|
||||
io.rom.bank = data;
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
|
||||
if((address & 0xe000) == 0x4000) { //$4000-5fff
|
||||
io.ram.bank = data;
|
||||
}
|
||||
|
||||
if((address & 0xe000) == 0xa000) { //$a000-bfff
|
||||
if(!io.ram.enable) return;
|
||||
cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::MMM01::power() -> void {
|
||||
rom.base = 0x00;
|
||||
rom.select = 0x01;
|
||||
ram.enable = false;
|
||||
ram.select = 0x00;
|
||||
mode = 0;
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::MMM01::serialize(serializer& s) -> void {
|
||||
s.integer(io.mode);
|
||||
s.integer(io.rom.base);
|
||||
s.integer(io.rom.bank);
|
||||
s.integer(io.ram.enable);
|
||||
s.integer(io.ram.bank);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
struct MMM01 : MMIO {
|
||||
auto readIO(uint16 addr) -> uint8;
|
||||
auto writeIO(uint16 addr, uint8 data) -> void;
|
||||
struct MMM01 : Mapper {
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer& s) -> void;
|
||||
|
||||
struct ROM {
|
||||
uint6 base;
|
||||
uint8 select;
|
||||
} rom;
|
||||
struct RAM {
|
||||
bool enable;
|
||||
uint8 select;
|
||||
} ram;
|
||||
bool mode;
|
||||
struct IO {
|
||||
uint1 mode;
|
||||
struct ROM {
|
||||
uint6 base;
|
||||
uint8 bank = 0x01;
|
||||
} rom;
|
||||
struct RAM {
|
||||
uint1 enable;
|
||||
uint8 bank;
|
||||
} ram;
|
||||
} io;
|
||||
} mmm01;
|
||||
|
|
|
@ -1,51 +1,8 @@
|
|||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(information.battery) s.array(ram.data, ram.size);
|
||||
if(ram.size) s.array(ram.data, ram.size);
|
||||
if(rtc.size) s.array(rtc.data, rtc.size);
|
||||
|
||||
s.integer(bootromEnable);
|
||||
|
||||
s.integer(mbc1.rom.select);
|
||||
s.integer(mbc1.ram.enable);
|
||||
s.integer(mbc1.ram.select);
|
||||
s.integer(mbc1.mode);
|
||||
|
||||
s.integer(mbc1m.rom.lo);
|
||||
s.integer(mbc1m.rom.hi);
|
||||
s.integer(mbc1m.mode);
|
||||
|
||||
s.integer(mbc2.rom.select);
|
||||
s.integer(mbc2.ram.enable);
|
||||
|
||||
s.integer(mbc3.rom.select);
|
||||
s.integer(mbc3.ram.enable);
|
||||
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.dayCarry);
|
||||
s.integer(mbc3.rtc.latchSecond);
|
||||
s.integer(mbc3.rtc.latchMinute);
|
||||
s.integer(mbc3.rtc.latchHour);
|
||||
s.integer(mbc3.rtc.latchDay);
|
||||
s.integer(mbc3.rtc.latchDayCarry);
|
||||
|
||||
s.integer(mbc5.rom.select);
|
||||
s.integer(mbc5.ram.enable);
|
||||
s.integer(mbc5.ram.select);
|
||||
|
||||
s.integer(mmm01.rom.base);
|
||||
s.integer(mmm01.rom.select);
|
||||
s.integer(mmm01.ram.enable);
|
||||
s.integer(mmm01.ram.select);
|
||||
s.integer(mmm01.mode);
|
||||
|
||||
s.integer(huc1.rom.select);
|
||||
s.integer(huc1.ram.writable);
|
||||
s.integer(huc1.ram.select);
|
||||
s.integer(huc1.model);
|
||||
|
||||
s.integer(huc3.rom.select);
|
||||
s.integer(huc3.ram.enable);
|
||||
s.integer(huc3.ram.select);
|
||||
mapper->serialize(s);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
//U1: TAMA7: Mask ROM (512KB)
|
||||
//U2: TAMA5: Game Boy cartridge connector interface
|
||||
//U3: TAMA6: Toshiba TMP47C243M (4-bit MCU)
|
||||
//U4: RTC: Toshiba TC8521AM
|
||||
|
||||
//note: the TMP47C243M's 2048 x 8-bit program ROM is currently undumped
|
||||
//as such, high level emulation is used as a necessary evil
|
||||
|
||||
auto Cartridge::TAMA::second() -> void {
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::read(uint16 address) -> uint8 {
|
||||
if((address & 0xc000) == 0x0000) { //$0000-3fff
|
||||
return cartridge.rom.read(address.bits(0,13));
|
||||
}
|
||||
|
||||
if((address & 0xc000) == 0x4000) { //$4000-7fff
|
||||
return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
|
||||
}
|
||||
|
||||
if((address & 0xe001) == 0xa000) { //$a000-bfff (even)
|
||||
if(io.select == 0x0a) {
|
||||
return 0xf0 | io.ready;
|
||||
}
|
||||
|
||||
if(io.select == 0x0c) {
|
||||
return 0xf0 | io.output.bits(0,3);
|
||||
}
|
||||
|
||||
if(io.select == 0x0d) {
|
||||
return 0xf0 | io.output.bits(4,7);
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if((address & 0xe001) == 0xa001) { //$a000-bfff (odd)
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::write(uint16 address, uint8 data) -> void {
|
||||
if((address & 0xe001) == 0xa000) { //$a000-bfff (even)
|
||||
if(io.select == 0x00) {
|
||||
io.rom.bank.bits(0,3) = data.bits(0,3);
|
||||
}
|
||||
|
||||
if(io.select == 0x01) {
|
||||
io.rom.bank.bit(4) = data.bit(0);
|
||||
}
|
||||
|
||||
if(io.select == 0x04) {
|
||||
io.input.bits(0,3) = data.bits(0,3);
|
||||
}
|
||||
|
||||
if(io.select == 0x05) {
|
||||
io.input.bits(4,7) = data.bits(0,3);
|
||||
}
|
||||
|
||||
if(io.select == 0x06) {
|
||||
io.index.bit(4) = data.bit(0);
|
||||
io.mode = data.bits(1,3);
|
||||
}
|
||||
|
||||
if(io.select == 0x07) {
|
||||
io.index.bits(0,3) = data.bits(0,3);
|
||||
|
||||
if(io.mode == 0) {
|
||||
cartridge.ram.write(io.index, io.input);
|
||||
}
|
||||
|
||||
if(io.mode == 1) {
|
||||
io.output = cartridge.ram.read(io.index);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if((address & 0xe001) == 0xa001) { //$a000-bfff (odd)
|
||||
io.select = data.bits(0,3);
|
||||
|
||||
if(io.select == 0x0a) {
|
||||
io.ready = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::readRTC(uint1 page, uint4 address) -> uint4 {
|
||||
if(address >= 13) return 0xf;
|
||||
auto ram = cartridge.rtc.read(page * 13 + address.bits(1,3));
|
||||
if(!address.bit(0)) {
|
||||
return ram.bits(0,3);
|
||||
} else {
|
||||
return ram.bits(4,7);
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::writeRTC(uint1 page, uint4 address, uint4 data) -> void {
|
||||
if(address >= 13) return;
|
||||
auto ram = cartridge.rtc.read(page * 13 + address.bits(1,3));
|
||||
if(!address.bit(0)) {
|
||||
ram.bits(0,3) = data;
|
||||
} else {
|
||||
ram.bits(4,7) = data;
|
||||
}
|
||||
cartridge.rtc.write(page * 13 + address.bits(1,3), ram);
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::power() -> void {
|
||||
io = {};
|
||||
}
|
||||
|
||||
auto Cartridge::TAMA::serialize(serializer& s) -> void {
|
||||
s.integer(io.ready);
|
||||
s.integer(io.select);
|
||||
s.integer(io.mode);
|
||||
s.integer(io.index);
|
||||
s.integer(io.input);
|
||||
s.integer(io.output);
|
||||
s.integer(io.rom.bank);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
struct TAMA : Mapper {
|
||||
auto second() -> void;
|
||||
auto read(uint16 address) -> uint8;
|
||||
auto write(uint16 address, uint8 data) -> void;
|
||||
auto readRTC(uint1 page, uint4 address) -> uint4;
|
||||
auto writeRTC(uint1 page, uint4 address, uint4 data) -> void;
|
||||
auto power() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
uint1 ready;
|
||||
uint4 select;
|
||||
uint3 mode;
|
||||
uint5 index;
|
||||
uint8 input;
|
||||
uint8 output;
|
||||
struct ROM {
|
||||
uint5 bank;
|
||||
} rom;
|
||||
} io;
|
||||
} tama;
|
|
@ -5,7 +5,7 @@
|
|||
auto CPU::step(uint clocks) -> void {
|
||||
for(auto n : range(clocks)) {
|
||||
if(++status.clock == 0) {
|
||||
cartridge.mbc3.second();
|
||||
cartridge.second();
|
||||
}
|
||||
|
||||
//4MHz / N(hz) - 1 = mask
|
||||
|
|
|
@ -63,7 +63,7 @@ auto VDP::readDataPort() -> uint16 {
|
|||
auto address = io.address.bits(1,6);
|
||||
auto data = cram.read(address);
|
||||
io.address += io.dataIncrement;
|
||||
return data.bits(0,2) << 1 | data.bits(3,5) << 2 | data.bits(6,8) << 3;
|
||||
return data.bits(0,2) << 1 | data.bits(3,5) << 5 | data.bits(6,8) << 9;
|
||||
}
|
||||
|
||||
return 0x0000;
|
||||
|
|
|
@ -313,13 +313,15 @@ auto Presentation::resizeViewport(bool resizeWindow) -> void {
|
|||
|
||||
auto Presentation::toggleFullScreen() -> void {
|
||||
if(!fullScreen()) {
|
||||
menuBar.setVisible(false);
|
||||
statusBar.setVisible(false);
|
||||
menuBar.setVisible(false);
|
||||
setFullScreen(true);
|
||||
video->setExclusive(settings["Video/Fullscreen/Exclusive"].boolean());
|
||||
if(video->exclusive()) setVisible(false);
|
||||
if(!input->acquired()) input->acquire();
|
||||
} else {
|
||||
if(input->acquired()) input->release();
|
||||
if(video->exclusive()) setVisible(true);
|
||||
video->setExclusive(false);
|
||||
setFullScreen(false);
|
||||
menuBar.setVisible(true);
|
||||
|
|
|
@ -99,5 +99,8 @@ auto Program::quit() -> void {
|
|||
unloadMedium();
|
||||
settings.quit();
|
||||
inputManager->quit();
|
||||
video.reset();
|
||||
audio.reset();
|
||||
input.reset();
|
||||
Application::quit();
|
||||
}
|
||||
|
|
|
@ -1,106 +1,224 @@
|
|||
struct GameBoyCartridge {
|
||||
GameBoyCartridge(uint8_t* data, unsigned size);
|
||||
GameBoyCartridge(uint8_t* data, uint size);
|
||||
|
||||
string markup;
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string mapper;
|
||||
bool ram;
|
||||
bool battery;
|
||||
bool rtc;
|
||||
bool rumble;
|
||||
bool black = false; //cartridge works in DMG+CGB mode
|
||||
bool clear = false; //cartridge works in CGB mode only
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
string mapper = "MBC0";
|
||||
bool battery = false;
|
||||
bool ram = false;
|
||||
bool rtc = false;
|
||||
bool rumble = false;
|
||||
bool accelerometer = false;
|
||||
|
||||
bool cgb;
|
||||
bool cgbonly;
|
||||
} info;
|
||||
uint romSize = 0;
|
||||
uint ramSize = 0;
|
||||
uint rtcSize = 0;
|
||||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t* romdata, unsigned romsize) {
|
||||
if(romsize < 0x4000) return;
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
|
||||
if(size < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
info.ram = false;
|
||||
info.battery = false;
|
||||
info.rtc = false;
|
||||
info.rumble = false;
|
||||
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
unsigned base = romsize - 0x8000;
|
||||
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||
uint index = size < 0x8000 ? size : size - 0x8000;
|
||||
if(data[index + 0x0104] == 0xce && data[index + 0x0105] == 0xed
|
||||
&& data[index + 0x0106] == 0x66 && data[index + 0x0107] == 0x66
|
||||
&& data[index + 0x0108] == 0xcc && data[index + 0x0109] == 0x0d
|
||||
&& data[index + 0x0147] >= 0x0b && data[index + 0x0147] <= 0x0d
|
||||
) {
|
||||
//MMM01 stores header at bottom of image
|
||||
//flip this around for consistency with all other mappers
|
||||
uint8_t header[0x8000];
|
||||
memcpy(header, romdata + base, 0x8000);
|
||||
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||
memcpy(romdata, header, 0x8000);
|
||||
//MMM01 stores header at bottom of data[]
|
||||
} else {
|
||||
//all other mappers store header at top of data[]
|
||||
index = 0;
|
||||
}
|
||||
|
||||
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
|
||||
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
|
||||
black = (data[index + 0x0143] & 0xc0) == 0x80;
|
||||
clear = (data[index + 0x0143] & 0xc0) == 0xc0;
|
||||
|
||||
switch(data[index + 0x0147]) {
|
||||
|
||||
case 0x00:
|
||||
mapper = "MBC0";
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
mapper = "MBC1";
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
mapper = "MBC1";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
mapper = "MBC1";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
mapper = "MBC2";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
mapper = "MBC2";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
mapper = "MBC0";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
mapper = "MBC0";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x0b:
|
||||
mapper = "MMM01";
|
||||
break;
|
||||
|
||||
case 0x0c:
|
||||
mapper = "MMM01";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
mapper = "MMM01";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x0f:
|
||||
mapper = "MBC3";
|
||||
battery = true;
|
||||
rtc = true;
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
mapper = "MBC3";
|
||||
battery = true;
|
||||
ram = true;
|
||||
rtc = true;
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
mapper = "MBC3";
|
||||
break;
|
||||
|
||||
case 0x12:
|
||||
mapper = "MBC3";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x13:
|
||||
mapper = "MBC3";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x19:
|
||||
mapper = "MBC5";
|
||||
break;
|
||||
|
||||
case 0x1a:
|
||||
mapper = "MBC5";
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x1b:
|
||||
mapper = "MBC5";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
case 0x1c:
|
||||
mapper = "MBC5";
|
||||
rumble = true;
|
||||
break;
|
||||
|
||||
case 0x1d:
|
||||
mapper = "MBC5";
|
||||
ram = true;
|
||||
rumble = true;
|
||||
break;
|
||||
|
||||
case 0x1e:
|
||||
mapper = "MBC5";
|
||||
battery = true;
|
||||
ram = true;
|
||||
rumble = true;
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
mapper = "MBC6";
|
||||
break;
|
||||
|
||||
case 0x22:
|
||||
mapper = "MBC7";
|
||||
battery = true;
|
||||
ram = true;
|
||||
rumble = true;
|
||||
accelerometer = true;
|
||||
break;
|
||||
|
||||
case 0xfc:
|
||||
mapper = "CAMERA";
|
||||
break;
|
||||
|
||||
case 0xfd:
|
||||
mapper = "TAMA";
|
||||
battery = true;
|
||||
ram = true;
|
||||
rtc = true;
|
||||
break;
|
||||
|
||||
case 0xfe:
|
||||
mapper = "HuC3";
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
mapper = "HuC1";
|
||||
battery = true;
|
||||
ram = true;
|
||||
break;
|
||||
|
||||
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(data[index + 0x0148]) { default:
|
||||
case 0x00: romSize = 2 * 16 * 1024; break;
|
||||
case 0x01: romSize = 4 * 16 * 1024; break;
|
||||
case 0x02: romSize = 8 * 16 * 1024; break;
|
||||
case 0x03: romSize = 16 * 16 * 1024; break;
|
||||
case 0x04: romSize = 32 * 16 * 1024; break;
|
||||
case 0x05: romSize = 64 * 16 * 1024; break;
|
||||
case 0x06: romSize = 128 * 16 * 1024; break;
|
||||
case 0x07: romSize = 256 * 16 * 1024; break;
|
||||
case 0x52: romSize = 72 * 16 * 1024; break;
|
||||
case 0x53: romSize = 80 * 16 * 1024; break;
|
||||
case 0x54: 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;
|
||||
switch(data[index + 0x0149]) { default:
|
||||
case 0x00: ramSize = 0 * 1024; break;
|
||||
case 0x01: ramSize = 2 * 1024; break;
|
||||
case 0x02: ramSize = 8 * 1024; break;
|
||||
case 0x03: ramSize = 32 * 1024; break;
|
||||
}
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
if(mapper == "MBC2" && ram) ramSize = 256;
|
||||
if(mapper == "TAMA" && ram) ramSize = 32;
|
||||
|
||||
markup.append("board mapper=", info.mapper, "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
|
||||
if(mapper == "MBC3" && rtc) rtcSize = 13;
|
||||
if(mapper == "TAMA" && rtc) rtcSize = 21;
|
||||
|
||||
markup.append("board mapper=", mapper, "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romSize), "\n");
|
||||
if(ram && ramSize) markup.append(" ram ", battery ? "name=save.ram " : "", "size=0x", hex(ramSize), "\n");
|
||||
if(rtc && rtcSize) markup.append(" rtc ", battery ? "name=rtc.ram " : "", "size=0x", hex(rtcSize), "\n");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define WINVER 0x0400
|
||||
#define _WIN32_WINNT 0x0400
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "libco.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define _BSD_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include "settings.h"
|
||||
|
||||
#define _BSD_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
#include <stdlib.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
|
|
Loading…
Reference in New Issue