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:
Tim Allen 2017-07-26 22:42:06 +10:00
parent d5c09c9ab1
commit 80841deaa5
32 changed files with 890 additions and 622 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -2,6 +2,7 @@
namespace GameBoy { namespace GameBoy {
Cartridge cartridge;
#include "mbc0/mbc0.cpp" #include "mbc0/mbc0.cpp"
#include "mbc1/mbc1.cpp" #include "mbc1/mbc1.cpp"
#include "mbc1m/mbc1m.cpp" #include "mbc1m/mbc1m.cpp"
@ -11,8 +12,8 @@ namespace GameBoy {
#include "mmm01/mmm01.cpp" #include "mmm01/mmm01.cpp"
#include "huc1/huc1.cpp" #include "huc1/huc1.cpp"
#include "huc3/huc3.cpp" #include "huc3/huc3.cpp"
#include "tama/tama.cpp"
#include "serialization.cpp" #include "serialization.cpp"
Cartridge cartridge;
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = {}; information = {};
@ -43,49 +44,40 @@ auto Cartridge::load() -> bool {
auto board = document["board"]; auto board = document["board"];
information.title = document["information/title"].text(); information.title = document["information/title"].text();
auto mapperid = document["board/mapper"].text(); auto mapperID = document["board/mapper"].text();
if(mapperid == "none" ) information.mapper = Mapper::MBC0; if(mapperID == "MBC0" ) mapper = &mbc0;
if(mapperid == "MBC1" ) information.mapper = Mapper::MBC1; if(mapperID == "MBC1" ) mapper = &mbc1;
if(mapperid == "MBC1M") information.mapper = Mapper::MBC1M; if(mapperID == "MBC1M") mapper = &mbc1m;
if(mapperid == "MBC2" ) information.mapper = Mapper::MBC2; if(mapperID == "MBC2" ) mapper = &mbc2;
if(mapperid == "MBC3" ) information.mapper = Mapper::MBC3; if(mapperID == "MBC3" ) mapper = &mbc3;
if(mapperid == "MBC5" ) information.mapper = Mapper::MBC5; if(mapperID == "MBC5" ) mapper = &mbc5;
if(mapperid == "MMM01") information.mapper = Mapper::MMM01; if(mapperID == "MMM01") mapper = &mmm01;
if(mapperid == "HuC1" ) information.mapper = Mapper::HuC1; if(mapperID == "HuC1" ) mapper = &huc1;
if(mapperid == "HuC3" ) information.mapper = Mapper::HuC3; if(mapperID == "HuC3" ) mapper = &huc3;
if(mapperID == "TAMA" ) mapper = &tama;
information.rtc = false; rom.size = max(0x4000, document["board/rom/size"].natural());
information.rumble = false;
rom.size = max(32768u, board["rom/size"].natural());
rom.data = (uint8*)memory::allocate(rom.size, 0xff); rom.data = (uint8*)memory::allocate(rom.size, 0xff);
if(auto name = document["board/rom/name"].text()) {
ram.size = board["ram/size"].natural();
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
if(auto name = board["rom/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, min(rom.size, fp->size())); 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)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
fp->read(ram.data, min(ram.size, fp->size())); fp->read(ram.data, min(ram.size, fp->size()));
} }
} }
information.battery = (bool)board["ram/name"]; rtc.size = document["board/rtc/size"].natural();
rtc.data = (uint8*)memory::allocate(rtc.size, 0xff);
switch(information.mapper) { default: if(auto name = document["board/rtc/name"].text()) {
case Mapper::MBC0: mapper = &mbc0; break; if(auto fp = platform->open(pathID(), name, File::Read, File::Optional)) {
case Mapper::MBC1: mapper = &mbc1; break; fp->read(rtc.data, min(rtc.size, fp->size()));
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;
} }
information.sha256 = Hash::SHA256(rom.data, rom.size).digest(); information.sha256 = Hash::SHA256(rom.data, rom.size).digest();
@ -100,35 +92,21 @@ auto Cartridge::save() -> void {
fp->write(ram.data, ram.size); 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 { auto Cartridge::unload() -> void {
delete[] rom.data; delete[] rom.data;
delete[] ram.data; delete[] ram.data;
delete[] rtc.data;
rom = {}; rom = {};
ram = {}; ram = {};
} rtc = {};
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;
} }
auto Cartridge::readIO(uint16 addr) -> uint8 { 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::GameBoyColor()) data = system.bootROM.cgb;
if(Model::SuperGameBoy()) data = system.bootROM.sgb; if(Model::SuperGameBoy()) data = system.bootROM.sgb;
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr]; 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 { auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
@ -152,25 +130,33 @@ auto Cartridge::writeIO(uint16 addr, uint8 data) -> void {
return; return;
} }
mapper->writeIO(addr, data); mapper->write(addr, data);
} }
auto Cartridge::power() -> void { 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 = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this;
for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this; for(uint n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this;
bus.mmio[0xff50] = 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;
} }
} }

View File

@ -8,19 +8,41 @@ struct Cartridge : MMIO {
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto readROM(uint addr) -> uint8; auto readIO(uint16 address) -> uint8;
auto writeROM(uint addr, uint8 data) -> void; auto writeIO(uint16 address, 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 power() -> void; auto power() -> void;
auto second() -> void;
auto serialize(serializer&) -> 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 "mbc0/mbc0.hpp"
#include "mbc1/mbc1.hpp" #include "mbc1/mbc1.hpp"
#include "mbc1m/mbc1m.hpp" #include "mbc1m/mbc1m.hpp"
@ -30,40 +52,7 @@ struct Cartridge : MMIO {
#include "mmm01/mmm01.hpp" #include "mmm01/mmm01.hpp"
#include "huc1/huc1.hpp" #include "huc1/huc1.hpp"
#include "huc3/huc3.hpp" #include "huc3/huc3.hpp"
#include "tama/tama.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;
}; };
extern Cartridge cartridge; extern Cartridge cartridge;

View File

@ -1,49 +1,54 @@
auto Cartridge::HuC1::readIO(uint16 addr) -> uint8 { auto Cartridge::HuC1::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
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((address & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.readRAM(ram.select << 13 | (uint13)addr); return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
} }
return 0xff; return 0xff;
} }
auto Cartridge::HuC1::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::HuC1::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
ram.writable = data.bits(0,3) == 0x0a; io.ram.writable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
rom.select = data + (data == 0); io.rom.bank = data;
if(!io.rom.bank) io.rom.bank = 0x01;
return; return;
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
ram.select = data; io.ram.bank = data;
return; return;
} }
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0x6000) { //$6000-7fff
model = data.bit(0); io.model = data.bit(0);
return; return;
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
if(!ram.writable) return; if(!io.ram.writable) return;
return cartridge.writeRAM(ram.select << 13 | (uint13)addr, data); return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
} }
} }
auto Cartridge::HuC1::power() -> void { auto Cartridge::HuC1::power() -> void {
rom.select = 0x01; io = {};
ram.writable = false; }
ram.select = 0x00;
model = 0; 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);
} }

View File

@ -1,14 +1,17 @@
struct HuC1 : MMIO { struct HuC1 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
struct ROM { struct IO {
uint8 select; uint1 model;
} rom; struct ROM {
struct RAM { uint8 bank = 0x01;
bool writable; } rom;
uint8 select; struct RAM {
} ram; uint1 writable;
bool model; uint8 bank;
} ram;
} io;
} huc1; } huc1;

View File

@ -1,49 +1,48 @@
auto Cartridge::HuC3::readIO(uint16 addr) -> uint8 { auto Cartridge::HuC3::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
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((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) return cartridge.readRAM(ram.select << 13 | (uint13)addr); if(!io.ram.enable) return 0x01; //does not return open collection
return 0x01; //does not return open collection return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
} }
return 0xff; return 0xff;
} }
auto Cartridge::HuC3::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::HuC3::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
ram.enable = data.bits(0,3) == 0x0a; io.ram.enable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
rom.select = data; io.rom.bank = data;
return; return;
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
ram.select = data; io.ram.bank = data;
return; return;
} }
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0xa000) { //$a000-bfff
//unknown purpose if(!io.ram.enable) return;
return; return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
}
if((addr & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data);
return;
} }
} }
auto Cartridge::HuC3::power() -> void { auto Cartridge::HuC3::power() -> void {
rom.select = 0x01; io = {};
ram.enable = false; }
ram.select = 0x00;
auto Cartridge::HuC3::serialize(serializer& s) -> void {
s.integer(io.rom.bank);
s.integer(io.ram.enable);
s.integer(io.ram.bank);
} }

View File

@ -1,13 +1,16 @@
struct HuC3 : MMIO { struct HuC3 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
struct ROM { struct IO {
uint8 select; struct ROM {
} rom; uint8 bank = 0x01;
struct RAM { } rom;
bool enable; struct RAM {
uint8 select; uint1 enable;
} ram; uint8 bank;
} ram;
} io;
} huc3; } huc3;

View File

@ -1,21 +1,24 @@
auto Cartridge::MBC0::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC0::read(uint16 address) -> uint8 {
if((addr & 0x8000) == 0x0000) { //$0000-7fff if((address & 0x8000) == 0x0000) { //$0000-7fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,14));
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.readRAM((uint13)addr); return cartridge.ram.read(address.bits(0,12));
} }
return 0xff; return 0xff;
} }
auto Cartridge::MBC0::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC0::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
cartridge.writeRAM((uint13)addr, data); cartridge.ram.write(address.bits(0,12), data);
return; return;
} }
} }
auto Cartridge::MBC0::power() -> void { auto Cartridge::MBC0::power() -> void {
} }
auto Cartridge::MBC0::serialize(serializer& s) -> void {
}

View File

@ -1,5 +1,6 @@
struct MBC0 : MMIO { struct MBC0 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
} mbc0; } mbc0;

View File

@ -1,66 +1,67 @@
auto Cartridge::MBC1::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC1::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
if(mode == 0) { if(io.mode == 0) {
return cartridge.readROM(ram.select << 19 | rom.select << 14 | (uint14)addr); return cartridge.rom.read(io.ram.bank << 19 | io.rom.bank << 14 | address.bits(0,13));
} else { } 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((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) { if(!io.ram.enable) return 0xff;
if(mode == 0) { if(io.mode == 0) {
return cartridge.readRAM((uint13)addr); return cartridge.ram.read(address.bits(0,12));
} else { } else {
return cartridge.readRAM(ram.select << 13 | (uint13)addr); return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
}
} }
return 0xff;
} }
return 0xff; return 0xff;
} }
auto Cartridge::MBC1::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC1::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
ram.enable = (data & 0x0f) == 0x0a; io.ram.enable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
rom.select = (data & 0x1f) + ((data & 0x1f) == 0); io.rom.bank = data.bits(0,4);
if(!io.rom.bank) io.rom.bank = 0x01;
return; return;
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
ram.select = data & 0x03; io.ram.bank = data.bits(0,1);
return; return;
} }
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0x6000) { //$6000-7fff
mode = data & 0x01; io.mode = data.bit(0);
return; return;
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) { if(!io.ram.enable) return;
if(mode == 0) { if(io.mode == 0) {
cartridge.writeRAM(addr & 0x1fff, data); return cartridge.ram.write(address.bits(0,12), data);
} else { } else {
cartridge.writeRAM(ram.select << 13 | (uint13)addr, data); return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
}
} }
return;
} }
} }
auto Cartridge::MBC1::power() -> void { auto Cartridge::MBC1::power() -> void {
rom.select = 0x01; io = {};
ram.enable = false; }
ram.select = 0x00;
mode = 0; 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);
} }

View File

@ -1,14 +1,17 @@
struct MBC1 : MMIO { struct MBC1 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer& s) -> void;
struct ROM { struct IO {
uint8 select; uint1 mode;
} rom; struct ROM {
struct RAM { uint8 bank = 0x01;
bool enable; } rom;
uint8 select; struct RAM {
} ram; uint1 enable;
bool mode; uint8 bank;
} ram;
} io;
} mbc1; } mbc1;

View File

@ -1,40 +1,43 @@
auto Cartridge::MBC1M::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC1M::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
if(mode == 0) return cartridge.readROM((uint14)addr); if(io.mode == 0) return cartridge.rom.read(address.bits(0,13));
return cartridge.readROM(rom.hi << 18 | (uint14)addr); return cartridge.rom.read(io.rom.bank.bits(4,5) << 18 | address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.readROM(rom.hi << 18 | rom.lo << 14 | (uint14)addr); return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
return cartridge.readRAM((uint13)addr); return cartridge.ram.read(address.bits(0,12));
} }
return 0xff; return 0xff;
} }
auto Cartridge::MBC1M::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC1M::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
rom.lo = data.bits(0,3); io.rom.bank.bits(0,3) = data.bits(0,3);
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
rom.hi = data.bits(0,1); io.rom.bank.bits(4,5) = data.bits(0,1);
} }
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0x6000) { //$6000-7fff
mode = data.bit(0); io.mode = data.bit(0);
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
cartridge.writeRAM((uint13)addr, data); cartridge.ram.write(address.bits(0,13), data);
} }
} }
auto Cartridge::MBC1M::power() -> void { auto Cartridge::MBC1M::power() -> void {
rom.lo = 1; io = {};
rom.hi = 0; }
mode = 0;
auto Cartridge::MBC1M::serialize(serializer& s) -> void {
s.integer(io.mode);
s.integer(io.rom.bank);
} }

View File

@ -1,11 +1,13 @@
struct MBC1M : MMIO { struct MBC1M : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
struct ROM { struct IO {
uint4 lo; uint1 mode;
uint2 hi; struct ROM {
} rom; uint6 bank = 0x01;
uint1 mode; } rom;
} io;
} mbc1m; } mbc1m;

View File

@ -1,38 +1,61 @@
auto Cartridge::MBC2::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC2::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.readROM(rom.select << 14 | (uint14)addr); return cartridge.rom.read(io.rom.bank << 14 | address.bits(0,13));
} }
if((addr & 0xee00) == 0xa000) { //$a000-a1ff if((address & 0xee01) == 0xa000) { //$a000-a1ff (even)
if(ram.enable) return cartridge.readRAM((uint9)addr); if(!io.ram.enable) return 0xff;
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; return 0xff;
} }
auto Cartridge::MBC2::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC2::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
if(!addr.bit(8)) ram.enable = data.bits(0,3) == 0x0a; if(!address.bit(8)) io.ram.enable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
if( addr.bit(8)) rom.select = data.bits(0,3) + (data.bits(0,3) == 0); if(address.bit(8)) io.rom.bank = data.bits(0,3);
if(!io.rom.bank) io.rom.bank = 0x01;
return; return;
} }
if((addr & 0xee00) == 0xa000) { //$a000-a1ff if((address & 0xee01) == 0xa000) { //$a000-a1ff (even)
if(ram.enable) cartridge.writeRAM((uint9)addr, data.bits(0,3)); 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; return;
} }
} }
auto Cartridge::MBC2::power() -> void { auto Cartridge::MBC2::power() -> void {
rom.select = 0x01; io = {};
ram.enable = false; }
auto Cartridge::MBC2::serialize(serializer& s) -> void {
s.integer(io.rom.bank);
s.integer(io.ram.enable);
} }

View File

@ -1,12 +1,15 @@
struct MBC2 : MMIO { struct MBC2 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
struct ROM { struct IO {
uint8 select; struct ROM {
} rom; uint8 bank = 0x01;
struct RAM { } rom;
bool enable; struct RAM {
} ram; uint1 enable = 0;
} ram;
} io;
} mbc2; } mbc2;

View File

@ -1,118 +1,113 @@
auto Cartridge::MBC3::second() -> void { auto Cartridge::MBC3::second() -> void {
if(!rtc.halt) { if(io.rtc.halt) return;
if(++rtc.second >= 60) { if(++io.rtc.second >= 60) {
rtc.second = 0; io.rtc.second = 0;
if(++rtc.minute >= 60) { if(++io.rtc.minute >= 60) {
rtc.minute = 0; io.rtc.minute = 0;
if(++rtc.hour >= 24) { if(++io.rtc.hour >= 24) {
rtc.hour = 0; io.rtc.hour = 0;
if(++rtc.day >= 512) { if(++io.rtc.day == 0) {
rtc.day = 0; io.rtc.dayCarry = true;
rtc.dayCarry = true;
}
} }
} }
} }
} }
} }
auto Cartridge::MBC3::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC3::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
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((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) { if(!io.ram.enable) return 0xff;
if(ram.select <= 0x03) { if(io.ram.bank <= 0x03) return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
return cartridge.readRAM(ram.select << 13 | (uint13)addr); if(io.ram.bank == 0x08) return io.rtc.latchSecond;
} if(io.ram.bank == 0x09) return io.rtc.latchMinute;
if(ram.select == 0x08) return rtc.latchSecond; if(io.ram.bank == 0x0a) return io.rtc.latchHour;
if(ram.select == 0x09) return rtc.latchMinute; if(io.ram.bank == 0x0b) return io.rtc.latchDay;
if(ram.select == 0x0a) return rtc.latchHour; if(io.ram.bank == 0x0c) return io.rtc.latchDayCarry << 7 | io.rtc.latchDay >> 8;
if(ram.select == 0x0b) return rtc.latchDay;
if(ram.select == 0x0c) return rtc.latchDayCarry << 7 | rtc.latchDay >> 8;
}
return 0xff; return 0xff;
} }
return 0xff; return 0xff;
} }
auto Cartridge::MBC3::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC3::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
ram.enable = (data & 0x0f) == 0x0a; io.ram.enable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
rom.select = (data & 0x7f) + ((data & 0x7f) == 0); io.rom.bank = data.bits(0,6);
if(!io.rom.bank) io.rom.bank = 0x01;
return; return;
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
ram.select = data; io.ram.bank = data;
return; return;
} }
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0x6000) { //$6000-7fff
if(rtc.latch == 0 && data == 1) { if(io.rtc.latch == 0 && data == 1) {
rtc.latchSecond = rtc.second; io.rtc.latchSecond = io.rtc.second;
rtc.latchMinute = rtc.minute; io.rtc.latchMinute = io.rtc.minute;
rtc.latchHour = rtc.hour; io.rtc.latchHour = io.rtc.hour;
rtc.latchDay = rtc.day; io.rtc.latchDay = io.rtc.day;
rtc.latchDayCarry = rtc.dayCarry; io.rtc.latchDayCarry = io.rtc.dayCarry;
} }
rtc.latch = data; io.rtc.latch = data;
return; return;
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) { if(!io.ram.enable) return;
if(ram.select <= 0x03) { if(io.ram.bank <= 0x03) {
cartridge.writeRAM(ram.select << 13 | (uint13)addr, data); cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
} else if(ram.select == 0x08) { } else if(io.ram.bank == 0x08) {
if(data >= 60) data = 0; if(data >= 60) data = 0;
rtc.second = data; io.rtc.second = data;
} else if(ram.select == 0x09) { } else if(io.ram.bank == 0x09) {
if(data >= 60) data = 0; if(data >= 60) data = 0;
rtc.minute = data; io.rtc.minute = data;
} else if(ram.select == 0x0a) { } else if(io.ram.bank == 0x0a) {
if(data >= 24) data = 0; if(data >= 24) data = 0;
rtc.hour = data; io.rtc.hour = data;
} else if(ram.select == 0x0b) { } else if(io.ram.bank == 0x0b) {
rtc.day = (rtc.day & 0x0100) | data; io.rtc.day.bits(0,7) = data.bits(0,7);
} else if(ram.select == 0x0c) { } else if(io.ram.bank == 0x0c) {
rtc.day = ((data & 1) << 8) | (rtc.day & 0xff); io.rtc.day.bit(8) = data.bit(0);
rtc.halt = data & 0x40; io.rtc.halt = data.bit(6);
rtc.dayCarry = data & 0x80; io.rtc.dayCarry = data.bit(7);
}
} }
return; return;
} }
} }
auto Cartridge::MBC3::power() -> void { auto Cartridge::MBC3::power() -> void {
rom.select = 0x01; io = {};
}
ram.enable = false;
ram.select = 0x00; auto Cartridge::MBC3::serialize(serializer& s) -> void {
s.integer(io.rom.bank);
rtc.latch = 0; s.integer(io.ram.enable);
s.integer(io.ram.bank);
rtc.halt = true; s.integer(io.rtc.halt);
rtc.second = 0; s.integer(io.rtc.latch);
rtc.minute = 0; s.integer(io.rtc.second);
rtc.hour = 0; s.integer(io.rtc.minute);
rtc.day = 0; s.integer(io.rtc.hour);
rtc.dayCarry = false; s.integer(io.rtc.day);
s.integer(io.rtc.dayCarry);
rtc.latchSecond = 0; s.integer(io.rtc.latchSecond);
rtc.latchMinute = 0; s.integer(io.rtc.latchMinute);
rtc.latchHour = 0; s.integer(io.rtc.latchHour);
rtc.latchDay = 0; s.integer(io.rtc.latchDay);
rtc.latchDayCarry = false; s.integer(io.rtc.latchDayCarry);
} }

View File

@ -1,30 +1,33 @@
struct MBC3 : MMIO { struct MBC3 : Mapper {
auto second() -> void; auto second() -> void;
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer& s) -> void;
struct ROM { struct IO {
uint8 select; struct ROM {
} rom; uint8 bank = 0x01;
struct RAM { } rom;
bool enable; struct RAM {
uint8 select; uint1 enable;
} ram; uint8 bank;
struct RTC { } ram;
bool latch; struct RTC {
uint1 halt = true;
uint1 latch;
bool halt; uint8 second;
uint second; uint8 minute;
uint minute; uint8 hour;
uint hour; uint9 day;
uint day; uint1 dayCarry;
bool dayCarry;
uint latchSecond; uint8 latchSecond;
uint latchMinute; uint8 latchMinute;
uint latchHour; uint8 latchHour;
uint latchDay; uint9 latchDay;
uint latchDayCarry; uint1 latchDayCarry;
} rtc; } rtc;
} io;
} mbc3; } mbc3;

View File

@ -1,49 +1,53 @@
auto Cartridge::MBC5::readIO(uint16 addr) -> uint8 { auto Cartridge::MBC5::read(uint16 address) -> uint8 {
if((addr & 0xc000) == 0x0000) { //$0000-3fff if((address & 0xc000) == 0x0000) { //$0000-3fff
return cartridge.readROM(addr); return cartridge.rom.read(address.bits(0,13));
} }
if((addr & 0xc000) == 0x4000) { //$4000-7fff if((address & 0xc000) == 0x4000) { //$4000-7fff
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((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) return cartridge.readRAM(ram.select << 13 | (uint13)addr); if(!io.ram.enable) return 0xff;
return 0xff; return cartridge.ram.read(io.ram.bank << 13 | address.bits(0,12));
} }
return 0xff; return 0xff;
} }
auto Cartridge::MBC5::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MBC5::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if((address & 0xe000) == 0x0000) { //$0000-1fff
ram.enable = data.bits(0,3) == 0x0a; io.ram.enable = data.bits(0,3) == 0x0a;
return; return;
} }
if((addr & 0xf000) == 0x2000) { //$2000-2fff if((address & 0xf000) == 0x2000) { //$2000-2fff
rom.select.byte(0) = data; io.rom.bank.bits(0,7) = data.bits(0,7);
return; return;
} }
if((addr & 0xf000) == 0x3000) { //$3000-3fff if((address & 0xf000) == 0x3000) { //$3000-3fff
rom.select.byte(1) = data.bit(0); io.rom.bank.bit(8) = data.bit(0);
return; return;
} }
if((addr & 0xe000) == 0x4000) { //$4000-5fff if((address & 0xe000) == 0x4000) { //$4000-5fff
ram.select = data.bits(0,3); io.ram.bank = data.bits(0,3);
return; return;
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0xa000) { //$a000-bfff
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data); if(!io.ram.enable) return;
return; return cartridge.ram.write(io.ram.bank << 13 | address.bits(0,12), data);
} }
} }
auto Cartridge::MBC5::power() -> void { auto Cartridge::MBC5::power() -> void {
rom.select = 0x001; io = {};
ram.enable = false; }
ram.select = 0x00;
auto Cartridge::MBC5::serialize(serializer& s) -> void {
s.integer(io.rom.bank);
s.integer(io.ram.enable);
s.integer(io.ram.bank);
} }

View File

@ -1,13 +1,16 @@
struct MBC5 : MMIO { struct MBC5 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void;
struct ROM { struct IO {
uint9 select; struct ROM {
} rom; uint9 bank = 0x01;
struct RAM { } rom;
bool enable; struct RAM {
uint4 select; uint1 enable;
} ram; uint4 bank;
} ram;
} io;
} mbc5; } mbc5;

View File

@ -1,60 +1,65 @@
auto Cartridge::MMM01::readIO(uint16 addr) -> uint8 { auto Cartridge::MMM01::read(uint16 address) -> uint8 {
if((addr & 0x8000) == 0x0000) { //$0000-7fff if(io.mode == 0) {
if(mode == 0) return cartridge.readROM(addr); 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 0xff;
return cartridge.readROM(0x8000 + (rom.base << 14) + (uint14)addr); } 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 if((address & 0xc000) == 0x4000) { //$4000-7fff
return cartridge.readROM(0x8000 + (rom.base << 14) + (rom.select<< 14) + (uint14)addr); 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;
} }
return 0xff;
} }
auto Cartridge::MMM01::writeIO(uint16 addr, uint8 data) -> void { auto Cartridge::MMM01::write(uint16 address, uint8 data) -> void {
if((addr & 0xe000) == 0x0000) { //$0000-1fff if(io.mode == 0) {
if(mode == 0) { if((address & 0xe000) == 0x0000) { //$0000-1fff
mode = 1; io.mode = 1;
} else {
ram.enable= data.bits(0,3) == 0x0a;
} }
}
if((addr & 0xe000) == 0x2000) { //$2000-3fff if((address & 0xe000) == 0x2000) { //$2000-3fff
if(mode == 0) { io.rom.base = data.bits(0,5);
rom.base = data.bits(0,5);
} else {
rom.select = data;
} }
} } else {
if((address & 0xe000) == 0x0000) { //$0000-1fff
if((addr & 0xe000) == 0x4000) { //$4000-5fff io.ram.enable = data.bits(0,3) == 0x0a;
if(mode == 1) {
ram.select = data;
} }
}
if((addr & 0xe000) == 0x6000) { //$6000-7fff if((address & 0xe000) == 0x2000) { //$2000-3fff
//unknown purpose io.rom.bank = data;
} }
if((addr & 0xe000) == 0xa000) { //$a000-bfff if((address & 0xe000) == 0x4000) { //$4000-5fff
if(ram.enable) cartridge.writeRAM(ram.select << 13 | (uint13)addr, data); 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 { auto Cartridge::MMM01::power() -> void {
rom.base = 0x00; io = {};
rom.select = 0x01; }
ram.enable = false;
ram.select = 0x00; auto Cartridge::MMM01::serialize(serializer& s) -> void {
mode = 0; s.integer(io.mode);
s.integer(io.rom.base);
s.integer(io.rom.bank);
s.integer(io.ram.enable);
s.integer(io.ram.bank);
} }

View File

@ -1,15 +1,18 @@
struct MMM01 : MMIO { struct MMM01 : Mapper {
auto readIO(uint16 addr) -> uint8; auto read(uint16 address) -> uint8;
auto writeIO(uint16 addr, uint8 data) -> void; auto write(uint16 address, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer& s) -> void;
struct ROM { struct IO {
uint6 base; uint1 mode;
uint8 select; struct ROM {
} rom; uint6 base;
struct RAM { uint8 bank = 0x01;
bool enable; } rom;
uint8 select; struct RAM {
} ram; uint1 enable;
bool mode; uint8 bank;
} ram;
} io;
} mmm01; } mmm01;

View File

@ -1,51 +1,8 @@
auto Cartridge::serialize(serializer& s) -> void { 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(bootromEnable);
s.integer(mbc1.rom.select); mapper->serialize(s);
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);
} }

View File

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

View File

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

View File

@ -5,7 +5,7 @@
auto CPU::step(uint clocks) -> void { auto CPU::step(uint clocks) -> void {
for(auto n : range(clocks)) { for(auto n : range(clocks)) {
if(++status.clock == 0) { if(++status.clock == 0) {
cartridge.mbc3.second(); cartridge.second();
} }
//4MHz / N(hz) - 1 = mask //4MHz / N(hz) - 1 = mask

View File

@ -63,7 +63,7 @@ auto VDP::readDataPort() -> uint16 {
auto address = io.address.bits(1,6); auto address = io.address.bits(1,6);
auto data = cram.read(address); auto data = cram.read(address);
io.address += io.dataIncrement; 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; return 0x0000;

View File

@ -313,13 +313,15 @@ auto Presentation::resizeViewport(bool resizeWindow) -> void {
auto Presentation::toggleFullScreen() -> void { auto Presentation::toggleFullScreen() -> void {
if(!fullScreen()) { if(!fullScreen()) {
menuBar.setVisible(false);
statusBar.setVisible(false); statusBar.setVisible(false);
menuBar.setVisible(false);
setFullScreen(true); setFullScreen(true);
video->setExclusive(settings["Video/Fullscreen/Exclusive"].boolean()); video->setExclusive(settings["Video/Fullscreen/Exclusive"].boolean());
if(video->exclusive()) setVisible(false);
if(!input->acquired()) input->acquire(); if(!input->acquired()) input->acquire();
} else { } else {
if(input->acquired()) input->release(); if(input->acquired()) input->release();
if(video->exclusive()) setVisible(true);
video->setExclusive(false); video->setExclusive(false);
setFullScreen(false); setFullScreen(false);
menuBar.setVisible(true); menuBar.setVisible(true);

View File

@ -99,5 +99,8 @@ auto Program::quit() -> void {
unloadMedium(); unloadMedium();
settings.quit(); settings.quit();
inputManager->quit(); inputManager->quit();
video.reset();
audio.reset();
input.reset();
Application::quit(); Application::quit();
} }

View File

@ -1,106 +1,224 @@
struct GameBoyCartridge { struct GameBoyCartridge {
GameBoyCartridge(uint8_t* data, unsigned size); GameBoyCartridge(uint8_t* data, uint size);
string markup; string markup;
//private: bool black = false; //cartridge works in DMG+CGB mode
struct Information { bool clear = false; //cartridge works in CGB mode only
string mapper;
bool ram;
bool battery;
bool rtc;
bool rumble;
unsigned romsize; string mapper = "MBC0";
unsigned ramsize; bool battery = false;
bool ram = false;
bool rtc = false;
bool rumble = false;
bool accelerometer = false;
bool cgb; uint romSize = 0;
bool cgbonly; uint ramSize = 0;
} info; uint rtcSize = 0;
}; };
GameBoyCartridge::GameBoyCartridge(uint8_t* romdata, unsigned romsize) { GameBoyCartridge::GameBoyCartridge(uint8_t* data, uint size) {
if(romsize < 0x4000) return; if(size < 0x4000) return;
info.mapper = "unknown"; uint index = size < 0x8000 ? size : size - 0x8000;
info.ram = false; if(data[index + 0x0104] == 0xce && data[index + 0x0105] == 0xed
info.battery = false; && data[index + 0x0106] == 0x66 && data[index + 0x0107] == 0x66
info.rtc = false; && data[index + 0x0108] == 0xcc && data[index + 0x0109] == 0x0d
info.rumble = false; && data[index + 0x0147] >= 0x0b && data[index + 0x0147] <= 0x0d
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
) { ) {
//MMM01 stores header at bottom of image //MMM01 stores header at bottom of data[]
//flip this around for consistency with all other mappers } else {
uint8_t header[0x8000]; //all other mappers store header at top of data[]
memcpy(header, romdata + base, 0x8000); index = 0;
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
memcpy(romdata, header, 0x8000);
} }
info.cgb = (romdata[0x0143] & 0x80) == 0x80; black = (data[index + 0x0143] & 0xc0) == 0x80;
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0; 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: switch(data[index + 0x0148]) { default:
case 0x00: info.romsize = 2 * 16 * 1024; break; case 0x00: romSize = 2 * 16 * 1024; break;
case 0x01: info.romsize = 4 * 16 * 1024; break; case 0x01: romSize = 4 * 16 * 1024; break;
case 0x02: info.romsize = 8 * 16 * 1024; break; case 0x02: romSize = 8 * 16 * 1024; break;
case 0x03: info.romsize = 16 * 16 * 1024; break; case 0x03: romSize = 16 * 16 * 1024; break;
case 0x04: info.romsize = 32 * 16 * 1024; break; case 0x04: romSize = 32 * 16 * 1024; break;
case 0x05: info.romsize = 64 * 16 * 1024; break; case 0x05: romSize = 64 * 16 * 1024; break;
case 0x06: info.romsize = 128 * 16 * 1024; break; case 0x06: romSize = 128 * 16 * 1024; break;
case 0x07: info.romsize = 256 * 16 * 1024; break; case 0x07: romSize = 256 * 16 * 1024; break;
case 0x52: info.romsize = 72 * 16 * 1024; break; case 0x52: romSize = 72 * 16 * 1024; break;
case 0x53: info.romsize = 80 * 16 * 1024; break; case 0x53: romSize = 80 * 16 * 1024; break;
case 0x54: info.romsize = 96 * 16 * 1024; break; case 0x54: romSize = 96 * 16 * 1024; break;
} }
switch(romdata[0x0149]) { default: switch(data[index + 0x0149]) { default:
case 0x00: info.ramsize = 0 * 1024; break; case 0x00: ramSize = 0 * 1024; break;
case 0x01: info.ramsize = 2 * 1024; break; case 0x01: ramSize = 2 * 1024; break;
case 0x02: info.ramsize = 8 * 1024; break; case 0x02: ramSize = 8 * 1024; break;
case 0x03: info.ramsize = 32 * 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"); if(mapper == "MBC3" && rtc) rtcSize = 13;
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n"); if(mapper == "TAMA" && rtc) rtcSize = 21;
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
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");
} }

View File

@ -1,5 +1,6 @@
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h"
#define WINVER 0x0400 #define WINVER 0x0400
#define _WIN32_WINNT 0x0400 #define _WIN32_WINNT 0x0400

View File

@ -7,6 +7,7 @@
#include "libco.h" #include "libco.h"
#include "settings.h" #include "settings.h"
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE 500
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <signal.h>

View File

@ -12,8 +12,10 @@
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h"
#define _BSD_SOURCE #define _BSD_SOURCE
#define _XOPEN_SOURCE 500
#include <stdlib.h> #include <stdlib.h>
#include <ucontext.h> #include <ucontext.h>