Update to v101r31 release.

byuu says:

Changelog:

  - converted Emulator::Interface::Bind to Emulator::Platform
  - temporarily disabled SGB hooks
  - SMS: emulated Game Gear palette (latching word-write behavior not
    implemented yet)
  - SMS: emulated Master System 'Reset' button, Game Gear 'Start' button
  - SMS: removed reset() functionality, driven by the mappable input now
    instead
  - SMS: split interface class in two: one for Master System, one for
    Game Gear
  - SMS: emulated Game Gear video cropping to 160x144
  - PCE: started on HuC6280 CPU core—so far only registers, NOP
    instruction has been implemented

Errata:

  - Super Game Boy support is broken and thus disabled
  - if you switch between Master System and Game Gear without
    restarting, bad things happen:
      - SMS→GG, no video output on the GG
      - GG→SMS, no input on the SMS

I'm not sure what's causing the SMS\<-\>GG switch bug, having a hard
time debugging it. Help would be very much appreciated, if anyone's up
for it. Otherwise I'll keep trying to track it down on my end.
This commit is contained in:
Tim Allen 2017-01-13 12:15:45 +11:00
parent 0ad70a30f8
commit bf90bdfcc8
109 changed files with 901 additions and 527 deletions

View File

@ -4,7 +4,7 @@ target := tomoko
# console := true # console := true
flags += -I. -I.. -O3 flags += -I. -I.. -O3
objects := libco audio video resource objects := libco emulator audio video resource
# profile-guided optimization mode # profile-guided optimization mode
# pgo := instrument # pgo := instrument
@ -52,10 +52,11 @@ compile = \
all: build; all: build;
obj/libco.o: ../libco/libco.c $(call rwildcard,../libco/) obj/libco.o: ../libco/libco.c $(call rwildcard,../libco)
obj/audio.o: audio/audio.cpp $(call rwildcard,audio/) obj/emulator.o: emulator/emulator.cpp $(call rwildcard,emulator)
obj/video.o: video/video.cpp $(call rwildcard,video/) obj/audio.o: audio/audio.cpp $(call rwildcard,audio)
obj/resource.o: resource/resource.cpp $(call rwildcard,resource/) obj/video.o: video/video.cpp $(call rwildcard,video)
obj/resource.o: resource/resource.cpp $(call rwildcard,resource)
ui := target-$(target) ui := target-$(target)
include $(ui)/GNUmakefile include $(ui)/GNUmakefile

View File

@ -6,6 +6,8 @@ namespace Emulator {
Audio audio; Audio audio;
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void { auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
interface = nullptr;
if(channels_) channels = channels_(); if(channels_) channels = channels_();
if(frequency_) frequency = frequency_(); if(frequency_) frequency = frequency_();
@ -81,7 +83,7 @@ auto Audio::process() -> void {
if(balance > 0.0) samples[0] *= 1.0 - balance; if(balance > 0.0) samples[0] *= 1.0 - balance;
} }
interface->audioSample(samples, channels); platform->audioSample(samples, channels);
} }
} }

View File

@ -11,7 +11,7 @@ struct Stream;
struct Audio { struct Audio {
auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void; auto reset(maybe<uint> channels = nothing, maybe<double> frequency = nothing) -> void;
auto setInterface(Interface*) -> void; auto setInterface(Interface* interface) -> void;
auto setVolume(double volume) -> void; auto setVolume(double volume) -> void;
auto setBalance(double balance) -> void; auto setBalance(double balance) -> void;

View File

@ -0,0 +1,7 @@
#include <emulator/emulator.hpp>
namespace Emulator {
Platform* platform = nullptr;
}

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 = "101.30"; static const string Version = "101.31";
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,18 @@
namespace Emulator { namespace Emulator {
struct Platform {
virtual auto path(uint id) -> string { return ""; }
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return {}; }
virtual auto load(uint id, string name, string type) -> maybe<uint> { return nothing; }
virtual auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void {}
virtual auto audioSample(const double* samples, uint channels) -> void {}
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
virtual auto notify(string text) -> void { print(text, "\n"); }
};
struct Interface { struct Interface {
struct Information { struct Information {
string manufacturer; string manufacturer;
@ -38,30 +50,6 @@ struct Interface {
}; };
vector<Port> ports; vector<Port> ports;
struct Bind {
virtual auto path(uint) -> string { return ""; }
virtual auto open(uint, string, vfs::file::mode, bool) -> vfs::shared::file { return {}; }
virtual auto load(uint, string, string) -> maybe<uint> { return nothing; }
virtual auto videoRefresh(const uint32*, uint, uint, uint) -> void {}
virtual auto audioSample(const double*, uint) -> void {}
virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; }
virtual auto inputRumble(uint, uint, uint, bool) -> void {}
virtual auto dipSettings(Markup::Node) -> uint { return 0; }
virtual auto notify(string text) -> void { print(text, "\n"); }
};
Bind* bind = nullptr;
//callback bindings (provided by user interface)
auto path(uint id) -> string { return bind->path(id); }
auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return bind->open(id, name, mode, required); }
auto load(uint id, string name, string type) -> maybe<uint> { return bind->load(id, name, type); }
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(data, pitch, width, height); }
auto audioSample(const double* samples, uint channels) -> void { return bind->audioSample(samples, channels); }
auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); }
auto inputRumble(uint port, uint device, uint input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); }
auto dipSettings(Markup::Node node) -> uint { return bind->dipSettings(node); }
template<typename... P> auto notify(P&&... p) -> void { return bind->notify({forward<P>(p)...}); }
//information //information
virtual auto manifest() -> string = 0; virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0; virtual auto title() -> string = 0;
@ -118,4 +106,6 @@ struct File {
static const auto Required = true; static const auto Required = true;
}; };
extern Platform* platform;
} }

View File

@ -42,22 +42,22 @@ Board::Board(Markup::Node& document) {
if(chrram.size) chrram.data = new uint8_t[chrram.size](); if(chrram.size) chrram.data = new uint8_t[chrram.size]();
if(prgrom.name = prom["name"].text()) { if(prgrom.name = prom["name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), prgrom.name, File::Read, File::Required)) { if(auto fp = platform->open(cartridge.pathID(), prgrom.name, File::Read, File::Required)) {
fp->read(prgrom.data, min(prgrom.size, fp->size())); fp->read(prgrom.data, min(prgrom.size, fp->size()));
} }
} }
if(prgram.name = pram["name"].text()) { if(prgram.name = pram["name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), prgram.name, File::Read)) { if(auto fp = platform->open(cartridge.pathID(), prgram.name, File::Read)) {
fp->read(prgram.data, min(prgram.size, fp->size())); fp->read(prgram.data, min(prgram.size, fp->size()));
} }
} }
if(chrrom.name = crom["name"].text()) { if(chrrom.name = crom["name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), chrrom.name, File::Read, File::Required)) { if(auto fp = platform->open(cartridge.pathID(), chrrom.name, File::Read, File::Required)) {
fp->read(chrrom.data, min(chrrom.size, fp->size())); fp->read(chrrom.data, min(chrrom.size, fp->size()));
} }
} }
if(chrram.name = cram["name"].text()) { if(chrram.name = cram["name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), chrram.name, File::Read)) { if(auto fp = platform->open(cartridge.pathID(), chrram.name, File::Read)) {
fp->read(chrram.data, min(chrram.size, fp->size())); fp->read(chrram.data, min(chrram.size, fp->size()));
} }
} }
@ -70,13 +70,13 @@ auto Board::save() -> void {
auto document = BML::unserialize(cartridge.manifest()); auto document = BML::unserialize(cartridge.manifest());
if(auto name = document["board/prg/ram/name"].text()) { if(auto name = document["board/prg/ram/name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), name, File::Write)) { if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
fp->write(prgram.data, prgram.size); fp->write(prgram.data, prgram.size);
} }
} }
if(auto name = document["board/chr/ram/name"].text()) { if(auto name = document["board/chr/ram/name"].text()) {
if(auto fp = interface->open(cartridge.pathID(), name, File::Write)) { if(auto fp = platform->open(cartridge.pathID(), name, File::Write)) {
fp->write(chrram.data, chrram.size); fp->write(chrram.data, chrram.size);
} }
} }

View File

@ -15,11 +15,11 @@ auto Cartridge::main() -> void {
} }
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
if(auto pathID = interface->load(ID::Famicom, "Famicom", "fc")) { if(auto pathID = platform->load(ID::Famicom, "Famicom", "fc")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else { } else {
return false; return false;

View File

@ -3,7 +3,7 @@ Gamepad::Gamepad(bool port) : Controller(port) {
auto Gamepad::data() -> uint3 { auto Gamepad::data() -> uint3 {
if(counter >= 8) return 1; if(counter >= 8) return 1;
if(latched == 1) return interface->inputPoll(port, ID::Device::Gamepad, A); if(latched == 1) return platform->inputPoll(port, ID::Device::Gamepad, A);
switch(counter++) { switch(counter++) {
case 0: return a; case 0: return a;
@ -24,13 +24,13 @@ auto Gamepad::latch(bool data) -> void {
counter = 0; counter = 0;
if(latched == 0) { if(latched == 0) {
a = interface->inputPoll(port, ID::Device::Gamepad, A); a = platform->inputPoll(port, ID::Device::Gamepad, A);
b = interface->inputPoll(port, ID::Device::Gamepad, B); b = platform->inputPoll(port, ID::Device::Gamepad, B);
select = interface->inputPoll(port, ID::Device::Gamepad, Select); select = platform->inputPoll(port, ID::Device::Gamepad, Select);
start = interface->inputPoll(port, ID::Device::Gamepad, Start); start = platform->inputPoll(port, ID::Device::Gamepad, Start);
up = interface->inputPoll(port, ID::Device::Gamepad, Up); up = platform->inputPoll(port, ID::Device::Gamepad, Up);
down = interface->inputPoll(port, ID::Device::Gamepad, Down); down = platform->inputPoll(port, ID::Device::Gamepad, Down);
left = interface->inputPoll(port, ID::Device::Gamepad, Left); left = platform->inputPoll(port, ID::Device::Gamepad, Left);
right = interface->inputPoll(port, ID::Device::Gamepad, Right); right = platform->inputPoll(port, ID::Device::Gamepad, Right);
} }
} }

View File

@ -11,6 +11,7 @@
#include <processor/r6502/r6502.hpp> #include <processor/r6502/r6502.hpp>
namespace Famicom { namespace Famicom {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
using Cheat = Emulator::Cheat; using Cheat = Emulator::Cheat;

View File

@ -2,12 +2,9 @@
namespace Famicom { namespace Famicom {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
information.manufacturer = "Nintendo"; information.manufacturer = "Nintendo";
information.name = "Famicom"; information.name = "Famicom";
information.overscan = true; information.overscan = true;
@ -134,7 +131,7 @@ auto Interface::sha256() -> string {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
return system.load(); return system.load(this);
} }
auto Interface::save() -> void { auto Interface::save() -> void {
@ -147,7 +144,7 @@ auto Interface::unload() -> void {
} }
auto Interface::connect(uint port, uint device) -> void { auto Interface::connect(uint port, uint device) -> void {
Famicom::peripherals.connect(port, device); peripherals.connect(port, device);
} }
auto Interface::power() -> void { auto Interface::power() -> void {

View File

@ -64,7 +64,6 @@ struct Settings {
uint expansionPort = 0; uint expansionPort = 0;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -21,15 +21,17 @@ auto System::runToSave() -> void {
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral); for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
} }
auto System::load() -> bool { auto System::load(Emulator::Interface* interface) -> bool {
information = Information(); information = Information();
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else { } else {
return false; return false;
} }
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC; information.colorburst = Emulator::Constants::Colorburst::NTSC;
serializeInit(); serializeInit();
return information.loaded = true; return information.loaded = true;

View File

@ -5,7 +5,7 @@ struct System {
auto run() -> void; auto run() -> void;
auto runToSave() -> void; auto runToSave() -> void;
auto load() -> bool; auto load(Emulator::Interface*) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
@ -26,13 +26,15 @@ struct System {
auto serializeAll(serializer&) -> void; auto serializeAll(serializer&) -> void;
auto serializeInit() -> void; auto serializeInit() -> void;
private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
bool loaded = false; bool loaded = false;
double colorburst = 0.0; double colorburst = 0.0;
string manifest; string manifest;
} information; } information;
private:
uint _serializeSize = 0; uint _serializeSize = 0;
}; };

View File

@ -29,7 +29,7 @@ auto APU::main() -> void {
stream->sample(sequencer.left / 32768.0, sequencer.right / 32768.0); stream->sample(sequencer.left / 32768.0, sequencer.right / 32768.0);
} else { } else {
double samples[] = {sequencer.left / 32768.0, sequencer.right / 32768.0}; double samples[] = {sequencer.left / 32768.0, sequencer.right / 32768.0};
interface->audioSample(samples, 2); //interface->audioSample(samples, 2);
} }
if(cycle == 0) { //512hz if(cycle == 0) { //512hz

View File

@ -19,23 +19,23 @@ auto Cartridge::load(System::Revision revision) -> bool {
switch(revision) { switch(revision) {
case System::Revision::GameBoy: case System::Revision::GameBoy:
if(auto pathID = interface->load(ID::GameBoy, "Game Boy", "gb")) { if(auto pathID = platform->load(ID::GameBoy, "Game Boy", "gb")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
break; break;
case System::Revision::SuperGameBoy: case System::Revision::SuperGameBoy:
if(auto pathID = interface->load(ID::SuperGameBoy, "Game Boy", "gb")) { if(auto pathID = platform->load(ID::SuperGameBoy, "Game Boy", "gb")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
break; break;
case System::Revision::GameBoyColor: case System::Revision::GameBoyColor:
if(auto pathID = interface->load(ID::GameBoyColor, "Game Boy Color", "gbc")) { if(auto pathID = platform->load(ID::GameBoyColor, "Game Boy Color", "gbc")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
break; break;
} }
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -64,12 +64,12 @@ auto Cartridge::load(System::Revision revision) -> bool {
ram.data = (uint8*)memory::allocate(ram.size, 0xff); ram.data = (uint8*)memory::allocate(ram.size, 0xff);
if(auto name = board["rom/name"].text()) { if(auto name = board["rom/name"].text()) {
if(auto fp = interface->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()) { if(auto name = board["ram/name"].text()) {
if(auto fp = interface->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()));
} }
} }
@ -96,7 +96,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) { if(auto name = document["board/ram/name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Write)) { if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size); fp->write(ram.data, ram.size);
} }
} }

View File

@ -8,15 +8,15 @@ auto CPU::wramAddress(uint16 addr) const -> uint {
auto CPU::joypPoll() -> void { auto CPU::joypPoll() -> void {
uint button = 0, dpad = 0; uint button = 0, dpad = 0;
button |= interface->inputPoll(0, 0, (uint)Input::Start) << 3; button |= platform->inputPoll(0, 0, (uint)Input::Start) << 3;
button |= interface->inputPoll(0, 0, (uint)Input::Select) << 2; button |= platform->inputPoll(0, 0, (uint)Input::Select) << 2;
button |= interface->inputPoll(0, 0, (uint)Input::B) << 1; button |= platform->inputPoll(0, 0, (uint)Input::B) << 1;
button |= interface->inputPoll(0, 0, (uint)Input::A) << 0; button |= platform->inputPoll(0, 0, (uint)Input::A) << 0;
dpad |= interface->inputPoll(0, 0, (uint)Input::Down) << 3; dpad |= platform->inputPoll(0, 0, (uint)Input::Down) << 3;
dpad |= interface->inputPoll(0, 0, (uint)Input::Up) << 2; dpad |= platform->inputPoll(0, 0, (uint)Input::Up) << 2;
dpad |= interface->inputPoll(0, 0, (uint)Input::Left) << 1; dpad |= platform->inputPoll(0, 0, (uint)Input::Left) << 1;
dpad |= interface->inputPoll(0, 0, (uint)Input::Right) << 0; dpad |= platform->inputPoll(0, 0, (uint)Input::Right) << 0;
if(system.revision() != System::Revision::SuperGameBoy) { if(system.revision() != System::Revision::SuperGameBoy) {
//D-pad pivot makes it impossible to press opposing directions at the same time //D-pad pivot makes it impossible to press opposing directions at the same time
@ -145,7 +145,7 @@ auto CPU::writeIO(uint16 addr, uint8 data) -> void {
if(addr == 0xff00) { //JOYP if(addr == 0xff00) { //JOYP
status.p15 = data & 0x20; status.p15 = data & 0x20;
status.p14 = data & 0x10; status.p14 = data & 0x10;
interface->joypWrite(status.p15, status.p14); //interface->joypWrite(status.p15, status.p14);
return; return;
} }

View File

@ -11,6 +11,7 @@
#include <processor/lr35902/lr35902.hpp> #include <processor/lr35902/lr35902.hpp>
namespace GameBoy { namespace GameBoy {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
using Cheat = Emulator::Cheat; using Cheat = Emulator::Cheat;

View File

@ -2,11 +2,9 @@
namespace GameBoy { namespace GameBoy {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
hook = nullptr; hook = nullptr;
information.manufacturer = "Nintendo"; information.manufacturer = "Nintendo";
@ -134,9 +132,9 @@ auto Interface::sha256() -> string {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
if(id == ID::GameBoy) return system.load(System::Revision::GameBoy); if(id == ID::GameBoy) return system.load(this, System::Revision::GameBoy);
if(id == ID::SuperGameBoy) return system.load(System::Revision::SuperGameBoy); if(id == ID::SuperGameBoy) return system.load(this, System::Revision::SuperGameBoy);
if(id == ID::GameBoyColor) return system.load(System::Revision::GameBoyColor); if(id == ID::GameBoyColor) return system.load(this, System::Revision::GameBoyColor);
return false; return false;
} }

View File

@ -70,7 +70,6 @@ struct Settings {
bool colorEmulation = true; bool colorEmulation = true;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -78,7 +78,7 @@ auto PPU::runDMG() -> void {
uint32* output = screen + status.ly * 160 + px++; uint32* output = screen + status.ly * 160 + px++;
*output = color; *output = color;
interface->lcdOutput(color); //Super Game Boy notification //interface->lcdOutput(color); //Super Game Boy notification
} }
auto PPU::runBackgroundDMG() -> void { auto PPU::runBackgroundDMG() -> void {

View File

@ -16,7 +16,7 @@ auto PPU::Enter() -> void {
auto PPU::main() -> void { auto PPU::main() -> void {
status.lx = 0; status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification //interface->lcdScanline(); //Super Game Boy notification
if(status.ly <= 143) { if(status.ly <= 143) {
mode(2); mode(2);

View File

@ -22,10 +22,10 @@ auto System::init() -> void {
assert(interface != nullptr); assert(interface != nullptr);
} }
auto System::load(Revision revision) -> bool { auto System::load(Emulator::Interface* interface, Revision revision) -> bool {
_revision = revision; _revision = revision;
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -34,7 +34,7 @@ auto System::load(Revision revision) -> bool {
if(revision == Revision::SuperGameBoy) path = "board/icd2/rom/name"; if(revision == Revision::SuperGameBoy) path = "board/icd2/rom/name";
if(auto name = document[path].text()) { if(auto name = document[path].text()) {
if(auto fp = interface->open(ID::System, name, File::Read, File::Required)) { if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) {
if(revision == Revision::GameBoy) fp->read(bootROM.dmg, 256); if(revision == Revision::GameBoy) fp->read(bootROM.dmg, 256);
if(revision == Revision::SuperGameBoy) fp->read(bootROM.sgb, 256); if(revision == Revision::SuperGameBoy) fp->read(bootROM.sgb, 256);
if(revision == Revision::GameBoyColor) fp->read(bootROM.cgb, 2048); if(revision == Revision::GameBoyColor) fp->read(bootROM.cgb, 2048);
@ -43,6 +43,7 @@ auto System::load(Revision revision) -> bool {
if(!cartridge.load(revision)) return false; if(!cartridge.load(revision)) return false;
serializeInit(); serializeInit();
this->interface = interface;
return _loaded = true; return _loaded = true;
} }

View File

@ -1,5 +1,3 @@
struct Interface;
enum class Input : uint { enum class Input : uint {
Up, Down, Left, Right, B, A, Select, Start, Up, Down, Left, Right, B, A, Select, Start,
}; };
@ -23,7 +21,7 @@ struct System {
auto runToSave() -> void; auto runToSave() -> void;
auto init() -> void; auto init() -> void;
auto load(Revision) -> bool; auto load(Emulator::Interface*, Revision) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
@ -40,6 +38,8 @@ struct System {
auto serializeAll(serializer&) -> void; auto serializeAll(serializer&) -> void;
auto serializeInit() -> void; auto serializeInit() -> void;
Emulator::Interface* interface = nullptr;
struct BootROM { struct BootROM {
uint8 dmg[ 256]; uint8 dmg[ 256];
uint8 sgb[ 256]; uint8 sgb[ 256];

View File

@ -26,11 +26,11 @@ Cartridge::~Cartridge() {
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = Information(); information = Information();
if(auto pathID = interface->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) { if(auto pathID = platform->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -43,7 +43,7 @@ auto Cartridge::load() -> bool {
if(auto node = document["board/rom"]) { if(auto node = document["board/rom"]) {
mrom.size = min(32 * 1024 * 1024, node["size"].natural()); mrom.size = min(32 * 1024 * 1024, node["size"].natural());
if(auto fp = interface->open(pathID(), node["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Read, File::Required)) {
fp->read(mrom.data, mrom.size); fp->read(mrom.data, mrom.size);
} }
} }
@ -55,7 +55,7 @@ auto Cartridge::load() -> bool {
sram.mask = sram.size - 1; sram.mask = sram.size - 1;
for(auto n : range(sram.size)) sram.data[n] = 0xff; for(auto n : range(sram.size)) sram.data[n] = 0xff;
if(auto fp = interface->open(pathID(), node["name"].text(), File::Read)) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) {
fp->read(sram.data, sram.size); fp->read(sram.data, sram.size);
} }
} }
@ -69,7 +69,7 @@ auto Cartridge::load() -> bool {
eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000; eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
for(auto n : range(eeprom.size)) eeprom.data[n] = 0xff; for(auto n : range(eeprom.size)) eeprom.data[n] = 0xff;
if(auto fp = interface->open(pathID(), node["name"].text(), File::Read)) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) {
fp->read(eeprom.data, eeprom.size); fp->read(eeprom.data, eeprom.size);
} }
} }
@ -85,7 +85,7 @@ auto Cartridge::load() -> bool {
if(!flash.id && flash.size == 64 * 1024) flash.id = 0x1cc2; if(!flash.id && flash.size == 64 * 1024) flash.id = 0x1cc2;
if(!flash.id && flash.size == 128 * 1024) flash.id = 0x09c2; if(!flash.id && flash.size == 128 * 1024) flash.id = 0x09c2;
if(auto fp = interface->open(pathID(), node["name"].text(), File::Read)) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) {
fp->read(flash.data, flash.size); fp->read(flash.data, flash.size);
} }
} }
@ -98,7 +98,7 @@ auto Cartridge::load() -> bool {
auto Cartridge::save() -> void { auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto node = document["board/ram"]) { if(auto node = document["board/ram"]) {
if(auto fp = interface->open(pathID(), node["name"].text(), File::Write)) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Write)) {
if(node["type"].text() == "sram") fp->write(sram.data, sram.size); if(node["type"].text() == "sram") fp->write(sram.data, sram.size);
if(node["type"].text() == "eeprom") fp->write(eeprom.data, eeprom.size); if(node["type"].text() == "eeprom") fp->write(eeprom.data, eeprom.size);
if(node["type"].text() == "flash") fp->write(flash.data, flash.size); if(node["type"].text() == "flash") fp->write(flash.data, flash.size);

View File

@ -97,7 +97,7 @@ auto CPU::keypadRun() -> void {
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(auto n : range(10)) { for(auto n : range(10)) {
if(!regs.keypad.control.flag[n]) continue; if(!regs.keypad.control.flag[n]) continue;
bool input = interface->inputPoll(0, 0, lookup[n]); bool input = platform->inputPoll(0, 0, lookup[n]);
if(regs.keypad.control.condition == 0) test |= input; if(regs.keypad.control.condition == 0) test |= input;
if(regs.keypad.control.condition == 1) test &= input; if(regs.keypad.control.condition == 1) test &= input;
} }

View File

@ -64,7 +64,7 @@ auto CPU::readIO(uint32 addr) -> uint8 {
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1}; static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1};
if(auto result = player.keyinput()) return result() >> 0; if(auto result = player.keyinput()) return result() >> 0;
uint8 result = 0; uint8 result = 0;
for(uint n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, lookup[n]) << n; for(uint n = 0; n < 8; n++) result |= platform->inputPoll(0, 0, lookup[n]) << n;
if((result & 0xc0) == 0xc0) result &= (uint8)~0xc0; //up+down cannot be pressed simultaneously if((result & 0xc0) == 0xc0) result &= (uint8)~0xc0; //up+down cannot be pressed simultaneously
if((result & 0x30) == 0x30) result &= (uint8)~0x30; //left+right cannot be pressed simultaneously if((result & 0x30) == 0x30) result &= (uint8)~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff; return result ^ 0xff;
@ -72,8 +72,8 @@ auto CPU::readIO(uint32 addr) -> uint8 {
case 0x04000131: { case 0x04000131: {
if(auto result = player.keyinput()) return result() >> 8; if(auto result = player.keyinput()) return result() >> 8;
uint8 result = 0; uint8 result = 0;
result |= interface->inputPoll(0, 0, 7) << 0; result |= platform->inputPoll(0, 0, 7) << 0;
result |= interface->inputPoll(0, 0, 6) << 1; result |= platform->inputPoll(0, 0, 6) << 1;
return result ^ 0x03; return result ^ 0x03;
} }

View File

@ -10,6 +10,7 @@
#include <processor/arm/arm.hpp> #include <processor/arm/arm.hpp>
namespace GameBoyAdvance { namespace GameBoyAdvance {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler; extern Scheduler scheduler;

View File

@ -2,12 +2,9 @@
namespace GameBoyAdvance { namespace GameBoyAdvance {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
information.manufacturer = "Nintendo"; information.manufacturer = "Nintendo";
information.name = "Game Boy Advance"; information.name = "Game Boy Advance";
information.overscan = false; information.overscan = false;
@ -96,7 +93,7 @@ auto Interface::loaded() -> bool {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
return system.load(); return system.load(this);
} }
auto Interface::save() -> void { auto Interface::save() -> void {

View File

@ -53,7 +53,6 @@ struct Settings {
bool colorEmulation = true; bool colorEmulation = true;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -100,7 +100,7 @@ auto Player::write(uint2 addr, uint8 byte) -> void {
if(addr == 3 && status.packet == 15) { if(addr == 3 && status.packet == 15) {
status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04 status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04
interface->inputRumble(0, 0, 10, status.rumble); platform->inputRumble(0, 0, 10, status.rumble);
} }
} }

View File

@ -34,20 +34,23 @@ auto System::power() -> void {
scheduler.primary(cpu); scheduler.primary(cpu);
} }
auto System::load() -> bool { auto System::load(Emulator::Interface* interface) -> bool {
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto name = document["system/cpu/rom/name"].text()) { if(auto name = document["system/cpu/rom/name"].text()) {
if(auto fp = interface->open(ID::System, name, File::Read, File::Required)) { if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) {
fp->read(bios.data, bios.size); fp->read(bios.data, bios.size);
} }
} }
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
serializeInit(); serializeInit();
this->interface = interface;
return _loaded = true; return _loaded = true;
} }

View File

@ -19,7 +19,7 @@ struct System {
auto init() -> void; auto init() -> void;
auto term() -> void; auto term() -> void;
auto load() -> bool; auto load(Emulator::Interface*) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
@ -38,6 +38,9 @@ struct System {
auto serializeAll(serializer&) -> void; auto serializeAll(serializer&) -> void;
auto serializeInit() -> void; auto serializeInit() -> void;
private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
string manifest; string manifest;
} information; } information;

View File

@ -5,13 +5,13 @@ namespace MegaDrive {
Cartridge cartridge; Cartridge cartridge;
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = Information(); information = {};
if(auto pathID = interface->load(ID::MegaDrive, "Mega Drive", "md")) { if(auto pathID = platform->load(ID::MegaDrive, "Mega Drive", "md")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -24,7 +24,7 @@ auto Cartridge::load() -> bool {
if(rom.size) { if(rom.size) {
rom.data = new uint8[rom.mask + 1]; rom.data = new uint8[rom.mask + 1];
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, rom.size); fp->read(rom.data, rom.size);
} }
} }
@ -37,7 +37,7 @@ auto Cartridge::load() -> bool {
if(ram.size) { if(ram.size) {
ram.data = new uint8[ram.mask + 1]; ram.data = new uint8[ram.mask + 1];
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read)) { if(auto fp = platform->open(pathID(), name, File::Read)) {
fp->read(ram.data, ram.size); fp->read(ram.data, ram.size);
} }
} }
@ -51,7 +51,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) { if(auto name = document["board/ram/name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Write)) { if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size); fp->write(ram.data, ram.size);
} }
} }

View File

@ -5,19 +5,19 @@ auto Gamepad::readData() -> uint8 {
uint6 data; uint6 data;
if(select == 0) { if(select == 0) {
data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up); data.bit(0) = platform->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down); data.bit(1) = platform->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = 1; data.bit(2) = 1;
data.bit(3) = 1; data.bit(3) = 1;
data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, A); data.bit(4) = platform->inputPoll(port, ID::Device::Gamepad, A);
data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, Start); data.bit(5) = platform->inputPoll(port, ID::Device::Gamepad, Start);
} else { } else {
data.bit(0) = interface->inputPoll(port, ID::Device::Gamepad, Up); data.bit(0) = platform->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = interface->inputPoll(port, ID::Device::Gamepad, Down); data.bit(1) = platform->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = interface->inputPoll(port, ID::Device::Gamepad, Left); data.bit(2) = platform->inputPoll(port, ID::Device::Gamepad, Left);
data.bit(3) = interface->inputPoll(port, ID::Device::Gamepad, Right); data.bit(3) = platform->inputPoll(port, ID::Device::Gamepad, Right);
data.bit(4) = interface->inputPoll(port, ID::Device::Gamepad, B); data.bit(4) = platform->inputPoll(port, ID::Device::Gamepad, B);
data.bit(5) = interface->inputPoll(port, ID::Device::Gamepad, C); data.bit(5) = platform->inputPoll(port, ID::Device::Gamepad, C);
} }
data = ~data; data = ~data;

View File

@ -2,12 +2,9 @@
namespace MegaDrive { namespace MegaDrive {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
information.manufacturer = "Sega"; information.manufacturer = "Sega";
information.name = "Mega Drive"; information.name = "Mega Drive";
information.overscan = true; information.overscan = true;
@ -97,7 +94,7 @@ auto Interface::loaded() -> bool {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
return system.load(); return system.load(this);
} }
auto Interface::save() -> void { auto Interface::save() -> void {
@ -109,7 +106,7 @@ auto Interface::unload() -> void {
} }
auto Interface::connect(uint port, uint device) -> void { auto Interface::connect(uint port, uint device) -> void {
MegaDrive::peripherals.connect(port, device); peripherals.connect(port, device);
} }
auto Interface::power() -> void { auto Interface::power() -> void {

View File

@ -58,7 +58,6 @@ struct Settings {
uint extensionPort = 0; uint extensionPort = 0;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -11,6 +11,7 @@
#include <processor/z80/z80.hpp> #include <processor/z80/z80.hpp>
namespace MegaDrive { namespace MegaDrive {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler; extern Scheduler scheduler;

View File

@ -10,14 +10,18 @@ auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh(); if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh();
} }
auto System::load() -> bool { auto System::load(Emulator::Interface* interface) -> bool {
information = Information(); information = {};
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) {
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
information.colorburst = Emulator::Constants::Colorburst::NTSC; information.colorburst = Emulator::Constants::Colorburst::NTSC;
this->interface = interface;
return information.loaded = true; return information.loaded = true;
} }

View File

@ -4,12 +4,15 @@ struct System {
auto run() -> void; auto run() -> void;
auto load() -> bool; auto load(Emulator::Interface*) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;
private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
bool loaded = false; bool loaded = false;
string manifest; string manifest;

View File

@ -19,6 +19,11 @@ auto Bus::in(uint8 addr) -> uint8 {
switch(addr >> 6) { switch(addr >> 6) {
case 0: { case 0: {
if(system.model() == Model::GameGear) {
auto hardware = peripherals.hardware->readData();
return hardware.bit(6) << 7 | 0x7f;
}
return 0xff; //SMS1 = MDR, SMS2 = 0xff return 0xff; //SMS1 = MDR, SMS2 = 0xff
} }
@ -31,16 +36,26 @@ auto Bus::in(uint8 addr) -> uint8 {
} }
case 3: { case 3: {
auto A = peripherals.controllerPort1->readData(); if(system.model() == Model::MasterSystem) {
auto B = peripherals.controllerPort2->readData(); auto hardware = peripherals.hardware->readData();
auto port1 = peripherals.controllerPort1->readData();
auto port2 = peripherals.controllerPort2->readData();
if(addr.bit(0) == 0) { if(addr.bit(0) == 0) {
return A.bits(0,5) << 0 | B.bits(0,1) << 6; return port1.bits(0,5) << 0 | port2.bits(0,1) << 6;
} else { } else {
//d4 = reset button return port2.bits(2,5) << 0 | hardware.bit(0) << 4 | 1 << 5 | port1.bit(6) << 6 | port2.bit(6) << 7;
//d5 = cartridge CONT pin
return B.bits(2,5) << 0 | 1 << 4 | 1 << 5 | A.bit(6) << 6 | B.bit(6) << 7;
} }
} }
if(system.model() == Model::GameGear) {
auto hardware = peripherals.hardware->readData();
if(addr.bit(0) == 0) {
return hardware.bits(0,5) << 0 | 0xc0;
} else {
return 0xff;
}
}
return 0xff;
}
} }

View File

@ -10,18 +10,18 @@ auto Cartridge::load() -> bool {
switch(system.model()) { switch(system.model()) {
case Model::MasterSystem: case Model::MasterSystem:
if(auto pathID = interface->load(ID::MasterSystem, "Master System", "ms")) { if(auto pathID = platform->load(ID::MasterSystem, "Master System", "ms")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
break; break;
case Model::GameGear: case Model::GameGear:
if(auto pathID = interface->load(ID::GameGear, "Game Gear", "gg")) { if(auto pathID = platform->load(ID::GameGear, "Game Gear", "gg")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
break; break;
} }
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -34,7 +34,7 @@ auto Cartridge::load() -> bool {
if(rom.size) { if(rom.size) {
rom.data = new uint8[rom.mask + 1]; rom.data = new uint8[rom.mask + 1];
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, rom.size); fp->read(rom.data, rom.size);
} }
} }
@ -47,7 +47,7 @@ auto Cartridge::load() -> bool {
if(ram.size) { if(ram.size) {
ram.data = new uint8[ram.mask + 1]; ram.data = new uint8[ram.mask + 1];
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read)) { if(auto fp = platform->open(pathID(), name, File::Read)) {
fp->read(ram.data, ram.size); fp->read(ram.data, ram.size);
} }
} }
@ -61,7 +61,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) { if(auto name = document["board/ram/name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Write)) { if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size); fp->write(ram.data, ram.size);
} }
} }
@ -75,9 +75,6 @@ auto Cartridge::unload() -> void {
} }
auto Cartridge::power() -> void { auto Cartridge::power() -> void {
}
auto Cartridge::reset() -> void {
memory::fill(&mapper, sizeof(Mapper)); memory::fill(&mapper, sizeof(Mapper));
mapper.romPage0 = 0; mapper.romPage0 = 0;
mapper.romPage1 = 1; mapper.romPage1 = 1;

View File

@ -9,7 +9,6 @@ struct Cartridge {
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
//mapper.cpp //mapper.cpp
auto read(uint16 addr) -> maybe<uint8>; auto read(uint16 addr) -> maybe<uint8>;

View File

@ -2,6 +2,8 @@
namespace MasterSystem { namespace MasterSystem {
#include "mastersystem/mastersystem.cpp"
#include "gamegear/gamegear.cpp"
#include "gamepad/gamepad.cpp" #include "gamepad/gamepad.cpp"
Controller::Controller(uint port) : port(port) { Controller::Controller(uint port) : port(port) {
@ -14,8 +16,9 @@ Controller::~Controller() {
auto Controller::Enter() -> void { auto Controller::Enter() -> void {
while(true) { while(true) {
scheduler.synchronize(); scheduler.synchronize();
if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main(); if(auto device = peripherals.hardware) if(device->active()) device->main();
if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main(); if(auto device = peripherals.controllerPort1) if(device->active()) device->main();
if(auto device = peripherals.controllerPort2) if(device->active()) device->main();
} }
} }

View File

@ -5,9 +5,11 @@ struct Controller : Thread {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
virtual auto readData() -> uint7 { return 0x7f; } virtual auto readData() -> uint8 { return 0xff; }
const uint port; const uint port;
}; };
#include "mastersystem/mastersystem.hpp"
#include "gamegear/gamegear.hpp"
#include "gamepad/gamepad.hpp" #include "gamepad/gamepad.hpp"

View File

@ -0,0 +1,14 @@
GameGearControls::GameGearControls(uint port) : Controller(port) {
}
auto GameGearControls::readData() -> uint8 {
uint8 data = 0xff;
data.bit(0) = !platform->inputPoll(port, ID::Device::GameGearControls, Up);
data.bit(1) = !platform->inputPoll(port, ID::Device::GameGearControls, Down);
data.bit(2) = !platform->inputPoll(port, ID::Device::GameGearControls, Left);
data.bit(3) = !platform->inputPoll(port, ID::Device::GameGearControls, Right);
data.bit(4) = !platform->inputPoll(port, ID::Device::GameGearControls, One);
data.bit(5) = !platform->inputPoll(port, ID::Device::GameGearControls, Two);
data.bit(6) = !platform->inputPoll(port, ID::Device::GameGearControls, Start);
return data;
}

View File

@ -0,0 +1,9 @@
struct GameGearControls : Controller {
enum : uint {
Up, Down, Left, Right, One, Two, Start,
};
GameGearControls(uint port);
auto readData() -> uint8 override;
};

View File

@ -1,16 +1,13 @@
Gamepad::Gamepad(uint port) : Controller(port) { Gamepad::Gamepad(uint port) : Controller(port) {
} }
auto Gamepad::readData() -> uint7 { auto Gamepad::readData() -> uint8 {
uint7 data; uint8 data = 0xff;
data.bit(0) = !platform->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(0) = !interface->inputPoll(port, ID::Device::Gamepad, Up); data.bit(1) = !platform->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(1) = !interface->inputPoll(port, ID::Device::Gamepad, Down); data.bit(2) = !platform->inputPoll(port, ID::Device::Gamepad, Left);
data.bit(2) = !interface->inputPoll(port, ID::Device::Gamepad, Left); data.bit(3) = !platform->inputPoll(port, ID::Device::Gamepad, Right);
data.bit(3) = !interface->inputPoll(port, ID::Device::Gamepad, Right); data.bit(4) = !platform->inputPoll(port, ID::Device::Gamepad, One);
data.bit(4) = !interface->inputPoll(port, ID::Device::Gamepad, One); data.bit(5) = !platform->inputPoll(port, ID::Device::Gamepad, Two);
data.bit(5) = !interface->inputPoll(port, ID::Device::Gamepad, Two);
data.bit(6) = 1;
return data; return data;
} }

View File

@ -5,5 +5,5 @@ struct Gamepad : Controller {
Gamepad(uint port); Gamepad(uint port);
auto readData() -> uint7 override; auto readData() -> uint8 override;
}; };

View File

@ -0,0 +1,8 @@
MasterSystemControls::MasterSystemControls(uint port) : Controller(port) {
}
auto MasterSystemControls::readData() -> uint8 {
uint8 data = 0xff;
data.bit(0) = !platform->inputPoll(port, ID::Device::MasterSystemControls, Reset);
return data;
}

View File

@ -0,0 +1,9 @@
struct MasterSystemControls : Controller {
enum : uint {
Reset,
};
MasterSystemControls(uint port);
auto readData() -> uint8 override;
};

View File

@ -42,10 +42,6 @@ auto CPU::setINT(bool value) -> void {
auto CPU::power() -> void { auto CPU::power() -> void {
Z80::bus = &MasterSystem::bus; Z80::bus = &MasterSystem::bus;
Z80::power(); Z80::power();
}
auto CPU::reset() -> void {
Z80::reset();
create(CPU::Enter, system.colorburst()); create(CPU::Enter, system.colorburst());
memory::fill(&state, sizeof(State)); memory::fill(&state, sizeof(State));

View File

@ -9,7 +9,6 @@ struct CPU : Processor::Z80, Thread {
auto setINT(bool value) -> void; auto setINT(bool value) -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
vector<Thread*> peripherals; vector<Thread*> peripherals;

View File

@ -0,0 +1,118 @@
GameGearInterface::GameGearInterface() {
information.manufacturer = "Sega";
information.name = "Game Gear";
information.overscan = false;
information.resettable = false;
information.capability.states = false;
information.capability.cheats = false;
media.append({ID::GameGear, "Game Gear", "gg"});
Port hardware{ID::Port::Hardware, "Hardware"};
{ Device device{ID::Device::GameGearControls, "Controls"};
device.inputs.append({0, "Up"});
device.inputs.append({0, "Down"});
device.inputs.append({0, "Left"});
device.inputs.append({0, "Right"});
device.inputs.append({0, "1"});
device.inputs.append({0, "2"});
device.inputs.append({0, "Start"});
hardware.devices.append(device);
}
ports.append(move(hardware));
}
auto GameGearInterface::manifest() -> string {
return cartridge.manifest();
}
auto GameGearInterface::title() -> string {
return cartridge.title();
}
auto GameGearInterface::videoSize() -> VideoSize {
return {160, 144};
}
auto GameGearInterface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 160;
uint h = 144;
uint m = min(width / w, height / h);
return {w * m, h * m};
}
auto GameGearInterface::videoFrequency() -> double {
return 60.0;
}
auto GameGearInterface::videoColors() -> uint32 {
return 1 << 12;
}
auto GameGearInterface::videoColor(uint32 color) -> uint64 {
uint4 B = color >> 8;
uint4 G = color >> 4;
uint4 R = color >> 0;
uint64 r = image::normalize(R, 4, 16);
uint64 g = image::normalize(G, 4, 16);
uint64 b = image::normalize(B, 4, 16);
return r << 32 | g << 16 | b << 0;
}
auto GameGearInterface::audioFrequency() -> double {
return 44'100.0;
}
auto GameGearInterface::loaded() -> bool {
return system.loaded();
}
auto GameGearInterface::load(uint id) -> bool {
if(id == ID::GameGear) return system.load(this, Model::GameGear);
return false;
}
auto GameGearInterface::save() -> void {
system.save();
}
auto GameGearInterface::unload() -> void {
system.unload();
}
auto GameGearInterface::connect(uint port, uint device) -> void {
peripherals.connect(port, device);
}
auto GameGearInterface::power() -> void {
system.power();
}
auto GameGearInterface::run() -> void {
system.run();
}
auto GameGearInterface::serialize() -> serializer {
return {};
}
auto GameGearInterface::unserialize(serializer& s) -> bool {
return false;
}
auto GameGearInterface::cap(const string& name) -> bool {
return false;
}
auto GameGearInterface::get(const string& name) -> any {
return {};
}
auto GameGearInterface::set(const string& name, const any& value) -> bool {
return false;
}

View File

@ -2,142 +2,8 @@
namespace MasterSystem { namespace MasterSystem {
Interface* interface = nullptr;
Settings settings; Settings settings;
#include "master-system.cpp"
Interface::Interface() { #include "game-gear.cpp"
interface = this;
information.manufacturer = "Sega";
information.name = "Master System";
information.overscan = true;
information.resettable = true;
information.capability.states = false;
information.capability.cheats = false;
media.append({ID::MasterSystem, "Master System", "ms"});
media.append({ID::GameGear, "Game Gear", "gg"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Gamepad, "Gamepad"};
device.inputs.append({0, "Up"});
device.inputs.append({0, "Down"});
device.inputs.append({0, "Left"});
device.inputs.append({0, "Right"});
device.inputs.append({0, "1"});
device.inputs.append({0, "2"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
}
auto Interface::manifest() -> string {
return cartridge.manifest();
}
auto Interface::title() -> string {
return cartridge.title();
}
auto Interface::videoSize() -> VideoSize {
return {256, 240};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 256;
uint h = 240;
uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)};
}
auto Interface::videoFrequency() -> double {
return 60.0;
}
auto Interface::videoColors() -> uint32 {
return 1 << 6;
}
auto Interface::videoColor(uint32 color) -> uint64 {
uint2 B = color >> 4;
uint2 G = color >> 2;
uint2 R = color >> 0;
uint64 r = image::normalize(R, 2, 16);
uint64 g = image::normalize(G, 2, 16);
uint64 b = image::normalize(B, 2, 16);
return r << 32 | g << 16 | b << 0;
}
auto Interface::audioFrequency() -> double {
return 44'100.0;
}
auto Interface::loaded() -> bool {
return system.loaded();
}
auto Interface::load(uint id) -> bool {
if(id == ID::MasterSystem) return system.load(Model::MasterSystem);
if(id == ID::GameGear) return system.load(Model::GameGear);
return false;
}
auto Interface::save() -> void {
system.save();
}
auto Interface::unload() -> void {
system.unload();
}
auto Interface::connect(uint port, uint device) -> void {
MasterSystem::peripherals.connect(port, device);
}
auto Interface::power() -> void {
system.power();
}
auto Interface::reset() -> void {
system.reset();
}
auto Interface::run() -> void {
system.run();
}
auto Interface::serialize() -> serializer {
return {};
}
auto Interface::unserialize(serializer& s) -> bool {
return false;
}
auto Interface::cap(const string& name) -> bool {
return false;
}
auto Interface::get(const string& name) -> any {
return {};
}
auto Interface::set(const string& name, const any& value) -> bool {
return false;
}
} }

View File

@ -8,20 +8,56 @@ struct ID {
}; };
struct Port { enum : uint { struct Port { enum : uint {
Hardware,
Controller1, Controller1,
Controller2, Controller2,
};}; };};
struct Device { enum : uint { struct Device { enum : uint {
None, None,
MasterSystemControls,
GameGearControls,
Gamepad, Gamepad,
};}; };};
}; };
struct Interface : Emulator::Interface { struct MasterSystemInterface : Emulator::Interface {
using Emulator::Interface::load; using Emulator::Interface::load;
Interface(); MasterSystemInterface();
auto manifest() -> string override;
auto title() -> string override;
auto videoSize() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoFrequency() -> double override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;
auto audioFrequency() -> double override;
auto loaded() -> bool override;
auto load(uint id) -> bool override;
auto save() -> void override;
auto unload() -> void override;
auto connect(uint port, uint device) -> void override;
auto power() -> void override;
auto run() -> void override;
auto serialize() -> serializer override;
auto unserialize(serializer&) -> bool override;
auto cap(const string& name) -> bool override;
auto get(const string& name) -> any override;
auto set(const string& name, const any& value) -> bool override;
};
struct GameGearInterface : Emulator::Interface {
using Emulator::Interface::load;
GameGearInterface();
auto manifest() -> string override; auto manifest() -> string override;
auto title() -> string override; auto title() -> string override;
@ -41,7 +77,6 @@ struct Interface : Emulator::Interface {
auto connect(uint port, uint device) -> void override; auto connect(uint port, uint device) -> void override;
auto power() -> void override; auto power() -> void override;
auto reset() -> void override;
auto run() -> void override; auto run() -> void override;
auto serialize() -> serializer override; auto serialize() -> serializer override;
@ -53,11 +88,11 @@ struct Interface : Emulator::Interface {
}; };
struct Settings { struct Settings {
uint hardware = 0;
uint controllerPort1 = 0; uint controllerPort1 = 0;
uint controllerPort2 = 0; uint controllerPort2 = 0;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -0,0 +1,133 @@
MasterSystemInterface::MasterSystemInterface() {
information.manufacturer = "Sega";
information.name = "Master System";
information.overscan = true;
information.resettable = false;
information.capability.states = false;
information.capability.cheats = false;
media.append({ID::MasterSystem, "Master System", "ms"});
Port hardware{ID::Port::Hardware, "Hardware"};
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
{ Device device{ID::Device::MasterSystemControls, "Controls"};
device.inputs.append({0, "Reset"});
hardware.devices.append(device);
}
{ Device device{ID::Device::None, "None"};
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
{ Device device{ID::Device::Gamepad, "Gamepad"};
device.inputs.append({0, "Up"});
device.inputs.append({0, "Down"});
device.inputs.append({0, "Left"});
device.inputs.append({0, "Right"});
device.inputs.append({0, "1"});
device.inputs.append({0, "2"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);
}
ports.append(move(hardware));
ports.append(move(controllerPort1));
ports.append(move(controllerPort2));
}
auto MasterSystemInterface::manifest() -> string {
return cartridge.manifest();
}
auto MasterSystemInterface::title() -> string {
return cartridge.title();
}
auto MasterSystemInterface::videoSize() -> VideoSize {
return {256, 240};
}
auto MasterSystemInterface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 256;
uint h = 240;
uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)};
}
auto MasterSystemInterface::videoFrequency() -> double {
return 60.0;
}
auto MasterSystemInterface::videoColors() -> uint32 {
return 1 << 6;
}
auto MasterSystemInterface::videoColor(uint32 color) -> uint64 {
uint2 B = color >> 4;
uint2 G = color >> 2;
uint2 R = color >> 0;
uint64 r = image::normalize(R, 2, 16);
uint64 g = image::normalize(G, 2, 16);
uint64 b = image::normalize(B, 2, 16);
return r << 32 | g << 16 | b << 0;
}
auto MasterSystemInterface::audioFrequency() -> double {
return 44'100.0;
}
auto MasterSystemInterface::loaded() -> bool {
return system.loaded();
}
auto MasterSystemInterface::load(uint id) -> bool {
if(id == ID::MasterSystem) return system.load(this, Model::MasterSystem);
return false;
}
auto MasterSystemInterface::save() -> void {
system.save();
}
auto MasterSystemInterface::unload() -> void {
system.unload();
}
auto MasterSystemInterface::connect(uint port, uint device) -> void {
peripherals.connect(port, device);
}
auto MasterSystemInterface::power() -> void {
system.power();
}
auto MasterSystemInterface::run() -> void {
system.run();
}
auto MasterSystemInterface::serialize() -> serializer {
return {};
}
auto MasterSystemInterface::unserialize(serializer& s) -> bool {
return false;
}
auto MasterSystemInterface::cap(const string& name) -> bool {
return false;
}
auto MasterSystemInterface::get(const string& name) -> any {
return {};
}
auto MasterSystemInterface::set(const string& name, const any& value) -> bool {
return false;
}

View File

@ -10,9 +10,11 @@
#include <processor/z80/z80.hpp> #include <processor/z80/z80.hpp>
namespace MasterSystem { namespace MasterSystem {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler; extern Scheduler scheduler;
struct Interface;
enum class Model : uint { enum class Model : uint {
MasterSystem, MasterSystem,

View File

@ -19,9 +19,6 @@ auto PSG::step(uint clocks) -> void {
} }
auto PSG::power() -> void { auto PSG::power() -> void {
}
auto PSG::reset() -> void {
create(PSG::Enter, system.colorburst()); create(PSG::Enter, system.colorburst());
stream = Emulator::audio.createStream(2, system.colorburst()); stream = Emulator::audio.createStream(2, system.colorburst());
} }

View File

@ -8,7 +8,6 @@ struct PSG : Thread {
auto step(uint clocks) -> void; auto step(uint clocks) -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
}; };
extern PSG psg; extern PSG psg;

View File

@ -1,26 +1,40 @@
Peripherals peripherals; Peripherals peripherals;
auto Peripherals::unload() -> void { auto Peripherals::unload() -> void {
delete hardware;
delete controllerPort1; delete controllerPort1;
delete controllerPort2; delete controllerPort2;
hardware = nullptr;
controllerPort1 = nullptr; controllerPort1 = nullptr;
controllerPort2 = nullptr; controllerPort2 = nullptr;
} }
auto Peripherals::reset() -> void { auto Peripherals::reset() -> void {
connect(ID::Port::Hardware, settings.hardware);
connect(ID::Port::Controller1, settings.controllerPort1); connect(ID::Port::Controller1, settings.controllerPort1);
connect(ID::Port::Controller2, settings.controllerPort2); connect(ID::Port::Controller2, settings.controllerPort2);
} }
auto Peripherals::connect(uint port, uint device) -> void { auto Peripherals::connect(uint port, uint device) -> void {
cpu.peripherals.reset();
if(system.model() == Model::MasterSystem) {
if(port == ID::Port::Hardware) {
settings.hardware = device;
if(!system.loaded()) return;
delete hardware;
hardware = new MasterSystemControls(ID::Port::Hardware);
}
if(port == ID::Port::Controller1) { if(port == ID::Port::Controller1) {
settings.controllerPort1 = device; settings.controllerPort1 = device;
if(!system.loaded()) return; if(!system.loaded()) return;
delete controllerPort1; delete controllerPort1;
switch(device) { default: switch(device) { default:
case ID::Device::None: controllerPort1 = new Controller(0); break; case ID::Device::None: controllerPort1 = new Controller(ID::Port::Controller1); break;
case ID::Device::Gamepad: controllerPort1 = new Gamepad(0); break; case ID::Device::Gamepad: controllerPort1 = new Gamepad(ID::Port::Controller1); break;
} }
} }
@ -30,12 +44,25 @@ auto Peripherals::connect(uint port, uint device) -> void {
delete controllerPort2; delete controllerPort2;
switch(device) { default: switch(device) { default:
case ID::Device::None: controllerPort2 = new Controller(1); break; case ID::Device::None: controllerPort2 = new Controller(ID::Port::Controller2); break;
case ID::Device::Gamepad: controllerPort2 = new Gamepad(1); break; case ID::Device::Gamepad: controllerPort2 = new Gamepad(ID::Port::Controller2); break;
} }
} }
cpu.peripherals.reset(); cpu.peripherals.append(hardware);
cpu.peripherals.append(controllerPort1); cpu.peripherals.append(controllerPort1);
cpu.peripherals.append(controllerPort2); cpu.peripherals.append(controllerPort2);
} }
if(system.model() == Model::GameGear) {
if(port == ID::Port::Hardware) {
settings.hardware = device;
if(!system.loaded()) return;
delete hardware;
hardware = new GameGearControls(ID::Port::Hardware);
}
cpu.peripherals.append(hardware);
}
}

View File

@ -10,17 +10,18 @@ auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh(); if(scheduler.enter() == Scheduler::Event::Frame) vdp.refresh();
} }
auto System::load(Model model) -> bool { auto System::load(Emulator::Interface* interface, Model model) -> bool {
information = {}; information = {};
information.model = model; information.model = model;
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC; information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true; return information.loaded = true;
} }
@ -35,14 +36,6 @@ auto System::unload() -> void {
} }
auto System::power() -> void { auto System::power() -> void {
cartridge.power();
cpu.power();
vdp.power();
psg.power();
reset();
}
auto System::reset() -> void {
Emulator::video.reset(); Emulator::video.reset();
Emulator::video.setInterface(interface); Emulator::video.setInterface(interface);
Emulator::video.setPalette(); Emulator::video.setPalette();
@ -51,10 +44,10 @@ auto System::reset() -> void {
Emulator::audio.setInterface(interface); Emulator::audio.setInterface(interface);
scheduler.reset(); scheduler.reset();
cartridge.reset(); cartridge.power();
cpu.reset(); cpu.power();
vdp.reset(); vdp.power();
psg.reset(); psg.power();
scheduler.primary(cpu); scheduler.primary(cpu);
peripherals.reset(); peripherals.reset();

View File

@ -5,14 +5,15 @@ struct System {
auto run() -> void; auto run() -> void;
auto load(Model model) -> bool; auto load(Emulator::Interface* interface, Model model) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
private: private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
bool loaded = false; bool loaded = false;
Model model = Model::MasterSystem; Model model = Model::MasterSystem;
@ -26,6 +27,7 @@ struct Peripherals {
auto reset() -> void; auto reset() -> void;
auto connect(uint port, uint device) -> void; auto connect(uint port, uint device) -> void;
Controller* hardware = nullptr;
Controller* controllerPort1 = nullptr; Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr; Controller* controllerPort2 = nullptr;
}; };

View File

@ -51,9 +51,6 @@ auto VDP::Background::run() -> void {
} }
auto VDP::Background::power() -> void { auto VDP::Background::power() -> void {
}
auto VDP::Background::reset() -> void {
memory::fill(&state, sizeof(State)); memory::fill(&state, sizeof(State));
memory::fill(&output, sizeof(Output)); memory::fill(&output, sizeof(Output));
} }

View File

@ -50,7 +50,10 @@ auto VDP::data(uint8 data) -> void {
if(io.code <= 2) { if(io.code <= 2) {
vram[io.address++] = data; vram[io.address++] = data;
} else { } else {
cram[io.address++ & 0x1f] = data; uint mask = 0;
if(system.model() == Model::MasterSystem) mask = 0x1f;
if(system.model() == Model::GameGear) mask = 0x3f;
cram[io.address++ & mask] = data;
} }
} }

View File

@ -63,9 +63,6 @@ auto VDP::Sprite::run() -> void {
} }
auto VDP::Sprite::power() -> void { auto VDP::Sprite::power() -> void {
}
auto VDP::Sprite::reset() -> void {
memory::fill(&state, sizeof(State)); memory::fill(&state, sizeof(State));
memory::fill(&output, sizeof(Output)); memory::fill(&output, sizeof(Output));
} }

View File

@ -29,19 +29,20 @@ auto VDP::main() -> void {
sprite.scanline(); sprite.scanline();
//684 clocks/scanline //684 clocks/scanline
uint y = io.vcounter;
for(uint x : range(256)) { for(uint x : range(256)) {
background.run(); background.run();
sprite.run(); sprite.run();
step(2); step(2);
uint6 color = cram[io.backdropColor]; uint12 color = palette(io.backdropColor);
if(background.output.color && (background.output.priority || !sprite.output.color)) { if(background.output.color && (background.output.priority || !sprite.output.color)) {
color = cram[background.output.palette << 4 | background.output.color]; color = palette(background.output.palette << 4 | background.output.color);
} else if(sprite.output.color) { } else if(sprite.output.color) {
color = cram[16 | sprite.output.color]; color = palette(16 | sprite.output.color);
} }
if(x <= 7 && io.leftClip) color = cram[io.backdropColor]; if(x <= 7 && io.leftClip) color = palette(io.backdropColor);
if(!io.displayEnable || io.vcounter >= vlines()) color = 0; if(!io.displayEnable || y >= vlines()) color = 0;
buffer[io.vcounter * 256 + x] = color; buffer[io.vcounter * 256 + x] = color;
} }
step(172); step(172);
@ -65,9 +66,15 @@ auto VDP::step(uint clocks) -> void {
} }
auto VDP::refresh() -> void { auto VDP::refresh() -> void {
if(system.model() == Model::MasterSystem) {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240); Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
} }
if(system.model() == Model::GameGear) {
Emulator::video.refresh(buffer + 24 * 256 + 48, 256 * sizeof(uint32), 160, 144);
}
}
auto VDP::vlines() -> uint { auto VDP::vlines() -> uint {
if(io.lines240) return 240; if(io.lines240) return 240;
if(io.lines224) return 224; if(io.lines224) return 224;
@ -79,17 +86,24 @@ auto VDP::vblank() -> bool {
} }
auto VDP::power() -> void { auto VDP::power() -> void {
background.power();
sprite.power();
}
auto VDP::reset() -> void {
create(VDP::Enter, system.colorburst() * 15.0 / 5.0); create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
memory::fill(&io, sizeof(IO)); memory::fill(&io, sizeof(IO));
background.reset(); background.power();
sprite.reset(); sprite.power();
}
auto VDP::palette(uint5 index) -> uint12 {
if(system.model() == Model::MasterSystem) {
return cram[index];
}
if(system.model() == Model::GameGear) {
return cram[index * 2 + 0] << 0 | cram[index * 2 + 1] << 8;
}
return 0;
} }
} }

View File

@ -10,7 +10,6 @@ struct VDP : Thread {
auto vblank() -> bool; auto vblank() -> bool;
auto power() -> void; auto power() -> void;
auto reset() -> void;
//io.cpp //io.cpp
auto vcounter() -> uint8; auto vcounter() -> uint8;
@ -28,7 +27,6 @@ struct VDP : Thread {
auto run() -> void; auto run() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
struct State { struct State {
uint x; uint x;
@ -48,7 +46,6 @@ struct VDP : Thread {
auto run() -> void; auto run() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void;
struct Object { struct Object {
uint8 x; uint8 x;
@ -69,9 +66,11 @@ struct VDP : Thread {
} sprite; } sprite;
private: private:
auto palette(uint5 index) -> uint12;
uint32 buffer[256 * 262]; uint32 buffer[256 * 262];
uint8 vram[0x4000]; uint8 vram[0x4000];
uint8 cram[0x20]; uint8 cram[0x40]; //MS = 0x20, GG = 0x40
struct IO { struct IO {
uint vcounter; //vertical counter uint vcounter; //vertical counter

View File

@ -7,11 +7,11 @@ Cartridge cartridge;
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = {}; information = {};
if(auto pathID = interface->load(ID::PCEngine, "PC Engine", "pce")) { if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
if(auto fp = interface->open(pathID(), "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -23,7 +23,7 @@ auto Cartridge::load() -> bool {
if(rom.size) { if(rom.size) {
rom.data = new uint8[rom.size](); rom.data = new uint8[rom.size]();
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read, File::Required)) { if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, rom.size); fp->read(rom.data, rom.size);
} }
} }
@ -35,7 +35,7 @@ auto Cartridge::load() -> bool {
if(ram.size) { if(ram.size) {
ram.data = new uint8[ram.size](); ram.data = new uint8[ram.size]();
if(auto name = node["name"].text()) { if(auto name = node["name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Read)) { if(auto fp = platform->open(pathID(), name, File::Read)) {
fp->read(ram.data, ram.size); fp->read(ram.data, ram.size);
} }
} }
@ -49,7 +49,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(auto name = document["board/ram/name"].text()) { if(auto name = document["board/ram/name"].text()) {
if(auto fp = interface->open(pathID(), name, File::Write)) { if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size); fp->write(ram.data, ram.size);
} }
} }

View File

@ -9,6 +9,13 @@ auto CPU::Enter() -> void {
} }
auto CPU::main() -> void { auto CPU::main() -> void {
#if 1
static uint counter = 0;
if(++counter < 10) {
print(disassemble(r.pc), "\n");
}
#endif
instruction(); instruction();
} }
@ -24,4 +31,20 @@ auto CPU::power() -> void {
create(CPU::Enter, system.colorburst() * 6.0); create(CPU::Enter, system.colorburst() * 6.0);
} }
auto CPU::read(uint16 addr) -> uint8 {
step(3);
return 0xea;
}
auto CPU::write(uint16 addr, uint8 data) -> void {
step(3);
}
auto CPU::lastCycle() -> void {
}
auto CPU::disassembleRead(uint16 pc) -> uint8 {
return 0xea;
}
} }

View File

@ -3,10 +3,16 @@
struct CPU : Processor::HuC6280, Thread { struct CPU : Processor::HuC6280, Thread {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto step(uint clocks) -> void; auto step(uint clocks) -> void override;
auto power() -> void; auto power() -> void;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
auto lastCycle() -> void override;
auto disassembleRead(uint16 addr) -> uint8 override;
vector<Thread*> peripherals; vector<Thread*> peripherals;
}; };

View File

@ -2,12 +2,9 @@
namespace PCEngine { namespace PCEngine {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
information.manufacturer = "NEC"; information.manufacturer = "NEC";
information.name = "PC Engine"; information.name = "PC Engine";
information.overscan = true; information.overscan = true;
@ -80,7 +77,7 @@ auto Interface::loaded() -> bool {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
if(id == ID::PCEngine) return system.load(); if(id == ID::PCEngine) return system.load(this);
return false; return false;
} }

View File

@ -53,7 +53,6 @@ struct Settings {
uint controllerPort = 0; uint controllerPort = 0;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -10,6 +10,7 @@
#include <processor/huc6280/huc6280.hpp> #include <processor/huc6280/huc6280.hpp>
namespace PCEngine { namespace PCEngine {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler; extern Scheduler scheduler;

View File

@ -10,16 +10,17 @@ auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) vdc.refresh(); if(scheduler.enter() == Scheduler::Event::Frame) vdc.refresh();
} }
auto System::load() -> bool { auto System::load(Emulator::Interface* interface) -> bool {
information = {}; information = {};
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC; information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true; return information.loaded = true;
} }

View File

@ -4,13 +4,15 @@ struct System {
auto run() -> void; auto run() -> void;
auto load() -> bool; auto load(Emulator::Interface*) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
private: private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
bool loaded = false; bool loaded = false;
string manifest; string manifest;

View File

@ -0,0 +1,29 @@
auto HuC6280::disassemble(uint16 pc) -> string {
string s{hex(r.pc, 4L), " "};
uint8 opcode = disassembleRead(pc++);
#define op(id, name, ...) case id: o = {name, string_vector{__VA_ARGS__}.merge(",")};
string o;
switch(opcode) {
op(0xea, "nop")
}
if(!o) o = {"??? (", hex(opcode, 2L), ")"};
s.append(pad(o, -16L, ' '));
#undef op
s.append(" A:", hex(r.a, 2L));
s.append(" X:", hex(r.x, 2L));
s.append(" Y:", hex(r.y, 2L));
s.append(" S:", hex(r.s, 2L));
s.append(" ");
s.append(r.p.n ? "N" : "n");
s.append(r.p.v ? "V" : "v");
s.append(r.p.t ? "T" : "t");
s.append(r.p.b ? "B" : "b");
s.append(r.p.d ? "D" : "d");
s.append(r.p.i ? "I" : "i");
s.append(r.p.z ? "Z" : "z");
s.append(r.p.c ? "C" : "c");
return s;
}

View File

@ -3,11 +3,21 @@
namespace Processor { namespace Processor {
#define L lastCycle();
#include "memory.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "disassembler.cpp"
#undef L
auto HuC6280::power() -> void { auto HuC6280::power() -> void {
} r.a = 0x00;
r.x = 0x00;
auto HuC6280::instruction() -> void { r.y = 0x00;
step(1); r.s = 0x00;
r.pc = 0x0000;
r.p = 0x00;
r.mdr = 0x00;
} }
} }

View File

@ -6,9 +6,56 @@ namespace Processor {
struct HuC6280 { struct HuC6280 {
virtual auto step(uint clocks) -> void = 0; virtual auto step(uint clocks) -> void = 0;
virtual auto read(uint16 addr) -> uint8 = 0;
virtual auto write(uint16 addr, uint8 data) -> void = 0;
virtual auto lastCycle() -> void = 0;
auto power() -> void; auto power() -> void;
//memory.cpp
auto io() -> uint8;
auto opcode() -> uint8;
auto operand() -> uint8;
//instruction.cpp
auto instruction() -> void; auto instruction() -> void;
//instructions.cpp
auto instructionNOP() -> void;
//disassembler.cpp
virtual auto disassembleRead(uint16 pc) -> uint8 = 0;
auto disassemble(uint16 pc) -> string;
struct Flags {
union {
uint8_t data = 0;
BooleanBitField<uint8_t, 0> c; //carry
BooleanBitField<uint8_t, 1> z; //zero
BooleanBitField<uint8_t, 2> i; //interrupt
BooleanBitField<uint8_t, 3> d; //decimal
BooleanBitField<uint8_t, 4> b; //break
BooleanBitField<uint8_t, 5> t; //...
BooleanBitField<uint8_t, 6> v; //overflow
BooleanBitField<uint8_t, 7> n; //negative
};
inline operator uint() const { return data; }
inline auto& operator =(uint value) { return data = value, *this; }
inline auto& operator&=(uint value) { return data &= value, *this; }
inline auto& operator|=(uint value) { return data |= value, *this; }
inline auto& operator^=(uint value) { return data ^= value, *this; }
};
struct Registers {
uint8 a;
uint8 x;
uint8 y;
uint8 s;
uint16 pc;
Flags p;
uint8 mdr;
} r;
}; };
} }

View File

@ -0,0 +1,9 @@
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
auto HuC6280::instruction() -> void {
switch(opcode()) {
op(0xea, NOP)
}
}
#undef op

View File

@ -0,0 +1,3 @@
auto HuC6280::instructionNOP() -> void {
L io();
}

View File

@ -0,0 +1,11 @@
auto HuC6280::io() -> uint8 {
return read(r.pc);
}
auto HuC6280::opcode() -> uint8 {
return read(r.pc++);
}
auto HuC6280::operand() -> uint8 {
return read(r.pc++);
}

View File

@ -26,14 +26,14 @@ auto Cartridge::title() const -> string {
} }
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = Information(); information = {};
has = Has(); has = {};
if(auto pathID = interface->load(ID::SuperFamicom, "Super Famicom", "sfc")) { if(auto pathID = platform->load(ID::SuperFamicom, "Super Famicom", "sfc")) {
information.pathID = pathID(); information.pathID = pathID();
} else return false; } else return false;
if(auto fp = interface->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, "manifest.bml", File::Read, File::Required)) {
information.manifest.cartridge = fp->reads(); information.manifest.cartridge = fp->reads();
} else return false; } else return false;
auto document = BML::unserialize(information.manifest.cartridge); auto document = BML::unserialize(information.manifest.cartridge);
@ -99,7 +99,7 @@ auto Cartridge::loadGameBoy() -> bool {
} }
auto Cartridge::loadBSMemory() -> bool { auto Cartridge::loadBSMemory() -> bool {
if(auto fp = interface->open(bsmemory.pathID, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(bsmemory.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.bsMemory = fp->reads(); information.manifest.bsMemory = fp->reads();
} else return false; } else return false;
loadBSMemory(BML::unserialize(information.manifest.bsMemory)); loadBSMemory(BML::unserialize(information.manifest.bsMemory));
@ -107,7 +107,7 @@ auto Cartridge::loadBSMemory() -> bool {
} }
auto Cartridge::loadSufamiTurboA() -> bool { auto Cartridge::loadSufamiTurboA() -> bool {
if(auto fp = interface->open(sufamiturboA.pathID, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(sufamiturboA.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.sufamiTurboA = fp->reads(); information.manifest.sufamiTurboA = fp->reads();
} else return false; } else return false;
loadSufamiTurboA(BML::unserialize(information.manifest.sufamiTurboA)); loadSufamiTurboA(BML::unserialize(information.manifest.sufamiTurboA));
@ -115,7 +115,7 @@ auto Cartridge::loadSufamiTurboA() -> bool {
} }
auto Cartridge::loadSufamiTurboB() -> bool { auto Cartridge::loadSufamiTurboB() -> bool {
if(auto fp = interface->open(sufamiturboB.pathID, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(sufamiturboB.pathID, "manifest.bml", File::Read, File::Required)) {
information.manifest.sufamiTurboB = fp->reads(); information.manifest.sufamiTurboB = fp->reads();
} else return false; } else return false;
loadSufamiTurboB(BML::unserialize(information.manifest.sufamiTurboB)); loadSufamiTurboB(BML::unserialize(information.manifest.sufamiTurboB));

View File

@ -4,13 +4,13 @@ auto Cartridge::loadCartridge(Markup::Node node) -> void {
information.region = board["region"].text() == "pal" ? Region::PAL : Region::NTSC; information.region = board["region"].text() == "pal" ? Region::PAL : Region::NTSC;
if(board["mcc"] || board["bsmemory"]) { if(board["mcc"] || board["bsmemory"]) {
if(auto pathID = interface->load(ID::BSMemory, "BS Memory", "bs")) { if(auto pathID = platform->load(ID::BSMemory, "BS Memory", "bs")) {
bsmemory.pathID = pathID(); bsmemory.pathID = pathID();
loadBSMemory(); loadBSMemory();
} }
} }
if(board["sufamiturbo"]) { if(board["sufamiturbo"]) {
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo", "st")) { if(auto pathID = platform->load(ID::SufamiTurboA, "Sufami Turbo", "st")) {
sufamiturboA.pathID = pathID(); sufamiturboA.pathID = pathID();
loadSufamiTurboA(); loadSufamiTurboA();
} }
@ -55,7 +55,7 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void {
loadMemory(sufamiturboA.ram, node["board/ram"], File::Optional, sufamiturboA.pathID); loadMemory(sufamiturboA.ram, node["board/ram"], File::Optional, sufamiturboA.pathID);
if(node["board/linkable"]) { if(node["board/linkable"]) {
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo", "st")) { if(auto pathID = platform->load(ID::SufamiTurboB, "Sufami Turbo", "st")) {
sufamiturboB.pathID = pathID(); sufamiturboB.pathID = pathID();
loadSufamiTurboB(); loadSufamiTurboB();
} }
@ -130,7 +130,7 @@ auto Cartridge::loadSufamiTurbo(Markup::Node node, bool slot) -> void {
auto Cartridge::loadNSS(Markup::Node node) -> void { auto Cartridge::loadNSS(Markup::Node node) -> void {
has.NSSDIP = true; has.NSSDIP = true;
nss.dip = interface->dipSettings(node); nss.dip = platform->dipSettings(node);
for(auto leaf : node.find("map")) loadMap(leaf, {&NSS::read, &nss}, {&NSS::write, &nss}); for(auto leaf : node.find("map")) loadMap(leaf, {&NSS::read, &nss}, {&NSS::write, &nss});
} }
@ -182,13 +182,13 @@ auto Cartridge::loadSuperFX(Markup::Node node) -> void {
auto Cartridge::loadARMDSP(Markup::Node node) -> void { auto Cartridge::loadARMDSP(Markup::Node node) -> void {
has.ARMDSP = true; has.ARMDSP = true;
if(auto fp = interface->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) {
for(auto n : range(128 * 1024)) armdsp.programROM[n] = fp->read(); for(auto n : range(128 * 1024)) armdsp.programROM[n] = fp->read();
} }
if(auto fp = interface->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) {
for(auto n : range( 32 * 1024)) armdsp.dataROM[n] = fp->read(); for(auto n : range( 32 * 1024)) armdsp.dataROM[n] = fp->read();
} }
if(auto fp = interface->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) {
for(auto n : range( 16 * 1024)) armdsp.programRAM[n] = fp->read(); for(auto n : range( 16 * 1024)) armdsp.programRAM[n] = fp->read();
} }
@ -208,10 +208,10 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
for(auto& word : hitachidsp.dataROM) word = 0x000000; for(auto& word : hitachidsp.dataROM) word = 0x000000;
for(auto& word : hitachidsp.dataRAM) word = 0x00; for(auto& word : hitachidsp.dataRAM) word = 0x00;
if(auto fp = interface->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) {
for(auto n : range(1 * 1024)) hitachidsp.dataROM[n] = fp->readl(3); for(auto n : range(1 * 1024)) hitachidsp.dataROM[n] = fp->readl(3);
} }
if(auto fp = interface->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) { if(auto fp = platform->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) {
for(auto n : range(3 * 1024)) hitachidsp.dataRAM[n] = fp->readl(1); for(auto n : range(3 * 1024)) hitachidsp.dataRAM[n] = fp->readl(1);
} }
@ -239,13 +239,13 @@ auto Cartridge::loadNECDSP(Markup::Node node) -> void {
if(necdsp.revision == NECDSP::Revision::uPD7725 ) memory::assign(size, 2048, 1024, 256); if(necdsp.revision == NECDSP::Revision::uPD7725 ) memory::assign(size, 2048, 1024, 256);
if(necdsp.revision == NECDSP::Revision::uPD96050) memory::assign(size, 16384, 2048, 2048); if(necdsp.revision == NECDSP::Revision::uPD96050) memory::assign(size, 16384, 2048, 2048);
if(auto fp = interface->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, node["prom"]["name"].text(), File::Read, File::Required)) {
for(auto n : range(size[0])) necdsp.programROM[n] = fp->readl(3); for(auto n : range(size[0])) necdsp.programROM[n] = fp->readl(3);
} }
if(auto fp = interface->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) { if(auto fp = platform->open(ID::SuperFamicom, node["drom"]["name"].text(), File::Read, File::Required)) {
for(auto n : range(size[1])) necdsp.dataROM[n] = fp->readl(2); for(auto n : range(size[1])) necdsp.dataROM[n] = fp->readl(2);
} }
if(auto fp = interface->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) { if(auto fp = platform->open(ID::SuperFamicom, node["dram"]["name"].text(), File::Read)) {
for(auto n : range(size[2])) necdsp.dataRAM[n] = fp->readl(2); for(auto n : range(size[2])) necdsp.dataRAM[n] = fp->readl(2);
} }
@ -256,7 +256,7 @@ auto Cartridge::loadNECDSP(Markup::Node node) -> void {
auto Cartridge::loadEpsonRTC(Markup::Node node) -> void { auto Cartridge::loadEpsonRTC(Markup::Node node) -> void {
has.EpsonRTC = true; has.EpsonRTC = true;
if(auto fp = interface->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) {
uint8 data[16] = {0}; uint8 data[16] = {0};
for(auto& byte : data) fp->read(); for(auto& byte : data) fp->read();
epsonrtc.load(data); epsonrtc.load(data);
@ -268,7 +268,7 @@ auto Cartridge::loadEpsonRTC(Markup::Node node) -> void {
auto Cartridge::loadSharpRTC(Markup::Node node) -> void { auto Cartridge::loadSharpRTC(Markup::Node node) -> void {
has.SharpRTC = true; has.SharpRTC = true;
if(auto fp = interface->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) { if(auto fp = platform->open(ID::SuperFamicom, node["ram"]["name"].text(), File::Read)) {
uint8 data[16] = {0}; uint8 data[16] = {0};
for(auto& byte : data) fp->read(); for(auto& byte : data) fp->read();
sharprtc.load(data); sharprtc.load(data);
@ -322,7 +322,7 @@ auto Cartridge::loadMemory(MappedRAM& ram, Markup::Node node, bool required, may
auto name = node["name"].text(); auto name = node["name"].text();
auto size = node["size"].natural(); auto size = node["size"].natural();
ram.allocate(size); ram.allocate(size);
if(auto fp = interface->open(id(), name, File::Read, required)) { if(auto fp = platform->open(id(), name, File::Read, required)) {
fp->read(ram.data(), ram.size()); fp->read(ram.data(), ram.size());
} }
} }

View File

@ -56,7 +56,7 @@ auto Cartridge::saveSuperFX(Markup::Node node) -> void {
auto Cartridge::saveARMDSP(Markup::Node node) -> void { auto Cartridge::saveARMDSP(Markup::Node node) -> void {
if(!node["ram/volatile"]) { if(!node["ram/volatile"]) {
if(auto name = node["ram/name"].text()) { if(auto name = node["ram/name"].text()) {
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) { if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) {
for(auto n : range(16 * 1024)) fp->write(armdsp.programRAM[n]); for(auto n : range(16 * 1024)) fp->write(armdsp.programRAM[n]);
} }
} }
@ -68,7 +68,7 @@ auto Cartridge::saveHitachiDSP(Markup::Node node) -> void {
if(!node["dram/volatile"]) { if(!node["dram/volatile"]) {
if(auto name = node["dram/name"].text()) { if(auto name = node["dram/name"].text()) {
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) { if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) {
for(auto n : range(3 * 1024)) fp->write(hitachidsp.dataRAM[n]); for(auto n : range(3 * 1024)) fp->write(hitachidsp.dataRAM[n]);
} }
} }
@ -79,7 +79,7 @@ auto Cartridge::saveNECDSP(Markup::Node node) -> void {
if(!node["dram/volatile"]) { if(!node["dram/volatile"]) {
uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048; uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048;
if(auto name = node["dram/name"].text()) { if(auto name = node["dram/name"].text()) {
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) { if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) {
for(auto n : range(size)) fp->writel(necdsp.dataRAM[n], 2); for(auto n : range(size)) fp->writel(necdsp.dataRAM[n], 2);
} }
} }
@ -89,7 +89,7 @@ auto Cartridge::saveNECDSP(Markup::Node node) -> void {
auto Cartridge::saveEpsonRTC(Markup::Node node) -> void { auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
if(!node["ram/volatile"]) { if(!node["ram/volatile"]) {
if(auto name = node["ram/name"].text()) { if(auto name = node["ram/name"].text()) {
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) { if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) {
uint8 data[16] = {0}; uint8 data[16] = {0};
epsonrtc.save(data); epsonrtc.save(data);
fp->write(data, 16); fp->write(data, 16);
@ -101,7 +101,7 @@ auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
auto Cartridge::saveSharpRTC(Markup::Node node) -> void { auto Cartridge::saveSharpRTC(Markup::Node node) -> void {
if(!node["ram/volatile"]) { if(!node["ram/volatile"]) {
if(auto name = node["ram/name"].text()) { if(auto name = node["ram/name"].text()) {
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) { if(auto fp = platform->open(ID::SuperFamicom, name, File::Write)) {
uint8 data[16] = {0}; uint8 data[16] = {0};
sharprtc.save(data); sharprtc.save(data);
fp->write(data, 16); fp->write(data, 16);
@ -129,7 +129,7 @@ auto Cartridge::saveMemory(MappedRAM& memory, Markup::Node node, maybe<uint> id)
if(!node || node["volatile"]) return; if(!node || node["volatile"]) return;
auto name = node["name"].text(); auto name = node["name"].text();
auto size = node["size"].natural(); auto size = node["size"].natural();
if(auto fp = interface->open(id(), name, File::Write)) { if(auto fp = platform->open(id(), name, File::Write)) {
fp->write(memory.data(), memory.size()); fp->write(memory.data(), memory.size());
} }
} }

View File

@ -5,7 +5,7 @@ Gamepad::Gamepad(bool port) : Controller(port) {
auto Gamepad::data() -> uint2 { auto Gamepad::data() -> uint2 {
if(counter >= 16) return 1; if(counter >= 16) return 1;
if(latched == 1) return interface->inputPoll(port, ID::Device::Gamepad, B); if(latched == 1) return platform->inputPoll(port, ID::Device::Gamepad, B);
//note: D-pad physically prevents up+down and left+right from being pressed at the same time //note: D-pad physically prevents up+down and left+right from being pressed at the same time
switch(counter++) { switch(counter++) {
@ -32,17 +32,17 @@ auto Gamepad::latch(bool data) -> void {
counter = 0; counter = 0;
if(latched == 0) { if(latched == 0) {
b = interface->inputPoll(port, ID::Device::Gamepad, B); b = platform->inputPoll(port, ID::Device::Gamepad, B);
y = interface->inputPoll(port, ID::Device::Gamepad, Y); y = platform->inputPoll(port, ID::Device::Gamepad, Y);
select = interface->inputPoll(port, ID::Device::Gamepad, Select); select = platform->inputPoll(port, ID::Device::Gamepad, Select);
start = interface->inputPoll(port, ID::Device::Gamepad, Start); start = platform->inputPoll(port, ID::Device::Gamepad, Start);
up = interface->inputPoll(port, ID::Device::Gamepad, Up); up = platform->inputPoll(port, ID::Device::Gamepad, Up);
down = interface->inputPoll(port, ID::Device::Gamepad, Down); down = platform->inputPoll(port, ID::Device::Gamepad, Down);
left = interface->inputPoll(port, ID::Device::Gamepad, Left); left = platform->inputPoll(port, ID::Device::Gamepad, Left);
right = interface->inputPoll(port, ID::Device::Gamepad, Right); right = platform->inputPoll(port, ID::Device::Gamepad, Right);
a = interface->inputPoll(port, ID::Device::Gamepad, A); a = platform->inputPoll(port, ID::Device::Gamepad, A);
x = interface->inputPoll(port, ID::Device::Gamepad, X); x = platform->inputPoll(port, ID::Device::Gamepad, X);
l = interface->inputPoll(port, ID::Device::Gamepad, L); l = platform->inputPoll(port, ID::Device::Gamepad, L);
r = interface->inputPoll(port, ID::Device::Gamepad, R); r = platform->inputPoll(port, ID::Device::Gamepad, R);
} }
} }

View File

@ -53,8 +53,8 @@ auto Justifier::main() -> void {
} }
if(next < prev) { if(next < prev) {
int nx1 = interface->inputPoll(port, device, 0 + X); int nx1 = platform->inputPoll(port, device, 0 + X);
int ny1 = interface->inputPoll(port, device, 0 + Y); int ny1 = platform->inputPoll(port, device, 0 + Y);
nx1 += player1.x; nx1 += player1.x;
ny1 += player1.y; ny1 += player1.y;
player1.x = max(-16, min(256 + 16, nx1)); player1.x = max(-16, min(256 + 16, nx1));
@ -64,8 +64,8 @@ auto Justifier::main() -> void {
} }
if(next < prev && chained) { if(next < prev && chained) {
int nx2 = interface->inputPoll(port, device, 4 + X); int nx2 = platform->inputPoll(port, device, 4 + X);
int ny2 = interface->inputPoll(port, device, 4 + Y); int ny2 = platform->inputPoll(port, device, 4 + Y);
nx2 += player2.x; nx2 += player2.x;
ny2 += player2.y; ny2 += player2.y;
player2.x = max(-16, min(256 + 16, nx2)); player2.x = max(-16, min(256 + 16, nx2));
@ -83,13 +83,13 @@ auto Justifier::data() -> uint2 {
if(counter >= 32) return 1; if(counter >= 32) return 1;
if(counter == 0) { if(counter == 0) {
player1.trigger = interface->inputPoll(port, device, 0 + Trigger); player1.trigger = platform->inputPoll(port, device, 0 + Trigger);
player1.start = interface->inputPoll(port, device, 0 + Start); player1.start = platform->inputPoll(port, device, 0 + Start);
} }
if(counter == 0 && chained) { if(counter == 0 && chained) {
player2.trigger = interface->inputPoll(port, device, 4 + Trigger); player2.trigger = platform->inputPoll(port, device, 4 + Trigger);
player2.start = interface->inputPoll(port, device, 4 + Start); player2.start = platform->inputPoll(port, device, 4 + Start);
} }
switch(counter++) { switch(counter++) {

View File

@ -64,10 +64,10 @@ auto Mouse::latch(bool data) -> void {
latched = data; latched = data;
counter = 0; counter = 0;
x = interface->inputPoll(port, ID::Device::Mouse, X); //-n = left, 0 = center, +n = right x = platform->inputPoll(port, ID::Device::Mouse, X); //-n = left, 0 = center, +n = right
y = interface->inputPoll(port, ID::Device::Mouse, Y); //-n = up, 0 = center, +n = down y = platform->inputPoll(port, ID::Device::Mouse, Y); //-n = up, 0 = center, +n = down
l = interface->inputPoll(port, ID::Device::Mouse, Left); l = platform->inputPoll(port, ID::Device::Mouse, Left);
r = interface->inputPoll(port, ID::Device::Mouse, Right); r = platform->inputPoll(port, ID::Device::Mouse, Right);
dx = x < 0; //0 = right, 1 = left dx = x < 0; //0 = right, 1 = left
dy = y < 0; //0 = down, 1 = up dy = y < 0; //0 = down, 1 = up

View File

@ -53,18 +53,18 @@ auto SuperMultitap::latch(bool data) -> void {
if(latched == 0) { if(latched == 0) {
for(uint id : range(4)) { for(uint id : range(4)) {
auto& gamepad = gamepads[id]; auto& gamepad = gamepads[id];
gamepad.b = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + B); gamepad.b = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + B);
gamepad.y = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Y); gamepad.y = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Y);
gamepad.select = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Select); gamepad.select = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Select);
gamepad.start = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Start); gamepad.start = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Start);
gamepad.up = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Up); gamepad.up = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Up);
gamepad.down = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Down); gamepad.down = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Down);
gamepad.left = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Left); gamepad.left = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Left);
gamepad.right = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Right); gamepad.right = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + Right);
gamepad.a = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + A); gamepad.a = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + A);
gamepad.x = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + X); gamepad.x = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + X);
gamepad.l = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + L); gamepad.l = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + L);
gamepad.r = interface->inputPoll(port, ID::Device::SuperMultitap, id * 12 + R); gamepad.r = platform->inputPoll(port, ID::Device::SuperMultitap, id * 12 + R);
} }
} }
} }

View File

@ -53,8 +53,8 @@ auto SuperScope::main() -> void {
if(next < prev) { if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame //Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = interface->inputPoll(port, ID::Device::SuperScope, X); int nx = platform->inputPoll(port, ID::Device::SuperScope, X);
int ny = interface->inputPoll(port, ID::Device::SuperScope, Y); int ny = platform->inputPoll(port, ID::Device::SuperScope, Y);
nx += x; nx += x;
ny += y; ny += y;
x = max(-16, min(256 + 16, nx)); x = max(-16, min(256 + 16, nx));
@ -74,7 +74,7 @@ auto SuperScope::data() -> uint2 {
if(counter == 0) { if(counter == 0) {
//turbo is a switch; toggle is edge sensitive //turbo is a switch; toggle is edge sensitive
bool newturbo = interface->inputPoll(port, ID::Device::SuperScope, Turbo); bool newturbo = platform->inputPoll(port, ID::Device::SuperScope, Turbo);
if(newturbo && !oldturbo) { if(newturbo && !oldturbo) {
turbo = !turbo; //toggle state turbo = !turbo; //toggle state
sprite->setPixels(turbo ? Resource::Sprite::CrosshairRed : Resource::Sprite::CrosshairGreen); sprite->setPixels(turbo ? Resource::Sprite::CrosshairRed : Resource::Sprite::CrosshairGreen);
@ -84,7 +84,7 @@ auto SuperScope::data() -> uint2 {
//trigger is a button //trigger is a button
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive //if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
trigger = false; trigger = false;
bool newtrigger = interface->inputPoll(port, ID::Device::SuperScope, Trigger); bool newtrigger = platform->inputPoll(port, ID::Device::SuperScope, Trigger);
if(newtrigger && (turbo || !triggerlock)) { if(newtrigger && (turbo || !triggerlock)) {
trigger = true; trigger = true;
triggerlock = true; triggerlock = true;
@ -93,11 +93,11 @@ auto SuperScope::data() -> uint2 {
} }
//cursor is a button; it is always level sensitive //cursor is a button; it is always level sensitive
cursor = interface->inputPoll(port, ID::Device::SuperScope, Cursor); cursor = platform->inputPoll(port, ID::Device::SuperScope, Cursor);
//pause is a button; it is always edge sensitive //pause is a button; it is always edge sensitive
pause = false; pause = false;
bool newpause = interface->inputPoll(port, ID::Device::SuperScope, Pause); bool newpause = platform->inputPoll(port, ID::Device::SuperScope, Pause);
if(newpause && !pauselock) { if(newpause && !pauselock) {
pause = true; pause = true;
pauselock = true; pauselock = true;

View File

@ -84,7 +84,7 @@ auto MSU1::dataOpen() -> void {
auto document = BML::unserialize(cartridge.information.manifest.cartridge); auto document = BML::unserialize(cartridge.information.manifest.cartridge);
string name = document["board/msu1/rom/name"].text(); string name = document["board/msu1/rom/name"].text();
if(!name) name = "msu1.rom"; if(!name) name = "msu1.rom";
if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) { if(dataFile = platform->open(ID::SuperFamicom, name, File::Read)) {
dataFile->seek(io.dataReadOffset); dataFile->seek(io.dataReadOffset);
} }
} }
@ -98,7 +98,7 @@ auto MSU1::audioOpen() -> void {
name = track["name"].text(); name = track["name"].text();
break; break;
} }
if(audioFile = interface->open(ID::SuperFamicom, name, File::Read)) { if(audioFile = platform->open(ID::SuperFamicom, name, File::Read)) {
if(audioFile->size() >= 8) { if(audioFile->size() >= 8) {
uint32 header = audioFile->readm(4); uint32 header = audioFile->readm(4);
if(header == 0x4d535531) { //"MSU1" if(header == 0x4d535531) { //"MSU1"

View File

@ -18,11 +18,11 @@ S21FX::S21FX() {
ram[1] = 0xfc; ram[1] = 0xfc;
ram[2] = 0xff; ram[2] = 0xff;
if(auto buffer = file::read({interface->path(ID::System), "21fx.rom"})) { if(auto buffer = file::read({platform->path(ID::System), "21fx.rom"})) {
memory::copy(ram, sizeof(ram), buffer.data(), buffer.size()); memory::copy(ram, sizeof(ram), buffer.data(), buffer.size());
} }
string filename{interface->path(ID::SuperFamicom), "21fx.so"}; string filename{platform->path(ID::SuperFamicom), "21fx.so"};
if(link.openAbsolute(filename)) { if(link.openAbsolute(filename)) {
linkInit = link.sym("fx_init"); linkInit = link.sym("fx_init");
linkMain = link.sym("fx_main"); linkMain = link.sym("fx_main");

View File

@ -2,11 +2,9 @@
namespace SuperFamicom { namespace SuperFamicom {
Interface* interface = nullptr;
Settings settings; Settings settings;
Interface::Interface() { Interface::Interface() {
interface = this;
system.init(); system.init();
information.manufacturer = "Nintendo"; information.manufacturer = "Nintendo";
@ -185,7 +183,7 @@ auto Interface::sha256() -> string {
} }
auto Interface::load(uint id) -> bool { auto Interface::load(uint id) -> bool {
if(id == ID::SuperFamicom) return system.load(); if(id == ID::SuperFamicom) return system.load(this);
if(id == ID::BSMemory) return cartridge.loadBSMemory(); if(id == ID::BSMemory) return cartridge.loadBSMemory();
if(id == ID::SufamiTurboA) return cartridge.loadSufamiTurboA(); if(id == ID::SufamiTurboA) return cartridge.loadSufamiTurboA();
if(id == ID::SufamiTurboB) return cartridge.loadSufamiTurboB(); if(id == ID::SufamiTurboB) return cartridge.loadSufamiTurboB();
@ -202,7 +200,7 @@ auto Interface::unload() -> void {
} }
auto Interface::connect(uint port, uint device) -> void { auto Interface::connect(uint port, uint device) -> void {
SuperFamicom::peripherals.connect(port, device); peripherals.connect(port, device);
} }
auto Interface::power() -> void { auto Interface::power() -> void {

View File

@ -81,7 +81,6 @@ struct Settings {
bool random = true; bool random = true;
}; };
extern Interface* interface;
extern Settings settings; extern Settings settings;
} }

View File

@ -20,6 +20,7 @@
#endif #endif
namespace SuperFamicom { namespace SuperFamicom {
#define platform Emulator::platform
using File = Emulator::File; using File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
using Cheat = Emulator::Cheat; using Cheat = Emulator::Cheat;

View File

@ -18,7 +18,7 @@ auto SMP::main() -> void {
auto SMP::load(Markup::Node node) -> bool { auto SMP::load(Markup::Node node) -> bool {
if(auto name = node["smp/rom/name"].text()) { if(auto name = node["smp/rom/name"].text()) {
if(auto fp = interface->open(ID::System, name, File::Read, File::Required)) { if(auto fp = platform->open(ID::System, name, File::Read, File::Required)) {
fp->read(iplrom, 64); fp->read(iplrom, 64);
return true; return true;
} }

View File

@ -24,8 +24,6 @@ auto System::runToSave() -> void {
} }
auto System::init() -> void { auto System::init() -> void {
assert(interface != nullptr);
icd2.init(); icd2.init();
mcc.init(); mcc.init();
nss.init(); nss.init();
@ -48,10 +46,10 @@ auto System::init() -> void {
auto System::term() -> void { auto System::term() -> void {
} }
auto System::load() -> bool { auto System::load(Emulator::Interface* interface) -> bool {
information = Information(); information = Information();
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) { if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads(); information.manifest = fp->reads();
} else return false; } else return false;
@ -93,6 +91,7 @@ auto System::load() -> bool {
if(cartridge.has.SufamiTurboSlots) sufamiturboA.load(), sufamiturboB.load(); if(cartridge.has.SufamiTurboSlots) sufamiturboA.load(), sufamiturboB.load();
serializeInit(); serializeInit();
this->interface = interface;
return information.loaded = true; return information.loaded = true;
} }

View File

@ -1,5 +1,3 @@
struct Interface;
struct System { struct System {
enum class Region : bool { NTSC = 0, PAL = 1 }; enum class Region : bool { NTSC = 0, PAL = 1 };
@ -12,7 +10,7 @@ struct System {
auto init() -> void; auto init() -> void;
auto term() -> void; auto term() -> void;
auto load() -> bool; auto load(Emulator::Interface*) -> bool;
auto save() -> void; auto save() -> void;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
@ -27,6 +25,8 @@ struct System {
auto unserialize(serializer&) -> bool; auto unserialize(serializer&) -> bool;
private: private:
Emulator::Interface* interface = nullptr;
struct Information { struct Information {
string manifest; string manifest;
bool loaded = false; bool loaded = false;

View File

@ -1,5 +1,5 @@
name := higan name := higan
flags += -DSFC_SUPERGAMEBOY #flags += -DSFC_SUPERGAMEBOY
include fc/GNUmakefile include fc/GNUmakefile
include sfc/GNUmakefile include sfc/GNUmakefile

View File

@ -17,15 +17,16 @@ Program::Program(string_vector args) {
program = this; program = this;
Application::onMain({&Program::main, this}); Application::onMain({&Program::main, this});
Emulator::platform = this;
emulators.append(new Famicom::Interface); emulators.append(new Famicom::Interface);
emulators.append(new SuperFamicom::Interface); emulators.append(new SuperFamicom::Interface);
emulators.append(new MasterSystem::Interface); emulators.append(new MasterSystem::MasterSystemInterface);
emulators.append(new MegaDrive::Interface); emulators.append(new MegaDrive::Interface);
emulators.append(new PCEngine::Interface); emulators.append(new PCEngine::Interface);
emulators.append(new GameBoy::Interface); emulators.append(new GameBoy::Interface);
emulators.append(new GameBoyAdvance::Interface); emulators.append(new GameBoyAdvance::Interface);
emulators.append(new MasterSystem::GameGearInterface);
emulators.append(new WonderSwan::Interface); emulators.append(new WonderSwan::Interface);
for(auto& emulator : emulators) emulator->bind = this;
new Presentation; new Presentation;
presentation->setVisible(); presentation->setVisible();

Some files were not shown because too many files have changed in this diff Show More