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

View File

@ -6,6 +6,8 @@ namespace Emulator {
Audio audio;
auto Audio::reset(maybe<uint> channels_, maybe<double> frequency_) -> void {
interface = nullptr;
if(channels_) channels = channels_();
if(frequency_) frequency = frequency_();
@ -81,7 +83,7 @@ auto Audio::process() -> void {
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 {
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 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 {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -2,6 +2,18 @@
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 Information {
string manufacturer;
@ -38,30 +50,6 @@ struct Interface {
};
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
virtual auto manifest() -> string = 0;
virtual auto title() -> string = 0;
@ -118,4 +106,6 @@ struct File {
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(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()));
}
}
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()));
}
}
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()));
}
}
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()));
}
}
@ -70,13 +70,13 @@ auto Board::save() -> void {
auto document = BML::unserialize(cartridge.manifest());
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);
}
}
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);
}
}

View File

@ -15,11 +15,11 @@ auto Cartridge::main() -> void {
}
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();
} 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();
} else {
return false;

View File

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

View File

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

View File

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

View File

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

View File

@ -21,15 +21,17 @@ auto System::runToSave() -> void {
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
}
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();
} else {
return false;
}
auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC;
serializeInit();
return information.loaded = true;

View File

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

View File

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

View File

@ -19,23 +19,23 @@ auto Cartridge::load(System::Revision revision) -> bool {
switch(revision) {
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();
} else return false;
break;
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();
} else return false;
break;
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();
} else return false;
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();
} else return false;
@ -64,12 +64,12 @@ auto Cartridge::load(System::Revision revision) -> bool {
ram.data = (uint8*)memory::allocate(ram.size, 0xff);
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()));
}
}
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()));
}
}
@ -96,7 +96,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
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);
}
}

View File

@ -8,15 +8,15 @@ auto CPU::wramAddress(uint16 addr) const -> uint {
auto CPU::joypPoll() -> void {
uint button = 0, dpad = 0;
button |= interface->inputPoll(0, 0, (uint)Input::Start) << 3;
button |= interface->inputPoll(0, 0, (uint)Input::Select) << 2;
button |= interface->inputPoll(0, 0, (uint)Input::B) << 1;
button |= interface->inputPoll(0, 0, (uint)Input::A) << 0;
button |= platform->inputPoll(0, 0, (uint)Input::Start) << 3;
button |= platform->inputPoll(0, 0, (uint)Input::Select) << 2;
button |= platform->inputPoll(0, 0, (uint)Input::B) << 1;
button |= platform->inputPoll(0, 0, (uint)Input::A) << 0;
dpad |= interface->inputPoll(0, 0, (uint)Input::Down) << 3;
dpad |= interface->inputPoll(0, 0, (uint)Input::Up) << 2;
dpad |= interface->inputPoll(0, 0, (uint)Input::Left) << 1;
dpad |= interface->inputPoll(0, 0, (uint)Input::Right) << 0;
dpad |= platform->inputPoll(0, 0, (uint)Input::Down) << 3;
dpad |= platform->inputPoll(0, 0, (uint)Input::Up) << 2;
dpad |= platform->inputPoll(0, 0, (uint)Input::Left) << 1;
dpad |= platform->inputPoll(0, 0, (uint)Input::Right) << 0;
if(system.revision() != System::Revision::SuperGameBoy) {
//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
status.p15 = data & 0x20;
status.p14 = data & 0x10;
interface->joypWrite(status.p15, status.p14);
//interface->joypWrite(status.p15, status.p14);
return;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,10 +22,10 @@ auto System::init() -> void {
assert(interface != nullptr);
}
auto System::load(Revision revision) -> bool {
auto System::load(Emulator::Interface* interface, Revision revision) -> bool {
_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();
} else return false;
@ -34,7 +34,7 @@ auto System::load(Revision revision) -> bool {
if(revision == Revision::SuperGameBoy) path = "board/icd2/rom/name";
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::SuperGameBoy) fp->read(bootROM.sgb, 256);
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;
serializeInit();
this->interface = interface;
return _loaded = true;
}

View File

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

View File

@ -26,11 +26,11 @@ Cartridge::~Cartridge() {
auto Cartridge::load() -> bool {
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();
} 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();
} else return false;
@ -43,7 +43,7 @@ auto Cartridge::load() -> bool {
if(auto node = document["board/rom"]) {
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);
}
}
@ -55,7 +55,7 @@ auto Cartridge::load() -> bool {
sram.mask = sram.size - 1;
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);
}
}
@ -69,7 +69,7 @@ auto Cartridge::load() -> bool {
eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
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);
}
}
@ -85,7 +85,7 @@ auto Cartridge::load() -> bool {
if(!flash.id && flash.size == 64 * 1024) flash.id = 0x1cc2;
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);
}
}
@ -98,7 +98,7 @@ auto Cartridge::load() -> bool {
auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
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() == "eeprom") fp->write(eeprom.data, eeprom.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
for(auto n : range(10)) {
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 == 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};
if(auto result = player.keyinput()) return 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 & 0x30) == 0x30) result &= (uint8)~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff;
@ -72,8 +72,8 @@ auto CPU::readIO(uint32 addr) -> uint8 {
case 0x04000131: {
if(auto result = player.keyinput()) return result() >> 8;
uint8 result = 0;
result |= interface->inputPoll(0, 0, 7) << 0;
result |= interface->inputPoll(0, 0, 6) << 1;
result |= platform->inputPoll(0, 0, 7) << 0;
result |= platform->inputPoll(0, 0, 6) << 1;
return result ^ 0x03;
}

View File

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

View File

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

View File

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

View File

@ -100,7 +100,7 @@ auto Player::write(uint2 addr, uint8 byte) -> void {
if(addr == 3 && status.packet == 15) {
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);
}
auto System::load() -> bool {
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) {
auto System::load(Emulator::Interface* interface) -> bool {
if(auto fp = platform->open(ID::System, "manifest.bml", File::Read, File::Required)) {
information.manifest = fp->reads();
} else return false;
auto document = BML::unserialize(information.manifest);
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);
}
}
if(!cartridge.load()) return false;
serializeInit();
this->interface = interface;
return _loaded = true;
}

View File

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

View File

@ -5,13 +5,13 @@ namespace MegaDrive {
Cartridge cartridge;
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();
} 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();
} else return false;
@ -24,7 +24,7 @@ auto Cartridge::load() -> bool {
if(rom.size) {
rom.data = new uint8[rom.mask + 1];
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);
}
}
@ -37,7 +37,7 @@ auto Cartridge::load() -> bool {
if(ram.size) {
ram.data = new uint8[ram.mask + 1];
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);
}
}
@ -51,7 +51,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,11 @@ auto Bus::in(uint8 addr) -> uint8 {
switch(addr >> 6) {
case 0: {
if(system.model() == Model::GameGear) {
auto hardware = peripherals.hardware->readData();
return hardware.bit(6) << 7 | 0x7f;
}
return 0xff; //SMS1 = MDR, SMS2 = 0xff
}
@ -31,15 +36,25 @@ auto Bus::in(uint8 addr) -> uint8 {
}
case 3: {
auto A = peripherals.controllerPort1->readData();
auto B = peripherals.controllerPort2->readData();
if(addr.bit(0) == 0) {
return A.bits(0,5) << 0 | B.bits(0,1) << 6;
} else {
//d4 = reset button
//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::MasterSystem) {
auto hardware = peripherals.hardware->readData();
auto port1 = peripherals.controllerPort1->readData();
auto port2 = peripherals.controllerPort2->readData();
if(addr.bit(0) == 0) {
return port1.bits(0,5) << 0 | port2.bits(0,1) << 6;
} else {
return port2.bits(2,5) << 0 | hardware.bit(0) << 4 | 1 << 5 | port1.bit(6) << 6 | port2.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()) {
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();
} else return false;
break;
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();
} else return false;
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();
} else return false;
@ -34,7 +34,7 @@ auto Cartridge::load() -> bool {
if(rom.size) {
rom.data = new uint8[rom.mask + 1];
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);
}
}
@ -47,7 +47,7 @@ auto Cartridge::load() -> bool {
if(ram.size) {
ram.data = new uint8[ram.mask + 1];
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);
}
}
@ -61,7 +61,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
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);
}
}
@ -75,9 +75,6 @@ auto Cartridge::unload() -> void {
}
auto Cartridge::power() -> void {
}
auto Cartridge::reset() -> void {
memory::fill(&mapper, sizeof(Mapper));
mapper.romPage0 = 0;
mapper.romPage1 = 1;

View File

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

View File

@ -2,6 +2,8 @@
namespace MasterSystem {
#include "mastersystem/mastersystem.cpp"
#include "gamegear/gamegear.cpp"
#include "gamepad/gamepad.cpp"
Controller::Controller(uint port) : port(port) {
@ -14,8 +16,9 @@ Controller::~Controller() {
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main();
if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main();
if(auto device = peripherals.hardware) if(device->active()) device->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;
auto main() -> void;
virtual auto readData() -> uint7 { return 0x7f; }
virtual auto readData() -> uint8 { return 0xff; }
const uint port;
};
#include "mastersystem/mastersystem.hpp"
#include "gamegear/gamegear.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) {
}
auto Gamepad::readData() -> uint7 {
uint7 data;
data.bit(0) = !interface->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = !interface->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = !interface->inputPoll(port, ID::Device::Gamepad, Left);
data.bit(3) = !interface->inputPoll(port, ID::Device::Gamepad, Right);
data.bit(4) = !interface->inputPoll(port, ID::Device::Gamepad, One);
data.bit(5) = !interface->inputPoll(port, ID::Device::Gamepad, Two);
data.bit(6) = 1;
auto Gamepad::readData() -> uint8 {
uint8 data = 0xff;
data.bit(0) = !platform->inputPoll(port, ID::Device::Gamepad, Up);
data.bit(1) = !platform->inputPoll(port, ID::Device::Gamepad, Down);
data.bit(2) = !platform->inputPoll(port, ID::Device::Gamepad, Left);
data.bit(3) = !platform->inputPoll(port, ID::Device::Gamepad, Right);
data.bit(4) = !platform->inputPoll(port, ID::Device::Gamepad, One);
data.bit(5) = !platform->inputPoll(port, ID::Device::Gamepad, Two);
return data;
}

View File

@ -5,5 +5,5 @@ struct Gamepad : Controller {
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 {
Z80::bus = &MasterSystem::bus;
Z80::power();
}
auto CPU::reset() -> void {
Z80::reset();
create(CPU::Enter, system.colorburst());
memory::fill(&state, sizeof(State));

View File

@ -9,7 +9,6 @@ struct CPU : Processor::Z80, Thread {
auto setINT(bool value) -> void;
auto power() -> void;
auto reset() -> void;
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 {
Interface* interface = nullptr;
Settings settings;
Interface::Interface() {
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;
}
#include "master-system.cpp"
#include "game-gear.cpp"
}

View File

@ -8,20 +8,56 @@ struct ID {
};
struct Port { enum : uint {
Hardware,
Controller1,
Controller2,
};};
struct Device { enum : uint {
None,
MasterSystemControls,
GameGearControls,
Gamepad,
};};
};
struct Interface : Emulator::Interface {
struct MasterSystemInterface : Emulator::Interface {
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 title() -> string override;
@ -41,7 +77,6 @@ struct Interface : Emulator::Interface {
auto connect(uint port, uint device) -> void override;
auto power() -> void override;
auto reset() -> void override;
auto run() -> void override;
auto serialize() -> serializer override;
@ -53,11 +88,11 @@ struct Interface : Emulator::Interface {
};
struct Settings {
uint hardware = 0;
uint controllerPort1 = 0;
uint controllerPort2 = 0;
};
extern Interface* interface;
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>
namespace MasterSystem {
#define platform Emulator::platform
using File = Emulator::File;
using Scheduler = Emulator::Scheduler;
extern Scheduler scheduler;
struct Interface;
enum class Model : uint {
MasterSystem,

View File

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

View File

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

View File

@ -1,41 +1,68 @@
Peripherals peripherals;
auto Peripherals::unload() -> void {
delete hardware;
delete controllerPort1;
delete controllerPort2;
hardware = nullptr;
controllerPort1 = nullptr;
controllerPort2 = nullptr;
}
auto Peripherals::reset() -> void {
connect(ID::Port::Hardware, settings.hardware);
connect(ID::Port::Controller1, settings.controllerPort1);
connect(ID::Port::Controller2, settings.controllerPort2);
}
auto Peripherals::connect(uint port, uint device) -> void {
if(port == ID::Port::Controller1) {
settings.controllerPort1 = device;
if(!system.loaded()) return;
delete controllerPort1;
switch(device) { default:
case ID::Device::None: controllerPort1 = new Controller(0); break;
case ID::Device::Gamepad: controllerPort1 = new Gamepad(0); break;
}
}
if(port == ID::Port::Controller2) {
settings.controllerPort2 = device;
if(!system.loaded()) return;
delete controllerPort2;
switch(device) { default:
case ID::Device::None: controllerPort2 = new Controller(1); break;
case ID::Device::Gamepad: controllerPort2 = new Gamepad(1); break;
}
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort1);
cpu.peripherals.append(controllerPort2);
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) {
settings.controllerPort1 = device;
if(!system.loaded()) return;
delete controllerPort1;
switch(device) { default:
case ID::Device::None: controllerPort1 = new Controller(ID::Port::Controller1); break;
case ID::Device::Gamepad: controllerPort1 = new Gamepad(ID::Port::Controller1); break;
}
}
if(port == ID::Port::Controller2) {
settings.controllerPort2 = device;
if(!system.loaded()) return;
delete controllerPort2;
switch(device) { default:
case ID::Device::None: controllerPort2 = new Controller(ID::Port::Controller2); break;
case ID::Device::Gamepad: controllerPort2 = new Gamepad(ID::Port::Controller2); break;
}
}
cpu.peripherals.append(hardware);
cpu.peripherals.append(controllerPort1);
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();
}
auto System::load(Model model) -> bool {
auto System::load(Emulator::Interface* interface, Model model) -> bool {
information = {};
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();
} else return false;
auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true;
}
@ -35,14 +36,6 @@ auto System::unload() -> void {
}
auto System::power() -> void {
cartridge.power();
cpu.power();
vdp.power();
psg.power();
reset();
}
auto System::reset() -> void {
Emulator::video.reset();
Emulator::video.setInterface(interface);
Emulator::video.setPalette();
@ -51,10 +44,10 @@ auto System::reset() -> void {
Emulator::audio.setInterface(interface);
scheduler.reset();
cartridge.reset();
cpu.reset();
vdp.reset();
psg.reset();
cartridge.power();
cpu.power();
vdp.power();
psg.power();
scheduler.primary(cpu);
peripherals.reset();

View File

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

View File

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

View File

@ -50,7 +50,10 @@ auto VDP::data(uint8 data) -> void {
if(io.code <= 2) {
vram[io.address++] = data;
} 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::reset() -> void {
memory::fill(&state, sizeof(State));
memory::fill(&output, sizeof(Output));
}

View File

@ -29,19 +29,20 @@ auto VDP::main() -> void {
sprite.scanline();
//684 clocks/scanline
uint y = io.vcounter;
for(uint x : range(256)) {
background.run();
sprite.run();
step(2);
uint6 color = cram[io.backdropColor];
uint12 color = palette(io.backdropColor);
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) {
color = cram[16 | sprite.output.color];
color = palette(16 | sprite.output.color);
}
if(x <= 7 && io.leftClip) color = cram[io.backdropColor];
if(!io.displayEnable || io.vcounter >= vlines()) color = 0;
if(x <= 7 && io.leftClip) color = palette(io.backdropColor);
if(!io.displayEnable || y >= vlines()) color = 0;
buffer[io.vcounter * 256 + x] = color;
}
step(172);
@ -65,7 +66,13 @@ auto VDP::step(uint clocks) -> void {
}
auto VDP::refresh() -> void {
Emulator::video.refresh(buffer, 256 * sizeof(uint32), 256, 240);
if(system.model() == Model::MasterSystem) {
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 {
@ -79,17 +86,24 @@ auto VDP::vblank() -> bool {
}
auto VDP::power() -> void {
background.power();
sprite.power();
}
auto VDP::reset() -> void {
create(VDP::Enter, system.colorburst() * 15.0 / 5.0);
memory::fill(&io, sizeof(IO));
background.reset();
sprite.reset();
background.power();
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 power() -> void;
auto reset() -> void;
//io.cpp
auto vcounter() -> uint8;
@ -28,7 +27,6 @@ struct VDP : Thread {
auto run() -> void;
auto power() -> void;
auto reset() -> void;
struct State {
uint x;
@ -48,7 +46,6 @@ struct VDP : Thread {
auto run() -> void;
auto power() -> void;
auto reset() -> void;
struct Object {
uint8 x;
@ -69,9 +66,11 @@ struct VDP : Thread {
} sprite;
private:
auto palette(uint5 index) -> uint12;
uint32 buffer[256 * 262];
uint8 vram[0x4000];
uint8 cram[0x20];
uint8 cram[0x40]; //MS = 0x20, GG = 0x40
struct IO {
uint vcounter; //vertical counter

View File

@ -7,11 +7,11 @@ Cartridge cartridge;
auto Cartridge::load() -> bool {
information = {};
if(auto pathID = interface->load(ID::PCEngine, "PC Engine", "pce")) {
if(auto pathID = platform->load(ID::PCEngine, "PC Engine", "pce")) {
information.pathID = pathID();
} 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();
} else return false;
@ -23,7 +23,7 @@ auto Cartridge::load() -> bool {
if(rom.size) {
rom.data = new uint8[rom.size]();
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);
}
}
@ -35,7 +35,7 @@ auto Cartridge::load() -> bool {
if(ram.size) {
ram.data = new uint8[ram.size]();
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);
}
}
@ -49,7 +49,7 @@ auto Cartridge::save() -> void {
auto document = BML::unserialize(information.manifest);
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);
}
}

View File

@ -9,6 +9,13 @@ auto CPU::Enter() -> void {
}
auto CPU::main() -> void {
#if 1
static uint counter = 0;
if(++counter < 10) {
print(disassemble(r.pc), "\n");
}
#endif
instruction();
}
@ -24,4 +31,20 @@ auto CPU::power() -> void {
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 {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto step(uint clocks) -> void override;
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;
};

View File

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

View File

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

View File

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

View File

@ -10,16 +10,17 @@ auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) vdc.refresh();
}
auto System::load() -> bool {
auto System::load(Emulator::Interface* interface) -> bool {
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();
} else return false;
auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false;
this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true;
}

View File

@ -4,13 +4,15 @@ struct System {
auto run() -> void;
auto load() -> bool;
auto load(Emulator::Interface*) -> bool;
auto save() -> void;
auto unload() -> void;
auto power() -> void;
private:
Emulator::Interface* interface = nullptr;
struct Information {
bool loaded = false;
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 {
#define L lastCycle();
#include "memory.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "disassembler.cpp"
#undef L
auto HuC6280::power() -> void {
}
auto HuC6280::instruction() -> void {
step(1);
r.a = 0x00;
r.x = 0x00;
r.y = 0x00;
r.s = 0x00;
r.pc = 0x0000;
r.p = 0x00;
r.mdr = 0x00;
}
}

View File

@ -6,9 +6,56 @@ namespace Processor {
struct HuC6280 {
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;
//memory.cpp
auto io() -> uint8;
auto opcode() -> uint8;
auto operand() -> uint8;
//instruction.cpp
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 {
information = Information();
has = Has();
information = {};
has = {};
if(auto pathID = interface->load(ID::SuperFamicom, "Super Famicom", "sfc")) {
if(auto pathID = platform->load(ID::SuperFamicom, "Super Famicom", "sfc")) {
information.pathID = pathID();
} 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();
} else return false;
auto document = BML::unserialize(information.manifest.cartridge);
@ -99,7 +99,7 @@ auto Cartridge::loadGameBoy() -> 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();
} else return false;
loadBSMemory(BML::unserialize(information.manifest.bsMemory));
@ -107,7 +107,7 @@ auto Cartridge::loadBSMemory() -> 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();
} else return false;
loadSufamiTurboA(BML::unserialize(information.manifest.sufamiTurboA));
@ -115,7 +115,7 @@ auto Cartridge::loadSufamiTurboA() -> 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();
} else return false;
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;
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();
loadBSMemory();
}
}
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();
loadSufamiTurboA();
}
@ -55,7 +55,7 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void {
loadMemory(sufamiturboA.ram, node["board/ram"], File::Optional, sufamiturboA.pathID);
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();
loadSufamiTurboB();
}
@ -130,7 +130,7 @@ auto Cartridge::loadSufamiTurbo(Markup::Node node, bool slot) -> void {
auto Cartridge::loadNSS(Markup::Node node) -> void {
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});
}
@ -182,13 +182,13 @@ auto Cartridge::loadSuperFX(Markup::Node node) -> void {
auto Cartridge::loadARMDSP(Markup::Node node) -> void {
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();
}
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();
}
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();
}
@ -208,10 +208,10 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
for(auto& word : hitachidsp.dataROM) word = 0x000000;
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);
}
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);
}
@ -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::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);
}
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);
}
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);
}
@ -256,7 +256,7 @@ auto Cartridge::loadNECDSP(Markup::Node node) -> void {
auto Cartridge::loadEpsonRTC(Markup::Node node) -> void {
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};
for(auto& byte : data) fp->read();
epsonrtc.load(data);
@ -268,7 +268,7 @@ auto Cartridge::loadEpsonRTC(Markup::Node node) -> void {
auto Cartridge::loadSharpRTC(Markup::Node node) -> void {
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};
for(auto& byte : data) fp->read();
sharprtc.load(data);
@ -322,7 +322,7 @@ auto Cartridge::loadMemory(MappedRAM& ram, Markup::Node node, bool required, may
auto name = node["name"].text();
auto size = node["size"].natural();
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());
}
}

View File

@ -56,7 +56,7 @@ auto Cartridge::saveSuperFX(Markup::Node node) -> void {
auto Cartridge::saveARMDSP(Markup::Node node) -> void {
if(!node["ram/volatile"]) {
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]);
}
}
@ -68,7 +68,7 @@ auto Cartridge::saveHitachiDSP(Markup::Node node) -> void {
if(!node["dram/volatile"]) {
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]);
}
}
@ -79,7 +79,7 @@ auto Cartridge::saveNECDSP(Markup::Node node) -> void {
if(!node["dram/volatile"]) {
uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048;
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);
}
}
@ -89,7 +89,7 @@ auto Cartridge::saveNECDSP(Markup::Node node) -> void {
auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
if(!node["ram/volatile"]) {
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};
epsonrtc.save(data);
fp->write(data, 16);
@ -101,7 +101,7 @@ auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
auto Cartridge::saveSharpRTC(Markup::Node node) -> void {
if(!node["ram/volatile"]) {
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};
sharprtc.save(data);
fp->write(data, 16);
@ -129,7 +129,7 @@ auto Cartridge::saveMemory(MappedRAM& memory, Markup::Node node, maybe<uint> id)
if(!node || node["volatile"]) return;
auto name = node["name"].text();
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());
}
}

View File

@ -5,7 +5,7 @@ Gamepad::Gamepad(bool port) : Controller(port) {
auto Gamepad::data() -> uint2 {
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
switch(counter++) {
@ -32,17 +32,17 @@ auto Gamepad::latch(bool data) -> void {
counter = 0;
if(latched == 0) {
b = interface->inputPoll(port, ID::Device::Gamepad, B);
y = interface->inputPoll(port, ID::Device::Gamepad, Y);
select = interface->inputPoll(port, ID::Device::Gamepad, Select);
start = interface->inputPoll(port, ID::Device::Gamepad, Start);
up = interface->inputPoll(port, ID::Device::Gamepad, Up);
down = interface->inputPoll(port, ID::Device::Gamepad, Down);
left = interface->inputPoll(port, ID::Device::Gamepad, Left);
right = interface->inputPoll(port, ID::Device::Gamepad, Right);
a = interface->inputPoll(port, ID::Device::Gamepad, A);
x = interface->inputPoll(port, ID::Device::Gamepad, X);
l = interface->inputPoll(port, ID::Device::Gamepad, L);
r = interface->inputPoll(port, ID::Device::Gamepad, R);
b = platform->inputPoll(port, ID::Device::Gamepad, B);
y = platform->inputPoll(port, ID::Device::Gamepad, Y);
select = platform->inputPoll(port, ID::Device::Gamepad, Select);
start = platform->inputPoll(port, ID::Device::Gamepad, Start);
up = platform->inputPoll(port, ID::Device::Gamepad, Up);
down = platform->inputPoll(port, ID::Device::Gamepad, Down);
left = platform->inputPoll(port, ID::Device::Gamepad, Left);
right = platform->inputPoll(port, ID::Device::Gamepad, Right);
a = platform->inputPoll(port, ID::Device::Gamepad, A);
x = platform->inputPoll(port, ID::Device::Gamepad, X);
l = platform->inputPoll(port, ID::Device::Gamepad, L);
r = platform->inputPoll(port, ID::Device::Gamepad, R);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,11 +18,11 @@ S21FX::S21FX() {
ram[1] = 0xfc;
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());
}
string filename{interface->path(ID::SuperFamicom), "21fx.so"};
string filename{platform->path(ID::SuperFamicom), "21fx.so"};
if(link.openAbsolute(filename)) {
linkInit = link.sym("fx_init");
linkMain = link.sym("fx_main");

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ auto SMP::main() -> void {
auto SMP::load(Markup::Node node) -> bool {
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);
return true;
}

View File

@ -24,8 +24,6 @@ auto System::runToSave() -> void {
}
auto System::init() -> void {
assert(interface != nullptr);
icd2.init();
mcc.init();
nss.init();
@ -48,10 +46,10 @@ auto System::init() -> void {
auto System::term() -> void {
}
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();
} else return false;
@ -93,6 +91,7 @@ auto System::load() -> bool {
if(cartridge.has.SufamiTurboSlots) sufamiturboA.load(), sufamiturboB.load();
serializeInit();
this->interface = interface;
return information.loaded = true;
}

View File

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

View File

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

View File

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

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