mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
7403e69307
commit
1929ad47d2
|
@ -7,7 +7,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -3,7 +3,7 @@ processors += r65816 spc700 arm gsu hg51b upd96050
|
|||
objects += sfc-interface sfc-system sfc-scheduler sfc-controller
|
||||
objects += sfc-cartridge sfc-cheat
|
||||
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-icd2 sfc-mcc sfc-nss sfc-event
|
||||
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-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-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/)
|
||||
|
|
|
@ -35,20 +35,6 @@ struct Cartridge : property<Cartridge> {
|
|||
MappedRAM rom;
|
||||
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 {
|
||||
unsigned id;
|
||||
string name;
|
||||
|
|
|
@ -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 document = BML::unserialize(markup);
|
||||
auto information = document["information"];
|
||||
|
@ -16,7 +6,6 @@ auto Cartridge::parseMarkup(const string& markup) -> void {
|
|||
this->information.title.cartridge = information["title"].text();
|
||||
_region = board["region"].text() == "pal" ? Region::PAL : Region::NTSC;
|
||||
|
||||
mapping.reset();
|
||||
if(auto node = board["rom"]) parseMarkupROM(node);
|
||||
if(auto node = board["ram"]) parseMarkupRAM(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 {
|
||||
Mapping m{memory};
|
||||
m.addr = map["address"].text();
|
||||
m.size = map["size"].natural();
|
||||
m.base = map["base"].natural();
|
||||
m.mask = map["mask"].natural();
|
||||
if(m.size == 0) m.size = memory.size();
|
||||
if(m.size != 0) mapping.append(m);
|
||||
auto addr = map["address"].text();
|
||||
auto size = map["size"].natural();
|
||||
auto base = map["base"].natural();
|
||||
auto mask = map["mask"].natural();
|
||||
if(size == 0) size = memory.size();
|
||||
if(size == 0) return;
|
||||
bus.map({&SuperFamicom::Memory::read, &memory}, {&SuperFamicom::Memory::write, &memory}, addr, size, base, mask);
|
||||
}
|
||||
|
||||
auto Cartridge::parseMarkupMap(
|
||||
|
@ -54,12 +43,11 @@ auto Cartridge::parseMarkupMap(
|
|||
const function<uint8 (uint24, uint8)>& reader,
|
||||
const function<void (uint24, uint8)>& writer
|
||||
) -> void {
|
||||
Mapping m{reader, writer};
|
||||
m.addr = map["address"].text();
|
||||
m.size = map["size"].natural();
|
||||
m.base = map["base"].natural();
|
||||
m.mask = map["mask"].natural();
|
||||
mapping.append(m);
|
||||
auto addr = map["address"].text();
|
||||
auto size = map["size"].natural();
|
||||
auto base = map["base"].natural();
|
||||
auto mask = map["mask"].natural();
|
||||
bus.map(reader, writer, addr, size, base, mask);
|
||||
}
|
||||
|
||||
auto Cartridge::parseMarkupMemory(MappedRAM& ram, Markup::Node node, uint id, bool writable) -> void {
|
||||
|
|
|
@ -15,8 +15,9 @@ Controller::Controller(bool port) : port(port) {
|
|||
|
||||
auto Controller::Enter() -> void {
|
||||
while(true) {
|
||||
if(co_active() == device.controllerPort1->thread) device.controllerPort1->main();
|
||||
if(co_active() == device.controllerPort2->thread) device.controllerPort2->main();
|
||||
scheduler.synchronize();
|
||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ Gamepad::Gamepad(bool port) : Controller(port) {
|
|||
|
||||
auto Gamepad::data() -> uint2 {
|
||||
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
|
||||
switch(counter++) {
|
||||
|
@ -36,7 +36,7 @@ auto Gamepad::latch(bool data) -> void {
|
|||
counter = 0;
|
||||
|
||||
if(latched == 0) {
|
||||
auto id = (uint)Device::ID::Gamepad;
|
||||
auto id = Device::Gamepad;
|
||||
b = interface->inputPoll(port, id, B);
|
||||
y = interface->inputPoll(port, id, Y);
|
||||
select = interface->inputPoll(port, id, Select);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Justifier::Justifier(bool port, bool chained):
|
||||
Controller(port),
|
||||
chained(chained),
|
||||
device(chained == false ? (unsigned)Device::ID::Justifier : (unsigned)Device::ID::Justifiers)
|
||||
device(chained == false ? Device::Justifier : Device::Justifiers)
|
||||
{
|
||||
create(Controller::Enter, 21477272);
|
||||
latched = 0;
|
||||
|
|
|
@ -64,10 +64,10 @@ auto Mouse::latch(bool data) -> void {
|
|||
latched = data;
|
||||
counter = 0;
|
||||
|
||||
x = interface->inputPoll(port, (unsigned)Device::ID::Mouse, X); //-n = left, 0 = center, +n = right
|
||||
y = interface->inputPoll(port, (unsigned)Device::ID::Mouse, Y); //-n = up, 0 = center, +n = down
|
||||
l = interface->inputPoll(port, (unsigned)Device::ID::Mouse, Left);
|
||||
r = interface->inputPoll(port, (unsigned)Device::ID::Mouse, Right);
|
||||
x = interface->inputPoll(port, Device::Mouse, X); //-n = left, 0 = center, +n = right
|
||||
y = interface->inputPoll(port, Device::Mouse, Y); //-n = up, 0 = center, +n = down
|
||||
l = interface->inputPoll(port, Device::Mouse, Left);
|
||||
r = interface->inputPoll(port, Device::Mouse, Right);
|
||||
|
||||
dx = x < 0; //0 = right, 1 = left
|
||||
dy = y < 0; //0 = down, 1 = up
|
||||
|
|
|
@ -24,8 +24,8 @@ auto Multitap::data() -> uint2 {
|
|||
port2 = 3; //controller 4
|
||||
}
|
||||
|
||||
bool data1 = interface->inputPoll(port, (unsigned)Device::ID::Multitap, port1 * 12 + index);
|
||||
bool data2 = interface->inputPoll(port, (unsigned)Device::ID::Multitap, port2 * 12 + index);
|
||||
bool data1 = interface->inputPoll(port, Device::Multitap, port1 * 12 + index);
|
||||
bool data2 = interface->inputPoll(port, Device::Multitap, port2 * 12 + index);
|
||||
return (data2 << 1) | (data1 << 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,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, (unsigned)Device::ID::SuperScope, X);
|
||||
int ny = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, Y);
|
||||
int nx = interface->inputPoll(port, Device::SuperScope, X);
|
||||
int ny = interface->inputPoll(port, Device::SuperScope, Y);
|
||||
nx += x;
|
||||
ny += y;
|
||||
x = max(-16, min(256 + 16, nx));
|
||||
|
@ -64,7 +64,7 @@ auto SuperScope::data() -> uint2 {
|
|||
|
||||
if(counter == 0) {
|
||||
//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) {
|
||||
turbo = !turbo; //toggle state
|
||||
turbolock = true;
|
||||
|
@ -75,7 +75,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, (unsigned)Device::ID::SuperScope, Trigger);
|
||||
bool newtrigger = interface->inputPoll(port, Device::SuperScope, Trigger);
|
||||
if(newtrigger && (turbo || !triggerlock)) {
|
||||
trigger = true;
|
||||
triggerlock = true;
|
||||
|
@ -84,11 +84,11 @@ auto SuperScope::data() -> uint2 {
|
|||
}
|
||||
|
||||
//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 = false;
|
||||
bool newpause = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, Pause);
|
||||
bool newpause = interface->inputPoll(port, Device::SuperScope, Pause);
|
||||
if(newpause && !pauselock) {
|
||||
pause = true;
|
||||
pauselock = true;
|
||||
|
|
|
@ -80,7 +80,7 @@ auto USART::data() -> uint2 {
|
|||
if(iobit()) {
|
||||
if(counter >= 16) return 1;
|
||||
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++;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -11,10 +11,6 @@ auto SDD1::init() -> void {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -26,6 +22,10 @@ auto SDD1::power() -> 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;
|
||||
xfer_enable = 0x00;
|
||||
dma_ready = false;
|
||||
|
|
|
@ -85,38 +85,6 @@ auto CPU::main() -> void {
|
|||
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 {
|
||||
for(auto& byte : wram) byte = random(0x55);
|
||||
|
||||
|
@ -155,6 +123,26 @@ auto CPU::reset() -> void {
|
|||
coprocessors.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
|
||||
regs.pc = 0x000000;
|
||||
regs.x.h = 0x00;
|
||||
|
|
|
@ -16,7 +16,6 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto enable() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
|
|
@ -7,14 +7,14 @@ auto CPU::stepAutoJoypadPoll() -> void {
|
|||
|
||||
if(status.auto_joypad_active && status.auto_joypad_latch) {
|
||||
if(status.auto_joypad_counter == 0) {
|
||||
device.controllerPort1->latch(1);
|
||||
device.controllerPort2->latch(1);
|
||||
device.controllerPort1->latch(0);
|
||||
device.controllerPort2->latch(0);
|
||||
SuperFamicom::peripherals.controllerPort1->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(1);
|
||||
SuperFamicom::peripherals.controllerPort1->latch(0);
|
||||
SuperFamicom::peripherals.controllerPort2->latch(0);
|
||||
}
|
||||
|
||||
uint2 port0 = device.controllerPort1->data();
|
||||
uint2 port1 = device.controllerPort2->data();
|
||||
uint2 port0 = SuperFamicom::peripherals.controllerPort1->data();
|
||||
uint2 port1 = SuperFamicom::peripherals.controllerPort2->data();
|
||||
|
||||
status.joy1 = status.joy1 << 1 | port0.bit(0);
|
||||
status.joy2 = status.joy2 << 1 | port1.bit(0);
|
||||
|
|
|
@ -16,7 +16,7 @@ auto CPU::cpuPortRead(uint24 addr, uint8 data) -> uint8 {
|
|||
//1-0 = Joypad serial data
|
||||
if(addr == 0x4016) {
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= device.controllerPort1->data();
|
||||
r |= SuperFamicom::peripherals.controllerPort1->data();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ auto CPU::cpuPortRead(uint24 addr, uint8 data) -> uint8 {
|
|||
//4-2 = Always 1 (pins are connected to GND)
|
||||
//1-0 = Joypad serial data
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= device.controllerPort2->data();
|
||||
r |= SuperFamicom::peripherals.controllerPort2->data();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -183,8 +183,8 @@ auto CPU::cpuPortWrite(uint24 addr, uint8 data) -> void {
|
|||
//bit 0 is shared between JOYSER0 and JOYSER1, therefore
|
||||
//strobing $4016.d0 affects both controller port latches.
|
||||
//$4017 bit 0 writes are ignored.
|
||||
device.controllerPort1->latch(data.bit(0));
|
||||
device.controllerPort2->latch(data.bit(0));
|
||||
SuperFamicom::peripherals.controllerPort1->latch(data.bit(0));
|
||||
SuperFamicom::peripherals.controllerPort2->latch(data.bit(0));
|
||||
}
|
||||
|
||||
//NMITIMEN
|
||||
|
|
|
@ -2,10 +2,56 @@
|
|||
|
||||
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 {
|
||||
while(true) scheduler.synchronize(), s21fx.main();
|
||||
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
|
||||
}
|
||||
|
||||
auto S21FX::main() -> void {
|
||||
|
@ -21,47 +67,6 @@ auto S21FX::main() -> void {
|
|||
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 {
|
||||
addr &= 0x40ffff;
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
struct S21FX : Cothread, Memory {
|
||||
struct S21FX : Expansion {
|
||||
S21FX();
|
||||
~S21FX();
|
||||
|
||||
static auto Enter() -> 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 write(uint24 addr, uint8 data) -> void;
|
||||
|
@ -36,5 +34,3 @@ private:
|
|||
vector<uint8> snesBuffer; //SNES -> Link
|
||||
vector<uint8> linkBuffer; //Link -> SNES
|
||||
};
|
||||
|
||||
extern S21FX s21fx;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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/superdisc/superdisc.hpp>
|
||||
#include <sfc/expansion/21fx/21fx.hpp>
|
||||
|
|
|
@ -2,30 +2,17 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
|
||||
Satellaview satellaview;
|
||||
|
||||
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 {
|
||||
Satellaview::Satellaview() {
|
||||
bus.map({&Satellaview::read, this}, {&Satellaview::write, this}, "00-3f,80-bf:2188-219f");
|
||||
memory::fill(®s, sizeof regs);
|
||||
}
|
||||
|
||||
auto Satellaview::read(uint24 addr, uint8 data) -> uint8 {
|
||||
addr &= 0xffff;
|
||||
Satellaview::~Satellaview() {
|
||||
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 0x2189: return regs.r2189;
|
||||
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 {
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
switch(addr &= 0xffff) {
|
||||
case 0x2188: {
|
||||
regs.r2188 = data;
|
||||
} break;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
struct Satellaview : Memory {
|
||||
auto init() -> void;
|
||||
auto load() -> void;
|
||||
auto unload() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
struct Satellaview : Expansion {
|
||||
Satellaview();
|
||||
~Satellaview();
|
||||
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
auto write(uint24 addr, uint8 data) -> void;
|
||||
|
@ -21,5 +18,3 @@ private:
|
|||
uint8 r2192_hour, r2192_minute, r2192_second;
|
||||
} regs;
|
||||
};
|
||||
|
||||
extern Satellaview satellaview;
|
||||
|
|
|
@ -2,12 +2,29 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
|
||||
SuperDisc superdisc;
|
||||
#include "nec.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 {
|
||||
while(true) scheduler.synchronize(), superdisc.main();
|
||||
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
|
||||
}
|
||||
|
||||
auto SuperDisc::main() -> void {
|
||||
|
@ -27,31 +44,6 @@ auto SuperDisc::main() -> void {
|
|||
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 {
|
||||
addr = 0x21e0 | (addr & 7);
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
struct SuperDisc : Cothread, Memory {
|
||||
struct SuperDisc : Expansion {
|
||||
SuperDisc();
|
||||
~SuperDisc();
|
||||
|
||||
static auto Enter() -> 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 write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
|
@ -39,5 +36,3 @@ private:
|
|||
uint8 data;
|
||||
} sony;
|
||||
};
|
||||
|
||||
extern SuperDisc superdisc;
|
||||
|
|
|
@ -425,7 +425,7 @@ auto Interface::unload() -> 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 {
|
||||
|
|
|
@ -4,63 +4,98 @@ namespace SuperFamicom {
|
|||
|
||||
Bus bus;
|
||||
|
||||
Bus::Bus() {
|
||||
lookup = new uint8 [16 * 1024 * 1024];
|
||||
target = new uint32[16 * 1024 * 1024];
|
||||
}
|
||||
|
||||
Bus::~Bus() {
|
||||
delete[] lookup;
|
||||
delete[] target;
|
||||
if(lookup) delete[] lookup;
|
||||
if(target) delete[] target;
|
||||
}
|
||||
|
||||
auto Bus::reset() -> void {
|
||||
function<auto (uint24, uint8) -> uint8> reader = [](uint24, uint8 data) { return data; };
|
||||
function<auto (uint24, uint8) -> void> writer = [](uint24, uint8) {};
|
||||
for(auto id : range(256)) {
|
||||
reader[id].reset();
|
||||
writer[id].reset();
|
||||
counter[id] = 0;
|
||||
}
|
||||
|
||||
idcount = 0;
|
||||
map(reader, writer, 0x00, 0xff, 0x0000, 0xffff);
|
||||
}
|
||||
if(lookup) delete[] lookup;
|
||||
if(target) delete[] target;
|
||||
|
||||
auto Bus::map() -> void {
|
||||
for(auto& m : cartridge.mapping) {
|
||||
lstring part = m.addr.split(":", 1L);
|
||||
lstring banks = part(0).split(",");
|
||||
lstring addrs = part(1).split(",");
|
||||
for(auto& bank : banks) {
|
||||
for(auto& addr : addrs) {
|
||||
lstring bankpart = bank.split("-", 1L);
|
||||
lstring addrpart = addr.split("-", 1L);
|
||||
uint banklo = hex(bankpart(0));
|
||||
uint bankhi = hex(bankpart(1, bankpart(0)));
|
||||
uint addrlo = hex(addrpart(0));
|
||||
uint addrhi = hex(addrpart(1, addrpart(0)));
|
||||
map(m.reader, m.writer, banklo, bankhi, addrlo, addrhi, m.size, m.base, m.mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
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(
|
||||
const function<uint8 (uint24, uint8)>& reader,
|
||||
const function<void (uint24, uint8)>& writer,
|
||||
uint8 banklo, uint8 bankhi, uint16 addrlo, uint16 addrhi,
|
||||
uint size, uint base, uint mask
|
||||
const function<uint8 (uint24, uint8)>& read,
|
||||
const function<void (uint24, uint8)>& write,
|
||||
const string& addr, uint size, uint base, uint mask
|
||||
) -> void {
|
||||
assert(banklo <= bankhi);
|
||||
assert(addrlo <= addrhi);
|
||||
assert(idcount < 255);
|
||||
uint id = 1;
|
||||
while(counter[id]) {
|
||||
if(++id >= 256) return print("SFC error: bus map exhausted\n");
|
||||
}
|
||||
//print("map[", hex(id, 2), "] => ", addr, "\n");
|
||||
|
||||
uint id = idcount++;
|
||||
this->reader[id] = reader;
|
||||
this->writer[id] = writer;
|
||||
reader[id] = read;
|
||||
writer[id] = write;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
for(uint bank = banklo; bank <= bankhi; bank++) {
|
||||
for(uint addr = addrlo; addr <= addrhi; addr++) {
|
||||
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::unmap(const string& addr) -> void {
|
||||
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(1)));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
lookup[bank << 16 | addr] = 0;
|
||||
target[bank << 16 | addr] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,27 +45,26 @@ struct Bus {
|
|||
alwaysinline static auto mirror(uint addr, uint size) -> uint;
|
||||
alwaysinline static auto reduce(uint addr, uint mask) -> uint;
|
||||
|
||||
Bus();
|
||||
~Bus();
|
||||
|
||||
alwaysinline auto read(uint24 addr, uint8 data) -> uint8;
|
||||
alwaysinline auto write(uint24 addr, uint8 data) -> void;
|
||||
|
||||
auto reset() -> void;
|
||||
auto map() -> void;
|
||||
auto map(
|
||||
const function<uint8 (uint24, uint8)>& reader,
|
||||
const function<void (uint24, uint8)>& writer,
|
||||
uint8 banklo, uint8 bankhi, uint16 addrlo, uint16 addrhi,
|
||||
uint size = 0, uint base = 0, uint mask = 0
|
||||
const function<uint8 (uint24, uint8)>& read,
|
||||
const function<void (uint24, uint8)>& write,
|
||||
const string& addr, uint size = 0, uint base = 0, uint mask = 0
|
||||
) -> void;
|
||||
auto unmap(const string& addr) -> void;
|
||||
|
||||
private:
|
||||
uint8* lookup = nullptr;
|
||||
uint32* target = nullptr;
|
||||
|
||||
uint idcount = 0;
|
||||
function<auto (uint24, uint8) -> uint8> reader[256];
|
||||
function<auto (uint24, uint8) -> void> writer[256];
|
||||
uint24 counter[256];
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
|
|
|
@ -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 {
|
||||
for(auto& n : vram) n = random(0x00);
|
||||
for(auto& n : oam) n = random(0x00);
|
||||
|
@ -105,6 +97,10 @@ auto PPU::reset() -> void {
|
|||
PPUcounter::reset();
|
||||
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.ppu2_mdr = random(0xff);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ struct PPU : Thread, PPUcounter {
|
|||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto enable() -> void;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
|
|
@ -144,17 +144,17 @@ auto Video::drawCursor(uint32 color, int x, int y) -> void {
|
|||
}
|
||||
|
||||
auto Video::drawCursors() -> void {
|
||||
switch((Device::ID)settings.controllerPort2) {
|
||||
case Device::ID::SuperScope:
|
||||
if(dynamic_cast<SuperScope*>(device.controllerPort2)) {
|
||||
auto& controller = (SuperScope&)*device.controllerPort2;
|
||||
switch(settings.controllerPort2) {
|
||||
case Device::SuperScope:
|
||||
if(dynamic_cast<SuperScope*>(peripherals.controllerPort2)) {
|
||||
auto& controller = (SuperScope&)*peripherals.controllerPort2;
|
||||
drawCursor(0xff0000ff, controller.x, controller.y);
|
||||
}
|
||||
break;
|
||||
case Device::ID::Justifier:
|
||||
case Device::ID::Justifiers:
|
||||
if(dynamic_cast<Justifier*>(device.controllerPort2)) {
|
||||
auto& controller = (Justifier&)*device.controllerPort2;
|
||||
case Device::Justifier:
|
||||
case Device::Justifiers:
|
||||
if(dynamic_cast<Justifier*>(peripherals.controllerPort2)) {
|
||||
auto& controller = (Justifier&)*peripherals.controllerPort2;
|
||||
drawCursor(0xffff0000, controller.player1.x, controller.player1.y);
|
||||
if(!controller.chained) break;
|
||||
drawCursor(0xff00bf00, controller.player2.x, controller.player2.y);
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace SuperFamicom {
|
|||
|
||||
namespace SuperFamicom {
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
virtual ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
}
|
||||
|
||||
|
@ -61,10 +61,10 @@ namespace SuperFamicom {
|
|||
#include <sfc/ppu/ppu.hpp>
|
||||
|
||||
#include <sfc/controller/controller.hpp>
|
||||
#include <sfc/expansion/expansion.hpp>
|
||||
#include <sfc/system/system.hpp>
|
||||
#include <sfc/scheduler/scheduler.hpp>
|
||||
#include <sfc/coprocessor/coprocessor.hpp>
|
||||
#include <sfc/expansion/expansion.hpp>
|
||||
#include <sfc/slot/slot.hpp>
|
||||
#include <sfc/cartridge/cartridge.hpp>
|
||||
#include <sfc/cheat/cheat.hpp>
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -39,7 +39,6 @@ auto System::unserialize(serializer& s) -> bool {
|
|||
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
s.integer((uint&)_region);
|
||||
s.integer((uint&)_expansionPort);
|
||||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
|
|
|
@ -4,13 +4,12 @@ namespace SuperFamicom {
|
|||
|
||||
System system;
|
||||
|
||||
#include "device.cpp"
|
||||
#include "peripherals.cpp"
|
||||
#include "random.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::loaded() const -> bool { return _loaded; }
|
||||
auto System::region() const -> Region { return _region; }
|
||||
auto System::expansionPort() const -> Device::ID { return _expansionPort; }
|
||||
auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
|
||||
auto System::apuFrequency() const -> uint { return _apuFrequency; }
|
||||
|
||||
|
@ -34,10 +33,6 @@ auto System::runToSave() -> void {
|
|||
auto System::init() -> void {
|
||||
assert(interface != nullptr);
|
||||
|
||||
satellaview.init();
|
||||
superdisc.init();
|
||||
s21fx.init();
|
||||
|
||||
icd2.init();
|
||||
mcc.init();
|
||||
nss.init();
|
||||
|
@ -55,16 +50,14 @@ auto System::init() -> void {
|
|||
msu1.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::load() -> void {
|
||||
bus.reset();
|
||||
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
||||
|
@ -74,20 +67,9 @@ auto System::load() -> void {
|
|||
|
||||
cartridge.load();
|
||||
_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;
|
||||
_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.hasMCC()) mcc.load();
|
||||
if(cartridge.hasNSSDIP()) nss.load();
|
||||
|
@ -113,9 +95,7 @@ auto System::load() -> void {
|
|||
|
||||
auto System::unload() -> void {
|
||||
if(!loaded()) return;
|
||||
if(expansionPort() == Device::ID::Satellaview) satellaview.unload();
|
||||
if(expansionPort() == Device::ID::SuperDisc) superdisc.unload();
|
||||
if(expansionPort() == Device::ID::S21FX) s21fx.unload();
|
||||
peripherals.unload();
|
||||
|
||||
if(cartridge.hasICD2()) icd2.unload();
|
||||
if(cartridge.hasMCC()) mcc.unload();
|
||||
|
@ -148,10 +128,6 @@ auto System::power() -> void {
|
|||
dsp.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.hasMCC()) mcc.power();
|
||||
if(cartridge.hasNSSDIP()) nss.power();
|
||||
|
@ -179,10 +155,6 @@ auto System::reset() -> void {
|
|||
dsp.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.hasMCC()) mcc.reset();
|
||||
if(cartridge.hasNSSDIP()) nss.reset();
|
||||
|
@ -212,13 +184,9 @@ auto System::reset() -> void {
|
|||
if(cartridge.hasSharpRTC()) cpu.coprocessors.append(&sharprtc);
|
||||
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
|
||||
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();
|
||||
device.connect(0, (Device::ID)settings.controllerPort1);
|
||||
device.connect(1, (Device::ID)settings.controllerPort2);
|
||||
device.connect(2, (Device::ID)settings.expansionPort);
|
||||
peripherals.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
struct Interface;
|
||||
|
||||
#include "device.hpp"
|
||||
#include "peripherals.hpp"
|
||||
|
||||
struct System {
|
||||
enum class Region : bool { NTSC = 0, PAL = 1 };
|
||||
|
||||
auto loaded() const -> bool;
|
||||
auto region() const -> Region;
|
||||
auto expansionPort() const -> Device::ID;
|
||||
auto cpuFrequency() const -> uint;
|
||||
auto apuFrequency() const -> uint;
|
||||
|
||||
|
@ -35,7 +34,6 @@ private:
|
|||
|
||||
bool _loaded = false;
|
||||
Region _region = Region::NTSC;
|
||||
Device::ID _expansionPort = Device::ID::None;
|
||||
uint _cpuFrequency = 0;
|
||||
uint _apuFrequency = 0;
|
||||
uint _serializeSize = 0;
|
||||
|
|
|
@ -20,10 +20,11 @@ auto Program::loadMedia(Emulator::Interface& interface, Emulator::Interface::Med
|
|||
mediaPaths(media.id) = location;
|
||||
folderPaths.append(location);
|
||||
|
||||
//note: the order of operations in this block of code is critical
|
||||
emulator = &interface;
|
||||
connectDevices(); //(expansion port) devices must be connected prior to load
|
||||
connectDevices();
|
||||
emulator->load(media.id);
|
||||
updateAudio(); //audio must be updated after load (audio frequency varies by region)
|
||||
updateAudio();
|
||||
emulator->power();
|
||||
|
||||
presentation->resizeViewport();
|
||||
|
|
|
@ -36,7 +36,9 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> str
|
|||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
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 {
|
||||
close(fd[1]);
|
||||
|
||||
|
|
Loading…
Reference in New Issue