Update to v103r05 release.

byuu says:

Changelog:

  - fc/controller: added ControllerPort class; removed Peripherals class
  - md/controller/gamepad: removed X,Y,Z buttons since this isn't a
    6-button controller
  - ms/controller: added ControllerPort class (not used in Game Gear
    mode); removed Peripherals class
  - pce/controller: added ControllerPort class; removed Peripherals
    class
  - processor/spc700: idle(address) is part of SMP class again, contains
    flag to detect mov (x)+ edge case
  - sfc/controller/super-scope,justifier: use CPU frequency instead of
    hard-coding NTSC frequency
  - sfc/cpu: move 4x8-bit SMP ports to SMP class
  - sfc/smp: move APU RAM to DSP class
  - sfc/smp: improved emulation of TEST registers bits 4-7 [information
    from nocash]
      - d4,d5 is RAM wait states (1,2,5,10)
      - d6,d7 is ROM/IO wait states (1,2,5,10)
  - sfc/smp: code cleanup to new style (order from lowest to highest
    bits; use .bit(s) functions)
  - sfc/smp: $00f8,$00f9 are P4/P5 auxiliary ports; named the registers
    better
This commit is contained in:
Tim Allen 2017-07-01 16:15:27 +10:00
parent ff3750de4f
commit 40802b0b9f
62 changed files with 403 additions and 375 deletions

View File

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

View File

@ -2,9 +2,11 @@
namespace Famicom {
ControllerPort controllerPort1;
ControllerPort controllerPort2;
#include "gamepad/gamepad.cpp"
Controller::Controller(bool port) : port(port) {
Controller::Controller(uint port) : port(port) {
if(!handle()) create(Controller::Enter, 1);
}
@ -15,8 +17,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();
}
}
@ -25,4 +27,32 @@ auto Controller::main() -> void {
synchronize(cpu);
}
//
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;
}
cpu.peripherals.reset();
if(auto device = controllerPort1.device) cpu.peripherals.append(device);
if(auto device = controllerPort2.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

@ -17,9 +17,7 @@
// 7: gnd
struct Controller : Thread {
enum : bool { Port1 = 0, Port2 = 1 };
Controller(bool port);
Controller(uint port);
virtual ~Controller();
static auto Enter() -> void;
@ -27,7 +25,21 @@ struct Controller : Thread {
virtual auto data() -> uint3 { return 0; }
virtual auto latch(bool data) -> void {}
const bool port;
const uint 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"

View File

@ -1,4 +1,4 @@
Gamepad::Gamepad(bool port) : Controller(port) {
Gamepad::Gamepad(uint port) : Controller(port) {
}
auto Gamepad::data() -> uint3 {

View File

@ -3,7 +3,7 @@ struct Gamepad : Controller {
Up, Down, Left, Right, B, A, Select, Start,
};
Gamepad(bool port);
Gamepad(uint port);
auto data() -> uint3;
auto latch(bool data) -> void;

View File

@ -10,12 +10,12 @@ auto CPU::readIO(uint16 addr) -> uint8 {
switch(addr) {
case 0x4016: {
auto data = Famicom::peripherals.controllerPort1->data();
auto data = controllerPort1.device->data();
return (mdr() & 0xc0) | data.bit(2) << 4 | data.bit(1) << 3 | data.bit(0) << 0;
}
case 0x4017: {
auto data = Famicom::peripherals.controllerPort2->data();
auto data = controllerPort2.device->data();
return (mdr() & 0xc0) | data.bit(2) << 4 | data.bit(1) << 3 | data.bit(0) << 0;
}
@ -34,8 +34,8 @@ auto CPU::writeIO(uint16 addr, uint8 data) -> void {
}
case 0x4016: {
Famicom::peripherals.controllerPort1->latch(data.bit(0));
Famicom::peripherals.controllerPort2->latch(data.bit(0));
controllerPort1.device->latch(data.bit(0));
controllerPort2.device->latch(data.bit(0));
return;
}

View File

@ -132,7 +132,8 @@ 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);
}
auto Interface::power() -> void {

View File

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

View File

@ -45,6 +45,8 @@ auto System::serializeAll(serializer& s) -> void {
cpu.serialize(s);
apu.serialize(s);
ppu.serialize(s);
controllerPort1.serialize(s);
controllerPort2.serialize(s);
}
auto System::serializeInit() -> void {

View File

@ -2,7 +2,6 @@
namespace Famicom {
#include "peripherals.cpp"
#include "video.cpp"
#include "serialization.cpp"
System system;
@ -56,7 +55,9 @@ auto System::save() -> void {
auto System::unload() -> void {
if(!loaded()) return;
peripherals.unload();
cpu.peripherals.reset();
controllerPort1.unload();
controllerPort2.unload();
cartridge.unload();
information.loaded = false;
}
@ -76,7 +77,12 @@ auto System::power() -> void {
apu.power();
ppu.power();
scheduler.primary(cpu);
peripherals.reset();
controllerPort1.power(ID::Port::Controller1);
controllerPort2.power(ID::Port::Controller2);
controllerPort1.connect(settings.controllerPort1);
controllerPort2.connect(settings.controllerPort2);
}
auto System::init() -> void {

View File

@ -41,17 +41,7 @@ private:
uint _serializeSize = 0;
};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint device) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
};
extern System system;
extern Peripherals peripherals;
auto Region::NTSCJ() -> bool { return system.region() == System::Region::NTSCJ; }
auto Region::NTSCU() -> bool { return system.region() == System::Region::NTSCU; }

View File

@ -1,6 +1,6 @@
struct Gamepad : Controller {
enum : uint {
Up, Down, Left, Right, A, B, C, X, Y, Z, Start,
Up, Down, Left, Right, A, B, C, Start,
};
Gamepad(uint port);

View File

@ -34,9 +34,6 @@ Interface::Interface() {
device.inputs.append({0, "A" });
device.inputs.append({0, "B" });
device.inputs.append({0, "C" });
device.inputs.append({0, "X" });
device.inputs.append({0, "Y" });
device.inputs.append({0, "Z" });
device.inputs.append({0, "Start"});
controllerPort1.devices.append(device);
controllerPort2.devices.append(device);

View File

@ -45,8 +45,8 @@ auto Bus::in(uint8 addr) -> uint8 {
case 3: {
if(Model::MasterSystem()) {
bool reset = !platform->inputPoll(ID::Port::Hardware, ID::Device::MasterSystemControls, 0);
auto port1 = peripherals.controllerPort1->readData();
auto port2 = peripherals.controllerPort2->readData();
auto port1 = controllerPort1.device->readData();
auto port2 = controllerPort2.device->readData();
if(addr.bit(0) == 0) {
return port1.bits(0,5) << 0 | port2.bits(0,1) << 6;
} else {

View File

@ -2,6 +2,8 @@
namespace MasterSystem {
ControllerPort controllerPort1;
ControllerPort controllerPort2;
#include "gamepad/gamepad.cpp"
Controller::Controller(uint port) : port(port) {
@ -15,8 +17,8 @@ Controller::~Controller() {
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(auto device = peripherals.controllerPort1) if(device->active()) device->main();
if(auto device = peripherals.controllerPort2) if(device->active()) device->main();
if(controllerPort1.device->active()) controllerPort1.device->main();
if(controllerPort2.device->active()) controllerPort2.device->main();
}
}
@ -25,4 +27,33 @@ auto Controller::main() -> void {
synchronize(cpu);
}
//
auto ControllerPort::connect(uint deviceID) -> void {
delete device;
if(!system.loaded()) return;
if(!Model::MasterSystem()) return;
switch(deviceID) { default:
case ID::Device::None: device = new Controller(port); break;
case ID::Device::Gamepad: device = new Gamepad(port); break;
}
cpu.peripherals.reset();
if(auto device = controllerPort1.device) cpu.peripherals.append(device);
if(auto device = controllerPort2.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

@ -10,4 +10,18 @@ struct Controller : Thread {
const uint 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"

View File

@ -30,10 +30,6 @@ auto Interface::unload() -> void {
system.unload();
}
auto Interface::connect(uint port, uint device) -> void {
peripherals.connect(port, device);
}
auto Interface::power() -> void {
system.power();
}

View File

@ -31,7 +31,7 @@ struct Interface : Emulator::Interface {
auto save() -> void override;
auto unload() -> void override;
auto connect(uint port, uint device) -> void override;
auto connect(uint port, uint device) -> void override {}
auto power() -> void override;
auto run() -> void override;
@ -56,6 +56,8 @@ struct MasterSystemInterface : Interface {
auto videoColor(uint32 color) -> uint64 override;
auto load(uint id) -> bool override;
auto connect(uint port, uint device) -> void override;
};
struct GameGearInterface : Interface {

View File

@ -68,3 +68,8 @@ auto MasterSystemInterface::load(uint id) -> bool {
if(id == ID::MasterSystem) return system.load(this, System::Model::MasterSystem);
return false;
}
auto MasterSystemInterface::connect(uint port, uint device) -> void {
if(port == ID::Port::Controller1) controllerPort1.connect(settings.controllerPort1 = device);
if(port == ID::Port::Controller2) controllerPort2.connect(settings.controllerPort2 = device);
}

View File

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

View File

@ -60,6 +60,8 @@ auto System::serializeAll(serializer& s) -> void {
cpu.serialize(s);
vdp.serialize(s);
psg.serialize(s);
controllerPort1.serialize(s);
controllerPort2.serialize(s);
}
auto System::serialize(serializer& s) -> void {

View File

@ -5,7 +5,6 @@ namespace MasterSystem {
System system;
Scheduler scheduler;
Cheat cheat;
#include "peripherals.cpp"
#include "serialization.cpp"
auto System::run() -> void {
@ -51,7 +50,11 @@ auto System::save() -> void {
}
auto System::unload() -> void {
peripherals.unload();
if(MasterSystem::Model::MasterSystem()) {
cpu.peripherals.reset();
controllerPort1.unload();
controllerPort2.unload();
}
cartridge.unload();
}
@ -70,7 +73,13 @@ auto System::power() -> void {
psg.power();
scheduler.primary(cpu);
peripherals.reset();
if(MasterSystem::Model::MasterSystem()) {
controllerPort1.power(ID::Port::Controller1);
controllerPort2.power(ID::Port::Controller2);
controllerPort1.connect(settings.controllerPort1);
controllerPort2.connect(settings.controllerPort2);
}
}
}

View File

@ -36,17 +36,7 @@ private:
} information;
};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint device) -> void;
Controller* controllerPort1 = nullptr;
Controller* controllerPort2 = nullptr;
};
extern System system;
extern Peripherals peripherals;
auto Model::MasterSystem() -> bool { return system.model() == System::Model::MasterSystem; }
auto Model::GameGear() -> bool { return system.model() == System::Model::GameGear; }

View File

@ -2,10 +2,11 @@
namespace PCEngine {
ControllerPort controllerPort;
#include "gamepad/gamepad.cpp"
Controller::Controller() {
if(!handle()) create(Controller::Enter, 100);
if(!handle()) create(Controller::Enter, 1);
}
Controller::~Controller() {
@ -15,7 +16,7 @@ Controller::~Controller() {
auto Controller::Enter() -> void {
while(true) {
scheduler.synchronize();
if(peripherals.controllerPort->active()) peripherals.controllerPort->main();
if(controllerPort.device->active()) controllerPort.device->main();
}
}
@ -24,4 +25,30 @@ auto Controller::main() -> void {
synchronize(cpu);
}
//
auto ControllerPort::connect(uint deviceID) -> void {
delete device;
if(!system.loaded()) return;
switch(deviceID) { default:
case ID::Device::None: device = new Controller; break;
case ID::Device::Gamepad: device = new Gamepad; break;
}
cpu.peripherals.reset();
if(auto device = controllerPort.device) cpu.peripherals.append(device);
}
auto ControllerPort::power() -> void {
}
auto ControllerPort::unload() -> void {
delete device;
device = nullptr;
}
auto ControllerPort::serialize(serializer& s) -> void {
}
}

View File

@ -9,4 +9,16 @@ struct Controller : Thread {
virtual auto writeData(uint2) -> void {}
};
struct ControllerPort {
auto connect(uint deviceID) -> void;
auto power() -> void;
auto unload() -> void;
auto serialize(serializer&) -> void;
Controller* device = nullptr;
};
extern ControllerPort controllerPort;
#include "gamepad/gamepad.hpp"

View File

@ -54,7 +54,7 @@ auto CPU::read_(uint8 bank, uint13 addr) -> uint8 {
//note 2: we state that the CD-ROM drive is present.
//this is so games can use its backup RAM for save data.
return (
PCEngine::peripherals.controllerPort->readData() << 0
controllerPort.device->readData() << 0
| 1 << 4
| 1 << 5
| 0 << 6 //device (0 = Turbografx-16; 1 = PC Engine)
@ -162,7 +162,7 @@ auto CPU::write(uint8 bank, uint13 addr, uint8 data) -> void {
//$1000-13ff I/O
if((addr & 0x1c00) == 0x1000) {
io.mdr = data;
PCEngine::peripherals.controllerPort->writeData(data.bits(0,1));
controllerPort.device->writeData(data.bits(0,1));
return;
}

View File

@ -85,7 +85,7 @@ auto Interface::unload() -> void {
}
auto Interface::connect(uint port, uint device) -> void {
PCEngine::peripherals.connect(port, device);
if(port == ID::Port::Controller) controllerPort.connect(settings.controllerPort = device);
}
auto Interface::power() -> void {

View File

@ -1,26 +0,0 @@
Peripherals peripherals;
auto Peripherals::unload() -> void {
delete controllerPort;
controllerPort = nullptr;
}
auto Peripherals::reset() -> void {
connect(ID::Port::Controller, settings.controllerPort);
}
auto Peripherals::connect(uint port, uint device) -> void {
if(port == ID::Port::Controller) {
settings.controllerPort = device;
if(!system.loaded()) return;
delete controllerPort;
switch(device) { default:
case ID::Device::None: controllerPort = new Controller; break;
case ID::Device::Gamepad: controllerPort = new Gamepad; break;
}
}
cpu.peripherals.reset();
cpu.peripherals.append(controllerPort);
}

View File

@ -61,6 +61,7 @@ auto System::serializeAll(serializer& s) -> void {
vdc0.serialize(s);
vdc1.serialize(s);
psg.serialize(s);
controllerPort.serialize(s);
}
auto System::serialize(serializer& s) -> void {

View File

@ -5,7 +5,6 @@ namespace PCEngine {
System system;
Scheduler scheduler;
Cheat cheat;
#include "peripherals.cpp"
#include "serialization.cpp"
auto System::run() -> void {
@ -44,7 +43,8 @@ auto System::save() -> void {
}
auto System::unload() -> void {
peripherals.unload();
cpu.peripherals.reset();
controllerPort.unload();
cartridge.unload();
}
@ -66,7 +66,8 @@ auto System::power() -> void {
psg.power();
scheduler.primary(cpu);
peripherals.reset();
controllerPort.power();
controllerPort.connect(settings.controllerPort);
}
}

View File

@ -33,16 +33,7 @@ private:
} information;
};
struct Peripherals {
auto unload() -> void;
auto reset() -> void;
auto connect(uint port, uint device) -> void;
Controller* controllerPort = nullptr;
};
extern System system;
extern Peripherals peripherals;
auto Model::PCEngine() -> bool { return system.model() == System::Model::PCEngine; }
auto Model::SuperGrafx() -> bool { return system.model() == System::Model::SuperGrafx; }

View File

@ -413,7 +413,7 @@ auto SPC700::instructionIndirectXWrite(uint8& data) -> void {
auto SPC700::instructionIndirectXIncrementRead(uint8& data) -> void {
idle(PC);
idle(); //quirk: does not read internal SMP registers
idle(page(X), false); //quirk: does not read internal SMP registers
data = read(page(X++));
ZF = data == 0;
NF = data & 0x80;
@ -421,7 +421,7 @@ auto SPC700::instructionIndirectXIncrementRead(uint8& data) -> void {
auto SPC700::instructionIndirectXIncrementWrite(uint8& data) -> void {
idle(PC);
idle(); //quirk: does not read internal SMP registers
idle(page(X), false); //quirk: does not read internal SMP registers
write(page(X++), data);
}

View File

@ -28,10 +28,6 @@ namespace Processor {
#include "serialization.cpp"
#include "disassembler.cpp"
auto SPC700::idle(uint16 address) -> void {
read(address);
}
auto SPC700::page(uint8 address) const -> uint16 {
return PF << 8 | address;
}

View File

@ -3,7 +3,7 @@
namespace Processor {
struct SPC700 {
virtual auto idle() -> void = 0;
virtual auto idle(uint16 address, bool read = true) -> void = 0;
virtual auto read(uint16 address) -> uint8 = 0;
virtual auto write(uint16 addessr, uint8 data) -> void = 0;
virtual auto synchronizing() const -> bool = 0;
@ -11,7 +11,6 @@ struct SPC700 {
virtual auto readDisassembler(uint16 address) -> uint8 { return 0; }
//spc700.cpp
inline auto idle(uint16 address) -> void;
inline auto page(uint8 address) const -> uint16;
inline auto stack(uint8 address) const -> uint16;

View File

@ -10,7 +10,7 @@ ControllerPort controllerPort2;
#include "super-scope/super-scope.cpp"
#include "justifier/justifier.cpp"
Controller::Controller(bool port) : port(port) {
Controller::Controller(uint port) : port(port) {
if(!handle()) create(Controller::Enter, 1);
}
@ -33,15 +33,16 @@ auto Controller::main() -> void {
auto Controller::iobit() -> bool {
switch(port) {
case Controller::Port1: return cpu.pio() & 0x40;
case Controller::Port2: return cpu.pio() & 0x80;
case ID::Port::Controller1: return cpu.pio() & 0x40;
case ID::Port::Controller2: return cpu.pio() & 0x80;
}
unreachable;
}
auto Controller::iobit(bool data) -> void {
switch(port) {
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
case ID::Port::Controller1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
case ID::Port::Controller2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
}
}

View File

@ -12,9 +12,7 @@
// 7: gnd
struct Controller : Thread {
enum : bool { Port1 = 0, Port2 = 1 };
Controller(bool port);
Controller(uint port);
virtual ~Controller();
static auto Enter() -> void;
@ -24,7 +22,7 @@ struct Controller : Thread {
virtual auto data() -> uint2 { return 0; }
virtual auto latch(bool data) -> void {}
const bool port;
const uint port;
};
struct ControllerPort {

View File

@ -1,4 +1,4 @@
Gamepad::Gamepad(bool port) : Controller(port) {
Gamepad::Gamepad(uint port) : Controller(port) {
latched = 0;
counter = 0;
}

View File

@ -3,7 +3,7 @@ struct Gamepad : Controller {
Up, Down, Left, Right, B, A, Y, X, L, R, Select, Start,
};
Gamepad(bool port);
Gamepad(uint port);
auto data() -> uint2;
auto latch(bool data) -> void;

View File

@ -1,9 +1,9 @@
Justifier::Justifier(bool port, bool chained):
Justifier::Justifier(uint port, bool chained):
Controller(port),
chained(chained),
device(!chained ? ID::Device::Justifier : ID::Device::Justifiers)
{
create(Controller::Enter, 21'477'272);
create(Controller::Enter, system.cpuFrequency());
latched = 0;
counter = 0;
active = 0;

View File

@ -3,7 +3,7 @@ struct Justifier : Controller {
X, Y, Trigger, Start,
};
Justifier(bool port, bool chained);
Justifier(uint port, bool chained);
~Justifier();
auto main() -> void;

View File

@ -1,4 +1,4 @@
Mouse::Mouse(bool port) : Controller(port) {
Mouse::Mouse(uint port) : Controller(port) {
latched = 0;
counter = 0;

View File

@ -3,7 +3,7 @@ struct Mouse : Controller {
X, Y, Left, Right,
};
Mouse(bool port);
Mouse(uint port);
auto data() -> uint2;
auto latch(bool data) -> void;

View File

@ -1,4 +1,4 @@
SuperMultitap::SuperMultitap(bool port) : Controller(port) {
SuperMultitap::SuperMultitap(uint port) : Controller(port) {
latched = 0;
counter1 = 0;
counter2 = 0;

View File

@ -3,7 +3,7 @@ struct SuperMultitap : Controller {
Up, Down, Left, Right, B, A, Y, X, L, R, Select, Start,
};
SuperMultitap(bool port);
SuperMultitap(uint port);
auto data() -> uint2;
auto latch(bool data) -> void;

View File

@ -10,8 +10,8 @@
//require manual polling of PIO ($4201.d6) to determine when iobit was written.
//Note that no commercial game ever utilizes a Super Scope in port 1.
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21'477'272);
SuperScope::SuperScope(uint port) : Controller(port) {
create(Controller::Enter, system.cpuFrequency());
sprite = Emulator::video.createSprite(32, 32);
sprite->setPixels(Resource::Sprite::CrosshairGreen);

View File

@ -5,7 +5,7 @@ struct SuperScope : Controller {
X, Y, Trigger, Cursor, Turbo, Pause,
};
SuperScope(bool port);
SuperScope(uint port);
~SuperScope();
auto main() -> void;

View File

@ -116,9 +116,6 @@ auto CPU::power() -> void {
channel.hdmaDoTransfer = false;
}
//$2140-217f
for(auto& port : io.port) port = 0x00;
//$2181-$2183
io.wramAddress = 0x000000;

View File

@ -4,11 +4,9 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
auto joylatch() const -> bool;
auto synchronizing() const -> bool override;
//cpu.cpp
CPU();
auto readPort(uint2 port) const -> uint8;
auto writePort(uint2 port, uint8 data) -> void;
static auto Enter() -> void;
auto main() -> void;
auto load(Markup::Node) -> bool;
@ -135,9 +133,6 @@ private:
} status;
struct IO {
//$2140-217f
uint8 port[4];
//$2181-$2183
uint17 wramAddress;

View File

@ -1,6 +1,6 @@
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
synchronize(smp);
return smp.readPort(addr.bits(0,1));
return smp.portRead(addr.bits(0,1));
}
auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
@ -158,7 +158,7 @@ auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 {
auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
synchronize(smp);
return writePort(addr.bits(0,1), data);
return smp.portWrite(addr.bits(0,1), data);
}
auto CPU::writeCPU(uint24 addr, uint8 data) -> void {

View File

@ -1,11 +1,3 @@
auto CPU::readPort(uint2 port) const -> uint8 {
return io.port[port];
}
auto CPU::writePort(uint2 port, uint8 data) -> void {
io.port[port] = data;
}
auto CPU::idle() -> void {
status.clockCount = 6;
dmaEdge();

View File

@ -49,8 +49,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(status.autoJoypadLatch);
s.integer(status.autoJoypadCounter);
s.array(io.port);
s.integer(io.wramAddress);
s.integer(io.joypadStrobeLatch);

View File

@ -1,6 +1,6 @@
auto DSP::brrDecode(Voice& v) -> void {
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
int nybbles = (state._brrByte << 8) + smp.apuram[(uint16)(v.brrAddress + v.brrOffset + 1)];
int nybbles = (state._brrByte << 8) + apuram[(uint16)(v.brrAddress + v.brrOffset + 1)];
const int filter = (state._brrHeader >> 2) & 3;
const int scale = (state._brrHeader >> 4);

View File

@ -232,6 +232,8 @@ auto DSP::power() -> void {
create(Enter, system.apuFrequency());
stream = Emulator::audio.createStream(2, frequency() / 768.0);
for(auto& byte : apuram) byte = random(0x00);
memory::fill(&state, sizeof(State));
state.noise = 0x4000;
state.echoHistoryOffset = 0;

View File

@ -2,6 +2,7 @@
struct DSP : Thread {
shared_pointer<Emulator::Stream> stream;
uint8 apuram[64 * 1024];
DSP();

View File

@ -11,8 +11,8 @@ auto DSP::echoOutput(bool channel) -> int {
auto DSP::echoRead(bool channel) -> void {
uint addr = state._echoPointer + channel * 2;
uint8 lo = smp.apuram[(uint16)(addr + 0)];
uint8 hi = smp.apuram[(uint16)(addr + 1)];
uint8 lo = apuram[(uint16)(addr + 0)];
uint8 hi = apuram[(uint16)(addr + 1)];
int s = (int16)((hi << 8) + lo);
state.echoHistory[channel][state.echoHistoryOffset] = s >> 1;
}
@ -21,8 +21,8 @@ auto DSP::echoWrite(bool channel) -> void {
if(!(state._echoDisabled & 0x20)) {
uint addr = state._echoPointer + channel * 2;
int s = state._echoOut[channel];
smp.apuram[(uint16)(addr + 0)] = s;
smp.apuram[(uint16)(addr + 1)] = s >> 8;
apuram[(uint16)(addr + 0)] = s;
apuram[(uint16)(addr + 1)] = s >> 8;
}
state._echoOut[channel] = 0;

View File

@ -1,6 +1,8 @@
void DSP::serialize(serializer& s) {
Thread::serialize(s);
s.array(apuram);
s.array(state.regs, 128);
s.array(state.echoHistory[0]);
s.array(state.echoHistory[1]);

View File

@ -22,8 +22,8 @@ auto DSP::voice2(Voice& v) -> void {
//read sample pointer (ignored if not needed)
uint16 addr = state._dirAddress;
if(!v.konDelay) addr += 2;
uint8 lo = smp.apuram[(uint16)(addr + 0)];
uint8 hi = smp.apuram[(uint16)(addr + 1)];
uint8 lo = apuram[(uint16)(addr + 0)];
uint8 hi = apuram[(uint16)(addr + 1)];
state._brrNextAddress = ((hi << 8) + lo);
state._adsr0 = VREG(ADSR0);
@ -43,8 +43,8 @@ auto DSP::voice3a(Voice& v) -> void {
}
auto DSP::voice3b(Voice& v) -> void {
state._brrByte = smp.apuram[(uint16)(v.brrAddress + v.brrOffset)];
state._brrHeader = smp.apuram[(uint16)(v.brrAddress)];
state._brrByte = apuram[(uint16)(v.brrAddress + v.brrOffset)];
state._brrHeader = apuram[(uint16)(v.brrAddress)];
}
auto DSP::voice3c(Voice& v) -> void {

View File

@ -1,30 +1,37 @@
alwaysinline auto SMP::readRAM(uint16 addr) -> uint8 {
alwaysinline auto SMP::ramRead(uint16 addr) -> uint8 {
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f];
if(io.ramDisable) return 0x5a; //0xff on mini-SNES
return apuram[addr];
return dsp.apuram[addr];
}
alwaysinline auto SMP::writeRAM(uint16 addr, uint8 data) -> void {
alwaysinline auto SMP::ramWrite(uint16 addr, uint8 data) -> void {
//writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled
if(io.ramWritable && !io.ramDisable) apuram[addr] = data;
if(io.ramWritable && !io.ramDisable) dsp.apuram[addr] = data;
}
auto SMP::readPort(uint2 port) const -> uint8 {
return io.port[0xf4 + port];
auto SMP::portRead(uint2 port) const -> uint8 {
if(port == 0) return io.cpu0;
if(port == 1) return io.cpu1;
if(port == 2) return io.cpu2;
if(port == 3) return io.cpu3;
unreachable;
}
auto SMP::writePort(uint2 port, uint8 data) -> void {
io.port[0xf4 + port] = data;
auto SMP::portWrite(uint2 port, uint8 data) -> void {
if(port == 0) io.apu0 = data;
if(port == 1) io.apu1 = data;
if(port == 2) io.apu2 = data;
if(port == 3) io.apu3 = data;
}
auto SMP::readBus(uint16 addr) -> uint8 {
auto SMP::busRead(uint16 addr) -> uint8 {
uint result;
switch(addr) {
case 0xf0: //TEST -- write-only register
case 0xf0: //TEST (write-only register)
return 0x00;
case 0xf1: //CONTROL -- write-only register
case 0xf1: //CONTROL (write-only register)
return 0x00;
case 0xf2: //DSPADDR
@ -35,55 +42,64 @@ auto SMP::readBus(uint16 addr) -> uint8 {
return dsp.read(io.dspAddr & 0x7f);
case 0xf4: //CPUIO0
synchronize(cpu);
return io.apu0;
case 0xf5: //CPUIO1
synchronize(cpu);
return io.apu1;
case 0xf6: //CPUIO2
synchronize(cpu);
return io.apu2;
case 0xf7: //CPUIO3
synchronize(cpu);
return cpu.readPort(addr);
return io.apu3;
case 0xf8: //RAM0
return io.ram00f8;
case 0xf8: //AUXIO4
return io.aux4;
case 0xf9: //RAM1
return io.ram00f9;
case 0xf9: //AUXIO5
return io.aux5;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET -- write-only registers
case 0xfc: //T2TARGET (write-only registers)
return 0x00;
case 0xfd: //T0OUT -- 4-bit counter value
case 0xfd: //T0OUT (4-bit counter value)
result = timer0.stage3;
timer0.stage3 = 0;
return result;
case 0xfe: //T1OUT -- 4-bit counter value
case 0xfe: //T1OUT (4-bit counter value)
result = timer1.stage3;
timer1.stage3 = 0;
return result;
case 0xff: //T2OUT -- 4-bit counter value
case 0xff: //T2OUT (4-bit counter value)
result = timer2.stage3;
timer2.stage3 = 0;
return result;
}
return readRAM(addr);
return ramRead(addr);
}
auto SMP::writeBus(uint16 addr, uint8 data) -> void {
auto SMP::busWrite(uint16 addr, uint8 data) -> void {
switch(addr) {
case 0xf0: //TEST
if(r.p.p) break; //writes only valid when P flag is clear
io.clockSpeed = (data >> 6) & 3;
io.timerSpeed = (data >> 4) & 3;
io.timersEnable = data & 0x08;
io.ramDisable = data & 0x04;
io.ramWritable = data & 0x02;
io.timersDisable = data & 0x01;
io.timersDisable = data.bit (0);
io.ramWritable = data.bit (1);
io.ramDisable = data.bit (2);
io.timersEnable = data.bit (3);
io.ramSpeed = data.bits(4,5);
io.romIOSpeed = data.bits(6,7);
io.timerStep = (1 << io.clockSpeed) + (2 << io.timerSpeed);
io.timerStep = (1 << io.romIOSpeed) + (2 << io.ramSpeed);
timer0.synchronizeStage1();
timer1.synchronizeStage1();
@ -91,40 +107,38 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
break;
case 0xf1: //CONTROL
io.iplromEnable = data & 0x80;
if(data & 0x30) {
//one-time clearing of APU port read registers,
//emulated by simulating CPU writes of 0x00
synchronize(cpu);
if(data & 0x20) {
cpu.writePort(2, 0x00);
cpu.writePort(3, 0x00);
}
if(data & 0x10) {
cpu.writePort(0, 0x00);
cpu.writePort(1, 0x00);
}
}
//0->1 transistion resets timers
if(!timer2.enable && (data & 0x04)) {
timer2.stage2 = 0;
timer2.stage3 = 0;
}
timer2.enable = data & 0x04;
if(!timer1.enable && (data & 0x02)) {
timer1.stage2 = 0;
timer1.stage3 = 0;
}
timer1.enable = data & 0x02;
if(!timer0.enable && (data & 0x01)) {
if(!timer0.enable && data.bit(0)) {
timer0.stage2 = 0;
timer0.stage3 = 0;
}
timer0.enable = data & 0x01;
timer0.enable = data.bit(0);
if(!timer1.enable && data.bit(1)) {
timer1.stage2 = 0;
timer1.stage3 = 0;
}
timer1.enable = data.bit(1);
if(!timer2.enable && data.bit(2)) {
timer2.stage2 = 0;
timer2.stage3 = 0;
}
timer2.enable = data.bit(2);
if(data.bit(4)) {
synchronize(cpu);
io.apu0 = 0x00;
io.apu1 = 0x00;
}
if(data.bit(5)) {
synchronize(cpu);
io.apu2 = 0x00;
io.apu3 = 0x00;
}
io.iplromEnable = data.bit(7);
break;
case 0xf2: //DSPADDR
@ -137,19 +151,31 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
break;
case 0xf4: //CPUIO0
synchronize(cpu);
io.cpu0 = data;
break;
case 0xf5: //CPUIO1
synchronize(cpu);
io.cpu1 = data;
break;
case 0xf6: //CPUIO2
synchronize(cpu);
io.cpu2 = data;
break;
case 0xf7: //CPUIO3
synchronize(cpu);
writePort(addr, data);
io.cpu3 = data;
break;
case 0xf8: //RAM0
io.ram00f8 = data;
case 0xf8: //AUXIO4
io.aux4 = data;
break;
case 0xf9: //RAM1
io.ram00f9 = data;
case 0xf9: //AUXIO5
io.aux5 = data;
break;
case 0xfa: //T0TARGET
@ -166,34 +192,41 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
case 0xfd: //T0OUT
case 0xfe: //T1OUT
case 0xff: //T2OUT -- read-only registers
case 0xff: //T2OUT (read-only registers)
break;
}
writeRAM(addr, data); //all writes, even to MMIO registers, appear on bus
ramWrite(addr, data); //all writes, even to I/O registers, appear on bus
}
auto SMP::idle() -> void {
step(24);
auto SMP::speed(uint16 addr) const -> uint {
static const uint waitStates[4] = {1, 2, 5, 10};
if((addr & 0xfff0) == 0x00f0) return waitStates[io.romIOSpeed];
if(addr >= 0xffc0 && io.iplromEnable) return waitStates[io.romIOSpeed];
return waitStates[io.ramSpeed];
}
auto SMP::idle(uint16 addr, bool read) -> void {
step(24 * speed(addr));
if(read) busRead(addr);
cycleEdge();
}
auto SMP::read(uint16 addr) -> uint8 {
step(12);
uint8 data = readBus(addr);
step(12);
step(24 * speed(addr));
uint8 data = busRead(addr);
cycleEdge();
return data;
}
auto SMP::write(uint16 addr, uint8 data) -> void {
step(24);
writeBus(addr, data);
step(24 * speed(addr));
busWrite(addr, data);
cycleEdge();
}
auto SMP::readDisassembler(uint16 addr) -> uint8 {
if((addr & 0xfff0) == 0x00f0) return 0x00;
if((addr & 0xffc0) == 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f];
return apuram[addr];
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f];
return dsp.apuram[addr];
}

View File

@ -2,27 +2,33 @@ auto SMP::serialize(serializer& s) -> void {
SPC700::serialize(s);
Thread::serialize(s);
s.array(apuram);
s.integer(io.clockCounter);
s.integer(io.dspCounter);
s.integer(io.timerStep);
s.integer(io.clockSpeed);
s.integer(io.timerSpeed);
s.integer(io.timersEnable);
s.integer(io.ramDisable);
s.integer(io.ramWritable);
s.integer(io.apu0);
s.integer(io.apu1);
s.integer(io.apu2);
s.integer(io.apu3);
s.integer(io.timersDisable);
s.integer(io.ramWritable);
s.integer(io.ramDisable);
s.integer(io.timersEnable);
s.integer(io.ramSpeed);
s.integer(io.romIOSpeed);
s.integer(io.iplromEnable);
s.integer(io.dspAddr);
s.array(io.port);
s.integer(io.cpu0);
s.integer(io.cpu1);
s.integer(io.cpu2);
s.integer(io.cpu3);
s.integer(io.ram00f8);
s.integer(io.ram00f9);
s.integer(io.aux4);
s.integer(io.aux5);
s.integer(timer0.stage0);
s.integer(timer0.stage1);

View File

@ -39,19 +39,24 @@ auto SMP::power() -> void {
r.pc.byte.l = iplrom[62];
r.pc.byte.h = iplrom[63];
for(auto& byte : apuram) byte = random(0x00);
//timing
io.clockCounter = 0;
io.dspCounter = 0;
io.timerStep = 3;
io.timerStep = (1 << 0) + (2 << 0);
//external
io.apu0 = 0x00;
io.apu1 = 0x00;
io.apu2 = 0x00;
io.apu3 = 0x00;
//$00f0
io.clockSpeed = 0;
io.timerSpeed = 0;
io.timersEnable = true;
io.ramDisable = false;
io.ramWritable = true;
io.timersDisable = false;
io.ramWritable = true;
io.ramDisable = false;
io.timersEnable = true;
io.ramSpeed = 0;
io.romIOSpeed = 0;
//$00f1
io.iplromEnable = true;
@ -60,14 +65,14 @@ auto SMP::power() -> void {
io.dspAddr = 0x00;
//$00f4-00f7
io.port[0] = 0x00;
io.port[1] = 0x00;
io.port[2] = 0x00;
io.port[3] = 0x00;
io.cpu0 = 0x00;
io.cpu1 = 0x00;
io.cpu2 = 0x00;
io.cpu3 = 0x00;
//$00f8,$00f9
io.ram00f8 = 0x00;
io.ram00f9 = 0x00;
//$00f8-$00f9
io.aux4 = 0x00;
io.aux5 = 0x00;
timer0.stage0 = 0;
timer1.stage0 = 0;

View File

@ -1,11 +1,13 @@
//Sony CXP1100Q-1
struct SMP : Processor::SPC700, Thread {
uint8 iplrom[64];
//smp.cpp
auto synchronizing() const -> bool override;
auto readPort(uint2 port) const -> uint8;
auto writePort(uint2 port, uint8 data) -> void;
auto portRead(uint2 port) const -> uint8;
auto portWrite(uint2 port, uint8 data) -> void;
auto main() -> void;
auto load(Markup::Node) -> bool;
@ -14,9 +16,6 @@ struct SMP : Processor::SPC700, Thread {
//serialization.cpp
auto serialize(serializer&) -> void;
uint8 iplrom[64];
uint8 apuram[64 * 1024];
private:
struct IO {
//timing
@ -24,13 +23,19 @@ private:
uint dspCounter;
uint timerStep;
//external
uint8 apu0;
uint8 apu1;
uint8 apu2;
uint8 apu3;
//$00f0
uint8 clockSpeed;
uint8 timerSpeed;
bool timersEnable;
bool ramDisable;
bool ramWritable;
bool timersDisable;
uint1 timersDisable;
uint1 ramWritable;
uint1 ramDisable;
uint1 timersEnable;
uint2 ramSpeed;
uint2 romIOSpeed;
//$00f1
bool iplromEnable;
@ -39,23 +44,27 @@ private:
uint8 dspAddr;
//$00f4-00f7
uint8 port[4];
uint8 cpu0;
uint8 cpu1;
uint8 cpu2;
uint8 cpu3;
//$00f8,$00f9
uint8 ram00f8;
uint8 ram00f9;
//$00f8-00f9
uint8 aux4;
uint8 aux5;
} io;
static auto Enter() -> void;
//memory.cpp
auto readRAM(uint16 addr) -> uint8;
auto writeRAM(uint16 addr, uint8 data) -> void;
auto ramRead(uint16 addr) -> uint8;
auto ramWrite(uint16 addr, uint8 data) -> void;
auto readBus(uint16 addr) -> uint8;
auto writeBus(uint16 addr, uint8 data) -> void;
auto busRead(uint16 addr) -> uint8;
auto busWrite(uint16 addr, uint8 data) -> void;
auto idle() -> void override;
auto speed(uint16 addr) const -> uint;
auto idle(uint16 addr, bool read = true) -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;

View File

@ -15,15 +15,6 @@ auto SMP::cycleEdge() -> void {
timer0.tick();
timer1.tick();
timer2.tick();
//TEST register S-SMP speed control
//24 clocks have already been added for this cycle at this point
switch(io.clockSpeed) {
case 0: break; //100% speed
case 1: step(24); break; // 50% speed
case 2: while(true) step(24); // 0% speed -- locks S-SMP
case 3: step(24 * 9); break; // 10% speed
}
}
template<uint Frequency> auto SMP::Timer<Frequency>::tick() -> void {