Update to v106r49 release.

byuu says:

This is a fairly radical WIP with extreme changes to lots of very
important parts.

The result is a ~7% emulation speedup (with bsnes, unsure how much it
helps higan), but it's quite possible there are regressions. As such, I
would really appreciate testing as many games as possible ... especially
the old finnicky games that had issues with DMA and/or interrupts.

One thing to note is that I removed an edge case test that suppresses
IRQs from firing on the very last dot of every field, which is a
behavior I've verified on real hardware in the past. I feel that the
main interrupt polling function (the hottest portion of the entire
emulator) is not the appropriate place for it, and I should instead
factor it into assignment of NMITIMEN/VTIME/HTIME using the new
io.irqEnable (==virqEnable||hirqEnable) flag. But since I haven't done
that yet ... there's an old IRQ test ROM of mine that'll fail for this
WIP. No commercial games will ever rely on this, so it's fine for
testing.

Changelog:

  - sfc/cpu.smp: inlined the global status functions
  - sfc/cpu: added readRAM, writeRAM to use a function pointer instead
    of a lambda for WRAM access
  - sfc/cpu,smp,ppu/counter: updated reset functionality to new style
    using class inline initializers
  - sfc/cpu: fixed power(false) to invoke the reset vector properly
  - sfc/cpu: completely rewrote DMA handling to have per-channel
    functions
  - sfc/cpu: removed unused joylatch(), io.joypadStrobeLatch
  - sfc/cpu: cleaned up io.cpp handlers
  - sfc/cpu: simplified interrupt polling code using
    nall::boolean::flip(),raise(),lower() functions
  - sfc/ppu/counter: cleaned up the class significantly and also
    optimized things for efficiency
  - sfc/ppu/counter: emulated PAL 1368-clock long scanline when
    interlace=1, field=1, vcounter=311
  - sfc/smp: factored out the I/O and port handlers to io.cpp
This commit is contained in:
Tim Allen
2018-07-19 19:01:44 +10:00
parent 393c2395bb
commit 65a3e6c676
16 changed files with 791 additions and 1072 deletions

View File

@@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.48"; static const string Version = "106.49";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";

View File

@@ -10,15 +10,6 @@ CPU cpu;
#include "irq.cpp" #include "irq.cpp"
#include "serialization.cpp" #include "serialization.cpp"
auto CPU::interruptPending() const -> bool { return status.interruptPending; }
auto CPU::pio() const -> uint8 { return io.pio; }
auto CPU::joylatch() const -> bool { return io.joypadStrobeLatch; }
auto CPU::synchronizing() const -> bool { return scheduler.synchronizing(); }
CPU::CPU() {
PPUcounter::scanline = {&CPU::scanline, this};
}
auto CPU::Enter() -> void { auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main(); while(true) scheduler.synchronize(), cpu.main();
} }
@@ -65,10 +56,16 @@ auto CPU::power(bool reset) -> void {
create(Enter, system.cpuFrequency()); create(Enter, system.cpuFrequency());
coprocessors.reset(); coprocessors.reset();
PPUcounter::reset(); PPUcounter::reset();
PPUcounter::scanline = {&CPU::scanline, this};
function<auto (uint24, uint8) -> uint8> reader; function<auto (uint24, uint8) -> uint8> reader;
function<auto (uint24, uint8) -> void> writer; function<auto (uint24, uint8) -> void> writer;
reader = {&CPU::readRAM, this};
writer = {&CPU::writeRAM, this};
bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000);
bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000);
reader = {&CPU::readAPU, this}; reader = {&CPU::readAPU, this};
writer = {&CPU::writeAPU, this}; writer = {&CPU::writeAPU, this};
bus.map(reader, writer, "00-3f,80-bf:2140-217f"); bus.map(reader, writer, "00-3f,80-bf:2140-217f");
@@ -81,132 +78,27 @@ auto CPU::power(bool reset) -> void {
writer = {&CPU::writeDMA, this}; writer = {&CPU::writeDMA, this};
bus.map(reader, writer, "00-3f,80-bf:4300-437f"); 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);
if(!reset) random.array(wram, sizeof(wram)); if(!reset) random.array(wram, sizeof(wram));
//DMA for(uint n : range(8)) {
for(auto& channel : this->channel) { channels[n] = {};
channel.dmaEnabled = false; if(n != 7) channels[n].next = channels[n + 1];
channel.hdmaEnabled = false;
channel.direction = 1;
channel.indirect = true;
channel.unused = true;
channel.reverseTransfer = true;
channel.fixedTransfer = true;
channel.transferMode = 7;
channel.targetAddress = 0xff;
channel.sourceAddress = 0xffff;
channel.sourceBank = 0xff;
channel.transferSize = 0xffff;
channel.indirectBank = 0xff;
channel.hdmaAddress = 0xffff;
channel.lineCounter = 0xff;
channel.unknown = 0xff;
channel.hdmaCompleted = false;
channel.hdmaDoTransfer = false;
} }
//$2181-$2183 io = {};
io.wramAddress = 0x000000; alu = {};
pipe = {};
//$4016-$4017 status = {};
io.joypadStrobeLatch = 0;
//$4200
io.nmiEnabled = false;
io.hirqEnabled = false;
io.virqEnabled = false;
io.autoJoypadPoll = false;
//$4201
io.pio = 0xff;
//$4202-$4203
io.wrmpya = 0xff;
io.wrmpyb = 0xff;
//$4204-$4206
io.wrdiva = 0xffff;
io.wrdivb = 0xff;
//$4207-$420a
io.hirqPos = 0x01ff;
io.virqPos = 0x01ff;
//$420d
io.romSpeed = 8;
//$4214-$4217
io.rddiv = 0x0000;
io.rdmpy = 0x0000;
//$4218-$421f
io.joy1 = 0x0000;
io.joy2 = 0x0000;
io.joy3 = 0x0000;
io.joy4 = 0x0000;
//ALU
alu.mpyctr = 0;
alu.divctr = 0;
alu.shift = 0;
//Pipe
pipe.valid = false;
pipe.addr = 0;
pipe.data = 0;
//Timing
clockCounter = 0;
status.clockCount = 0;
status.lineClocks = lineclocks(); status.lineClocks = lineclocks();
status.irqLock = false;
status.dramRefreshPosition = (version == 1 ? 530 : 538); status.dramRefreshPosition = (version == 1 ? 530 : 538);
status.dramRefreshed = false; status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaInitTriggered = false;
status.hdmaPosition = 1104; status.hdmaPosition = 1104;
status.hdmaTriggered = false; status.powerPending = reset == 0;
status.resetPending = reset == 1;
status.nmiValid = false;
status.nmiLine = false;
status.nmiTransition = false;
status.nmiPending = false;
status.nmiHold = false;
status.irqValid = false;
status.irqLine = false;
status.irqTransition = false;
status.irqPending = false;
status.irqHold = false;
status.powerPending = true;
status.resetPending = false;
status.interruptPending = true; status.interruptPending = true;
status.dmaActive = false; clockCounter = 0;
status.dmaClocks = 0;
status.dmaPending = false;
status.hdmaPending = false;
status.hdmaMode = 0;
status.autoJoypadActive = false;
status.autoJoypadLatch = false;
status.autoJoypadCounter = 0;
} }
} }

View File

@@ -1,41 +1,26 @@
struct CPU : Processor::WDC65816, Thread, PPUcounter { struct CPU : Processor::WDC65816, Thread, PPUcounter {
auto interruptPending() const -> bool override; inline auto interruptPending() const -> bool override { return status.interruptPending; }
auto pio() const -> uint8; inline auto pio() const -> uint8 { return io.pio; }
auto joylatch() const -> bool; inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
auto synchronizing() const -> bool override;
//cpu.cpp //cpu.cpp
CPU();
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load(Markup::Node) -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
//dma.cpp //dma.cpp
auto dmaStep(uint clocks) -> void; inline auto dmaEnable() -> bool;
auto dmaTransferValid(uint8 bbus, uint24 abus) -> bool; inline auto hdmaEnable() -> bool;
auto dmaAddressValid(uint24 abus) -> bool; inline auto hdmaActive() -> bool;
auto dmaRead(uint24 abus) -> uint8;
auto dmaWrite(bool valid, uint addr = 0, uint8 data = 0) -> void;
auto dmaTransfer(bool direction, uint8 bbus, uint24 abus) -> void;
inline auto dmaAddressB(uint n, uint channel) -> uint8; inline auto dmaStep(uint clocks) -> void;
inline auto dmaAddress(uint n) -> uint24; inline auto dmaFlush() -> void;
inline auto hdmaAddress(uint n) -> uint24;
inline auto hdmaIndirectAddress(uint n) -> uint24;
inline auto dmaEnabledChannels() -> uint;
inline auto hdmaActive(uint n) -> bool;
inline auto hdmaActiveAfter(uint s) -> bool;
inline auto hdmaEnabledChannels() -> uint;
inline auto hdmaActiveChannels() -> uint;
auto dmaRun() -> void; auto dmaRun() -> void;
auto hdmaUpdate(uint n) -> void; auto hdmaReset() -> void;
auto hdmaSetup() -> void;
auto hdmaRun() -> void; auto hdmaRun() -> void;
auto hdmaInitReset() -> void;
auto hdmaInit() -> void;
//memory.cpp //memory.cpp
auto idle() -> void override; auto idle() -> void override;
@@ -45,12 +30,14 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
auto readDisassembler(uint24 addr) -> uint8 override; auto readDisassembler(uint24 addr) -> uint8 override;
//io.cpp //io.cpp
auto readAPU(uint24 addr, uint8 data) -> uint8; auto readRAM(uint24 address, uint8 data) -> uint8;
auto readCPU(uint24 addr, uint8 data) -> uint8; auto readAPU(uint24 address, uint8 data) -> uint8;
auto readDMA(uint24 addr, uint8 data) -> uint8; auto readCPU(uint24 address, uint8 data) -> uint8;
auto writeAPU(uint24 addr, uint8 data) -> void; auto readDMA(uint24 address, uint8 data) -> uint8;
auto writeCPU(uint24 addr, uint8 data) -> void; auto writeRAM(uint24 address, uint8 data) -> void;
auto writeDMA(uint24 addr, uint8 data) -> void; auto writeAPU(uint24 address, uint8 data) -> void;
auto writeCPU(uint24 address, uint8 data) -> void;
auto writeDMA(uint24 address, uint8 data) -> void;
//timing.cpp //timing.cpp
inline auto dmaCounter() const -> uint; inline auto dmaCounter() const -> uint;
@@ -87,81 +74,76 @@ private:
uint clockCounter; uint clockCounter;
struct Status { struct Status {
bool interruptPending; uint clockCount = 0;
uint lineClocks = 0;
uint clockCount; bool irqLock = false;
uint lineClocks;
//timing uint dramRefreshPosition = 0;
bool irqLock; bool dramRefreshed = false;
uint dramRefreshPosition; uint hdmaSetupPosition = 0;
bool dramRefreshed; bool hdmaSetupTriggered = false;
uint hdmaInitPosition; uint hdmaPosition = 0;
bool hdmaInitTriggered; bool hdmaTriggered = false;
uint hdmaPosition; boolean nmiValid;
bool hdmaTriggered; boolean nmiLine;
boolean nmiTransition;
boolean nmiPending;
boolean nmiHold;
bool nmiValid; boolean irqValid;
bool nmiLine; boolean irqLine;
bool nmiTransition; boolean irqTransition;
bool nmiPending; boolean irqPending;
bool nmiHold; boolean irqHold;
bool irqValid; bool powerPending = false;
bool irqLine; bool resetPending = false;
bool irqTransition;
bool irqPending;
bool irqHold;
bool powerPending; bool interruptPending = false;
bool resetPending;
//DMA bool dmaActive = false;
bool dmaActive; uint dmaClocks = 0;
uint dmaClocks; bool dmaPending = false;
bool dmaPending; bool hdmaPending = false;
bool hdmaPending; bool hdmaMode = 0; //0 = init, 1 = run
bool hdmaMode; //0 = init, 1 = run
//auto joypad polling bool autoJoypadActive = false;
bool autoJoypadActive; bool autoJoypadLatch = false;
bool autoJoypadLatch; uint autoJoypadCounter = 0;
uint autoJoypadCounter;
} status; } status;
struct IO { struct IO {
//$2181-$2183 //$2181-$2183
uint17 wramAddress; uint17 wramAddress;
//$4016-$4017
bool joypadStrobeLatch;
//$4200 //$4200
bool nmiEnabled; boolean hirqEnable;
bool hirqEnabled; boolean virqEnable;
bool virqEnabled; boolean irqEnable;
bool autoJoypadPoll; boolean nmiEnable;
boolean autoJoypadPoll;
//$4201 //$4201
uint8 pio; uint8 pio = 0xff;
//$4202-$4203 //$4202-$4203
uint8 wrmpya; uint8 wrmpya = 0xff;
uint8 wrmpyb; uint8 wrmpyb = 0xff;
//$4204-$4206 //$4204-$4206
uint16 wrdiva; uint16 wrdiva = 0xffff;
uint8 wrdivb; uint8 wrdivb = 0xff;
//$4207-$420a //$4207-$420a
uint9 hirqPos; uint9 hirqPos = 0x1ff;
uint9 virqPos; uint9 virqPos = 0x1ff;
//$420d //$420d
uint romSpeed; uint romSpeed = 8;
//$4214-$4217 //$4214-$4217
uint16 rddiv; uint16 rddiv;
@@ -175,34 +157,54 @@ private:
} io; } io;
struct ALU { struct ALU {
uint mpyctr; uint mpyctr = 0;
uint divctr; uint divctr = 0;
uint shift; uint shift = 0;
} alu; } alu;
struct Channel { struct Channel {
//dma.cpp
inline auto step(uint clocks) -> void;
inline auto edge() -> void;
inline auto valid(uint24 address) -> bool;
inline auto read(uint24 address, bool valid) -> uint8;
inline auto read(uint24 address) -> uint8;
inline auto flush() -> void;
inline auto write(uint24 address, uint8 data, bool valid) -> void;
inline auto write(uint24 address, uint8 data) -> void;
inline auto transfer(uint24 address, uint2 index) -> void;
inline auto dmaRun() -> void;
inline auto hdmaActive() -> bool;
inline auto hdmaFinished() -> bool;
inline auto hdmaReset() -> void;
inline auto hdmaSetup() -> void;
inline auto hdmaReload() -> void;
inline auto hdmaTransfer() -> void;
inline auto hdmaAdvance() -> void;
//$420b //$420b
bool dmaEnabled; uint1 dmaEnable;
//$420c //$420c
bool hdmaEnabled; uint1 hdmaEnable;
//$43x0 //$43x0
bool direction; uint3 transferMode = 7;
bool indirect; uint1 fixedTransfer = 1;
bool unused; uint1 reverseTransfer = 1;
bool reverseTransfer; uint1 unused = 1;
bool fixedTransfer; uint1 indirect = 1;
uint3 transferMode; uint1 direction = 1;
//$43x1 //$43x1
uint8 targetAddress; uint8 targetAddress = 0xff;
//$43x2-$43x3 //$43x2-$43x3
uint16 sourceAddress; uint16 sourceAddress = 0xffff;
//$43x4 //$43x4
uint8 sourceBank; uint8 sourceBank = 0xff;
//$43x5-$43x6 //$43x5-$43x6
union { union {
@@ -211,27 +213,29 @@ private:
}; };
//$43x7 //$43x7
uint8 indirectBank; uint8 indirectBank = 0xff;
//$43x8-$43x9 //$43x8-$43x9
uint16 hdmaAddress; uint16 hdmaAddress = 0xffff;
//$43xa //$43xa
uint8 lineCounter; uint8 lineCounter = 0xff;
//$43xb/$43xf //$43xb/$43xf
uint8 unknown; uint8 unknown = 0xff;
//internal state //internal state
bool hdmaCompleted; uint1 hdmaCompleted;
bool hdmaDoTransfer; uint1 hdmaDoTransfer;
Channel() : transferSize(0) {} maybe<Channel&> next;
} channel[8];
Channel() : transferSize(0xffff) {}
} channels[8];
struct Pipe { struct Pipe {
bool valid; uint1 valid;
uint addr; uint24 address;
uint8 data; uint8 data;
} pipe; } pipe;
}; };

View File

@@ -1,240 +1,204 @@
auto CPU::dmaEnable() -> bool {
for(auto& channel : channels) if(channel.dmaEnable) return true;
return false;
}
auto CPU::hdmaEnable() -> bool {
for(auto& channel : channels) if(channel.hdmaEnable) return true;
return false;
}
auto CPU::hdmaActive() -> bool {
for(auto& channel : channels) if(channel.hdmaActive()) return true;
return false;
}
auto CPU::dmaStep(uint clocks) -> void { auto CPU::dmaStep(uint clocks) -> void {
status.dmaClocks += clocks; status.dmaClocks += clocks;
step(clocks); step(clocks);
} }
//============= auto CPU::dmaFlush() -> void {
//memory access if(!pipe.valid) return;
//============= pipe.valid = false;
bus.write(pipe.address, pipe.data);
auto CPU::dmaTransferValid(uint8 bbus, uint24 abus) -> bool {
//transfers from WRAM to WRAM are invalid; chip only has one address bus
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false;
return true;
} }
auto CPU::dmaAddressValid(uint24 abus) -> bool {
//A-bus access to B-bus or S-CPU registers are invalid
if((abus & 0x40ff00) == 0x2100) return false; //$00-3f,80-bf:2100-21ff
if((abus & 0x40fe00) == 0x4000) return false; //$00-3f,80-bf:4000-41ff
if((abus & 0x40ffe0) == 0x4200) return false; //$00-3f,80-bf:4200-421f
if((abus & 0x40ff80) == 0x4300) return false; //$00-3f,80-bf:4300-437f
return true;
}
auto CPU::dmaRead(uint24 abus) -> uint8 {
if(!dmaAddressValid(abus)) return 0x00;
return bus.read(abus, r.mdr);
}
//simulate two-stage pipeline for DMA transfers; example:
//cycle 0: read N+0
//cycle 1: write N+0 & read N+1 (parallel; one on A-bus, one on B-bus)
//cycle 2: write N+1 & read N+2 (parallel)
//cycle 3: write N+2
auto CPU::dmaWrite(bool valid, uint addr, uint8 data) -> void {
if(pipe.valid) bus.write(pipe.addr, pipe.data);
pipe.valid = valid;
pipe.addr = addr;
pipe.data = data;
}
auto CPU::dmaTransfer(bool direction, uint8 bbus, uint24 abus) -> void {
if(direction == 0) {
dmaStep(4);
r.mdr = dmaRead(abus);
dmaStep(4);
dmaWrite(dmaTransferValid(bbus, abus), 0x2100 | bbus, r.mdr);
} else {
dmaStep(4);
r.mdr = dmaTransferValid(bbus, abus) ? bus.read(0x2100 | bbus, r.mdr) : (uint8)0x00;
dmaStep(4);
dmaWrite(dmaAddressValid(abus), abus, r.mdr);
}
}
//===================
//address calculation
//===================
auto CPU::dmaAddressB(uint n, uint index) -> uint8 {
switch(channel[n].transferMode) {
case 0: return (channel[n].targetAddress); //0
case 1: return (channel[n].targetAddress + (index & 1)); //0,1
case 2: return (channel[n].targetAddress); //0,0
case 3: return (channel[n].targetAddress + ((index >> 1) & 1)); //0,0,1,1
case 4: return (channel[n].targetAddress + (index & 3)); //0,1,2,3
case 5: return (channel[n].targetAddress + (index & 1)); //0,1,0,1
case 6: return (channel[n].targetAddress); //0,0 [2]
case 7: return (channel[n].targetAddress + ((index >> 1) & 1)); //0,0,1,1 [3]
}
unreachable;
}
auto CPU::dmaAddress(uint n) -> uint24 {
uint24 addr = channel[n].sourceBank << 16 | channel[n].sourceAddress;
if(!channel[n].fixedTransfer) {
if(!channel[n].reverseTransfer) {
channel[n].sourceAddress++;
} else {
channel[n].sourceAddress--;
}
}
return addr;
}
auto CPU::hdmaAddress(uint n) -> uint24 {
return channel[n].sourceBank << 16 | channel[n].hdmaAddress++;
}
auto CPU::hdmaIndirectAddress(uint n) -> uint24 {
return channel[n].indirectBank << 16 | channel[n].indirectAddress++;
}
//==============
//channel status
//==============
auto CPU::dmaEnabledChannels() -> uint {
uint count = 0;
for(auto n : range(8)) count += channel[n].dmaEnabled;
return count;
}
auto CPU::hdmaActive(uint n) -> bool {
return channel[n].hdmaEnabled && !channel[n].hdmaCompleted;
}
auto CPU::hdmaActiveAfter(uint s) -> bool {
for(uint n = s + 1; n < 8; n++) {
if(hdmaActive(n)) return true;
}
return false;
}
auto CPU::hdmaEnabledChannels() -> uint {
uint count = 0;
for(auto n : range(8)) count += channel[n].hdmaEnabled;
return count;
}
auto CPU::hdmaActiveChannels() -> uint {
uint count = 0;
for(auto n : range(8)) count += hdmaActive(n);
return count;
}
//==============
//core functions
//==============
auto CPU::dmaRun() -> void { auto CPU::dmaRun() -> void {
dmaStep(8); dmaStep(8);
dmaWrite(false); dmaFlush();
dmaEdge(); dmaEdge();
for(auto& channel : channels) channel.dmaRun();
for(auto n : range(8)) { dmaFlush();
if(!channel[n].dmaEnabled) continue;
uint index = 0;
do {
dmaTransfer(channel[n].direction, dmaAddressB(n, index++), dmaAddress(n));
dmaEdge();
} while(channel[n].dmaEnabled && --channel[n].transferSize);
dmaStep(8);
dmaWrite(false);
dmaEdge();
channel[n].dmaEnabled = false;
}
status.irqLock = true; status.irqLock = true;
} }
auto CPU::hdmaUpdate(uint n) -> void { auto CPU::hdmaReset() -> void {
dmaStep(4); for(auto& channel : channels) channel.hdmaReset();
r.mdr = dmaRead(channel[n].sourceBank << 16 | channel[n].hdmaAddress);
dmaStep(4);
dmaWrite(false);
if((channel[n].lineCounter & 0x7f) == 0) {
channel[n].lineCounter = r.mdr;
channel[n].hdmaAddress++;
channel[n].hdmaCompleted = channel[n].lineCounter == 0;
channel[n].hdmaDoTransfer = !channel[n].hdmaCompleted;
if(channel[n].indirect) {
dmaStep(4);
r.mdr = dmaRead(hdmaAddress(n));
channel[n].indirectAddress = r.mdr << 8;
dmaStep(4);
dmaWrite(false);
if(!channel[n].hdmaCompleted || hdmaActiveAfter(n)) {
dmaStep(4);
r.mdr = dmaRead(hdmaAddress(n));
channel[n].indirectAddress >>= 8;
channel[n].indirectAddress |= r.mdr << 8;
dmaStep(4);
dmaWrite(false);
}
}
} }
auto CPU::hdmaSetup() -> void {
dmaStep(8);
dmaFlush();
for(auto& channel : channels) channel.hdmaSetup();
dmaFlush();
status.irqLock = true;
} }
auto CPU::hdmaRun() -> void { auto CPU::hdmaRun() -> void {
dmaStep(8); dmaStep(8);
dmaWrite(false); dmaFlush();
for(auto& channel : channels) channel.hdmaTransfer();
for(auto n : range(8)) { for(auto& channel : channels) channel.hdmaAdvance();
if(!hdmaActive(n)) continue; dmaFlush();
channel[n].dmaEnabled = false; //HDMA run during DMA will stop DMA mid-transfer
if(channel[n].hdmaDoTransfer) {
static const uint transferLength[8] = {1, 2, 2, 4, 4, 4, 2, 4};
uint length = transferLength[channel[n].transferMode];
for(auto index : range(length)) {
uint addr = !channel[n].indirect ? hdmaAddress(n) : hdmaIndirectAddress(n);
dmaTransfer(channel[n].direction, dmaAddressB(n, index), addr);
}
}
}
for(auto n : range(8)) {
if(!hdmaActive(n)) continue;
channel[n].lineCounter--;
channel[n].hdmaDoTransfer = channel[n].lineCounter & 0x80;
hdmaUpdate(n);
}
status.irqLock = true; status.irqLock = true;
} }
auto CPU::hdmaInitReset() -> void { //
for(auto n : range(8)) {
channel[n].hdmaCompleted = false; auto CPU::Channel::step(uint clocks) -> void {
channel[n].hdmaDoTransfer = false; return cpu.dmaStep(clocks);
}
auto CPU::Channel::edge() -> void {
return cpu.dmaEdge();
}
auto CPU::Channel::valid(uint24 address) -> bool {
//A-bus cannot access the B-bus or CPU I/O registers
if((address & 0x40ff00) == 0x2100) return false; //00-3f,80-bf:2100-21ff
if((address & 0x40fe00) == 0x4000) return false; //00-3f,80-bf:4000-41ff
if((address & 0x40ffe0) == 0x4200) return false; //00-3f,80-bf:4200-421f
if((address & 0x40ff80) == 0x4300) return false; //00-3f,80-bf:4300-437f
return true;
}
auto CPU::Channel::read(uint24 address, bool valid) -> uint8 {
step(4);
cpu.r.mdr = valid ? bus.read(address, cpu.r.mdr) : (uint8)0x00;
step(4);
flush();
return cpu.r.mdr;
}
auto CPU::Channel::read(uint24 address) -> uint8 {
return read(address, valid(address));
}
auto CPU::Channel::flush() -> void {
return cpu.dmaFlush();
}
auto CPU::Channel::write(uint24 address, uint8 data, bool valid) -> void {
cpu.pipe.valid = valid;
cpu.pipe.address = address;
cpu.pipe.data = data;
}
auto CPU::Channel::write(uint24 address, uint8 data) -> void {
return write(address, data, valid(address));
}
auto CPU::Channel::transfer(uint24 aAddress, uint2 index) -> void {
uint24 bAddress = 0x2100 | targetAddress;
switch(transferMode) {
case 1: case 5: bAddress += index.bit(0); break;
case 3: case 7: bAddress += index.bit(1); break;
case 4: bAddress += index; break;
}
//transfers from WRAM to WRAM are invalid
bool valid = bAddress != 0x2180 || ((aAddress & 0xfe0000) != 0x7e0000 && (aAddress & 0x40e000) != 0x0000);
if(direction == 0) {
auto data = read(aAddress);
write(bAddress, data, valid);
} else {
auto data = read(bAddress, valid);
write(aAddress, data);
} }
} }
auto CPU::hdmaInit() -> void { auto CPU::Channel::dmaRun() -> void {
dmaStep(8); if(!dmaEnable) return;
dmaWrite(false);
for(auto n : range(8)) { uint2 index = 0;
channel[n].hdmaDoTransfer = true; //note: needs hardware verification (2017-08-09) do {
if(!channel[n].hdmaEnabled) continue; transfer(sourceBank << 16 | sourceAddress, index++);
channel[n].dmaEnabled = false; //HDMA init during DMA will stop DMA mid-transfer if(!fixedTransfer) !reverseTransfer ? sourceAddress++ : sourceAddress--;
edge();
} while(dmaEnable && --transferSize);
channel[n].hdmaAddress = channel[n].sourceAddress; step(8);
channel[n].lineCounter = 0; flush();
hdmaUpdate(n); edge();
dmaEnable = false;
} }
status.irqLock = true; auto CPU::Channel::hdmaActive() -> bool {
return hdmaEnable && !hdmaCompleted;
}
auto CPU::Channel::hdmaFinished() -> bool {
auto channel = next;
while(channel) {
if(channel->hdmaActive()) return false;
channel = channel->next;
}
return true;
}
auto CPU::Channel::hdmaReset() -> void {
hdmaCompleted = false;
hdmaDoTransfer = false;
}
auto CPU::Channel::hdmaSetup() -> void {
hdmaDoTransfer = true; //note: needs hardware verification
if(!hdmaEnable) return;
dmaEnable = false; //HDMA will stop active DMA mid-transfer
hdmaAddress = sourceAddress;
lineCounter = 0;
hdmaReload();
}
auto CPU::Channel::hdmaReload() -> void {
auto data = read(sourceBank << 16 | hdmaAddress);
if((uint7)lineCounter == 0) {
lineCounter = data;
hdmaAddress++;
hdmaCompleted = lineCounter == 0;
hdmaDoTransfer = !hdmaCompleted;
if(indirect) {
data = read(sourceBank << 16 | hdmaAddress++);
indirectAddress = data << 8 | 0x00; //todo: should 0x00 be indirectAddress >> 8 ?
if(hdmaCompleted && hdmaFinished()) return;
data = read(sourceBank << 16 | hdmaAddress++);
indirectAddress = data << 8 | indirectAddress >> 8;
}
}
}
auto CPU::Channel::hdmaTransfer() -> void {
if(!hdmaActive()) return;
dmaEnable = false; //HDMA will stop active DMA mid-transfer
if(!hdmaDoTransfer) return;
static const uint lengths[8] = {1, 2, 2, 4, 4, 4, 2, 4};
for(uint2 index : range(lengths[transferMode])) {
uint24 address = !indirect ? sourceBank << 16 | hdmaAddress++ : indirectBank << 16 | indirectAddress++;
transfer(address, index);
}
}
auto CPU::Channel::hdmaAdvance() -> void {
if(!hdmaActive()) return;
lineCounter--;
hdmaDoTransfer = lineCounter.bit(7);
hdmaReload();
} }

View File

@@ -1,3 +1,7 @@
auto CPU::readRAM(uint24 addr, uint8 data) -> uint8 {
return wram[addr];
}
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 { auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
synchronize(smp); synchronize(smp);
return smp.portRead(addr.bits(0,1)); return smp.portRead(addr.bits(0,1));
@@ -5,88 +9,44 @@ auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 { auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
switch((uint16)addr) { switch((uint16)addr) {
case 0x2180: //WMDATA
return bus.read(0x7e0000 | io.wramAddress++, data);
//WMDATA case 0x4016: //JOYSER0
case 0x2180: { data &= 0xfc;
return bus.read(0x7e0000 | io.wramAddress++, r.mdr); data |= controllerPort1.device->data();
} return data;
//JOYSER0 case 0x4017: //JOYSER1
//7-2 = MDR data &= 0xe0;
//1-0 = Joypad serial data data |= 0x1c; //pins are connected to GND
case 0x4016: { data |= controllerPort2.device->data();
uint8 v = r.mdr & 0xfc; return data;
v |= controllerPort1.device->data();
return v;
}
//JOYSER1 case 0x4210: //RDNMI
case 0x4017: { data &= 0x70;
//7-5 = MDR data |= rdnmi() << 7;
//4-2 = Always 1 (pins are connected to GND) data |= (uint4)version;
//1-0 = Joypad serial data return data;
uint8 v = (r.mdr & 0xe0) | 0x1c;
v |= controllerPort2.device->data();
return v;
}
//RDNMI case 0x4211: //TIMEUP
case 0x4210: { data &= 0x7f;
//7 = NMI acknowledge data |= timeup() << 7;
//6-4 = MDR return data;
//3-0 = CPU (5a22) version
uint8 v = (r.mdr & 0x70);
v |= (uint8)(rdnmi()) << 7;
v |= (version & 0x0f);
return v;
}
//TIMEUP case 0x4212: //HVBJOY
case 0x4211: { data &= 0x3e;
//7 = IRQ acknowledge data |= (status.autoJoypadActive) << 0;
//6-0 = MDR data |= (hcounter() <= 2 || hcounter() >= 1096) << 6; //hblank
uint8 v = (r.mdr & 0x7f); data |= (vcounter() >= ppu.vdisp()) << 7; //vblank
v |= (uint8)(timeup()) << 7; return data;
return v;
}
//HVBJOY case 0x4213: return io.pio; //RDIO
case 0x4212: {
//7 = VBLANK acknowledge
//6 = HBLANK acknowledge
//5-1 = MDR
//0 = JOYPAD acknowledge
uint8 v = (r.mdr & 0x3e);
if(status.autoJoypadActive) v |= 0x01;
if(hcounter() <= 2 || hcounter() >= 1096) v |= 0x40; //hblank
if(vcounter() >= ppu.vdisp()) v |= 0x80; //vblank
return v;
}
//RDIO case 0x4214: return io.rddiv.byte(0); //RDDIVL
case 0x4213: { case 0x4215: return io.rddiv.byte(1); //RDDIVH
return io.pio; case 0x4216: return io.rdmpy.byte(0); //RDMPYL
} case 0x4217: return io.rdmpy.byte(1); //RDMPYH
//RDDIVL
case 0x4214: {
return io.rddiv.byte(0);
}
//RDDIVH
case 0x4215: {
return io.rddiv.byte(1);
}
//RDMPYL
case 0x4216: {
return io.rdmpy.byte(0);
}
//RDMPYH
case 0x4217: {
return io.rdmpy.byte(1);
}
case 0x4218: return io.joy1.byte(0); //JOY1L case 0x4218: return io.joy1.byte(0); //JOY1L
case 0x4219: return io.joy1.byte(1); //JOY1H case 0x4219: return io.joy1.byte(1); //JOY1H
@@ -103,12 +63,12 @@ auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
} }
auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 { auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 {
auto& channel = this->channel[addr.bits(4,6)]; auto& channel = this->channels[addr.bits(4,6)];
switch(addr & 0xff0f) { switch(addr & 0xff8f) {
//DMAPx case 0x4300: //DMAPx
case 0x4300: return ( return (
channel.transferMode << 0 channel.transferMode << 0
| channel.fixedTransfer << 3 | channel.fixedTransfer << 3
| channel.reverseTransfer << 4 | channel.reverseTransfer << 4
@@ -117,45 +77,28 @@ auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 {
| channel.direction << 7 | channel.direction << 7
); );
//BBADx case 0x4301: return channel.targetAddress; //BBADx
case 0x4301: return channel.targetAddress; case 0x4302: return channel.sourceAddress.byte(0); //A1TxL
case 0x4303: return channel.sourceAddress.byte(1); //A1TxH
//A1TxL case 0x4304: return channel.sourceBank; //A1Bx
case 0x4302: return channel.sourceAddress >> 0; case 0x4305: return channel.transferSize.byte(0); //DASxL
case 0x4306: return channel.transferSize.byte(1); //DASxH
//A1TxH case 0x4307: return channel.indirectBank; //DASBx
case 0x4303: return channel.sourceAddress >> 8; case 0x4308: return channel.hdmaAddress.byte(0); //A2AxL
case 0x4309: return channel.hdmaAddress.byte(1); //A2AxH
//A1Bx case 0x430a: return channel.lineCounter; //NTRLx
case 0x4304: return channel.sourceBank; case 0x430b: return channel.unknown; //???x
case 0x430f: return channel.unknown; //???x ($43xb mirror)
//DASxL -- union { uint16 transferSize; uint16 indirectAddress; };
case 0x4305: return channel.transferSize.byte(0);
//DASxH -- union { uint16 transferSize; uint16 indirectAddress; };
case 0x4306: return channel.transferSize.byte(1);
//DASBx
case 0x4307: return channel.indirectBank;
//A2AxL
case 0x4308: return channel.hdmaAddress.byte(0);
//A2AxH
case 0x4309: return channel.hdmaAddress.byte(1);
//NTRLx
case 0x430a: return channel.lineCounter;
//???
case 0x430b:
case 0x430f: return channel.unknown;
} }
return data; return data;
} }
auto CPU::writeRAM(uint24 addr, uint8 data) -> void {
wram[addr] = data;
}
auto CPU::writeAPU(uint24 addr, uint8 data) -> void { auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
synchronize(smp); synchronize(smp);
return smp.portWrite(addr.bits(0,1), data); return smp.portWrite(addr.bits(0,1), data);
@@ -164,44 +107,44 @@ auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
auto CPU::writeCPU(uint24 addr, uint8 data) -> void { auto CPU::writeCPU(uint24 addr, uint8 data) -> void {
switch((uint16)addr) { switch((uint16)addr) {
//WMDATA case 0x2180: //WMDATA
case 0x2180: {
return bus.write(0x7e0000 | io.wramAddress++, data); return bus.write(0x7e0000 | io.wramAddress++, data);
}
case 0x2181: io.wramAddress.bits( 0, 7) = data; return; //WMADDL case 0x2181: //WMADDL
case 0x2182: io.wramAddress.bits( 8,15) = data; return; //WMADDM io.wramAddress.bits(0,7) = data;
case 0x2183: io.wramAddress.bit (16 ) = data.bit(0); return; //WMADDH return;
//JOYSER0 case 0x2182: //WMADDM
case 0x4016: { io.wramAddress.bits(8,15) = data;
//bit 0 is shared between JOYSER0 and JOYSER1, therefore return;
case 0x2183: //WMADDH
io.wramAddress.bit(16) = data.bit(0);
return;
case 0x4016: //JOYSER0
//bit 0 is shared between JOYSER0 and JOYSER1:
//strobing $4016.d0 affects both controller port latches. //strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored. //$4017 bit 0 writes are ignored.
controllerPort1.device->latch(data.bit(0)); controllerPort1.device->latch(data.bit(0));
controllerPort2.device->latch(data.bit(0)); controllerPort2.device->latch(data.bit(0));
return; return;
}
//NMITIMEN case 0x4200: //NMITIMEN
case 0x4200: {
io.autoJoypadPoll = data.bit(0); io.autoJoypadPoll = data.bit(0);
nmitimenUpdate(data); nmitimenUpdate(data);
return; return;
}
//WRIO case 0x4201: //WRIO
case 0x4201: {
if(io.pio.bit(7) && !data.bit(7)) ppu.latchCounters(); if(io.pio.bit(7) && !data.bit(7)) ppu.latchCounters();
io.pio = data; io.pio = data;
return; return;
}
//WRMPYA case 0x4202: //WRMPYA
case 0x4202: io.wrmpya = data; return; io.wrmpya = data;
return;
//WRMPYB case 0x4203: //WRMPYB
case 0x4203: {
io.rdmpy = 0; io.rdmpy = 0;
if(alu.mpyctr || alu.divctr) return; if(alu.mpyctr || alu.divctr) return;
@@ -211,13 +154,16 @@ auto CPU::writeCPU(uint24 addr, uint8 data) -> void {
alu.mpyctr = 8; //perform multiplication over the next eight cycles alu.mpyctr = 8; //perform multiplication over the next eight cycles
alu.shift = io.wrmpyb; alu.shift = io.wrmpyb;
return; return;
}
case 0x4204: { io.wrdiva.byte(0) = data; return; } //WRDIVL case 0x4204: //WRDIVL
case 0x4205: { io.wrdiva.byte(1) = data; return; } //WRDIVH io.wrdiva.byte(0) = data;
return;
//WRDIVB case 0x4205: //WRDIVH
case 0x4206: { io.wrdiva.byte(1) = data;
return;
case 0x4206: //WRDIVB
io.rdmpy = io.wrdiva; io.rdmpy = io.wrdiva;
if(alu.mpyctr || alu.divctr) return; if(alu.mpyctr || alu.divctr) return;
@@ -226,43 +172,45 @@ auto CPU::writeCPU(uint24 addr, uint8 data) -> void {
alu.divctr = 16; //perform division over the next sixteen cycles alu.divctr = 16; //perform division over the next sixteen cycles
alu.shift = io.wrdivb << 16; alu.shift = io.wrdivb << 16;
return; return;
}
case 0x4207: io.hirqPos.bits(0,7) = data; return; //HTIMEL case 0x4207: //HTIMEL
case 0x4208: io.hirqPos.bit (8 ) = data.bit(0); return; //HTIMEH io.hirqPos.bits(0,7) = data;
return;
case 0x4209: io.virqPos.bits(0,7) = data; return; //VTIMEL case 0x4208: //HTIMEH
case 0x420a: io.virqPos.bit (8 ) = data.bit(0); return; //VTIMEH io.hirqPos.bit(8) = data.bit(0);
return;
//DMAEN case 0x4209: //VTIMEL
case 0x420b: { io.virqPos.bits(0,7) = data;
for(auto n : range(8)) channel[n].dmaEnabled = data.bit(n); return;
case 0x420a: //VTIMEH
io.virqPos.bit(8) = data.bit(0);
return;
case 0x420b: //DMAEN
for(auto n : range(8)) channels[n].dmaEnable = data.bit(n);
if(data) status.dmaPending = true; if(data) status.dmaPending = true;
return; return;
}
//HDMAEN case 0x420c: //HDMAEN
case 0x420c: { for(auto n : range(8)) channels[n].hdmaEnable = data.bit(n);
for(auto n : range(8)) channel[n].hdmaEnabled = data.bit(n);
return; return;
}
//MEMSEL case 0x420d: //MEMSEL
case 0x420d: {
io.romSpeed = data.bit(0) ? 6 : 8; io.romSpeed = data.bit(0) ? 6 : 8;
return; return;
}
} }
} }
auto CPU::writeDMA(uint24 addr, uint8 data) -> void { auto CPU::writeDMA(uint24 addr, uint8 data) -> void {
auto& channel = this->channel[addr.bits(4,6)]; auto& channel = this->channels[addr.bits(4,6)];
switch(addr & 0xff0f) { switch(addr & 0xff8f) {
//DMAPx case 0x4300: //DMAPx
case 0x4300: {
channel.transferMode = data.bits(0,2); channel.transferMode = data.bits(0,2);
channel.fixedTransfer = data.bit (3); channel.fixedTransfer = data.bit (3);
channel.reverseTransfer = data.bit (4); channel.reverseTransfer = data.bit (4);
@@ -270,41 +218,54 @@ auto CPU::writeDMA(uint24 addr, uint8 data) -> void {
channel.indirect = data.bit (6); channel.indirect = data.bit (6);
channel.direction = data.bit (7); channel.direction = data.bit (7);
return; return;
}
//DDBADx case 0x4301: //BBADx
case 0x4301: channel.targetAddress = data; return; channel.targetAddress = data;
return;
//A1TxL case 0x4302: //A1TxL
case 0x4302: channel.sourceAddress.byte(0) = data; return; channel.sourceAddress.byte(0) = data;
return;
//A1TxH case 0x4303: //A1TxH
case 0x4303: channel.sourceAddress.byte(1) = data; return; channel.sourceAddress.byte(1) = data;
return;
//A1Bx case 0x4304: //A1Bx
case 0x4304: channel.sourceBank = data; return; channel.sourceBank = data;
return;
//DASxL -- union { uint16 transferSize; uint16 indirectAddress; }; case 0x4305: //DASxL
case 0x4305: channel.transferSize.byte(0) = data; return; channel.transferSize.byte(0) = data;
return;
//DASxH -- union { uint16 transferSize; uint16 indirectAddress; }; case 0x4306: //DASxH
case 0x4306: channel.transferSize.byte(1) = data; return; channel.transferSize.byte(1) = data;
return;
//DASBx case 0x4307: //DASBx
case 0x4307: channel.indirectBank = data; return; channel.indirectBank = data;
return;
//A2AxL case 0x4308: //A2AxL
case 0x4308: channel.hdmaAddress.byte(0) = data; return; channel.hdmaAddress.byte(0) = data;
return;
//A2AxH case 0x4309: //A2AxH
case 0x4309: channel.hdmaAddress.byte(1) = data; return; channel.hdmaAddress.byte(1) = data;
return;
//NTRLx case 0x430a: //NTRLx
case 0x430a: channel.lineCounter = data; return; channel.lineCounter = data;
return;
//??? case 0x430b: //???x
case 0x430b: channel.unknown = data;
case 0x430f: channel.unknown = data; return; return;
case 0x430f: //???x ($43xb mirror)
channel.unknown = data;
return;
} }
} }

View File

@@ -5,68 +5,40 @@
//it is used to emulate hardware communication delay between opcode and interrupt units. //it is used to emulate hardware communication delay between opcode and interrupt units.
auto CPU::pollInterrupts() -> void { auto CPU::pollInterrupts() -> void {
//NMI hold //NMI hold
if(status.nmiHold) { if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true;
status.nmiHold = false;
if(io.nmiEnabled) status.nmiTransition = true;
}
//NMI test //NMI test
bool nmiValid = vcounter(2) >= ppu.vdisp(); if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
if(!status.nmiValid && nmiValid) { if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles
//0->1 edge sensitive transition
status.nmiLine = true;
status.nmiHold = true; //hold /NMI for four cycles
} else if(status.nmiValid && !nmiValid) {
//1->0 edge sensitive transition
status.nmiLine = false;
} }
status.nmiValid = nmiValid;
//IRQ hold //IRQ hold
status.irqHold = false; status.irqHold = false;
if(status.irqLine) { if(status.irqLine && io.irqEnable) status.irqTransition = true;
if(io.virqEnabled || io.hirqEnabled) status.irqTransition = true;
}
//IRQ test //IRQ test
bool irqValid = io.virqEnabled || io.hirqEnabled; if(status.irqValid.raise(io.irqEnable
if(irqValid) { && (!io.virqEnable || vcounter(10) == io.virqPos)
if((io.virqEnabled && vcounter(10) != (io.virqPos)) && (!io.hirqEnable || hcounter(10) == io.hirqPos + 1 << 2)
|| (io.hirqEnabled && hcounter(10) != (io.hirqPos + 1) * 4) )) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles
|| (io.virqPos && vcounter(6) == 0) //IRQs cannot trigger on last dot of field
) irqValid = false;
}
if(!status.irqValid && irqValid) {
//0->1 edge sensitive transition
status.irqLine = true;
status.irqHold = true; //hold /IRQ for four cycles
}
status.irqValid = irqValid;
} }
auto CPU::nmitimenUpdate(uint8 data) -> void { auto CPU::nmitimenUpdate(uint8 data) -> void {
bool nmiEnabled = io.nmiEnabled; io.hirqEnable = data.bit(4);
bool virqEnabled = io.virqEnabled; io.virqEnable = data.bit(5);
bool hirqEnabled = io.hirqEnabled; io.irqEnable = io.hirqEnable || io.virqEnable;
io.nmiEnabled = data & 0x80;
io.virqEnabled = data & 0x20;
io.hirqEnabled = data & 0x10;
//0->1 edge sensitive transition if(io.virqEnable && !io.hirqEnable && status.irqLine) {
if(!nmiEnabled && io.nmiEnabled && status.nmiLine) {
status.nmiTransition = true;
}
//?->1 level sensitive transition
if(io.virqEnabled && !io.hirqEnabled && status.irqLine) {
status.irqTransition = true; status.irqTransition = true;
} } else if(!io.irqEnable) {
if(!io.virqEnabled && !io.hirqEnabled) {
status.irqLine = false; status.irqLine = false;
status.irqTransition = false; status.irqTransition = false;
} }
if(io.nmiEnable.raise(data.bit(7)) && status.nmiLine) {
status.nmiTransition = true;
}
status.irqLock = true; status.irqLock = true;
} }

View File

@@ -8,8 +8,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(version); s.integer(version);
s.integer(clockCounter); s.integer(clockCounter);
s.integer(status.interruptPending);
s.integer(status.clockCount); s.integer(status.clockCount);
s.integer(status.lineClocks); s.integer(status.lineClocks);
@@ -18,27 +16,29 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(status.dramRefreshPosition); s.integer(status.dramRefreshPosition);
s.integer(status.dramRefreshed); s.integer(status.dramRefreshed);
s.integer(status.hdmaInitPosition); s.integer(status.hdmaSetupPosition);
s.integer(status.hdmaInitTriggered); s.integer(status.hdmaSetupTriggered);
s.integer(status.hdmaPosition); s.integer(status.hdmaPosition);
s.integer(status.hdmaTriggered); s.integer(status.hdmaTriggered);
s.integer(status.nmiValid); s.boolean(status.nmiValid);
s.integer(status.nmiLine); s.boolean(status.nmiLine);
s.integer(status.nmiTransition); s.boolean(status.nmiTransition);
s.integer(status.nmiPending); s.boolean(status.nmiPending);
s.integer(status.nmiHold); s.boolean(status.nmiHold);
s.integer(status.irqValid); s.boolean(status.irqValid);
s.integer(status.irqLine); s.boolean(status.irqLine);
s.integer(status.irqTransition); s.boolean(status.irqTransition);
s.integer(status.irqPending); s.boolean(status.irqPending);
s.integer(status.irqHold); s.boolean(status.irqHold);
s.integer(status.powerPending); s.integer(status.powerPending);
s.integer(status.resetPending); s.integer(status.resetPending);
s.integer(status.interruptPending);
s.integer(status.dmaActive); s.integer(status.dmaActive);
s.integer(status.dmaClocks); s.integer(status.dmaClocks);
s.integer(status.dmaPending); s.integer(status.dmaPending);
@@ -51,12 +51,11 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(io.wramAddress); s.integer(io.wramAddress);
s.integer(io.joypadStrobeLatch); s.boolean(io.hirqEnable);
s.boolean(io.virqEnable);
s.integer(io.nmiEnabled); s.boolean(io.irqEnable);
s.integer(io.hirqEnabled); s.boolean(io.nmiEnable);
s.integer(io.virqEnabled); s.boolean(io.autoJoypadPoll);
s.integer(io.autoJoypadPoll);
s.integer(io.pio); s.integer(io.pio);
@@ -83,9 +82,9 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(alu.divctr); s.integer(alu.divctr);
s.integer(alu.shift); s.integer(alu.shift);
for(auto& channel : this->channel) { for(auto& channel : channels) {
s.integer(channel.dmaEnabled); s.integer(channel.dmaEnable);
s.integer(channel.hdmaEnabled); s.integer(channel.hdmaEnable);
s.integer(channel.direction); s.integer(channel.direction);
s.integer(channel.indirect); s.integer(channel.indirect);
s.integer(channel.unused); s.integer(channel.unused);
@@ -105,6 +104,6 @@ auto CPU::serialize(serializer& s) -> void {
} }
s.integer(pipe.valid); s.integer(pipe.valid);
s.integer(pipe.addr); s.integer(pipe.address);
s.integer(pipe.data); s.integer(pipe.data);
} }

View File

@@ -39,9 +39,9 @@ auto CPU::scanline() -> void {
for(auto coprocessor : coprocessors) synchronize(*coprocessor); for(auto coprocessor : coprocessors) synchronize(*coprocessor);
if(vcounter() == 0) { if(vcounter() == 0) {
//HDMA init triggers once every frame //HDMA setup triggers once every frame
status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter()); status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaInitTriggered = false; status.hdmaSetupTriggered = false;
status.autoJoypadCounter = 0; status.autoJoypadCounter = 0;
} }
@@ -88,12 +88,12 @@ auto CPU::dmaEdge() -> void {
if(status.dmaActive) { if(status.dmaActive) {
if(status.hdmaPending) { if(status.hdmaPending) {
status.hdmaPending = false; status.hdmaPending = false;
if(hdmaEnabledChannels()) { if(hdmaEnable()) {
if(!dmaEnabledChannels()) { if(!dmaEnable()) {
dmaStep(8 - dmaCounter()); dmaStep(8 - dmaCounter());
} }
status.hdmaMode == 0 ? hdmaInit() : hdmaRun(); status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnabledChannels()) { if(!dmaEnable()) {
step(status.clockCount - (status.dmaClocks % status.clockCount)); step(status.clockCount - (status.dmaClocks % status.clockCount));
status.dmaActive = false; status.dmaActive = false;
} }
@@ -102,7 +102,7 @@ auto CPU::dmaEdge() -> void {
if(status.dmaPending) { if(status.dmaPending) {
status.dmaPending = false; status.dmaPending = false;
if(dmaEnabledChannels()) { if(dmaEnable()) {
dmaStep(8 - dmaCounter()); dmaStep(8 - dmaCounter());
dmaRun(); dmaRun();
step(status.clockCount - (status.dmaClocks % status.clockCount)); step(status.clockCount - (status.dmaClocks % status.clockCount));
@@ -111,10 +111,10 @@ auto CPU::dmaEdge() -> void {
} }
} }
if(!status.hdmaInitTriggered && hcounter() >= status.hdmaInitPosition) { if(!status.hdmaSetupTriggered && hcounter() >= status.hdmaSetupPosition) {
status.hdmaInitTriggered = true; status.hdmaSetupTriggered = true;
hdmaInitReset(); hdmaReset();
if(hdmaEnabledChannels()) { if(hdmaEnable()) {
status.hdmaPending = true; status.hdmaPending = true;
status.hdmaMode = 0; status.hdmaMode = 0;
} }
@@ -122,7 +122,7 @@ auto CPU::dmaEdge() -> void {
if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) { if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) {
status.hdmaTriggered = true; status.hdmaTriggered = true;
if(hdmaActiveChannels()) { if(hdmaActive()) {
status.hdmaPending = true; status.hdmaPending = true;
status.hdmaMode = 1; status.hdmaMode = 1;
} }
@@ -177,8 +177,8 @@ auto CPU::joypadEdge() -> void {
//trigger during certain events (immediately after DMA, writes to $4200, etc) //trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void { auto CPU::lastCycle() -> void {
if(!status.irqLock) { if(!status.irqLock) {
status.nmiPending |= nmiTest(); if(nmiTest()) status.nmiPending = true;
status.irqPending |= irqTest(); if(irqTest()) status.irqPending = true;
status.interruptPending = (status.nmiPending || status.irqPending); status.interruptPending = (status.nmiPending || status.irqPending);
} }
} }

View File

@@ -26,27 +26,27 @@ auto PPUcounter::tick(uint clocks) -> void {
//internal //internal
auto PPUcounter::vcounterTick() -> void { auto PPUcounter::vcounterTick() -> void {
if(++status.vcounter == 128) status.interlace = ppu.interlace(); if(++status.vcounter == 128) status.interlace = ppu.interlace();
if(vcounter() == (Region::NTSC() ? 262 : 312) + (interlace() && !field())) {
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.vcounter = 0;
status.field = !status.field; status.field ^= 1;
} }
status.lineclocks = 1364;
//NTSC and PAL scanlines rates would not match up with color clocks if every scanline were 1364 clocks
//to offset for this error, NTSC has one short scanline, and PAL has one long scanline
if(Region::NTSC() && interlace() == 0 && field() == 1 && vcounter() == 240) status.lineclocks -= 4;
if(Region::PAL() && interlace() == 1 && field() == 1 && vcounter() == 311) status.lineclocks += 4;
if(scanline) scanline(); if(scanline) scanline();
} }
auto PPUcounter::interlace() const -> bool { return status.interlace; }
auto PPUcounter::field() const -> bool { return status.field; } auto PPUcounter::field() const -> bool { return status.field; }
auto PPUcounter::vcounter() const -> uint16 { return status.vcounter; } auto PPUcounter::vcounter() const -> uint { return status.vcounter; }
auto PPUcounter::hcounter() const -> uint16 { return status.hcounter; } auto PPUcounter::hcounter() const -> uint { return status.hcounter; }
auto PPUcounter::lineclocks() const -> uint { return status.lineclocks; }
auto PPUcounter::field(uint offset) const -> bool { return history.field[(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::field(uint offset) const -> bool { return history.field[(history.index - (offset >> 1)) & 2047]; }
auto PPUcounter::vcounter(uint offset) const -> uint16 { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::vcounter(uint offset) const -> uint { return history.vcounter[(history.index - (offset >> 1)) & 2047]; }
auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::hcounter(uint offset) const -> uint { return history.hcounter[(history.index - (offset >> 1)) & 2047]; }
//one PPU dot = 4 CPU clocks //one PPU dot = 4 CPU clocks
// //
@@ -57,29 +57,15 @@ auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter
//dot 323 range = {1292, 1294, 1296} //dot 323 range = {1292, 1294, 1296}
//dot 327 range = {1310, 1312, 1314} //dot 327 range = {1310, 1312, 1314}
auto PPUcounter::hdot() const -> uint16 { auto PPUcounter::hdot() const -> uint {
if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) { if(lineclocks() == 1360) {
return (hcounter() >> 2); return (hcounter() >> 2);
} else { } else {
return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2; return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2;
} }
} }
auto PPUcounter::lineclocks() const -> uint16 {
if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) return 1360;
return 1364;
}
auto PPUcounter::reset() -> void { auto PPUcounter::reset() -> void {
status.interlace = 0; status = {};
status.field = 0; history = {};
status.vcounter = 0;
status.hcounter = 0;
history.index = 0;
for(auto n : range(2048)) {
history.field [n] = 0;
history.vcounter[n] = 0;
history.hcounter[n] = 0;
}
} }

View File

@@ -14,36 +14,37 @@ struct PPUcounter {
alwaysinline auto tick() -> void; alwaysinline auto tick() -> void;
alwaysinline auto tick(uint clocks) -> void; alwaysinline auto tick(uint clocks) -> void;
alwaysinline auto interlace() const -> bool;
alwaysinline auto field() const -> bool; alwaysinline auto field() const -> bool;
alwaysinline auto vcounter() const -> uint16; alwaysinline auto vcounter() const -> uint;
alwaysinline auto hcounter() const -> uint16; alwaysinline auto hcounter() const -> uint;
inline auto hdot() const -> uint16; alwaysinline auto hdot() const -> uint;
inline auto lineclocks() const -> uint16; alwaysinline auto lineclocks() const -> uint;
alwaysinline auto field(uint offset) const -> bool; alwaysinline auto field(uint offset) const -> bool;
alwaysinline auto vcounter(uint offset) const -> uint16; alwaysinline auto vcounter(uint offset) const -> uint;
alwaysinline auto hcounter(uint offset) const -> uint16; alwaysinline auto hcounter(uint offset) const -> uint;
inline auto reset() -> void; inline auto reset() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
function<auto () -> void> scanline; function<void ()> scanline;
private: private:
inline auto vcounterTick() -> void; alwaysinline auto vcounterTick() -> void;
struct { struct {
bool interlace; bool interlace = 0;
bool field; bool field = 0;
uint16 vcounter; uint vcounter = 0;
uint16 hcounter; uint hcounter = 0;
uint lineclocks = 1364;
} status; } status;
struct { struct {
bool field[2048]; uint index = 0;
uint16 vcounter[2048]; bool field[2048] = {};
uint16 hcounter[2048]; uint vcounter[2048] = {};
uint hcounter[2048] = {};
int32 index;
} history; } history;
}; };

182
higan/sfc/smp/io.cpp Normal file
View File

@@ -0,0 +1,182 @@
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::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::readIO(uint16 address) -> uint8 {
uint8 data;
switch(address) {
case 0xf0: //TEST (write-only register)
return 0x00;
case 0xf1: //CONTROL (write-only register)
return 0x00;
case 0xf2: //DSPADDR
return io.dspAddr;
case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f
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 io.apu3;
case 0xf8: //AUXIO4
return io.aux4;
case 0xf9: //AUXIO5
return io.aux5;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET (write-only registers)
return 0x00;
case 0xfd: //T0OUT (4-bit counter value)
data = timer0.stage3;
timer0.stage3 = 0;
return data;
case 0xfe: //T1OUT (4-bit counter value)
data = timer1.stage3;
timer1.stage3 = 0;
return data;
case 0xff: //T2OUT (4-bit counter value)
data = timer2.stage3;
timer2.stage3 = 0;
return data;
}
return data; //unreachable
}
auto SMP::writeIO(uint16 address, uint8 data) -> void {
switch(address) {
case 0xf0: //TEST
if(r.p.p) break; //writes only valid when P flag is clear
io.timersDisable = data.bit (0);
io.ramWritable = data.bit (1);
io.ramDisable = data.bit (2);
io.timersEnable = data.bit (3);
io.externalWaitStates = data.bits(4,5);
io.internalWaitStates = data.bits(6,7);
timer0.synchronizeStage1();
timer1.synchronizeStage1();
timer2.synchronizeStage1();
break;
case 0xf1: //CONTROL
//0->1 transistion resets timers
if(timer0.enable.raise(data.bit(0))) {
timer0.stage2 = 0;
timer0.stage3 = 0;
}
if(timer1.enable.raise(data.bit(1))) {
timer1.stage2 = 0;
timer1.stage3 = 0;
}
if(!timer2.enable.raise(data.bit(2))) {
timer2.stage2 = 0;
timer2.stage3 = 0;
}
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
io.dspAddr = data;
break;
case 0xf3: //DSPDATA
if(io.dspAddr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f
dsp.write(io.dspAddr & 0x7f, data);
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);
io.cpu3 = data;
break;
case 0xf8: //AUXIO4
io.aux4 = data;
break;
case 0xf9: //AUXIO5
io.aux5 = data;
break;
case 0xfa: //T0TARGET
timer0.target = data;
break;
case 0xfb: //T1TARGET
timer1.target = data;
break;
case 0xfc: //T2TARGET
timer2.target = data;
break;
case 0xfd: //T0OUT
case 0xfe: //T1OUT
case 0xff: //T2OUT (read-only registers)
break;
}
}

View File

@@ -1,219 +1,32 @@
alwaysinline auto SMP::ramRead(uint16 addr) -> uint8 { auto SMP::readRAM(uint16 address) -> uint8 {
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f]; if(address >= 0xffc0 && io.iplromEnable) return iplrom[address & 0x3f];
if(io.ramDisable) return 0x5a; //0xff on mini-SNES if(io.ramDisable) return 0x5a; //0xff on mini-SNES
return dsp.apuram[addr]; return dsp.apuram[address];
} }
alwaysinline auto SMP::ramWrite(uint16 addr, uint8 data) -> void { auto SMP::writeRAM(uint16 address, uint8 data) -> void {
//writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled //writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled
if(io.ramWritable && !io.ramDisable) dsp.apuram[addr] = data; if(io.ramWritable && !io.ramDisable) dsp.apuram[address] = data;
}
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::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::busRead(uint16 addr) -> uint8 {
uint result;
switch(addr) {
case 0xf0: //TEST (write-only register)
return 0x00;
case 0xf1: //CONTROL (write-only register)
return 0x00;
case 0xf2: //DSPADDR
return io.dspAddr;
case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f
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 io.apu3;
case 0xf8: //AUXIO4
return io.aux4;
case 0xf9: //AUXIO5
return io.aux5;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET (write-only registers)
return 0x00;
case 0xfd: //T0OUT (4-bit counter value)
result = timer0.stage3;
timer0.stage3 = 0;
return result;
case 0xfe: //T1OUT (4-bit counter value)
result = timer1.stage3;
timer1.stage3 = 0;
return result;
case 0xff: //T2OUT (4-bit counter value)
result = timer2.stage3;
timer2.stage3 = 0;
return result;
}
return ramRead(addr);
}
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.timersDisable = data.bit (0);
io.ramWritable = data.bit (1);
io.ramDisable = data.bit (2);
io.timersEnable = data.bit (3);
io.externalWaitStates = data.bits(4,5);
io.internalWaitStates = data.bits(6,7);
timer0.synchronizeStage1();
timer1.synchronizeStage1();
timer2.synchronizeStage1();
break;
case 0xf1: //CONTROL
//0->1 transistion resets timers
if(!timer0.enable && data.bit(0)) {
timer0.stage2 = 0;
timer0.stage3 = 0;
}
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
io.dspAddr = data;
break;
case 0xf3: //DSPDATA
if(io.dspAddr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f
dsp.write(io.dspAddr & 0x7f, data);
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);
io.cpu3 = data;
break;
case 0xf8: //AUXIO4
io.aux4 = data;
break;
case 0xf9: //AUXIO5
io.aux5 = data;
break;
case 0xfa: //T0TARGET
timer0.target = data;
break;
case 0xfb: //T1TARGET
timer1.target = data;
break;
case 0xfc: //T2TARGET
timer2.target = data;
break;
case 0xfd: //T0OUT
case 0xfe: //T1OUT
case 0xff: //T2OUT (read-only registers)
break;
}
ramWrite(addr, data); //all writes, even to I/O registers, appear on bus
} }
auto SMP::idle() -> void { auto SMP::idle() -> void {
wait(); wait();
} }
auto SMP::read(uint16 addr) -> uint8 { auto SMP::read(uint16 address) -> uint8 {
wait(addr); wait(address);
uint8 data = busRead(addr); uint8 data = readRAM(address);
if((address & 0xfff0) == 0x00f0) data = readIO(address);
return data; return data;
} }
auto SMP::write(uint16 addr, uint8 data) -> void { auto SMP::write(uint16 address, uint8 data) -> void {
wait(addr); wait(address);
busWrite(addr, data); writeRAM(address, data); //even IO writes affect underlying RAM
if((address & 0xfff0) == 0x00f0) writeIO(address, data);
} }
auto SMP::readDisassembler(uint16 addr) -> uint8 { auto SMP::readDisassembler(uint16 address) -> uint8 {
if((addr & 0xfff0) == 0x00f0) return 0x00; if((address & 0xfff0) == 0x00f0) return 0x00;
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f]; return readRAM(address);
return dsp.apuram[addr];
} }

View File

@@ -33,23 +33,23 @@ auto SMP::serialize(serializer& s) -> void {
s.integer(timer0.stage1); s.integer(timer0.stage1);
s.integer(timer0.stage2); s.integer(timer0.stage2);
s.integer(timer0.stage3); s.integer(timer0.stage3);
s.integer(timer0.line); s.boolean(timer0.line);
s.integer(timer0.enable); s.boolean(timer0.enable);
s.integer(timer0.target); s.integer(timer0.target);
s.integer(timer1.stage0); s.integer(timer1.stage0);
s.integer(timer1.stage1); s.integer(timer1.stage1);
s.integer(timer1.stage2); s.integer(timer1.stage2);
s.integer(timer1.stage3); s.integer(timer1.stage3);
s.integer(timer1.line); s.boolean(timer1.line);
s.integer(timer1.enable); s.boolean(timer1.enable);
s.integer(timer1.target); s.integer(timer1.target);
s.integer(timer2.stage0); s.integer(timer2.stage0);
s.integer(timer2.stage1); s.integer(timer2.stage1);
s.integer(timer2.stage2); s.integer(timer2.stage2);
s.integer(timer2.stage3); s.integer(timer2.stage3);
s.integer(timer2.line); s.boolean(timer2.line);
s.integer(timer2.enable); s.boolean(timer2.enable);
s.integer(timer2.target); s.integer(timer2.target);
} }

View File

@@ -3,15 +3,11 @@
namespace SuperFamicom { namespace SuperFamicom {
SMP smp; SMP smp;
#include "memory.cpp" #include "memory.cpp"
#include "io.cpp"
#include "timing.cpp" #include "timing.cpp"
#include "serialization.cpp" #include "serialization.cpp"
auto SMP::synchronizing() const -> bool {
return scheduler.synchronizing();
}
auto SMP::Enter() -> void { auto SMP::Enter() -> void {
while(true) scheduler.synchronize(), smp.main(); while(true) scheduler.synchronize(), smp.main();
} }
@@ -39,67 +35,10 @@ auto SMP::power(bool reset) -> void {
r.pc.byte.l = iplrom[62]; r.pc.byte.l = iplrom[62];
r.pc.byte.h = iplrom[63]; r.pc.byte.h = iplrom[63];
//timing io = {};
io.clockCounter = 0; timer0 = {};
io.dspCounter = 0; timer1 = {};
timer2 = {};
//external
io.apu0 = 0x00;
io.apu1 = 0x00;
io.apu2 = 0x00;
io.apu3 = 0x00;
//$00f0
io.timersDisable = false;
io.ramWritable = true;
io.ramDisable = false;
io.timersEnable = true;
io.externalWaitStates = 0;
io.internalWaitStates = 0;
//$00f1
io.iplromEnable = true;
//$00f2
io.dspAddr = 0x00;
//$00f4-00f7
io.cpu0 = 0x00;
io.cpu1 = 0x00;
io.cpu2 = 0x00;
io.cpu3 = 0x00;
//$00f8-$00f9
io.aux4 = 0x00;
io.aux5 = 0x00;
timer0.stage0 = 0;
timer1.stage0 = 0;
timer2.stage0 = 0;
timer0.stage1 = 0;
timer1.stage1 = 0;
timer2.stage1 = 0;
timer0.stage2 = 0;
timer1.stage2 = 0;
timer2.stage2 = 0;
timer0.stage3 = 0;
timer1.stage3 = 0;
timer2.stage3 = 0;
timer0.line = 0;
timer1.line = 0;
timer2.line = 0;
timer0.enable = false;
timer1.enable = false;
timer2.enable = false;
timer0.target = 0;
timer1.target = 0;
timer2.target = 0;
} }
} }

View File

@@ -1,14 +1,14 @@
//Sony CXP1100Q-1 //Sony CXP1100Q-1
struct SMP : Processor::SPC700, Thread { struct SMP : Processor::SPC700, Thread {
uint8 iplrom[64]; inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
//smp.cpp
auto synchronizing() const -> bool override;
//io.cpp
auto portRead(uint2 port) const -> uint8; auto portRead(uint2 port) const -> uint8;
auto portWrite(uint2 port, uint8 data) -> void; auto portWrite(uint2 port, uint8 data) -> void;
//smp.cpp
static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto load(Markup::Node) -> bool; auto load(Markup::Node) -> bool;
auto power(bool reset) -> void; auto power(bool reset) -> void;
@@ -16,11 +16,13 @@ struct SMP : Processor::SPC700, Thread {
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint8 iplrom[64];
private: private:
struct IO { struct IO {
//timing //timing
uint clockCounter; uint clockCounter = 0;
uint dspCounter; uint dspCounter = 0;
//external //external
uint8 apu0; uint8 apu0;
@@ -30,14 +32,14 @@ private:
//$00f0 //$00f0
uint1 timersDisable; uint1 timersDisable;
uint1 ramWritable; uint1 ramWritable = true;
uint1 ramDisable; uint1 ramDisable;
uint1 timersEnable; uint1 timersEnable = true;
uint2 externalWaitStates; uint2 externalWaitStates;
uint2 internalWaitStates; uint2 internalWaitStates;
//$00f1 //$00f1
bool iplromEnable; uint1 iplromEnable = true;
//$00f2 //$00f2
uint8 dspAddr; uint8 dspAddr;
@@ -53,29 +55,29 @@ private:
uint8 aux5; uint8 aux5;
} io; } io;
static auto Enter() -> void;
//memory.cpp //memory.cpp
auto ramRead(uint16 addr) -> uint8; inline auto readRAM(uint16 address) -> uint8;
auto ramWrite(uint16 addr, uint8 data) -> void; inline auto writeRAM(uint16 address, uint8 data) -> void;
auto busRead(uint16 addr) -> uint8;
auto busWrite(uint16 addr, uint8 data) -> void;
auto idle() -> void override; auto idle() -> void override;
auto read(uint16 addr) -> uint8 override; auto read(uint16 address) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override; auto write(uint16 address, uint8 data) -> void override;
auto readDisassembler(uint16 addr) -> uint8 override; auto readDisassembler(uint16 address) -> uint8 override;
//io.cpp
inline auto readIO(uint16 address) -> uint8;
inline auto writeIO(uint16 address, uint8 data) -> void;
//timing.cpp //timing.cpp
template<uint Frequency> struct Timer { template<uint Frequency>
struct Timer {
uint8 stage0; uint8 stage0;
uint8 stage1; uint8 stage1;
uint8 stage2; uint8 stage2;
uint4 stage3; uint4 stage3;
bool line; boolean line;
bool enable; boolean enable;
uint8 target; uint8 target;
auto step(uint clocks) -> void; auto step(uint clocks) -> void;

View File

@@ -16,6 +16,10 @@ struct Boolean {
inline auto raise() { return data == 0 ? data = 1, true : false; } inline auto raise() { return data == 0 ? data = 1, true : false; }
inline auto lower() { return data == 1 ? data = 0, true : false; } inline auto lower() { return data == 1 ? data = 0, true : false; }
inline auto flip(bool value) { return data != value ? (data = value, true) : false; }
inline auto raise(bool value) { return !data && value ? (data = value, true) : (data = value, false); }
inline auto lower(bool value) { return data && !value ? (data = value, true) : (data = value, false); }
inline auto serialize(serializer& s) { s(data); } inline auto serialize(serializer& s) { s(data); }
private: private: