Update to v103r04 release.

byuu says:

Changelog:

  - fc/apu: $4003,$4007 writes initialize duty counter to 0 instead of 7
  - fc/apu: corrected duty table entries for use with decrementing duty
    counter
  - processor/spc700: emulated the behavior of cycle 3 of (x)+
    instructions to not read I/O registers
      - specifically, this prevents reads from $fd-ff from resetting the
        timers, as observed on real hardware
  - sfc/controller: added ControllerPort class to match Mega Drive
    design
  - sfc/expansion: added ExpansionPort class to match Mega Drive design
  - sfc/system: removed Peripherals class
  - sfc/system: changed `colorburst()` to `cpuFrequency()`; added
    `apuFrequency()`
  - sfc: replaced calls to `system.region == System::Region::*` with
    `Region::*()`
  - sfc/expansion: remove thread from scheduler when device is destroyed
  - sfc/smp: `{read,write}Port` now use a separate 4x8-bit buffer instead
    of underlying APU RAM [hex\_usr]
This commit is contained in:
Tim Allen 2017-06-30 14:17:23 +10:00
parent 78f341489e
commit ff3750de4f
34 changed files with 210 additions and 164 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "103.03";
static const string Version = "103.04";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -153,7 +153,7 @@ auto APU::writeIO(uint16 addr, uint8 data) -> void {
pulse[n].period = (pulse[n].period & 0x00ff) | (data << 8);
pulse[n].sweep.pulsePeriod = (pulse[n].sweep.pulsePeriod & 0x00ff) | (data << 8);
pulse[n].dutyCounter = 7;
pulse[n].dutyCounter = 0;
pulse[n].envelope.reloadDecay = true;
if(enabledChannels & (1 << n)) {

View File

@ -9,10 +9,10 @@ auto APU::Pulse::clock() -> uint8 {
if(lengthCounter == 0) return 0;
static const uint dutyTable[4][8] = {
{0, 1, 0, 0, 0, 0, 0, 0}, //12.5%
{0, 1, 1, 0, 0, 0, 0, 0}, //25.0%
{0, 1, 1, 1, 1, 0, 0, 0}, //50.0%
{1, 0, 0, 1, 1, 1, 1, 1}, //25.0% (inverted)
{0, 0, 0, 0, 0, 0, 0, 1}, //12.5%
{0, 0, 0, 0, 0, 0, 1, 1}, //25.0%
{0, 0, 0, 0, 1, 1, 1, 1}, //50.0%
{1, 1, 1, 1, 1, 1, 0, 0}, //25.0% (negated)
};
uint8 result = dutyTable[duty][dutyCounter] ? envelope.volume() : 0;
if(sweep.pulsePeriod < 0x008) result = 0;

View File

@ -73,9 +73,9 @@ auto BusCPU::readIO(uint24 addr) -> uint16 {
| 0 << 0 //0 = Model 1; 1 = Model 2+
);
case 0xa10002: return controllerPort1.readData();
case 0xa10004: return controllerPort2.readData();
case 0xa10006: return extensionPort.readData();
case 0xa10002: return controllerPort1.device->readData();
case 0xa10004: return controllerPort2.device->readData();
case 0xa10006: return extensionPort.device->readData();
case 0xa10008: return controllerPort1.readControl();
case 0xa1000a: return controllerPort2.readControl();
@ -89,9 +89,9 @@ auto BusCPU::readIO(uint24 addr) -> uint16 {
auto BusCPU::writeIO(uint24 addr, uint16 data) -> void {
switch(addr & ~1) {
case 0xa10002: return controllerPort1.writeData(data);
case 0xa10004: return controllerPort2.writeData(data);
case 0xa10006: return extensionPort.writeData(data);
case 0xa10002: return controllerPort1.device->writeData(data);
case 0xa10004: return controllerPort2.device->writeData(data);
case 0xa10006: return extensionPort.device->writeData(data);
case 0xa10008: return controllerPort1.writeControl(data);
case 0xa1000a: return controllerPort2.writeControl(data);

View File

@ -8,7 +8,7 @@ ControllerPort extensionPort;
#include "gamepad/gamepad.cpp"
Controller::Controller(uint port) : port(port) {
if(!handle()) create(Controller::Enter, 100);
if(!handle()) create(Controller::Enter, 1);
}
Controller::~Controller() {
@ -18,9 +18,9 @@ Controller::~Controller() {
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(controllerPort1.controller->active()) controllerPort1.controller->main();
if(controllerPort2.controller->active()) controllerPort2.controller->main();
if(extensionPort.controller->active()) extensionPort.controller->main();
if(controllerPort1.device->active()) controllerPort1.device->main();
if(controllerPort2.device->active()) controllerPort2.device->main();
if(extensionPort.device->active()) extensionPort.device->main();
}
}
@ -31,27 +31,19 @@ auto Controller::main() -> void {
//
auto ControllerPort::connect(uint device) -> void {
auto ControllerPort::connect(uint deviceID) -> void {
if(!system.loaded()) return;
delete controller;
delete device;
switch(device) { default:
case ID::Device::None: controller = new Controller(port); break;
case ID::Device::Gamepad: controller = new Gamepad(port); break;
switch(deviceID) { default:
case ID::Device::None: device = new Controller(port); break;
case ID::Device::Gamepad: device = new Gamepad(port); break;
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort1.controller);
cpu.peripherals.append(controllerPort2.controller);
cpu.peripherals.append(extensionPort.controller);
}
auto ControllerPort::readData() -> uint8 {
return controller->readData();
}
auto ControllerPort::writeData(uint8 data) -> void {
return controller->writeData(data);
if(auto device = controllerPort1.device) cpu.peripherals.append(device);
if(auto device = controllerPort2.device) cpu.peripherals.append(device);
if(auto device = extensionPort.device) cpu.peripherals.append(device);
}
auto ControllerPort::readControl() -> uint8 {
@ -68,8 +60,8 @@ auto ControllerPort::power(uint port) -> void {
}
auto ControllerPort::unload() -> void {
delete controller;
controller = nullptr;
delete device;
device = nullptr;
}
auto ControllerPort::serialize(serializer& s) -> void {

View File

@ -12,10 +12,7 @@ struct Controller : Thread {
};
struct ControllerPort {
auto connect(uint device) -> void;
auto readData() -> uint8;
auto writeData(uint8 data) -> void;
auto connect(uint deviceID) -> void;
auto readControl() -> uint8;
auto writeControl(uint8 data) -> void;
@ -26,7 +23,7 @@ struct ControllerPort {
uint port;
uint8 control;
Controller* controller = nullptr;
Controller* device = nullptr;
};
extern ControllerPort controllerPort1;

View File

@ -52,6 +52,7 @@ auto System::save() -> void {
}
auto System::unload() -> void {
cpu.peripherals.reset();
controllerPort1.unload();
controllerPort2.unload();
extensionPort.unload();

View File

@ -413,15 +413,15 @@ auto SPC700::instructionIndirectXWrite(uint8& data) -> void {
auto SPC700::instructionIndirectXIncrementRead(uint8& data) -> void {
idle(PC);
data = read(page(X)); //todo: $f0-ff not accessible on this cycle?
idle(page(X++));
idle(); //quirk: does not read internal SMP registers
data = read(page(X++));
ZF = data == 0;
NF = data & 0x80;
}
auto SPC700::instructionIndirectXIncrementWrite(uint8& data) -> void {
idle(PC);
idle(page(X)); //todo: $f0-ff not accessible on this cycle?
idle(); //quirk: does not read internal SMP registers
write(page(X++), data);
}

View File

@ -3,6 +3,7 @@
namespace Processor {
struct SPC700 {
virtual auto idle() -> void = 0;
virtual auto read(uint16 address) -> uint8 = 0;
virtual auto write(uint16 addessr, uint8 data) -> void = 0;
virtual auto synchronizing() const -> bool = 0;

View File

@ -2,6 +2,8 @@
namespace SuperFamicom {
ControllerPort controllerPort1;
ControllerPort controllerPort2;
#include "gamepad/gamepad.cpp"
#include "mouse/mouse.cpp"
#include "super-multitap/super-multitap.cpp"
@ -19,8 +21,8 @@ Controller::~Controller() {
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(peripherals.controllerPort1->active()) peripherals.controllerPort1->main();
if(peripherals.controllerPort2->active()) peripherals.controllerPort2->main();
if(controllerPort1.device->active()) controllerPort1.device->main();
if(controllerPort2.device->active()) controllerPort2.device->main();
}
}
@ -43,4 +45,38 @@ auto Controller::iobit(bool data) -> void {
}
}
//
auto ControllerPort::connect(uint deviceID) -> void {
if(!system.loaded()) return;
delete device;
switch(deviceID) { default:
case ID::Device::None: device = new Controller(port); break;
case ID::Device::Gamepad: device = new Gamepad(port); break;
case ID::Device::Mouse: device = new Mouse(port); break;
case ID::Device::SuperMultitap: device = new SuperMultitap(port); break;
case ID::Device::SuperScope: device = new SuperScope(port); break;
case ID::Device::Justifier: device = new Justifier(port, false); break;
case ID::Device::Justifiers: device = new Justifier(port, true); break;
}
cpu.peripherals.reset();
if(auto device = controllerPort1.device) cpu.peripherals.append(device);
if(auto device = controllerPort2.device) cpu.peripherals.append(device);
if(auto device = expansionPort.device) cpu.peripherals.append(device);
}
auto ControllerPort::power(uint port) -> void {
this->port = port;
}
auto ControllerPort::unload() -> void {
delete device;
device = nullptr;
}
auto ControllerPort::serialize(serializer& s) -> void {
}
}

View File

@ -27,6 +27,20 @@ struct Controller : Thread {
const bool port;
};
struct ControllerPort {
auto connect(uint deviceID) -> void;
auto power(uint port) -> void;
auto unload() -> void;
auto serialize(serializer&) -> void;
uint port;
Controller* device = nullptr;
};
extern ControllerPort controllerPort1;
extern ControllerPort controllerPort2;
#include "gamepad/gamepad.hpp"
#include "mouse/mouse.hpp"
#include "super-multitap/super-multitap.hpp"

View File

@ -46,7 +46,7 @@ auto ICD2::unload() -> void {
}
auto ICD2::power() -> void {
create(ICD2::Enter, system.colorburst() * 6.0 / 5.0);
create(ICD2::Enter, system.cpuFrequency() / 5.0);
stream = Emulator::audio.createStream(2, frequency() / 2.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);
@ -76,8 +76,7 @@ auto ICD2::power() -> void {
}
auto ICD2::reset() -> void {
auto frequency = system.colorburst() * 6.0;
create(ICD2::Enter, frequency / 5);
create(ICD2::Enter, system.cpuFrequency() / 5.0);
r6003 = 0x00;
r6004 = 0xff;

View File

@ -56,7 +56,7 @@ auto ICD2::writeIO(uint24 addr, uint8 data) -> void {
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
reset();
}
auto frequency = system.colorburst() * 6.0;
auto frequency = system.cpuFrequency();
switch(data & 3) {
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
case 1: setFrequency(frequency / 5); break; //normal

View File

@ -131,7 +131,7 @@ auto SA1::unload() -> void {
auto SA1::power() -> void {
WDC65816::power();
create(SA1::Enter, system.colorburst() * 6.0);
create(SA1::Enter, system.cpuFrequency());
cpubwram.dma = false;
for(auto addr : range(iram.size())) {
@ -142,7 +142,7 @@ auto SA1::power() -> void {
status.interruptPending = false;
status.scanlines = (system.region() == System::Region::NTSC ? 262 : 312);
status.scanlines = Region::PAL() ? 312 : 262;
status.vcounter = 0;
status.hcounter = 0;

View File

@ -44,7 +44,7 @@ auto SuperFX::unload() -> void {
auto SuperFX::power() -> void {
GSU::power();
create(SuperFX::Enter, system.colorburst() * 6.0);
create(SuperFX::Enter, system.cpuFrequency());
romMask = rom.size() - 1;
ramMask = ram.size() - 1;

View File

@ -62,7 +62,7 @@ auto CPU::load(Markup::Node node) -> bool {
auto CPU::power() -> void {
WDC65816::power();
create(Enter, system.colorburst() * 6.0);
create(Enter, system.cpuFrequency());
coprocessors.reset();
PPUcounter::reset();

View File

@ -16,7 +16,7 @@ auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
//1-0 = Joypad serial data
case 0x4016: {
uint8 v = r.mdr & 0xfc;
v |= SuperFamicom::peripherals.controllerPort1->data();
v |= controllerPort1.device->data();
return v;
}
@ -26,7 +26,7 @@ auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
//4-2 = Always 1 (pins are connected to GND)
//1-0 = Joypad serial data
uint8 v = (r.mdr & 0xe0) | 0x1c;
v |= SuperFamicom::peripherals.controllerPort2->data();
v |= controllerPort2.device->data();
return v;
}
@ -178,8 +178,8 @@ auto CPU::writeCPU(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.
SuperFamicom::peripherals.controllerPort1->latch(data.bit(0));
SuperFamicom::peripherals.controllerPort2->latch(data.bit(0));
controllerPort1.device->latch(data.bit(0));
controllerPort2.device->latch(data.bit(0));
return;
}

View File

@ -145,14 +145,14 @@ auto CPU::joypadEdge() -> void {
if(status.autoJoypadActive && status.autoJoypadLatch) {
if(status.autoJoypadCounter == 0) {
SuperFamicom::peripherals.controllerPort1->latch(1);
SuperFamicom::peripherals.controllerPort2->latch(1);
SuperFamicom::peripherals.controllerPort1->latch(0);
SuperFamicom::peripherals.controllerPort2->latch(0);
controllerPort1.device->latch(1);
controllerPort2.device->latch(1);
controllerPort1.device->latch(0);
controllerPort2.device->latch(0);
}
uint2 port0 = SuperFamicom::peripherals.controllerPort1->data();
uint2 port1 = SuperFamicom::peripherals.controllerPort2->data();
uint2 port0 = controllerPort1.device->data();
uint2 port1 = controllerPort2.device->data();
io.joy1 = io.joy1 << 1 | port0.bit(0);
io.joy2 = io.joy2 << 1 | port1.bit(0);

View File

@ -229,7 +229,7 @@ auto DSP::load(Markup::Node node) -> bool {
}
auto DSP::power() -> void {
create(Enter, 32040.0 * 768.0);
create(Enter, system.apuFrequency());
stream = Emulator::audio.createStream(2, frequency() / 768.0);
memory::fill(&state, sizeof(State));

View File

@ -52,7 +52,7 @@ S21FX::~S21FX() {
}
auto S21FX::Enter() -> void {
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
while(true) scheduler.synchronize(), expansionPort.device->main();
}
auto S21FX::step(uint clocks) -> void {

View File

@ -2,12 +2,18 @@
namespace SuperFamicom {
ExpansionPort expansionPort;
Expansion::Expansion() {
if(!handle()) create(Expansion::Enter, 1);
}
Expansion::~Expansion() {
scheduler.remove(*this);
}
auto Expansion::Enter() -> void {
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
while(true) scheduler.synchronize(), expansionPort.device->main();
}
auto Expansion::main() -> void {
@ -15,4 +21,33 @@ auto Expansion::main() -> void {
synchronize(cpu);
}
//
auto ExpansionPort::connect(uint deviceID) -> void {
if(!system.loaded()) return;
delete device;
switch(deviceID) { default:
case ID::Device::None: device = new Expansion; break;
case ID::Device::Satellaview: device = new Satellaview; break;
case ID::Device::S21FX: device = new S21FX; break;
}
cpu.peripherals.reset();
if(auto device = controllerPort1.device) cpu.peripherals.append(device);
if(auto device = controllerPort2.device) cpu.peripherals.append(device);
if(auto device = expansionPort.device) cpu.peripherals.append(device);
}
auto ExpansionPort::power() -> void {
}
auto ExpansionPort::unload() -> void {
delete device;
device = nullptr;
}
auto ExpansionPort::serialize(serializer& s) -> void {
}
}

View File

@ -1,8 +1,21 @@
struct Expansion : Thread {
Expansion();
virtual ~Expansion();
static auto Enter() -> void;
virtual auto main() -> void;
};
struct ExpansionPort {
auto connect(uint deviceID) -> void;
auto power() -> void;
auto unload() -> void;
auto serialize(serializer&) -> void;
Expansion* device = nullptr;
};
extern ExpansionPort expansionPort;
#include <sfc/expansion/satellaview/satellaview.hpp>
#include <sfc/expansion/21fx/21fx.hpp>

View File

@ -187,7 +187,9 @@ auto Interface::unload() -> void {
}
auto Interface::connect(uint port, uint device) -> void {
peripherals.connect(port, device);
if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device);
if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device);
if(port == ID::Port::Expansion) expansionPort.connect(settings.expansionPort = device);
}
auto Interface::power() -> void {

View File

@ -27,12 +27,12 @@ auto PPUcounter::tick(uint clocks) -> void {
auto PPUcounter::vcounterTick() -> void {
if(++status.vcounter == 128) status.interlace = ppu.interlace();
if((system.region() == System::Region::NTSC && status.interlace == 0 && status.vcounter == 262)
|| (system.region() == System::Region::NTSC && status.interlace == 1 && status.vcounter == 263)
|| (system.region() == System::Region::NTSC && status.interlace == 1 && status.vcounter == 262 && status.field == 1)
|| (system.region() == System::Region::PAL && status.interlace == 0 && status.vcounter == 312)
|| (system.region() == System::Region::PAL && status.interlace == 1 && status.vcounter == 313)
|| (system.region() == System::Region::PAL && status.interlace == 1 && status.vcounter == 312 && status.field == 1)
if((Region::NTSC() && status.interlace == 0 && status.vcounter == 262)
|| (Region::NTSC() && status.interlace == 1 && status.vcounter == 263)
|| (Region::NTSC() && status.interlace == 1 && status.vcounter == 262 && status.field == 1)
|| (Region::PAL() && status.interlace == 0 && status.vcounter == 312)
|| (Region::PAL() && status.interlace == 1 && status.vcounter == 313)
|| (Region::PAL() && status.interlace == 1 && status.vcounter == 312 && status.field == 1)
) {
status.vcounter = 0;
status.field = !status.field;
@ -58,7 +58,7 @@ auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter
//dot 327 range = {1310, 1312, 1314}
auto PPUcounter::hdot() const -> uint16 {
if(system.region() == System::Region::NTSC && status.interlace == 0 && vcounter() == 240 && field() == 1) {
if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) {
return (hcounter() >> 2);
} else {
return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2;
@ -66,7 +66,7 @@ auto PPUcounter::hdot() const -> uint16 {
}
auto PPUcounter::lineclocks() const -> uint16 {
if(system.region() == System::Region::NTSC && status.interlace == 0 && vcounter() == 240 && field() == 1) return 1360;
if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) return 1360;
return 1364;
}

View File

@ -156,7 +156,7 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
latch.vcounter = 0;
ppu2.mdr.bits(0,3) = ppu2.version;
ppu2.mdr.bit ( 4) = system.region() == System::Region::PAL; //0 = NTSC
ppu2.mdr.bit ( 4) = Region::PAL(); //0 = NTSC, 1 = PAL
if(!cpu.pio().bit(7)) {
ppu2.mdr.bit( 6) = 1;
} else {

View File

@ -87,7 +87,7 @@ auto PPU::load(Markup::Node node) -> bool {
}
auto PPU::power() -> void {
create(Enter, system.colorburst() * 6.0);
create(Enter, system.cpuFrequency());
PPUcounter::reset();
memory::fill(output, 512 * 480 * sizeof(uint32));

View File

@ -10,11 +10,11 @@ alwaysinline auto SMP::writeRAM(uint16 addr, uint8 data) -> void {
}
auto SMP::readPort(uint2 port) const -> uint8 {
return apuram[0xf4 + port];
return io.port[0xf4 + port];
}
auto SMP::writePort(uint2 port, uint8 data) -> void {
apuram[0xf4 + port] = data;
io.port[0xf4 + port] = data;
}
auto SMP::readBus(uint16 addr) -> uint8 {
@ -173,6 +173,11 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
writeRAM(addr, data); //all writes, even to MMIO registers, appear on bus
}
auto SMP::idle() -> void {
step(24);
cycleEdge();
}
auto SMP::read(uint16 addr) -> uint8 {
step(12);
uint8 data = readBus(addr);

View File

@ -19,6 +19,8 @@ auto SMP::serialize(serializer& s) -> void {
s.integer(io.dspAddr);
s.array(io.port);
s.integer(io.ram00f8);
s.integer(io.ram00f9);

View File

@ -34,16 +34,12 @@ auto SMP::load(Markup::Node node) -> bool {
auto SMP::power() -> void {
SPC700::power();
create(Enter, 32040.0 * 768.0);
create(Enter, system.apuFrequency());
r.pc.byte.l = iplrom[62];
r.pc.byte.h = iplrom[63];
for(auto& byte : apuram) byte = random(0x00);
apuram[0x00f4] = 0x00;
apuram[0x00f5] = 0x00;
apuram[0x00f6] = 0x00;
apuram[0x00f7] = 0x00;
io.clockCounter = 0;
io.dspCounter = 0;
@ -63,6 +59,12 @@ auto SMP::power() -> void {
//$00f2
io.dspAddr = 0x00;
//$00f4-00f7
io.port[0] = 0x00;
io.port[1] = 0x00;
io.port[2] = 0x00;
io.port[3] = 0x00;
//$00f8,$00f9
io.ram00f8 = 0x00;
io.ram00f9 = 0x00;

View File

@ -38,6 +38,9 @@ private:
//$00f2
uint8 dspAddr;
//$00f4-00f7
uint8 port[4];
//$00f8,$00f9
uint8 ram00f8;
uint8 ram00f9;
@ -52,6 +55,7 @@ private:
auto readBus(uint16 addr) -> uint8;
auto writeBus(uint16 addr, uint8 data) -> void;
auto idle() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;

View File

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

View File

@ -66,6 +66,10 @@ auto System::serializeAll(serializer& s) -> void {
if(cartridge.has.MSU1) msu1.serialize(s);
if(cartridge.has.SufamiTurboSlots) sufamiturboA.serialize(s), sufamiturboB.serialize(s);
controllerPort1.serialize(s);
controllerPort2.serialize(s);
expansionPort.serialize(s);
}
//perform dry-run state save:

View File

@ -6,7 +6,6 @@ System system;
Scheduler scheduler;
Cheat cheat;
#include "video.cpp"
#include "peripherals.cpp"
#include "random.cpp"
#include "serialization.cpp"
@ -65,11 +64,11 @@ auto System::load(Emulator::Interface* interface) -> bool {
if(cartridge.region() == "NTSC") {
information.region = Region::NTSC;
information.colorburst = Emulator::Constants::Colorburst::NTSC;
information.cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0;
}
if(cartridge.region() == "PAL") {
information.region = Region::PAL;
information.colorburst = Emulator::Constants::Colorburst::PAL * 4.0 / 5.0;
information.cpuFrequency = Emulator::Constants::Colorburst::PAL * 4.8;
}
if(cartridge.has.ICD2) icd2.load();
@ -98,12 +97,17 @@ auto System::load(Emulator::Interface* interface) -> bool {
auto System::save() -> void {
if(!loaded()) return;
cartridge.save();
}
auto System::unload() -> void {
if(!loaded()) return;
peripherals.unload();
cpu.peripherals.reset();
controllerPort1.unload();
controllerPort2.unload();
expansionPort.unload();
if(cartridge.has.ICD2) icd2.unload();
if(cartridge.has.MCC) mcc.unload();
@ -176,7 +180,14 @@ auto System::power() -> void {
if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1);
scheduler.primary(cpu);
peripherals.reset();
controllerPort1.power(ID::Port::Controller1);
controllerPort2.power(ID::Port::Controller2);
expansionPort.power();
controllerPort1.connect(settings.controllerPort1);
controllerPort2.connect(settings.controllerPort2);
expansionPort.connect(settings.expansionPort);
}
}

View File

@ -3,7 +3,8 @@ struct System {
inline auto loaded() const -> bool { return information.loaded; }
inline auto region() const -> Region { return information.region; }
inline auto colorburst() const -> double { return information.colorburst; }
inline auto cpuFrequency() const -> double { return information.cpuFrequency; }
inline auto apuFrequency() const -> double { return information.apuFrequency; }
auto run() -> void;
auto runToSave() -> void;
@ -30,7 +31,8 @@ private:
string manifest;
bool loaded = false;
Region region = Region::NTSC;
double colorburst = Emulator::Constants::Colorburst::NTSC;
double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0;
double apuFrequency = 32040.0 * 768.0;
} information;
uint serializeSize = 0;
@ -42,16 +44,6 @@ private:
friend class Cartridge;
};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint device) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
Expansion* expansionPort = nullptr;
};
struct Random {
auto seed(uint seed) -> void;
auto operator()(uint result) -> uint;
@ -62,7 +54,6 @@ private:
};
extern System system;
extern Peripherals peripherals;
extern Random random;
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }