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 {
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/";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 {
for(auto n : range(clocks)) {
if(++status.clock == 0) {
cartridge.mbc3.second();
cartridge.second();
}
//4MHz / N(hz) - 1 = mask

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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