Update to v098r03 release.

byuu says:

It took several hours, but I've rebuilt much of the SNES' bus memory
mapping architecture.

The new design unifies the cartridge string-based mapping
("00-3f,80-bf:8000-ffff") and internal bus.map calls. The map() function
now has an accompanying unmap() function, and instead of a fixed 256
callbacks, it'll scan to find the first available slot. unmap() will
free slots up when zero addresses reference a given slot.

The controllers and expansion port are now both entirely dynamic.
Instead of load/unload/power/reset, they only have the constructor
(power/reset/load) and destructor (unload). What this means is you can
now dynamically change even expansion port devices after the system is
loaded.

Note that this is incredibly dangerous and stupid, but ... oh well. The
whole point of this was for 21fx. There's no way to change the expansion
port device prior to loading a game, but if the 21fx isn't active, then
the reset vector hijack won't work. Now you can load a 21fx game, change
the expansion port device, and simply reset the system to active the
device.

The unification of design between controller port devices and expansion
port devices is nice, and overall this results in a reduction of code
(all of the Mapping stuff in Cartridge is gone, replaced with direct bus
mapping.) And there's always the potential to expand this system more in
the future now.

The big missing feature right now is the ability to push/pop mappings.
So if you look at how the 21fx does the reset vector, you might vomit
a little bit. But ... it works.

Also changed exit(0) to _exit(0) in the POSIX version of nall::execute.

[The _exit(0) thing is an attempt to make higan not crash when it tries
to launch icarus and it's not on $PATH. The theory is that higan forks,
then the child tries to exec icarus and fails, so it exits, all the
unique_ptrs clean up their resources and tell the X server to free
things the parent process is still using. Calling _exit() prevents
destructors from running, and seems to prevent the problem. -Ed.]
This commit is contained in:
Tim Allen 2016-04-09 20:21:18 +10:00
parent 7403e69307
commit 1929ad47d2
40 changed files with 389 additions and 419 deletions

View File

@ -7,7 +7,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "098.02"; static const string Version = "098.03";
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

@ -3,7 +3,7 @@ processors += r65816 spc700 arm gsu hg51b upd96050
objects += sfc-interface sfc-system sfc-scheduler sfc-controller objects += sfc-interface sfc-system sfc-scheduler sfc-controller
objects += sfc-cartridge sfc-cheat objects += sfc-cartridge sfc-cheat
objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
objects += sfc-satellaview sfc-superdisc objects += sfc-expansion sfc-satellaview sfc-superdisc
objects += sfc-21fx objects += sfc-21fx
objects += sfc-icd2 sfc-mcc sfc-nss sfc-event objects += sfc-icd2 sfc-mcc sfc-nss sfc-event
objects += sfc-sa1 sfc-superfx objects += sfc-sa1 sfc-superfx
@ -26,6 +26,7 @@ obj/sfc-smp.o: sfc/smp/smp.cpp $(call rwildcard,sfc/smp/)
obj/sfc-dsp.o: sfc/dsp/dsp.cpp $(call rwildcard,sfc/dsp/) obj/sfc-dsp.o: sfc/dsp/dsp.cpp $(call rwildcard,sfc/dsp/)
obj/sfc-ppu.o: sfc/ppu/ppu.cpp $(call rwildcard,sfc/ppu/) obj/sfc-ppu.o: sfc/ppu/ppu.cpp $(call rwildcard,sfc/ppu/)
obj/sfc-expansion.o: sfc/expansion/expansion.cpp $(call rwildcard,sfc/expansion/)
obj/sfc-satellaview.o: sfc/expansion/satellaview/satellaview.cpp $(call rwildcard,sfc/expansion/satellaview/) obj/sfc-satellaview.o: sfc/expansion/satellaview/satellaview.cpp $(call rwildcard,sfc/expansion/satellaview/)
obj/sfc-superdisc.o: sfc/expansion/superdisc/superdisc.cpp $(call rwildcard,sfc/expansion/superdisc/) obj/sfc-superdisc.o: sfc/expansion/superdisc/superdisc.cpp $(call rwildcard,sfc/expansion/superdisc/)
obj/sfc-21fx.o: sfc/expansion/21fx/21fx.cpp $(call rwildcard,sfc/expansion/21fx/) obj/sfc-21fx.o: sfc/expansion/21fx/21fx.cpp $(call rwildcard,sfc/expansion/21fx/)

View File

@ -35,20 +35,6 @@ struct Cartridge : property<Cartridge> {
MappedRAM rom; MappedRAM rom;
MappedRAM ram; MappedRAM ram;
struct Mapping {
function<auto (uint24, uint8) -> uint8> reader;
function<auto (uint24, uint8) -> void> writer;
string addr;
uint size = 0;
uint base = 0;
uint mask = 0;
Mapping() = default;
Mapping(const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&);
Mapping(SuperFamicom::Memory&);
};
vector<Mapping> mapping;
struct Memory { struct Memory {
unsigned id; unsigned id;
string name; string name;

View File

@ -1,13 +1,3 @@
Cartridge::Mapping::Mapping(SuperFamicom::Memory& memory) {
this->reader = {&SuperFamicom::Memory::read, &memory};
this->writer = {&SuperFamicom::Memory::write, &memory};
}
Cartridge::Mapping::Mapping(const function<uint8 (uint24, uint8)>& reader, const function<void (uint24, uint8)>& writer) {
this->reader = reader;
this->writer = writer;
}
auto Cartridge::parseMarkup(const string& markup) -> void { auto Cartridge::parseMarkup(const string& markup) -> void {
auto document = BML::unserialize(markup); auto document = BML::unserialize(markup);
auto information = document["information"]; auto information = document["information"];
@ -16,7 +6,6 @@ auto Cartridge::parseMarkup(const string& markup) -> void {
this->information.title.cartridge = information["title"].text(); this->information.title.cartridge = information["title"].text();
_region = board["region"].text() == "pal" ? Region::PAL : Region::NTSC; _region = board["region"].text() == "pal" ? Region::PAL : Region::NTSC;
mapping.reset();
if(auto node = board["rom"]) parseMarkupROM(node); if(auto node = board["rom"]) parseMarkupROM(node);
if(auto node = board["ram"]) parseMarkupRAM(node); if(auto node = board["ram"]) parseMarkupRAM(node);
if(auto node = board["icd2"]) parseMarkupICD2(node); if(auto node = board["icd2"]) parseMarkupICD2(node);
@ -40,13 +29,13 @@ auto Cartridge::parseMarkup(const string& markup) -> void {
} }
auto Cartridge::parseMarkupMap(Markup::Node map, SuperFamicom::Memory& memory) -> void { auto Cartridge::parseMarkupMap(Markup::Node map, SuperFamicom::Memory& memory) -> void {
Mapping m{memory}; auto addr = map["address"].text();
m.addr = map["address"].text(); auto size = map["size"].natural();
m.size = map["size"].natural(); auto base = map["base"].natural();
m.base = map["base"].natural(); auto mask = map["mask"].natural();
m.mask = map["mask"].natural(); if(size == 0) size = memory.size();
if(m.size == 0) m.size = memory.size(); if(size == 0) return;
if(m.size != 0) mapping.append(m); bus.map({&SuperFamicom::Memory::read, &memory}, {&SuperFamicom::Memory::write, &memory}, addr, size, base, mask);
} }
auto Cartridge::parseMarkupMap( auto Cartridge::parseMarkupMap(
@ -54,12 +43,11 @@ auto Cartridge::parseMarkupMap(
const function<uint8 (uint24, uint8)>& reader, const function<uint8 (uint24, uint8)>& reader,
const function<void (uint24, uint8)>& writer const function<void (uint24, uint8)>& writer
) -> void { ) -> void {
Mapping m{reader, writer}; auto addr = map["address"].text();
m.addr = map["address"].text(); auto size = map["size"].natural();
m.size = map["size"].natural(); auto base = map["base"].natural();
m.base = map["base"].natural(); auto mask = map["mask"].natural();
m.mask = map["mask"].natural(); bus.map(reader, writer, addr, size, base, mask);
mapping.append(m);
} }
auto Cartridge::parseMarkupMemory(MappedRAM& ram, Markup::Node node, uint id, bool writable) -> void { auto Cartridge::parseMarkupMemory(MappedRAM& ram, Markup::Node node, uint id, bool writable) -> void {

View File

@ -15,8 +15,9 @@ Controller::Controller(bool port) : port(port) {
auto Controller::Enter() -> void { auto Controller::Enter() -> void {
while(true) { while(true) {
if(co_active() == device.controllerPort1->thread) device.controllerPort1->main(); scheduler.synchronize();
if(co_active() == device.controllerPort2->thread) device.controllerPort2->main(); if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
} }
} }

View File

@ -9,7 +9,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, (uint)Device::ID::Gamepad, B); if(latched == 1) return interface->inputPoll(port, 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++) {
@ -36,7 +36,7 @@ auto Gamepad::latch(bool data) -> void {
counter = 0; counter = 0;
if(latched == 0) { if(latched == 0) {
auto id = (uint)Device::ID::Gamepad; auto id = Device::Gamepad;
b = interface->inputPoll(port, id, B); b = interface->inputPoll(port, id, B);
y = interface->inputPoll(port, id, Y); y = interface->inputPoll(port, id, Y);
select = interface->inputPoll(port, id, Select); select = interface->inputPoll(port, id, Select);

View File

@ -1,7 +1,7 @@
Justifier::Justifier(bool port, bool chained): Justifier::Justifier(bool port, bool chained):
Controller(port), Controller(port),
chained(chained), chained(chained),
device(chained == false ? (unsigned)Device::ID::Justifier : (unsigned)Device::ID::Justifiers) device(chained == false ? Device::Justifier : Device::Justifiers)
{ {
create(Controller::Enter, 21477272); create(Controller::Enter, 21477272);
latched = 0; latched = 0;

View File

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

@ -24,8 +24,8 @@ auto Multitap::data() -> uint2 {
port2 = 3; //controller 4 port2 = 3; //controller 4
} }
bool data1 = interface->inputPoll(port, (unsigned)Device::ID::Multitap, port1 * 12 + index); bool data1 = interface->inputPoll(port, Device::Multitap, port1 * 12 + index);
bool data2 = interface->inputPoll(port, (unsigned)Device::ID::Multitap, port2 * 12 + index); bool data2 = interface->inputPoll(port, Device::Multitap, port2 * 12 + index);
return (data2 << 1) | (data1 << 0); return (data2 << 1) | (data1 << 0);
} }

View File

@ -46,8 +46,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, (unsigned)Device::ID::SuperScope, X); int nx = interface->inputPoll(port, Device::SuperScope, X);
int ny = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, Y); int ny = interface->inputPoll(port, 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));
@ -64,7 +64,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, (unsigned)Device::ID::SuperScope, Turbo); bool newturbo = interface->inputPoll(port, Device::SuperScope, Turbo);
if(newturbo && !turbo) { if(newturbo && !turbo) {
turbo = !turbo; //toggle state turbo = !turbo; //toggle state
turbolock = true; turbolock = true;
@ -75,7 +75,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, (unsigned)Device::ID::SuperScope, Trigger); bool newtrigger = interface->inputPoll(port, Device::SuperScope, Trigger);
if(newtrigger && (turbo || !triggerlock)) { if(newtrigger && (turbo || !triggerlock)) {
trigger = true; trigger = true;
triggerlock = true; triggerlock = true;
@ -84,11 +84,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, (unsigned)Device::ID::SuperScope, Cursor); cursor = interface->inputPoll(port, 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, (unsigned)Device::ID::SuperScope, Pause); bool newpause = interface->inputPoll(port, Device::SuperScope, Pause);
if(newpause && !pauselock) { if(newpause && !pauselock) {
pause = true; pause = true;
pauselock = true; pauselock = true;

View File

@ -80,7 +80,7 @@ auto USART::data() -> uint2 {
if(iobit()) { if(iobit()) {
if(counter >= 16) return 1; if(counter >= 16) return 1;
uint2 result = 0; uint2 result = 0;
if(counter < 12) result = interface->inputPoll(port, (uint)Device::ID::Gamepad, counter); if(counter < 12) result = interface->inputPoll(port, Device::Gamepad, counter);
if(latched == 0) counter++; if(latched == 0) counter++;
return result; return result;
} }

View File

@ -11,10 +11,6 @@ auto SDD1::init() -> void {
} }
void SDD1::load() { void SDD1::load() {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::mcu_read()
bus.map({&SDD1::dma_read, &sdd1}, {&SDD1::dma_write, &sdd1}, 0x00, 0x3f, 0x4300, 0x437f);
bus.map({&SDD1::dma_read, &sdd1}, {&SDD1::dma_write, &sdd1}, 0x80, 0xbf, 0x4300, 0x437f);
} }
auto SDD1::unload() -> void { auto SDD1::unload() -> void {
@ -26,6 +22,10 @@ auto SDD1::power() -> void {
} }
auto SDD1::reset() -> void { auto SDD1::reset() -> void {
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::mcu_read()
bus.map({&SDD1::dma_read, &sdd1}, {&SDD1::dma_write, &sdd1}, "00-3f,80-bf:4300-437f");
sdd1_enable = 0x00; sdd1_enable = 0x00;
xfer_enable = 0x00; xfer_enable = 0x00;
dma_ready = false; dma_ready = false;

View File

@ -85,38 +85,6 @@ auto CPU::main() -> void {
instruction(); instruction();
} }
auto CPU::enable() -> void {
function<auto (uint24, uint8) -> uint8> reader;
function<auto (uint24, uint8) -> void> writer;
reader = {&CPU::apuPortRead, this};
writer = {&CPU::apuPortWrite, this};
bus.map(reader, writer, 0x00, 0x3f, 0x2140, 0x217f);
bus.map(reader, writer, 0x80, 0xbf, 0x2140, 0x217f);
reader = {&CPU::cpuPortRead, this};
writer = {&CPU::cpuPortWrite, this};
bus.map(reader, writer, 0x00, 0x3f, 0x2180, 0x2183);
bus.map(reader, writer, 0x80, 0xbf, 0x2180, 0x2183);
bus.map(reader, writer, 0x00, 0x3f, 0x4016, 0x4017);
bus.map(reader, writer, 0x80, 0xbf, 0x4016, 0x4017);
bus.map(reader, writer, 0x00, 0x3f, 0x4200, 0x421f);
bus.map(reader, writer, 0x80, 0xbf, 0x4200, 0x421f);
reader = {&CPU::dmaPortRead, this};
writer = {&CPU::dmaPortWrite, this};
bus.map(reader, writer, 0x00, 0x3f, 0x4300, 0x437f);
bus.map(reader, writer, 0x80, 0xbf, 0x4300, 0x437f);
reader = [](uint24 addr, uint8) -> uint8 { return cpu.wram[addr]; };
writer = [](uint24 addr, uint8 data) -> void { cpu.wram[addr] = data; };
bus.map(reader, writer, 0x00, 0x3f, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x80, 0xbf, 0x0000, 0x1fff, 0x002000);
bus.map(reader, writer, 0x7e, 0x7f, 0x0000, 0xffff, 0x020000);
}
auto CPU::power() -> void { auto CPU::power() -> void {
for(auto& byte : wram) byte = random(0x55); for(auto& byte : wram) byte = random(0x55);
@ -155,6 +123,26 @@ auto CPU::reset() -> void {
coprocessors.reset(); coprocessors.reset();
PPUcounter::reset(); PPUcounter::reset();
function<auto (uint24, uint8) -> uint8> reader;
function<auto (uint24, uint8) -> void> writer;
reader = {&CPU::apuPortRead, this};
writer = {&CPU::apuPortWrite, this};
bus.map(reader, writer, "00-3f,80-bf:2140-217f");
reader = {&CPU::cpuPortRead, this};
writer = {&CPU::cpuPortWrite, this};
bus.map(reader, writer, "00-3f,80-bf:2180-2183,4016-4017,4200-421f");
reader = {&CPU::dmaPortRead, this};
writer = {&CPU::dmaPortWrite, this};
bus.map(reader, writer, "00-3f,80-bf:4300-437f");
reader = [](uint24 addr, uint8) -> uint8 { return cpu.wram[addr]; };
writer = [](uint24 addr, uint8 data) -> void { cpu.wram[addr] = data; };
bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000);
bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000);
//CPU //CPU
regs.pc = 0x000000; regs.pc = 0x000000;
regs.x.h = 0x00; regs.x.h = 0x00;

View File

@ -16,7 +16,6 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto enable() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;

View File

@ -7,14 +7,14 @@ auto CPU::stepAutoJoypadPoll() -> void {
if(status.auto_joypad_active && status.auto_joypad_latch) { if(status.auto_joypad_active && status.auto_joypad_latch) {
if(status.auto_joypad_counter == 0) { if(status.auto_joypad_counter == 0) {
device.controllerPort1->latch(1); SuperFamicom::peripherals.controllerPort1->latch(1);
device.controllerPort2->latch(1); SuperFamicom::peripherals.controllerPort2->latch(1);
device.controllerPort1->latch(0); SuperFamicom::peripherals.controllerPort1->latch(0);
device.controllerPort2->latch(0); SuperFamicom::peripherals.controllerPort2->latch(0);
} }
uint2 port0 = device.controllerPort1->data(); uint2 port0 = SuperFamicom::peripherals.controllerPort1->data();
uint2 port1 = device.controllerPort2->data(); uint2 port1 = SuperFamicom::peripherals.controllerPort2->data();
status.joy1 = status.joy1 << 1 | port0.bit(0); status.joy1 = status.joy1 << 1 | port0.bit(0);
status.joy2 = status.joy2 << 1 | port1.bit(0); status.joy2 = status.joy2 << 1 | port1.bit(0);

View File

@ -16,7 +16,7 @@ auto CPU::cpuPortRead(uint24 addr, uint8 data) -> uint8 {
//1-0 = Joypad serial data //1-0 = Joypad serial data
if(addr == 0x4016) { if(addr == 0x4016) {
uint8 r = regs.mdr & 0xfc; uint8 r = regs.mdr & 0xfc;
r |= device.controllerPort1->data(); r |= SuperFamicom::peripherals.controllerPort1->data();
return r; return r;
} }
@ -26,7 +26,7 @@ auto CPU::cpuPortRead(uint24 addr, uint8 data) -> uint8 {
//4-2 = Always 1 (pins are connected to GND) //4-2 = Always 1 (pins are connected to GND)
//1-0 = Joypad serial data //1-0 = Joypad serial data
uint8 r = (regs.mdr & 0xe0) | 0x1c; uint8 r = (regs.mdr & 0xe0) | 0x1c;
r |= device.controllerPort2->data(); r |= SuperFamicom::peripherals.controllerPort2->data();
return r; return r;
} }
@ -183,8 +183,8 @@ auto CPU::cpuPortWrite(uint24 addr, uint8 data) -> void {
//bit 0 is shared between JOYSER0 and JOYSER1, therefore //bit 0 is shared between JOYSER0 and JOYSER1, therefore
//strobing $4016.d0 affects both controller port latches. //strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored. //$4017 bit 0 writes are ignored.
device.controllerPort1->latch(data.bit(0)); SuperFamicom::peripherals.controllerPort1->latch(data.bit(0));
device.controllerPort2->latch(data.bit(0)); SuperFamicom::peripherals.controllerPort2->latch(data.bit(0));
} }
//NMITIMEN //NMITIMEN

View File

@ -2,10 +2,56 @@
namespace SuperFamicom { namespace SuperFamicom {
S21FX s21fx; S21FX::S21FX() {
create(S21FX::Enter, 10'000'000);
resetVector.byte(0) = bus.read(0xfffc, 0x00);
resetVector.byte(1) = bus.read(0xfffd, 0x00);
bus.map({&S21FX::read, this}, {&S21FX::write, this}, "00-3f,80-bf:2184-21ff");
bus.map({&S21FX::read, this}, {&S21FX::write, this}, "00:fffc-fffd");
booted = false;
for(auto& byte : ram) byte = 0xdb; //stp
ram[0] = 0x6c; //jmp ($fffc)
ram[1] = 0xfc;
ram[2] = 0xff;
if(auto buffer = file::read({interface->path(ID::System), "21fx.rom"})) {
memory::copy(ram, sizeof(ram), buffer.data(), buffer.size());
}
string filename{interface->path(ID::SuperFamicom), "21fx.so"};
if(link.openAbsolute(filename)) {
linkInit = link.sym("fx_init");
linkMain = link.sym("fx_main");
}
}
S21FX::~S21FX() {
bus.unmap("00-3f,80-bf:2184-21ff");
bus.unmap("00:fffc-fffd");
//note: this is an awful hack ...
//since the bus maps are lambdas, we can't safely restore the original reset vector handler
//as such, we install a basic read-only lambda that simply returns the known reset vector
//the downside is that if 00:fffc-fffd were anything but ROM; it will now only act as ROM
//given that this is the only device that hooks the reset vector like this,
//it's not worth the added complexity to support some form of reversible bus mapping hooks
uint vector = resetVector;
bus.map([vector](uint24 addr, uint8) -> uint8 {
return vector >> addr * 8;
}, [](uint24, uint8) -> void {
}, "00:fffc-fffd", 2);
if(link.open()) link.close();
linkInit.reset();
linkMain.reset();
}
auto S21FX::Enter() -> void { auto S21FX::Enter() -> void {
while(true) scheduler.synchronize(), s21fx.main(); while(true) scheduler.synchronize(), peripherals.expansionPort->main();
} }
auto S21FX::main() -> void { auto S21FX::main() -> void {
@ -21,47 +67,6 @@ auto S21FX::main() -> void {
while(true) step(10'000'000); while(true) step(10'000'000);
} }
auto S21FX::init() -> void {
}
auto S21FX::load() -> void {
resetVector.byte(0) = bus.read(0xfffc, 0x00);
resetVector.byte(1) = bus.read(0xfffd, 0x00);
for(auto& byte : ram) byte = 0xdb; //stp
ram[0] = 0x6c; //jmp ($fffc)
ram[1] = 0xfc;
ram[2] = 0xff;
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x00, 0x00, 0xfffc, 0xfffd);
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x00, 0x3f, 0x2184, 0x21ff);
bus.map({&S21FX::read, &s21fx}, {&S21FX::write, &s21fx}, 0x80, 0xbf, 0x2184, 0x21ff);
if(auto buffer = file::read({interface->path(ID::System), "21fx.rom"})) {
memory::copy(ram, sizeof(ram), buffer.data(), buffer.size());
}
string filename{interface->path(ID::SuperFamicom), "21fx.so"};
if(link.openAbsolute(filename)) {
linkInit = link.sym("fx_init");
linkMain = link.sym("fx_main");
}
}
auto S21FX::unload() -> void {
if(link.open()) link.close();
linkInit.reset();
linkMain.reset();
}
auto S21FX::power() -> void {
}
auto S21FX::reset() -> void {
create(S21FX::Enter, 10'000'000);
booted = false;
}
auto S21FX::read(uint24 addr, uint8 data) -> uint8 { auto S21FX::read(uint24 addr, uint8 data) -> uint8 {
addr &= 0x40ffff; addr &= 0x40ffff;

View File

@ -1,11 +1,9 @@
struct S21FX : Cothread, Memory { struct S21FX : Expansion {
S21FX();
~S21FX();
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint24 addr, uint8 data) -> uint8; auto read(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void; auto write(uint24 addr, uint8 data) -> void;
@ -36,5 +34,3 @@ private:
vector<uint8> snesBuffer; //SNES -> Link vector<uint8> snesBuffer; //SNES -> Link
vector<uint8> linkBuffer; //Link -> SNES vector<uint8> linkBuffer; //Link -> SNES
}; };
extern S21FX s21fx;

View File

@ -0,0 +1,17 @@
#include <sfc/sfc.hpp>
namespace SuperFamicom {
Expansion::Expansion() {
if(!thread) create(Expansion::Enter, 1);
}
auto Expansion::Enter() -> void {
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
}
auto Expansion::main() -> void {
step(1);
}
}

View File

@ -1,3 +1,9 @@
struct Expansion : Cothread {
Expansion();
static auto Enter() -> void;
virtual auto main() -> void;
};
#include <sfc/expansion/satellaview/satellaview.hpp> #include <sfc/expansion/satellaview/satellaview.hpp>
#include <sfc/expansion/superdisc/superdisc.hpp> #include <sfc/expansion/superdisc/superdisc.hpp>
#include <sfc/expansion/21fx/21fx.hpp> #include <sfc/expansion/21fx/21fx.hpp>

View File

@ -2,30 +2,17 @@
namespace SuperFamicom { namespace SuperFamicom {
Satellaview satellaview; Satellaview::Satellaview() {
bus.map({&Satellaview::read, this}, {&Satellaview::write, this}, "00-3f,80-bf:2188-219f");
auto Satellaview::init() -> void {
}
auto Satellaview::load() -> void {
bus.map({&Satellaview::read, &satellaview}, {&Satellaview::write, &satellaview}, 0x00, 0x3f, 0x2188, 0x219f);
bus.map({&Satellaview::read, &satellaview}, {&Satellaview::write, &satellaview}, 0x80, 0xbf, 0x2188, 0x219f);
}
auto Satellaview::unload() -> void {
}
auto Satellaview::power() -> void {
}
auto Satellaview::reset() -> void {
memory::fill(&regs, sizeof regs); memory::fill(&regs, sizeof regs);
} }
auto Satellaview::read(uint24 addr, uint8 data) -> uint8 { Satellaview::~Satellaview() {
addr &= 0xffff; bus.unmap("00-3f,80-bf:2188-219f");
}
switch(addr) { auto Satellaview::read(uint24 addr, uint8 data) -> uint8 {
switch(addr &= 0xffff) {
case 0x2188: return regs.r2188; case 0x2188: return regs.r2188;
case 0x2189: return regs.r2189; case 0x2189: return regs.r2189;
case 0x218a: return regs.r218a; case 0x218a: return regs.r218a;
@ -81,9 +68,7 @@ auto Satellaview::read(uint24 addr, uint8 data) -> uint8 {
} }
auto Satellaview::write(uint24 addr, uint8 data) -> void { auto Satellaview::write(uint24 addr, uint8 data) -> void {
addr &= 0xffff; switch(addr &= 0xffff) {
switch(addr) {
case 0x2188: { case 0x2188: {
regs.r2188 = data; regs.r2188 = data;
} break; } break;

View File

@ -1,9 +1,6 @@
struct Satellaview : Memory { struct Satellaview : Expansion {
auto init() -> void; Satellaview();
auto load() -> void; ~Satellaview();
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint24 addr, uint8 data) -> uint8; auto read(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void; auto write(uint24 addr, uint8 data) -> void;
@ -21,5 +18,3 @@ private:
uint8 r2192_hour, r2192_minute, r2192_second; uint8 r2192_hour, r2192_minute, r2192_second;
} regs; } regs;
}; };
extern Satellaview satellaview;

View File

@ -2,12 +2,29 @@
namespace SuperFamicom { namespace SuperFamicom {
SuperDisc superdisc;
#include "nec.cpp" #include "nec.cpp"
#include "sony.cpp" #include "sony.cpp"
SuperDisc::SuperDisc() {
create(&SuperDisc::Enter, 75);
bus.map({&SuperDisc::read, this}, {&SuperDisc::write, this}, "00-3f,80-bf:21e0-21e5");
r.irqEnable = 0x00;
nec.command.reset();
nec.data = 0x00;
sony.command = 0x00;
sony.data = 0x00;
}
SuperDisc::~SuperDisc() {
bus.unmap("00-3f,80-bf:21e0-21e5");
}
auto SuperDisc::Enter() -> void { auto SuperDisc::Enter() -> void {
while(true) scheduler.synchronize(), superdisc.main(); while(true) scheduler.synchronize(), peripherals.expansionPort->main();
} }
auto SuperDisc::main() -> void { auto SuperDisc::main() -> void {
@ -27,31 +44,6 @@ auto SuperDisc::main() -> void {
synchronizeCPU(); synchronizeCPU();
} }
auto SuperDisc::init() -> void {
}
auto SuperDisc::load() -> void {
bus.map({&SuperDisc::read, &superdisc}, {&SuperDisc::write, &superdisc}, 0x00, 0x3f, 0x21e0, 0x21e5);
bus.map({&SuperDisc::read, &superdisc}, {&SuperDisc::write, &superdisc}, 0x80, 0xbf, 0x21e0, 0x21e5);
}
auto SuperDisc::unload() -> void {
}
auto SuperDisc::power() -> void {
create(&SuperDisc::Enter, 75);
}
auto SuperDisc::reset() -> void {
r.irqEnable = 0x00;
nec.command.reset();
nec.data = 0x00;
sony.command = 0x00;
sony.data = 0x00;
}
auto SuperDisc::read(uint24 addr, uint8 data) -> uint8 { auto SuperDisc::read(uint24 addr, uint8 data) -> uint8 {
addr = 0x21e0 | (addr & 7); addr = 0x21e0 | (addr & 7);

View File

@ -1,13 +1,10 @@
struct SuperDisc : Cothread, Memory { struct SuperDisc : Expansion {
SuperDisc();
~SuperDisc();
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;
auto power() -> void;
auto reset() -> void;
auto read(uint24 addr, uint8 data) -> uint8; auto read(uint24 addr, uint8 data) -> uint8;
auto write(uint24 addr, uint8 data) -> void; auto write(uint24 addr, uint8 data) -> void;
@ -39,5 +36,3 @@ private:
uint8 data; uint8 data;
} sony; } sony;
}; };
extern SuperDisc superdisc;

View File

@ -425,7 +425,7 @@ auto Interface::unload() -> void {
} }
auto Interface::connect(uint port, uint device) -> void { auto Interface::connect(uint port, uint device) -> void {
SuperFamicom::device.connect(port, (SuperFamicom::Device::ID)device); SuperFamicom::peripherals.connect(port, device);
} }
auto Interface::power() -> void { auto Interface::power() -> void {

View File

@ -4,63 +4,98 @@ namespace SuperFamicom {
Bus bus; Bus bus;
Bus::Bus() {
lookup = new uint8 [16 * 1024 * 1024];
target = new uint32[16 * 1024 * 1024];
}
Bus::~Bus() { Bus::~Bus() {
delete[] lookup; if(lookup) delete[] lookup;
delete[] target; if(target) delete[] target;
} }
auto Bus::reset() -> void { auto Bus::reset() -> void {
function<auto (uint24, uint8) -> uint8> reader = [](uint24, uint8 data) { return data; }; for(auto id : range(256)) {
function<auto (uint24, uint8) -> void> writer = [](uint24, uint8) {}; reader[id].reset();
writer[id].reset();
counter[id] = 0;
}
idcount = 0; if(lookup) delete[] lookup;
map(reader, writer, 0x00, 0xff, 0x0000, 0xffff); if(target) delete[] target;
lookup = new uint8 [16 * 1024 * 1024]();
target = new uint32[16 * 1024 * 1024]();
reader[0] = [](uint24, uint8 data) -> uint8 { return data; };
writer[0] = [](uint24, uint8) -> void {};
} }
auto Bus::map() -> void { auto Bus::map(
for(auto& m : cartridge.mapping) { const function<uint8 (uint24, uint8)>& read,
lstring part = m.addr.split(":", 1L); const function<void (uint24, uint8)>& write,
lstring banks = part(0).split(","); const string& addr, uint size, uint base, uint mask
lstring addrs = part(1).split(","); ) -> void {
for(auto& bank : banks) { uint id = 1;
for(auto& addr : addrs) { while(counter[id]) {
lstring bankpart = bank.split("-", 1L); if(++id >= 256) return print("SFC error: bus map exhausted\n");
lstring addrpart = addr.split("-", 1L); }
uint banklo = hex(bankpart(0)); //print("map[", hex(id, 2), "] => ", addr, "\n");
uint bankhi = hex(bankpart(1, bankpart(0)));
uint addrlo = hex(addrpart(0)); reader[id] = read;
uint addrhi = hex(addrpart(1, addrpart(0))); writer[id] = write;
map(m.reader, m.writer, banklo, bankhi, addrlo, addrhi, m.size, m.base, m.mask);
auto p = addr.split(":", 1L);
auto banks = p(0).split(",");
auto addrs = p(1).split(",");
for(auto& bank : banks) {
for(auto& addr : addrs) {
auto bankRange = bank.split("-", 1L);
auto addrRange = addr.split("-", 1L);
uint bankLo = hex(bankRange(0));
uint bankHi = hex(bankRange(1, bankRange(0)));
uint addrLo = hex(addrRange(0));
uint addrHi = hex(addrRange(1, addrRange(0)));
for(uint bank = bankLo; bank <= bankHi; bank++) {
for(uint addr = addrLo; addr <= addrHi; addr++) {
uint pid = lookup[bank << 16 | addr];
if(pid && --counter[pid] == 0) {
reader[pid].reset();
writer[pid].reset();
}
uint offset = reduce(bank << 16 | addr, mask);
if(size) offset = base + mirror(offset, size - base);
lookup[bank << 16 | addr] = id;
target[bank << 16 | addr] = offset;
counter[id]++;
}
} }
} }
} }
} }
auto Bus::map( auto Bus::unmap(const string& addr) -> void {
const function<uint8 (uint24, uint8)>& reader, auto p = addr.split(":", 1L);
const function<void (uint24, uint8)>& writer, auto banks = p(0).split(",");
uint8 banklo, uint8 bankhi, uint16 addrlo, uint16 addrhi, auto addrs = p(1).split(",");
uint size, uint base, uint mask for(auto& bank : banks) {
) -> void { for(auto& addr : addrs) {
assert(banklo <= bankhi); auto bankRange = bank.split("-", 1L);
assert(addrlo <= addrhi); auto addrRange = addr.split("-", 1L);
assert(idcount < 255); uint bankLo = hex(bankRange(0));
uint bankHi = hex(bankRange(1, bankRange(0)));
uint addrLo = hex(addrRange(0));
uint addrHi = hex(addrRange(1, addrRange(1)));
uint id = idcount++; for(uint bank = bankLo; bank <= bankHi; bank++) {
this->reader[id] = reader; for(uint addr = addrLo; addr <= addrHi; addr++) {
this->writer[id] = writer; uint pid = lookup[bank << 16 | addr];
if(pid && --counter[pid] == 0) {
reader[pid].reset();
writer[pid].reset();
}
for(uint bank = banklo; bank <= bankhi; bank++) { lookup[bank << 16 | addr] = 0;
for(uint addr = addrlo; addr <= addrhi; addr++) { target[bank << 16 | addr] = 0;
uint offset = reduce(bank << 16 | addr, mask); }
if(size) offset = base + mirror(offset, size - base); }
lookup[bank << 16 | addr] = id;
target[bank << 16 | addr] = offset;
} }
} }
} }

View File

@ -45,27 +45,26 @@ struct Bus {
alwaysinline static auto mirror(uint addr, uint size) -> uint; alwaysinline static auto mirror(uint addr, uint size) -> uint;
alwaysinline static auto reduce(uint addr, uint mask) -> uint; alwaysinline static auto reduce(uint addr, uint mask) -> uint;
Bus();
~Bus(); ~Bus();
alwaysinline auto read(uint24 addr, uint8 data) -> uint8; alwaysinline auto read(uint24 addr, uint8 data) -> uint8;
alwaysinline auto write(uint24 addr, uint8 data) -> void; alwaysinline auto write(uint24 addr, uint8 data) -> void;
auto reset() -> void; auto reset() -> void;
auto map() -> void;
auto map( auto map(
const function<uint8 (uint24, uint8)>& reader, const function<uint8 (uint24, uint8)>& read,
const function<void (uint24, uint8)>& writer, const function<void (uint24, uint8)>& write,
uint8 banklo, uint8 bankhi, uint16 addrlo, uint16 addrhi, const string& addr, uint size = 0, uint base = 0, uint mask = 0
uint size = 0, uint base = 0, uint mask = 0
) -> void; ) -> void;
auto unmap(const string& addr) -> void;
private:
uint8* lookup = nullptr; uint8* lookup = nullptr;
uint32* target = nullptr; uint32* target = nullptr;
uint idcount = 0;
function<auto (uint24, uint8) -> uint8> reader[256]; function<auto (uint24, uint8) -> uint8> reader[256];
function<auto (uint24, uint8) -> void> writer[256]; function<auto (uint24, uint8) -> void> writer[256];
uint24 counter[256];
}; };
extern Bus bus; extern Bus bus;

View File

@ -86,14 +86,6 @@ auto PPU::addClocks(uint clocks) -> void {
} }
} }
auto PPU::enable() -> void {
function<auto (uint24, uint8) -> uint8> reader{&PPU::read, this};
function<auto (uint24, uint8) -> void> writer{&PPU::write, this};
bus.map(reader, writer, 0x00, 0x3f, 0x2100, 0x213f);
bus.map(reader, writer, 0x80, 0xbf, 0x2100, 0x213f);
}
auto PPU::power() -> void { auto PPU::power() -> void {
for(auto& n : vram) n = random(0x00); for(auto& n : vram) n = random(0x00);
for(auto& n : oam) n = random(0x00); for(auto& n : oam) n = random(0x00);
@ -105,6 +97,10 @@ auto PPU::reset() -> void {
PPUcounter::reset(); PPUcounter::reset();
memory::fill(output, 512 * 480 * sizeof(uint32)); memory::fill(output, 512 * 480 * sizeof(uint32));
function<auto (uint24, uint8) -> uint8> reader{&PPU::read, this};
function<auto (uint24, uint8) -> void> writer{&PPU::write, this};
bus.map(reader, writer, "00-3f,80-bf:2100-213f");
regs.ppu1_mdr = random(0xff); regs.ppu1_mdr = random(0xff);
regs.ppu2_mdr = random(0xff); regs.ppu2_mdr = random(0xff);

View File

@ -13,7 +13,6 @@ struct PPU : Thread, PPUcounter {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto enable() -> void;
auto power() -> void; auto power() -> void;
auto reset() -> void; auto reset() -> void;

View File

@ -144,17 +144,17 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
} }
auto Video::drawCursors() -> void { auto Video::drawCursors() -> void {
switch((Device::ID)settings.controllerPort2) { switch(settings.controllerPort2) {
case Device::ID::SuperScope: case Device::SuperScope:
if(dynamic_cast<SuperScope*>(device.controllerPort2)) { if(dynamic_cast<SuperScope*>(peripherals.controllerPort2)) {
auto& controller = (SuperScope&)*device.controllerPort2; auto& controller = (SuperScope&)*peripherals.controllerPort2;
drawCursor(0xff0000ff, controller.x, controller.y); drawCursor(0xff0000ff, controller.x, controller.y);
} }
break; break;
case Device::ID::Justifier: case Device::Justifier:
case Device::ID::Justifiers: case Device::Justifiers:
if(dynamic_cast<Justifier*>(device.controllerPort2)) { if(dynamic_cast<Justifier*>(peripherals.controllerPort2)) {
auto& controller = (Justifier&)*device.controllerPort2; auto& controller = (Justifier&)*peripherals.controllerPort2;
drawCursor(0xffff0000, controller.player1.x, controller.player1.y); drawCursor(0xffff0000, controller.player1.x, controller.player1.y);
if(!controller.chained) break; if(!controller.chained) break;
drawCursor(0xff00bf00, controller.player2.x, controller.player2.y); drawCursor(0xff00bf00, controller.player2.x, controller.player2.y);

View File

@ -25,7 +25,7 @@ namespace SuperFamicom {
namespace SuperFamicom { namespace SuperFamicom {
struct Thread { struct Thread {
~Thread() { virtual ~Thread() {
if(thread) co_delete(thread); if(thread) co_delete(thread);
} }
@ -61,10 +61,10 @@ namespace SuperFamicom {
#include <sfc/ppu/ppu.hpp> #include <sfc/ppu/ppu.hpp>
#include <sfc/controller/controller.hpp> #include <sfc/controller/controller.hpp>
#include <sfc/expansion/expansion.hpp>
#include <sfc/system/system.hpp> #include <sfc/system/system.hpp>
#include <sfc/scheduler/scheduler.hpp> #include <sfc/scheduler/scheduler.hpp>
#include <sfc/coprocessor/coprocessor.hpp> #include <sfc/coprocessor/coprocessor.hpp>
#include <sfc/expansion/expansion.hpp>
#include <sfc/slot/slot.hpp> #include <sfc/slot/slot.hpp>
#include <sfc/cartridge/cartridge.hpp> #include <sfc/cartridge/cartridge.hpp>
#include <sfc/cheat/cheat.hpp> #include <sfc/cheat/cheat.hpp>

View File

@ -1,47 +0,0 @@
Device device;
Device::Device() {
connect(0, ID::None);
connect(1, ID::None);
connect(2, ID::None);
}
Device::~Device() {
if(controllerPort1) delete controllerPort1;
if(controllerPort2) delete controllerPort2;
}
auto Device::connect(uint port, Device::ID id) -> void {
if(port == 0 || port == 1) {
Controller*& controller = (port == 0 ? controllerPort1 : controllerPort2);
if(controller) {
delete controller;
controller = nullptr;
}
switch(id) { default:
case ID::None: controller = new Controller(port); break;
case ID::Gamepad: controller = new Gamepad(port); break;
case ID::Multitap: controller = new Multitap(port); break;
case ID::Mouse: controller = new Mouse(port); break;
case ID::SuperScope: controller = new SuperScope(port); break;
case ID::Justifier: controller = new Justifier(port, false); break;
case ID::Justifiers: controller = new Justifier(port, true); break;
case ID::USART: controller = new USART(port); break;
}
switch(port) {
case 0: settings.controllerPort1 = (uint)id; break;
case 1: settings.controllerPort2 = (uint)id; break;
}
}
if(port == 2) {
settings.expansionPort = (uint)id;
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort1);
cpu.peripherals.append(controllerPort2);
}

View File

@ -1,35 +0,0 @@
struct Device {
enum class Port : uint {
Controller1,
Controller2,
Expansion,
};
enum class ID : uint {
None,
//controller port devices
Gamepad,
Multitap,
Mouse,
SuperScope,
Justifier,
Justifiers,
USART,
//expansion port devices
Satellaview,
SuperDisc,
S21FX,
};
Device();
~Device();
auto connect(uint port, Device::ID id) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
};
extern Device device;

View File

@ -0,0 +1,66 @@
Peripherals peripherals;
auto Peripherals::unload() -> void {
delete controllerPort1;
delete controllerPort2;
delete expansionPort;
controllerPort1 = nullptr;
controllerPort2 = nullptr;
expansionPort = nullptr;
}
auto Peripherals::reset() -> void {
connect(0, settings.controllerPort1);
connect(1, settings.controllerPort2);
connect(2, settings.expansionPort);
}
auto Peripherals::connect(uint port, uint id) -> void {
if(port == Port::Controller1) {
settings.controllerPort1 = id;
if(!system.loaded()) return;
delete controllerPort1;
switch(id) { default:
case Device::None: controllerPort1 = new Controller(0); break;
case Device::Gamepad: controllerPort1 = new Gamepad(0); break;
case Device::Multitap: controllerPort1 = new Multitap(0); break;
case Device::Mouse: controllerPort1 = new Mouse(0); break;
case Device::USART: controllerPort1 = new USART(0); break;
}
}
if(port == Port::Controller2) {
settings.controllerPort2 = id;
if(!system.loaded()) return;
delete controllerPort2;
switch(id) { default:
case Device::None: controllerPort2 = new Controller(1); break;
case Device::Gamepad: controllerPort2 = new Gamepad(1); break;
case Device::Multitap: controllerPort2 = new Multitap(1); break;
case Device::Mouse: controllerPort2 = new Mouse(1); break;
case Device::SuperScope: controllerPort2 = new SuperScope(1); break;
case Device::Justifier: controllerPort2 = new Justifier(1, false); break;
case Device::Justifiers: controllerPort2 = new Justifier(1, true); break;
}
}
if(port == Port::Expansion) {
settings.expansionPort = id;
if(!system.loaded()) return;
delete expansionPort;
switch(id) { default:
case Device::None: expansionPort = new Expansion; break;
case Device::Satellaview: expansionPort = new Satellaview; break;
case Device::SuperDisc: expansionPort = new SuperDisc; break;
case Device::S21FX: expansionPort = new S21FX; break;
}
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort1);
cpu.peripherals.append(controllerPort2);
cpu.peripherals.append(expansionPort);
}

View File

@ -0,0 +1,35 @@
struct Port { enum : uint {
Controller1,
Controller2,
Expansion,
};};
struct Device { enum : uint {
None,
//controller port peripherals
Gamepad,
Multitap,
Mouse,
SuperScope,
Justifier,
Justifiers,
USART,
//expansion port peripherals
Satellaview,
SuperDisc,
S21FX,
};};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint id) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
Expansion* expansionPort = nullptr;
};
extern Peripherals peripherals;

View File

@ -39,7 +39,6 @@ auto System::unserialize(serializer& s) -> bool {
auto System::serialize(serializer& s) -> void { auto System::serialize(serializer& s) -> void {
s.integer((uint&)_region); s.integer((uint&)_region);
s.integer((uint&)_expansionPort);
} }
auto System::serializeAll(serializer& s) -> void { auto System::serializeAll(serializer& s) -> void {

View File

@ -4,13 +4,12 @@ namespace SuperFamicom {
System system; System system;
#include "device.cpp" #include "peripherals.cpp"
#include "random.cpp" #include "random.cpp"
#include "serialization.cpp" #include "serialization.cpp"
auto System::loaded() const -> bool { return _loaded; } auto System::loaded() const -> bool { return _loaded; }
auto System::region() const -> Region { return _region; } auto System::region() const -> Region { return _region; }
auto System::expansionPort() const -> Device::ID { return _expansionPort; }
auto System::cpuFrequency() const -> uint { return _cpuFrequency; } auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
auto System::apuFrequency() const -> uint { return _apuFrequency; } auto System::apuFrequency() const -> uint { return _apuFrequency; }
@ -34,10 +33,6 @@ auto System::runToSave() -> void {
auto System::init() -> void { auto System::init() -> void {
assert(interface != nullptr); assert(interface != nullptr);
satellaview.init();
superdisc.init();
s21fx.init();
icd2.init(); icd2.init();
mcc.init(); mcc.init();
nss.init(); nss.init();
@ -55,16 +50,14 @@ auto System::init() -> void {
msu1.init(); msu1.init();
bsmemory.init(); bsmemory.init();
device.connect(0, (Device::ID)settings.controllerPort1);
device.connect(1, (Device::ID)settings.controllerPort2);
device.connect(2, (Device::ID)settings.expansionPort);
} }
auto System::term() -> void { auto System::term() -> void {
} }
auto System::load() -> void { auto System::load() -> void {
bus.reset();
interface->loadRequest(ID::SystemManifest, "manifest.bml", true); interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
@ -74,20 +67,9 @@ auto System::load() -> void {
cartridge.load(); cartridge.load();
_region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL; _region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
_expansionPort = (Device::ID)settings.expansionPort;
_cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370; _cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370;
_apuFrequency = 24'606'720; _apuFrequency = 24'606'720;
bus.reset();
bus.map();
cpu.enable();
ppu.enable();
if(expansionPort() == Device::ID::Satellaview) satellaview.load();
if(expansionPort() == Device::ID::SuperDisc) superdisc.load();
if(expansionPort() == Device::ID::S21FX) s21fx.load();
if(cartridge.hasICD2()) icd2.load(); if(cartridge.hasICD2()) icd2.load();
if(cartridge.hasMCC()) mcc.load(); if(cartridge.hasMCC()) mcc.load();
if(cartridge.hasNSSDIP()) nss.load(); if(cartridge.hasNSSDIP()) nss.load();
@ -113,9 +95,7 @@ auto System::load() -> void {
auto System::unload() -> void { auto System::unload() -> void {
if(!loaded()) return; if(!loaded()) return;
if(expansionPort() == Device::ID::Satellaview) satellaview.unload(); peripherals.unload();
if(expansionPort() == Device::ID::SuperDisc) superdisc.unload();
if(expansionPort() == Device::ID::S21FX) s21fx.unload();
if(cartridge.hasICD2()) icd2.unload(); if(cartridge.hasICD2()) icd2.unload();
if(cartridge.hasMCC()) mcc.unload(); if(cartridge.hasMCC()) mcc.unload();
@ -148,10 +128,6 @@ auto System::power() -> void {
dsp.power(); dsp.power();
ppu.power(); ppu.power();
if(expansionPort() == Device::ID::Satellaview) satellaview.power();
if(expansionPort() == Device::ID::SuperDisc) superdisc.power();
if(expansionPort() == Device::ID::S21FX) s21fx.power();
if(cartridge.hasICD2()) icd2.power(); if(cartridge.hasICD2()) icd2.power();
if(cartridge.hasMCC()) mcc.power(); if(cartridge.hasMCC()) mcc.power();
if(cartridge.hasNSSDIP()) nss.power(); if(cartridge.hasNSSDIP()) nss.power();
@ -179,10 +155,6 @@ auto System::reset() -> void {
dsp.reset(); dsp.reset();
ppu.reset(); ppu.reset();
if(expansionPort() == Device::ID::Satellaview) satellaview.reset();
if(expansionPort() == Device::ID::SuperDisc) superdisc.reset();
if(expansionPort() == Device::ID::S21FX) s21fx.reset();
if(cartridge.hasICD2()) icd2.reset(); if(cartridge.hasICD2()) icd2.reset();
if(cartridge.hasMCC()) mcc.reset(); if(cartridge.hasMCC()) mcc.reset();
if(cartridge.hasNSSDIP()) nss.reset(); if(cartridge.hasNSSDIP()) nss.reset();
@ -212,13 +184,9 @@ auto System::reset() -> void {
if(cartridge.hasSharpRTC()) cpu.coprocessors.append(&sharprtc); if(cartridge.hasSharpRTC()) cpu.coprocessors.append(&sharprtc);
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110); if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1); if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
if(expansionPort() == Device::ID::SuperDisc) cpu.coprocessors.append(&superdisc);
if(expansionPort() == Device::ID::S21FX) cpu.coprocessors.append(&s21fx);
scheduler.reset(); scheduler.reset();
device.connect(0, (Device::ID)settings.controllerPort1); peripherals.reset();
device.connect(1, (Device::ID)settings.controllerPort2);
device.connect(2, (Device::ID)settings.expansionPort);
} }
} }

View File

@ -1,13 +1,12 @@
struct Interface; struct Interface;
#include "device.hpp" #include "peripherals.hpp"
struct System { struct System {
enum class Region : bool { NTSC = 0, PAL = 1 }; enum class Region : bool { NTSC = 0, PAL = 1 };
auto loaded() const -> bool; auto loaded() const -> bool;
auto region() const -> Region; auto region() const -> Region;
auto expansionPort() const -> Device::ID;
auto cpuFrequency() const -> uint; auto cpuFrequency() const -> uint;
auto apuFrequency() const -> uint; auto apuFrequency() const -> uint;
@ -35,7 +34,6 @@ private:
bool _loaded = false; bool _loaded = false;
Region _region = Region::NTSC; Region _region = Region::NTSC;
Device::ID _expansionPort = Device::ID::None;
uint _cpuFrequency = 0; uint _cpuFrequency = 0;
uint _apuFrequency = 0; uint _apuFrequency = 0;
uint _serializeSize = 0; uint _serializeSize = 0;

View File

@ -20,10 +20,11 @@ auto Program::loadMedia(Emulator::Interface& interface, Emulator::Interface::Med
mediaPaths(media.id) = location; mediaPaths(media.id) = location;
folderPaths.append(location); folderPaths.append(location);
//note: the order of operations in this block of code is critical
emulator = &interface; emulator = &interface;
connectDevices(); //(expansion port) devices must be connected prior to load connectDevices();
emulator->load(media.id); emulator->load(media.id);
updateAudio(); //audio must be updated after load (audio frequency varies by region) updateAudio();
emulator->power(); emulator->power();
presentation->resizeViewport(); presentation->resizeViewport();

View File

@ -36,7 +36,9 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> str
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
execvp(name, (char* const*)argv); execvp(name, (char* const*)argv);
exit(0); //this is called only if execvp fails:
//use _exit instead of exit, to avoid destroying key shared file descriptors
_exit(0);
} else { } else {
close(fd[1]); close(fd[1]);