mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-23 08:59:40 +00:00
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:
parent
393c2395bb
commit
65a3e6c676
@ -13,7 +13,7 @@ using namespace nall;
|
||||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "https://byuu.org/";
|
||||
|
@ -10,15 +10,6 @@ CPU cpu;
|
||||
#include "irq.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 {
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
@ -65,10 +56,16 @@ auto CPU::power(bool reset) -> void {
|
||||
create(Enter, system.cpuFrequency());
|
||||
coprocessors.reset();
|
||||
PPUcounter::reset();
|
||||
PPUcounter::scanline = {&CPU::scanline, this};
|
||||
|
||||
function<auto (uint24, uint8) -> uint8> reader;
|
||||
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};
|
||||
writer = {&CPU::writeAPU, this};
|
||||
bus.map(reader, writer, "00-3f,80-bf:2140-217f");
|
||||
@ -81,132 +78,27 @@ auto CPU::power(bool reset) -> void {
|
||||
writer = {&CPU::writeDMA, this};
|
||||
bus.map(reader, writer, "00-3f,80-bf:4300-437f");
|
||||
|
||||
reader = [](uint24 addr, uint8) -> uint8 { return cpu.wram[addr]; };
|
||||
writer = [](uint24 addr, uint8 data) -> void { cpu.wram[addr] = data; };
|
||||
bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000);
|
||||
bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000);
|
||||
|
||||
if(!reset) random.array(wram, sizeof(wram));
|
||||
|
||||
//DMA
|
||||
for(auto& channel : this->channel) {
|
||||
channel.dmaEnabled = false;
|
||||
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;
|
||||
for(uint n : range(8)) {
|
||||
channels[n] = {};
|
||||
if(n != 7) channels[n].next = channels[n + 1];
|
||||
}
|
||||
|
||||
//$2181-$2183
|
||||
io.wramAddress = 0x000000;
|
||||
io = {};
|
||||
alu = {};
|
||||
pipe = {};
|
||||
|
||||
//$4016-$4017
|
||||
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 = {};
|
||||
status.lineClocks = lineclocks();
|
||||
|
||||
status.irqLock = false;
|
||||
status.dramRefreshPosition = (version == 1 ? 530 : 538);
|
||||
status.dramRefreshed = false;
|
||||
|
||||
status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
||||
status.hdmaInitTriggered = false;
|
||||
|
||||
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
||||
status.hdmaPosition = 1104;
|
||||
status.hdmaTriggered = false;
|
||||
|
||||
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.powerPending = reset == 0;
|
||||
status.resetPending = reset == 1;
|
||||
status.interruptPending = true;
|
||||
|
||||
status.dmaActive = false;
|
||||
status.dmaClocks = 0;
|
||||
status.dmaPending = false;
|
||||
status.hdmaPending = false;
|
||||
status.hdmaMode = 0;
|
||||
|
||||
status.autoJoypadActive = false;
|
||||
status.autoJoypadLatch = false;
|
||||
status.autoJoypadCounter = 0;
|
||||
clockCounter = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,41 +1,26 @@
|
||||
struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
||||
auto interruptPending() const -> bool override;
|
||||
auto pio() const -> uint8;
|
||||
auto joylatch() const -> bool;
|
||||
auto synchronizing() const -> bool override;
|
||||
inline auto interruptPending() const -> bool override { return status.interruptPending; }
|
||||
inline auto pio() const -> uint8 { return io.pio; }
|
||||
inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
|
||||
|
||||
//cpu.cpp
|
||||
CPU();
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dmaStep(uint clocks) -> void;
|
||||
auto dmaTransferValid(uint8 bbus, uint24 abus) -> bool;
|
||||
auto dmaAddressValid(uint24 abus) -> 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 dmaEnable() -> bool;
|
||||
inline auto hdmaEnable() -> bool;
|
||||
inline auto hdmaActive() -> bool;
|
||||
|
||||
inline auto dmaAddressB(uint n, uint channel) -> uint8;
|
||||
inline auto dmaAddress(uint n) -> uint24;
|
||||
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;
|
||||
inline auto dmaStep(uint clocks) -> void;
|
||||
inline auto dmaFlush() -> void;
|
||||
|
||||
auto dmaRun() -> void;
|
||||
auto hdmaUpdate(uint n) -> void;
|
||||
auto hdmaReset() -> void;
|
||||
auto hdmaSetup() -> void;
|
||||
auto hdmaRun() -> void;
|
||||
auto hdmaInitReset() -> void;
|
||||
auto hdmaInit() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto idle() -> void override;
|
||||
@ -45,12 +30,14 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
|
||||
auto readDisassembler(uint24 addr) -> uint8 override;
|
||||
|
||||
//io.cpp
|
||||
auto readAPU(uint24 addr, uint8 data) -> uint8;
|
||||
auto readCPU(uint24 addr, uint8 data) -> uint8;
|
||||
auto readDMA(uint24 addr, uint8 data) -> uint8;
|
||||
auto writeAPU(uint24 addr, uint8 data) -> void;
|
||||
auto writeCPU(uint24 addr, uint8 data) -> void;
|
||||
auto writeDMA(uint24 addr, uint8 data) -> void;
|
||||
auto readRAM(uint24 address, uint8 data) -> uint8;
|
||||
auto readAPU(uint24 address, uint8 data) -> uint8;
|
||||
auto readCPU(uint24 address, uint8 data) -> uint8;
|
||||
auto readDMA(uint24 address, uint8 data) -> uint8;
|
||||
auto writeRAM(uint24 address, 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
|
||||
inline auto dmaCounter() const -> uint;
|
||||
@ -87,81 +74,76 @@ private:
|
||||
uint clockCounter;
|
||||
|
||||
struct Status {
|
||||
bool interruptPending;
|
||||
uint clockCount = 0;
|
||||
uint lineClocks = 0;
|
||||
|
||||
uint clockCount;
|
||||
uint lineClocks;
|
||||
bool irqLock = false;
|
||||
|
||||
//timing
|
||||
bool irqLock;
|
||||
uint dramRefreshPosition = 0;
|
||||
bool dramRefreshed = false;
|
||||
|
||||
uint dramRefreshPosition;
|
||||
bool dramRefreshed;
|
||||
uint hdmaSetupPosition = 0;
|
||||
bool hdmaSetupTriggered = false;
|
||||
|
||||
uint hdmaInitPosition;
|
||||
bool hdmaInitTriggered;
|
||||
uint hdmaPosition = 0;
|
||||
bool hdmaTriggered = false;
|
||||
|
||||
uint hdmaPosition;
|
||||
bool hdmaTriggered;
|
||||
boolean nmiValid;
|
||||
boolean nmiLine;
|
||||
boolean nmiTransition;
|
||||
boolean nmiPending;
|
||||
boolean nmiHold;
|
||||
|
||||
bool nmiValid;
|
||||
bool nmiLine;
|
||||
bool nmiTransition;
|
||||
bool nmiPending;
|
||||
bool nmiHold;
|
||||
boolean irqValid;
|
||||
boolean irqLine;
|
||||
boolean irqTransition;
|
||||
boolean irqPending;
|
||||
boolean irqHold;
|
||||
|
||||
bool irqValid;
|
||||
bool irqLine;
|
||||
bool irqTransition;
|
||||
bool irqPending;
|
||||
bool irqHold;
|
||||
bool powerPending = false;
|
||||
bool resetPending = false;
|
||||
|
||||
bool powerPending;
|
||||
bool resetPending;
|
||||
bool interruptPending = false;
|
||||
|
||||
//DMA
|
||||
bool dmaActive;
|
||||
uint dmaClocks;
|
||||
bool dmaPending;
|
||||
bool hdmaPending;
|
||||
bool hdmaMode; //0 = init, 1 = run
|
||||
bool dmaActive = false;
|
||||
uint dmaClocks = 0;
|
||||
bool dmaPending = false;
|
||||
bool hdmaPending = false;
|
||||
bool hdmaMode = 0; //0 = init, 1 = run
|
||||
|
||||
//auto joypad polling
|
||||
bool autoJoypadActive;
|
||||
bool autoJoypadLatch;
|
||||
uint autoJoypadCounter;
|
||||
bool autoJoypadActive = false;
|
||||
bool autoJoypadLatch = false;
|
||||
uint autoJoypadCounter = 0;
|
||||
} status;
|
||||
|
||||
struct IO {
|
||||
//$2181-$2183
|
||||
uint17 wramAddress;
|
||||
|
||||
//$4016-$4017
|
||||
bool joypadStrobeLatch;
|
||||
|
||||
//$4200
|
||||
bool nmiEnabled;
|
||||
bool hirqEnabled;
|
||||
bool virqEnabled;
|
||||
bool autoJoypadPoll;
|
||||
boolean hirqEnable;
|
||||
boolean virqEnable;
|
||||
boolean irqEnable;
|
||||
boolean nmiEnable;
|
||||
boolean autoJoypadPoll;
|
||||
|
||||
//$4201
|
||||
uint8 pio;
|
||||
uint8 pio = 0xff;
|
||||
|
||||
//$4202-$4203
|
||||
uint8 wrmpya;
|
||||
uint8 wrmpyb;
|
||||
uint8 wrmpya = 0xff;
|
||||
uint8 wrmpyb = 0xff;
|
||||
|
||||
//$4204-$4206
|
||||
uint16 wrdiva;
|
||||
uint8 wrdivb;
|
||||
uint16 wrdiva = 0xffff;
|
||||
uint8 wrdivb = 0xff;
|
||||
|
||||
//$4207-$420a
|
||||
uint9 hirqPos;
|
||||
uint9 virqPos;
|
||||
uint9 hirqPos = 0x1ff;
|
||||
uint9 virqPos = 0x1ff;
|
||||
|
||||
//$420d
|
||||
uint romSpeed;
|
||||
uint romSpeed = 8;
|
||||
|
||||
//$4214-$4217
|
||||
uint16 rddiv;
|
||||
@ -175,34 +157,54 @@ private:
|
||||
} io;
|
||||
|
||||
struct ALU {
|
||||
uint mpyctr;
|
||||
uint divctr;
|
||||
uint shift;
|
||||
uint mpyctr = 0;
|
||||
uint divctr = 0;
|
||||
uint shift = 0;
|
||||
} alu;
|
||||
|
||||
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
|
||||
bool dmaEnabled;
|
||||
uint1 dmaEnable;
|
||||
|
||||
//$420c
|
||||
bool hdmaEnabled;
|
||||
uint1 hdmaEnable;
|
||||
|
||||
//$43x0
|
||||
bool direction;
|
||||
bool indirect;
|
||||
bool unused;
|
||||
bool reverseTransfer;
|
||||
bool fixedTransfer;
|
||||
uint3 transferMode;
|
||||
uint3 transferMode = 7;
|
||||
uint1 fixedTransfer = 1;
|
||||
uint1 reverseTransfer = 1;
|
||||
uint1 unused = 1;
|
||||
uint1 indirect = 1;
|
||||
uint1 direction = 1;
|
||||
|
||||
//$43x1
|
||||
uint8 targetAddress;
|
||||
uint8 targetAddress = 0xff;
|
||||
|
||||
//$43x2-$43x3
|
||||
uint16 sourceAddress;
|
||||
uint16 sourceAddress = 0xffff;
|
||||
|
||||
//$43x4
|
||||
uint8 sourceBank;
|
||||
uint8 sourceBank = 0xff;
|
||||
|
||||
//$43x5-$43x6
|
||||
union {
|
||||
@ -211,28 +213,30 @@ private:
|
||||
};
|
||||
|
||||
//$43x7
|
||||
uint8 indirectBank;
|
||||
uint8 indirectBank = 0xff;
|
||||
|
||||
//$43x8-$43x9
|
||||
uint16 hdmaAddress;
|
||||
uint16 hdmaAddress = 0xffff;
|
||||
|
||||
//$43xa
|
||||
uint8 lineCounter;
|
||||
uint8 lineCounter = 0xff;
|
||||
|
||||
//$43xb/$43xf
|
||||
uint8 unknown;
|
||||
uint8 unknown = 0xff;
|
||||
|
||||
//internal state
|
||||
bool hdmaCompleted;
|
||||
bool hdmaDoTransfer;
|
||||
uint1 hdmaCompleted;
|
||||
uint1 hdmaDoTransfer;
|
||||
|
||||
Channel() : transferSize(0) {}
|
||||
} channel[8];
|
||||
maybe<Channel&> next;
|
||||
|
||||
Channel() : transferSize(0xffff) {}
|
||||
} channels[8];
|
||||
|
||||
struct Pipe {
|
||||
bool valid;
|
||||
uint addr;
|
||||
uint8 data;
|
||||
uint1 valid;
|
||||
uint24 address;
|
||||
uint8 data;
|
||||
} pipe;
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
status.dmaClocks += clocks;
|
||||
step(clocks);
|
||||
}
|
||||
|
||||
//=============
|
||||
//memory access
|
||||
//=============
|
||||
|
||||
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::dmaFlush() -> void {
|
||||
if(!pipe.valid) return;
|
||||
pipe.valid = false;
|
||||
bus.write(pipe.address, pipe.data);
|
||||
}
|
||||
|
||||
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 {
|
||||
dmaStep(8);
|
||||
dmaWrite(false);
|
||||
dmaFlush();
|
||||
dmaEdge();
|
||||
|
||||
for(auto n : range(8)) {
|
||||
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;
|
||||
}
|
||||
|
||||
for(auto& channel : channels) channel.dmaRun();
|
||||
dmaFlush();
|
||||
status.irqLock = true;
|
||||
}
|
||||
|
||||
auto CPU::hdmaUpdate(uint n) -> void {
|
||||
dmaStep(4);
|
||||
r.mdr = dmaRead(channel[n].sourceBank << 16 | channel[n].hdmaAddress);
|
||||
dmaStep(4);
|
||||
dmaWrite(false);
|
||||
auto CPU::hdmaReset() -> void {
|
||||
for(auto& channel : channels) channel.hdmaReset();
|
||||
}
|
||||
|
||||
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 {
|
||||
dmaStep(8);
|
||||
dmaWrite(false);
|
||||
dmaFlush();
|
||||
for(auto& channel : channels) channel.hdmaTransfer();
|
||||
for(auto& channel : channels) channel.hdmaAdvance();
|
||||
dmaFlush();
|
||||
status.irqLock = true;
|
||||
}
|
||||
|
||||
for(auto n : range(8)) {
|
||||
if(!hdmaActive(n)) continue;
|
||||
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);
|
||||
}
|
||||
auto CPU::Channel::step(uint clocks) -> void {
|
||||
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::Channel::dmaRun() -> void {
|
||||
if(!dmaEnable) return;
|
||||
|
||||
uint2 index = 0;
|
||||
do {
|
||||
transfer(sourceBank << 16 | sourceAddress, index++);
|
||||
if(!fixedTransfer) !reverseTransfer ? sourceAddress++ : sourceAddress--;
|
||||
edge();
|
||||
} while(dmaEnable && --transferSize);
|
||||
|
||||
step(8);
|
||||
flush();
|
||||
edge();
|
||||
dmaEnable = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto n : range(8)) {
|
||||
if(!hdmaActive(n)) continue;
|
||||
|
||||
channel[n].lineCounter--;
|
||||
channel[n].hdmaDoTransfer = channel[n].lineCounter & 0x80;
|
||||
hdmaUpdate(n);
|
||||
}
|
||||
|
||||
status.irqLock = true;
|
||||
}
|
||||
|
||||
auto CPU::hdmaInitReset() -> void {
|
||||
for(auto n : range(8)) {
|
||||
channel[n].hdmaCompleted = false;
|
||||
channel[n].hdmaDoTransfer = false;
|
||||
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::hdmaInit() -> void {
|
||||
dmaStep(8);
|
||||
dmaWrite(false);
|
||||
|
||||
for(auto n : range(8)) {
|
||||
channel[n].hdmaDoTransfer = true; //note: needs hardware verification (2017-08-09)
|
||||
if(!channel[n].hdmaEnabled) continue;
|
||||
channel[n].dmaEnabled = false; //HDMA init during DMA will stop DMA mid-transfer
|
||||
|
||||
channel[n].hdmaAddress = channel[n].sourceAddress;
|
||||
channel[n].lineCounter = 0;
|
||||
hdmaUpdate(n);
|
||||
}
|
||||
|
||||
status.irqLock = true;
|
||||
auto CPU::Channel::hdmaAdvance() -> void {
|
||||
if(!hdmaActive()) return;
|
||||
lineCounter--;
|
||||
hdmaDoTransfer = lineCounter.bit(7);
|
||||
hdmaReload();
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
auto CPU::readRAM(uint24 addr, uint8 data) -> uint8 {
|
||||
return wram[addr];
|
||||
}
|
||||
|
||||
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
|
||||
synchronize(smp);
|
||||
return smp.portRead(addr.bits(0,1));
|
||||
@ -5,97 +9,53 @@ auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
|
||||
|
||||
auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 {
|
||||
switch((uint16)addr) {
|
||||
case 0x2180: //WMDATA
|
||||
return bus.read(0x7e0000 | io.wramAddress++, data);
|
||||
|
||||
//WMDATA
|
||||
case 0x2180: {
|
||||
return bus.read(0x7e0000 | io.wramAddress++, r.mdr);
|
||||
}
|
||||
case 0x4016: //JOYSER0
|
||||
data &= 0xfc;
|
||||
data |= controllerPort1.device->data();
|
||||
return data;
|
||||
|
||||
//JOYSER0
|
||||
//7-2 = MDR
|
||||
//1-0 = Joypad serial data
|
||||
case 0x4016: {
|
||||
uint8 v = r.mdr & 0xfc;
|
||||
v |= controllerPort1.device->data();
|
||||
return v;
|
||||
}
|
||||
case 0x4017: //JOYSER1
|
||||
data &= 0xe0;
|
||||
data |= 0x1c; //pins are connected to GND
|
||||
data |= controllerPort2.device->data();
|
||||
return data;
|
||||
|
||||
//JOYSER1
|
||||
case 0x4017: {
|
||||
//7-5 = MDR
|
||||
//4-2 = Always 1 (pins are connected to GND)
|
||||
//1-0 = Joypad serial data
|
||||
uint8 v = (r.mdr & 0xe0) | 0x1c;
|
||||
v |= controllerPort2.device->data();
|
||||
return v;
|
||||
}
|
||||
case 0x4210: //RDNMI
|
||||
data &= 0x70;
|
||||
data |= rdnmi() << 7;
|
||||
data |= (uint4)version;
|
||||
return data;
|
||||
|
||||
//RDNMI
|
||||
case 0x4210: {
|
||||
//7 = NMI acknowledge
|
||||
//6-4 = MDR
|
||||
//3-0 = CPU (5a22) version
|
||||
uint8 v = (r.mdr & 0x70);
|
||||
v |= (uint8)(rdnmi()) << 7;
|
||||
v |= (version & 0x0f);
|
||||
return v;
|
||||
}
|
||||
case 0x4211: //TIMEUP
|
||||
data &= 0x7f;
|
||||
data |= timeup() << 7;
|
||||
return data;
|
||||
|
||||
//TIMEUP
|
||||
case 0x4211: {
|
||||
//7 = IRQ acknowledge
|
||||
//6-0 = MDR
|
||||
uint8 v = (r.mdr & 0x7f);
|
||||
v |= (uint8)(timeup()) << 7;
|
||||
return v;
|
||||
}
|
||||
case 0x4212: //HVBJOY
|
||||
data &= 0x3e;
|
||||
data |= (status.autoJoypadActive) << 0;
|
||||
data |= (hcounter() <= 2 || hcounter() >= 1096) << 6; //hblank
|
||||
data |= (vcounter() >= ppu.vdisp()) << 7; //vblank
|
||||
return data;
|
||||
|
||||
//HVBJOY
|
||||
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;
|
||||
}
|
||||
case 0x4213: return io.pio; //RDIO
|
||||
|
||||
//RDIO
|
||||
case 0x4213: {
|
||||
return io.pio;
|
||||
}
|
||||
case 0x4214: return io.rddiv.byte(0); //RDDIVL
|
||||
case 0x4215: return io.rddiv.byte(1); //RDDIVH
|
||||
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 0x4219: return io.joy1.byte(1); //JOY1H
|
||||
case 0x421a: return io.joy2.byte(0); //JOY2L
|
||||
case 0x421b: return io.joy2.byte(1); //JOY2H
|
||||
case 0x421c: return io.joy3.byte(0); //JOY3L
|
||||
case 0x421d: return io.joy3.byte(1); //JOY3H
|
||||
case 0x421e: return io.joy4.byte(0); //JOY4L
|
||||
case 0x421f: return io.joy4.byte(1); //JOY4H
|
||||
case 0x4218: return io.joy1.byte(0); //JOY1L
|
||||
case 0x4219: return io.joy1.byte(1); //JOY1H
|
||||
case 0x421a: return io.joy2.byte(0); //JOY2L
|
||||
case 0x421b: return io.joy2.byte(1); //JOY2H
|
||||
case 0x421c: return io.joy3.byte(0); //JOY3L
|
||||
case 0x421d: return io.joy3.byte(1); //JOY3H
|
||||
case 0x421e: return io.joy4.byte(0); //JOY4L
|
||||
case 0x421f: return io.joy4.byte(1); //JOY4H
|
||||
|
||||
}
|
||||
|
||||
@ -103,59 +63,42 @@ auto CPU::readCPU(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: return (
|
||||
channel.transferMode << 0
|
||||
| channel.fixedTransfer << 3
|
||||
| channel.reverseTransfer << 4
|
||||
| channel.unused << 5
|
||||
| channel.indirect << 6
|
||||
| channel.direction << 7
|
||||
);
|
||||
case 0x4300: //DMAPx
|
||||
return (
|
||||
channel.transferMode << 0
|
||||
| channel.fixedTransfer << 3
|
||||
| channel.reverseTransfer << 4
|
||||
| channel.unused << 5
|
||||
| channel.indirect << 6
|
||||
| channel.direction << 7
|
||||
);
|
||||
|
||||
//BBADx
|
||||
case 0x4301: return channel.targetAddress;
|
||||
|
||||
//A1TxL
|
||||
case 0x4302: return channel.sourceAddress >> 0;
|
||||
|
||||
//A1TxH
|
||||
case 0x4303: return channel.sourceAddress >> 8;
|
||||
|
||||
//A1Bx
|
||||
case 0x4304: return channel.sourceBank;
|
||||
|
||||
//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;
|
||||
case 0x4301: return channel.targetAddress; //BBADx
|
||||
case 0x4302: return channel.sourceAddress.byte(0); //A1TxL
|
||||
case 0x4303: return channel.sourceAddress.byte(1); //A1TxH
|
||||
case 0x4304: return channel.sourceBank; //A1Bx
|
||||
case 0x4305: return channel.transferSize.byte(0); //DASxL
|
||||
case 0x4306: return channel.transferSize.byte(1); //DASxH
|
||||
case 0x4307: return channel.indirectBank; //DASBx
|
||||
case 0x4308: return channel.hdmaAddress.byte(0); //A2AxL
|
||||
case 0x4309: return channel.hdmaAddress.byte(1); //A2AxH
|
||||
case 0x430a: return channel.lineCounter; //NTRLx
|
||||
case 0x430b: return channel.unknown; //???x
|
||||
case 0x430f: return channel.unknown; //???x ($43xb mirror)
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto CPU::writeRAM(uint24 addr, uint8 data) -> void {
|
||||
wram[addr] = data;
|
||||
}
|
||||
|
||||
auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
|
||||
synchronize(smp);
|
||||
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 {
|
||||
switch((uint16)addr) {
|
||||
|
||||
//WMDATA
|
||||
case 0x2180: {
|
||||
case 0x2180: //WMDATA
|
||||
return bus.write(0x7e0000 | io.wramAddress++, data);
|
||||
}
|
||||
|
||||
case 0x2181: io.wramAddress.bits( 0, 7) = data; return; //WMADDL
|
||||
case 0x2182: io.wramAddress.bits( 8,15) = data; return; //WMADDM
|
||||
case 0x2183: io.wramAddress.bit (16 ) = data.bit(0); return; //WMADDH
|
||||
case 0x2181: //WMADDL
|
||||
io.wramAddress.bits(0,7) = data;
|
||||
return;
|
||||
|
||||
//JOYSER0
|
||||
case 0x4016: {
|
||||
//bit 0 is shared between JOYSER0 and JOYSER1, therefore
|
||||
case 0x2182: //WMADDM
|
||||
io.wramAddress.bits(8,15) = data;
|
||||
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.
|
||||
//$4017 bit 0 writes are ignored.
|
||||
controllerPort1.device->latch(data.bit(0));
|
||||
controllerPort2.device->latch(data.bit(0));
|
||||
return;
|
||||
}
|
||||
|
||||
//NMITIMEN
|
||||
case 0x4200: {
|
||||
case 0x4200: //NMITIMEN
|
||||
io.autoJoypadPoll = data.bit(0);
|
||||
nmitimenUpdate(data);
|
||||
return;
|
||||
}
|
||||
|
||||
//WRIO
|
||||
case 0x4201: {
|
||||
case 0x4201: //WRIO
|
||||
if(io.pio.bit(7) && !data.bit(7)) ppu.latchCounters();
|
||||
io.pio = data;
|
||||
return;
|
||||
}
|
||||
|
||||
//WRMPYA
|
||||
case 0x4202: io.wrmpya = data; return;
|
||||
case 0x4202: //WRMPYA
|
||||
io.wrmpya = data;
|
||||
return;
|
||||
|
||||
//WRMPYB
|
||||
case 0x4203: {
|
||||
case 0x4203: //WRMPYB
|
||||
io.rdmpy = 0;
|
||||
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.shift = io.wrmpyb;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4204: { io.wrdiva.byte(0) = data; return; } //WRDIVL
|
||||
case 0x4205: { io.wrdiva.byte(1) = data; return; } //WRDIVH
|
||||
case 0x4204: //WRDIVL
|
||||
io.wrdiva.byte(0) = data;
|
||||
return;
|
||||
|
||||
//WRDIVB
|
||||
case 0x4206: {
|
||||
case 0x4205: //WRDIVH
|
||||
io.wrdiva.byte(1) = data;
|
||||
return;
|
||||
|
||||
case 0x4206: //WRDIVB
|
||||
io.rdmpy = io.wrdiva;
|
||||
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.shift = io.wrdivb << 16;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x4207: io.hirqPos.bits(0,7) = data; return; //HTIMEL
|
||||
case 0x4208: io.hirqPos.bit (8 ) = data.bit(0); return; //HTIMEH
|
||||
case 0x4207: //HTIMEL
|
||||
io.hirqPos.bits(0,7) = data;
|
||||
return;
|
||||
|
||||
case 0x4209: io.virqPos.bits(0,7) = data; return; //VTIMEL
|
||||
case 0x420a: io.virqPos.bit (8 ) = data.bit(0); return; //VTIMEH
|
||||
case 0x4208: //HTIMEH
|
||||
io.hirqPos.bit(8) = data.bit(0);
|
||||
return;
|
||||
|
||||
//DMAEN
|
||||
case 0x420b: {
|
||||
for(auto n : range(8)) channel[n].dmaEnabled = data.bit(n);
|
||||
case 0x4209: //VTIMEL
|
||||
io.virqPos.bits(0,7) = data;
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
//HDMAEN
|
||||
case 0x420c: {
|
||||
for(auto n : range(8)) channel[n].hdmaEnabled = data.bit(n);
|
||||
case 0x420c: //HDMAEN
|
||||
for(auto n : range(8)) channels[n].hdmaEnable = data.bit(n);
|
||||
return;
|
||||
}
|
||||
|
||||
//MEMSEL
|
||||
case 0x420d: {
|
||||
case 0x420d: //MEMSEL
|
||||
io.romSpeed = data.bit(0) ? 6 : 8;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
case 0x4300: //DMAPx
|
||||
channel.transferMode = data.bits(0,2);
|
||||
channel.fixedTransfer = data.bit (3);
|
||||
channel.reverseTransfer = data.bit (4);
|
||||
@ -270,41 +218,54 @@ auto CPU::writeDMA(uint24 addr, uint8 data) -> void {
|
||||
channel.indirect = data.bit (6);
|
||||
channel.direction = data.bit (7);
|
||||
return;
|
||||
}
|
||||
|
||||
//DDBADx
|
||||
case 0x4301: channel.targetAddress = data; return;
|
||||
case 0x4301: //BBADx
|
||||
channel.targetAddress = data;
|
||||
return;
|
||||
|
||||
//A1TxL
|
||||
case 0x4302: channel.sourceAddress.byte(0) = data; return;
|
||||
case 0x4302: //A1TxL
|
||||
channel.sourceAddress.byte(0) = data;
|
||||
return;
|
||||
|
||||
//A1TxH
|
||||
case 0x4303: channel.sourceAddress.byte(1) = data; return;
|
||||
case 0x4303: //A1TxH
|
||||
channel.sourceAddress.byte(1) = data;
|
||||
return;
|
||||
|
||||
//A1Bx
|
||||
case 0x4304: channel.sourceBank = data; return;
|
||||
case 0x4304: //A1Bx
|
||||
channel.sourceBank = data;
|
||||
return;
|
||||
|
||||
//DASxL -- union { uint16 transferSize; uint16 indirectAddress; };
|
||||
case 0x4305: channel.transferSize.byte(0) = data; return;
|
||||
case 0x4305: //DASxL
|
||||
channel.transferSize.byte(0) = data;
|
||||
return;
|
||||
|
||||
//DASxH -- union { uint16 transferSize; uint16 indirectAddress; };
|
||||
case 0x4306: channel.transferSize.byte(1) = data; return;
|
||||
case 0x4306: //DASxH
|
||||
channel.transferSize.byte(1) = data;
|
||||
return;
|
||||
|
||||
//DASBx
|
||||
case 0x4307: channel.indirectBank = data; return;
|
||||
case 0x4307: //DASBx
|
||||
channel.indirectBank = data;
|
||||
return;
|
||||
|
||||
//A2AxL
|
||||
case 0x4308: channel.hdmaAddress.byte(0) = data; return;
|
||||
case 0x4308: //A2AxL
|
||||
channel.hdmaAddress.byte(0) = data;
|
||||
return;
|
||||
|
||||
//A2AxH
|
||||
case 0x4309: channel.hdmaAddress.byte(1) = data; return;
|
||||
case 0x4309: //A2AxH
|
||||
channel.hdmaAddress.byte(1) = data;
|
||||
return;
|
||||
|
||||
//NTRLx
|
||||
case 0x430a: channel.lineCounter = data; return;
|
||||
case 0x430a: //NTRLx
|
||||
channel.lineCounter = data;
|
||||
return;
|
||||
|
||||
//???
|
||||
case 0x430b:
|
||||
case 0x430f: channel.unknown = data; return;
|
||||
case 0x430b: //???x
|
||||
channel.unknown = data;
|
||||
return;
|
||||
|
||||
case 0x430f: //???x ($43xb mirror)
|
||||
channel.unknown = data;
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,68 +5,40 @@
|
||||
//it is used to emulate hardware communication delay between opcode and interrupt units.
|
||||
auto CPU::pollInterrupts() -> void {
|
||||
//NMI hold
|
||||
if(status.nmiHold) {
|
||||
status.nmiHold = false;
|
||||
if(io.nmiEnabled) status.nmiTransition = true;
|
||||
}
|
||||
if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true;
|
||||
|
||||
//NMI test
|
||||
bool nmiValid = vcounter(2) >= ppu.vdisp();
|
||||
if(!status.nmiValid && nmiValid) {
|
||||
//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;
|
||||
if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
|
||||
if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles
|
||||
}
|
||||
status.nmiValid = nmiValid;
|
||||
|
||||
//IRQ hold
|
||||
status.irqHold = false;
|
||||
if(status.irqLine) {
|
||||
if(io.virqEnabled || io.hirqEnabled) status.irqTransition = true;
|
||||
}
|
||||
if(status.irqLine && io.irqEnable) status.irqTransition = true;
|
||||
|
||||
//IRQ test
|
||||
bool irqValid = io.virqEnabled || io.hirqEnabled;
|
||||
if(irqValid) {
|
||||
if((io.virqEnabled && vcounter(10) != (io.virqPos))
|
||||
|| (io.hirqEnabled && hcounter(10) != (io.hirqPos + 1) * 4)
|
||||
|| (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;
|
||||
if(status.irqValid.raise(io.irqEnable
|
||||
&& (!io.virqEnable || vcounter(10) == io.virqPos)
|
||||
&& (!io.hirqEnable || hcounter(10) == io.hirqPos + 1 << 2)
|
||||
)) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles
|
||||
}
|
||||
|
||||
auto CPU::nmitimenUpdate(uint8 data) -> void {
|
||||
bool nmiEnabled = io.nmiEnabled;
|
||||
bool virqEnabled = io.virqEnabled;
|
||||
bool hirqEnabled = io.hirqEnabled;
|
||||
io.nmiEnabled = data & 0x80;
|
||||
io.virqEnabled = data & 0x20;
|
||||
io.hirqEnabled = data & 0x10;
|
||||
io.hirqEnable = data.bit(4);
|
||||
io.virqEnable = data.bit(5);
|
||||
io.irqEnable = io.hirqEnable || io.virqEnable;
|
||||
|
||||
//0->1 edge sensitive transition
|
||||
if(!nmiEnabled && io.nmiEnabled && status.nmiLine) {
|
||||
status.nmiTransition = true;
|
||||
}
|
||||
|
||||
//?->1 level sensitive transition
|
||||
if(io.virqEnabled && !io.hirqEnabled && status.irqLine) {
|
||||
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
|
||||
status.irqTransition = true;
|
||||
}
|
||||
|
||||
if(!io.virqEnabled && !io.hirqEnabled) {
|
||||
} else if(!io.irqEnable) {
|
||||
status.irqLine = false;
|
||||
status.irqTransition = false;
|
||||
}
|
||||
|
||||
if(io.nmiEnable.raise(data.bit(7)) && status.nmiLine) {
|
||||
status.nmiTransition = true;
|
||||
}
|
||||
|
||||
status.irqLock = true;
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,6 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s.integer(version);
|
||||
s.integer(clockCounter);
|
||||
|
||||
s.integer(status.interruptPending);
|
||||
|
||||
s.integer(status.clockCount);
|
||||
s.integer(status.lineClocks);
|
||||
|
||||
@ -18,27 +16,29 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s.integer(status.dramRefreshPosition);
|
||||
s.integer(status.dramRefreshed);
|
||||
|
||||
s.integer(status.hdmaInitPosition);
|
||||
s.integer(status.hdmaInitTriggered);
|
||||
s.integer(status.hdmaSetupPosition);
|
||||
s.integer(status.hdmaSetupTriggered);
|
||||
|
||||
s.integer(status.hdmaPosition);
|
||||
s.integer(status.hdmaTriggered);
|
||||
|
||||
s.integer(status.nmiValid);
|
||||
s.integer(status.nmiLine);
|
||||
s.integer(status.nmiTransition);
|
||||
s.integer(status.nmiPending);
|
||||
s.integer(status.nmiHold);
|
||||
s.boolean(status.nmiValid);
|
||||
s.boolean(status.nmiLine);
|
||||
s.boolean(status.nmiTransition);
|
||||
s.boolean(status.nmiPending);
|
||||
s.boolean(status.nmiHold);
|
||||
|
||||
s.integer(status.irqValid);
|
||||
s.integer(status.irqLine);
|
||||
s.integer(status.irqTransition);
|
||||
s.integer(status.irqPending);
|
||||
s.integer(status.irqHold);
|
||||
s.boolean(status.irqValid);
|
||||
s.boolean(status.irqLine);
|
||||
s.boolean(status.irqTransition);
|
||||
s.boolean(status.irqPending);
|
||||
s.boolean(status.irqHold);
|
||||
|
||||
s.integer(status.powerPending);
|
||||
s.integer(status.resetPending);
|
||||
|
||||
s.integer(status.interruptPending);
|
||||
|
||||
s.integer(status.dmaActive);
|
||||
s.integer(status.dmaClocks);
|
||||
s.integer(status.dmaPending);
|
||||
@ -51,12 +51,11 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
|
||||
s.integer(io.wramAddress);
|
||||
|
||||
s.integer(io.joypadStrobeLatch);
|
||||
|
||||
s.integer(io.nmiEnabled);
|
||||
s.integer(io.hirqEnabled);
|
||||
s.integer(io.virqEnabled);
|
||||
s.integer(io.autoJoypadPoll);
|
||||
s.boolean(io.hirqEnable);
|
||||
s.boolean(io.virqEnable);
|
||||
s.boolean(io.irqEnable);
|
||||
s.boolean(io.nmiEnable);
|
||||
s.boolean(io.autoJoypadPoll);
|
||||
|
||||
s.integer(io.pio);
|
||||
|
||||
@ -83,9 +82,9 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s.integer(alu.divctr);
|
||||
s.integer(alu.shift);
|
||||
|
||||
for(auto& channel : this->channel) {
|
||||
s.integer(channel.dmaEnabled);
|
||||
s.integer(channel.hdmaEnabled);
|
||||
for(auto& channel : channels) {
|
||||
s.integer(channel.dmaEnable);
|
||||
s.integer(channel.hdmaEnable);
|
||||
s.integer(channel.direction);
|
||||
s.integer(channel.indirect);
|
||||
s.integer(channel.unused);
|
||||
@ -105,6 +104,6 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
||||
s.integer(pipe.valid);
|
||||
s.integer(pipe.addr);
|
||||
s.integer(pipe.address);
|
||||
s.integer(pipe.data);
|
||||
}
|
||||
|
@ -39,9 +39,9 @@ auto CPU::scanline() -> void {
|
||||
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
||||
|
||||
if(vcounter() == 0) {
|
||||
//HDMA init triggers once every frame
|
||||
status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
||||
status.hdmaInitTriggered = false;
|
||||
//HDMA setup triggers once every frame
|
||||
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
|
||||
status.hdmaSetupTriggered = false;
|
||||
|
||||
status.autoJoypadCounter = 0;
|
||||
}
|
||||
@ -88,12 +88,12 @@ auto CPU::dmaEdge() -> void {
|
||||
if(status.dmaActive) {
|
||||
if(status.hdmaPending) {
|
||||
status.hdmaPending = false;
|
||||
if(hdmaEnabledChannels()) {
|
||||
if(!dmaEnabledChannels()) {
|
||||
if(hdmaEnable()) {
|
||||
if(!dmaEnable()) {
|
||||
dmaStep(8 - dmaCounter());
|
||||
}
|
||||
status.hdmaMode == 0 ? hdmaInit() : hdmaRun();
|
||||
if(!dmaEnabledChannels()) {
|
||||
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
|
||||
if(!dmaEnable()) {
|
||||
step(status.clockCount - (status.dmaClocks % status.clockCount));
|
||||
status.dmaActive = false;
|
||||
}
|
||||
@ -102,7 +102,7 @@ auto CPU::dmaEdge() -> void {
|
||||
|
||||
if(status.dmaPending) {
|
||||
status.dmaPending = false;
|
||||
if(dmaEnabledChannels()) {
|
||||
if(dmaEnable()) {
|
||||
dmaStep(8 - dmaCounter());
|
||||
dmaRun();
|
||||
step(status.clockCount - (status.dmaClocks % status.clockCount));
|
||||
@ -111,10 +111,10 @@ auto CPU::dmaEdge() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(!status.hdmaInitTriggered && hcounter() >= status.hdmaInitPosition) {
|
||||
status.hdmaInitTriggered = true;
|
||||
hdmaInitReset();
|
||||
if(hdmaEnabledChannels()) {
|
||||
if(!status.hdmaSetupTriggered && hcounter() >= status.hdmaSetupPosition) {
|
||||
status.hdmaSetupTriggered = true;
|
||||
hdmaReset();
|
||||
if(hdmaEnable()) {
|
||||
status.hdmaPending = true;
|
||||
status.hdmaMode = 0;
|
||||
}
|
||||
@ -122,7 +122,7 @@ auto CPU::dmaEdge() -> void {
|
||||
|
||||
if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) {
|
||||
status.hdmaTriggered = true;
|
||||
if(hdmaActiveChannels()) {
|
||||
if(hdmaActive()) {
|
||||
status.hdmaPending = true;
|
||||
status.hdmaMode = 1;
|
||||
}
|
||||
@ -177,8 +177,8 @@ auto CPU::joypadEdge() -> void {
|
||||
//trigger during certain events (immediately after DMA, writes to $4200, etc)
|
||||
auto CPU::lastCycle() -> void {
|
||||
if(!status.irqLock) {
|
||||
status.nmiPending |= nmiTest();
|
||||
status.irqPending |= irqTest();
|
||||
if(nmiTest()) status.nmiPending = true;
|
||||
if(irqTest()) status.irqPending = true;
|
||||
status.interruptPending = (status.nmiPending || status.irqPending);
|
||||
}
|
||||
}
|
||||
|
@ -26,27 +26,27 @@ auto PPUcounter::tick(uint clocks) -> void {
|
||||
//internal
|
||||
auto PPUcounter::vcounterTick() -> void {
|
||||
if(++status.vcounter == 128) status.interlace = ppu.interlace();
|
||||
|
||||
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)
|
||||
) {
|
||||
if(vcounter() == (Region::NTSC() ? 262 : 312) + (interlace() && !field())) {
|
||||
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();
|
||||
}
|
||||
|
||||
auto PPUcounter::interlace() const -> bool { return status.interlace; }
|
||||
auto PPUcounter::field() const -> bool { return status.field; }
|
||||
auto PPUcounter::vcounter() const -> uint16 { return status.vcounter; }
|
||||
auto PPUcounter::hcounter() const -> uint16 { return status.hcounter; }
|
||||
auto PPUcounter::vcounter() const -> uint { return status.vcounter; }
|
||||
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::vcounter(uint offset) const -> uint16 { 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::vcounter(uint offset) const -> uint { return history.vcounter[(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
|
||||
//
|
||||
@ -57,29 +57,15 @@ auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter
|
||||
//dot 323 range = {1292, 1294, 1296}
|
||||
//dot 327 range = {1310, 1312, 1314}
|
||||
|
||||
auto PPUcounter::hdot() const -> uint16 {
|
||||
if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) {
|
||||
auto PPUcounter::hdot() const -> uint {
|
||||
if(lineclocks() == 1360) {
|
||||
return (hcounter() >> 2);
|
||||
} else {
|
||||
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 {
|
||||
status.interlace = 0;
|
||||
status.field = 0;
|
||||
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;
|
||||
}
|
||||
status = {};
|
||||
history = {};
|
||||
}
|
||||
|
@ -14,36 +14,37 @@ struct PPUcounter {
|
||||
alwaysinline auto tick() -> void;
|
||||
alwaysinline auto tick(uint clocks) -> void;
|
||||
|
||||
alwaysinline auto interlace() const -> bool;
|
||||
alwaysinline auto field() const -> bool;
|
||||
alwaysinline auto vcounter() const -> uint16;
|
||||
alwaysinline auto hcounter() const -> uint16;
|
||||
inline auto hdot() const -> uint16;
|
||||
inline auto lineclocks() const -> uint16;
|
||||
alwaysinline auto vcounter() const -> uint;
|
||||
alwaysinline auto hcounter() const -> uint;
|
||||
alwaysinline auto hdot() const -> uint;
|
||||
alwaysinline auto lineclocks() const -> uint;
|
||||
|
||||
alwaysinline auto field(uint offset) const -> bool;
|
||||
alwaysinline auto vcounter(uint offset) const -> uint16;
|
||||
alwaysinline auto hcounter(uint offset) const -> uint16;
|
||||
alwaysinline auto vcounter(uint offset) const -> uint;
|
||||
alwaysinline auto hcounter(uint offset) const -> uint;
|
||||
|
||||
inline auto reset() -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
function<auto () -> void> scanline;
|
||||
function<void ()> scanline;
|
||||
|
||||
private:
|
||||
inline auto vcounterTick() -> void;
|
||||
alwaysinline auto vcounterTick() -> void;
|
||||
|
||||
struct {
|
||||
bool interlace;
|
||||
bool field;
|
||||
uint16 vcounter;
|
||||
uint16 hcounter;
|
||||
bool interlace = 0;
|
||||
bool field = 0;
|
||||
uint vcounter = 0;
|
||||
uint hcounter = 0;
|
||||
uint lineclocks = 1364;
|
||||
} status;
|
||||
|
||||
struct {
|
||||
bool field[2048];
|
||||
uint16 vcounter[2048];
|
||||
uint16 hcounter[2048];
|
||||
|
||||
int32 index;
|
||||
uint index = 0;
|
||||
bool field[2048] = {};
|
||||
uint vcounter[2048] = {};
|
||||
uint hcounter[2048] = {};
|
||||
} history;
|
||||
};
|
||||
|
182
higan/sfc/smp/io.cpp
Normal file
182
higan/sfc/smp/io.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -1,219 +1,32 @@
|
||||
alwaysinline auto SMP::ramRead(uint16 addr) -> uint8 {
|
||||
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f];
|
||||
auto SMP::readRAM(uint16 address) -> uint8 {
|
||||
if(address >= 0xffc0 && io.iplromEnable) return iplrom[address & 0x3f];
|
||||
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
|
||||
if(io.ramWritable && !io.ramDisable) dsp.apuram[addr] = 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
|
||||
if(io.ramWritable && !io.ramDisable) dsp.apuram[address] = data;
|
||||
}
|
||||
|
||||
auto SMP::idle() -> void {
|
||||
wait();
|
||||
}
|
||||
|
||||
auto SMP::read(uint16 addr) -> uint8 {
|
||||
wait(addr);
|
||||
uint8 data = busRead(addr);
|
||||
auto SMP::read(uint16 address) -> uint8 {
|
||||
wait(address);
|
||||
uint8 data = readRAM(address);
|
||||
if((address & 0xfff0) == 0x00f0) data = readIO(address);
|
||||
return data;
|
||||
}
|
||||
|
||||
auto SMP::write(uint16 addr, uint8 data) -> void {
|
||||
wait(addr);
|
||||
busWrite(addr, data);
|
||||
auto SMP::write(uint16 address, uint8 data) -> void {
|
||||
wait(address);
|
||||
writeRAM(address, data); //even IO writes affect underlying RAM
|
||||
if((address & 0xfff0) == 0x00f0) writeIO(address, data);
|
||||
}
|
||||
|
||||
auto SMP::readDisassembler(uint16 addr) -> uint8 {
|
||||
if((addr & 0xfff0) == 0x00f0) return 0x00;
|
||||
if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f];
|
||||
return dsp.apuram[addr];
|
||||
auto SMP::readDisassembler(uint16 address) -> uint8 {
|
||||
if((address & 0xfff0) == 0x00f0) return 0x00;
|
||||
return readRAM(address);
|
||||
}
|
||||
|
@ -33,23 +33,23 @@ auto SMP::serialize(serializer& s) -> void {
|
||||
s.integer(timer0.stage1);
|
||||
s.integer(timer0.stage2);
|
||||
s.integer(timer0.stage3);
|
||||
s.integer(timer0.line);
|
||||
s.integer(timer0.enable);
|
||||
s.boolean(timer0.line);
|
||||
s.boolean(timer0.enable);
|
||||
s.integer(timer0.target);
|
||||
|
||||
s.integer(timer1.stage0);
|
||||
s.integer(timer1.stage1);
|
||||
s.integer(timer1.stage2);
|
||||
s.integer(timer1.stage3);
|
||||
s.integer(timer1.line);
|
||||
s.integer(timer1.enable);
|
||||
s.boolean(timer1.line);
|
||||
s.boolean(timer1.enable);
|
||||
s.integer(timer1.target);
|
||||
|
||||
s.integer(timer2.stage0);
|
||||
s.integer(timer2.stage1);
|
||||
s.integer(timer2.stage2);
|
||||
s.integer(timer2.stage3);
|
||||
s.integer(timer2.line);
|
||||
s.integer(timer2.enable);
|
||||
s.boolean(timer2.line);
|
||||
s.boolean(timer2.enable);
|
||||
s.integer(timer2.target);
|
||||
}
|
||||
|
@ -3,15 +3,11 @@
|
||||
namespace SuperFamicom {
|
||||
|
||||
SMP smp;
|
||||
|
||||
#include "memory.cpp"
|
||||
#include "io.cpp"
|
||||
#include "timing.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto SMP::synchronizing() const -> bool {
|
||||
return scheduler.synchronizing();
|
||||
}
|
||||
|
||||
auto SMP::Enter() -> void {
|
||||
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.h = iplrom[63];
|
||||
|
||||
//timing
|
||||
io.clockCounter = 0;
|
||||
io.dspCounter = 0;
|
||||
|
||||
//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;
|
||||
io = {};
|
||||
timer0 = {};
|
||||
timer1 = {};
|
||||
timer2 = {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
//Sony CXP1100Q-1
|
||||
|
||||
struct SMP : Processor::SPC700, Thread {
|
||||
uint8 iplrom[64];
|
||||
|
||||
//smp.cpp
|
||||
auto synchronizing() const -> bool override;
|
||||
inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
|
||||
|
||||
//io.cpp
|
||||
auto portRead(uint2 port) const -> uint8;
|
||||
auto portWrite(uint2 port, uint8 data) -> void;
|
||||
|
||||
//smp.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power(bool reset) -> void;
|
||||
@ -16,11 +16,13 @@ struct SMP : Processor::SPC700, Thread {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint8 iplrom[64];
|
||||
|
||||
private:
|
||||
struct IO {
|
||||
//timing
|
||||
uint clockCounter;
|
||||
uint dspCounter;
|
||||
uint clockCounter = 0;
|
||||
uint dspCounter = 0;
|
||||
|
||||
//external
|
||||
uint8 apu0;
|
||||
@ -30,14 +32,14 @@ private:
|
||||
|
||||
//$00f0
|
||||
uint1 timersDisable;
|
||||
uint1 ramWritable;
|
||||
uint1 ramWritable = true;
|
||||
uint1 ramDisable;
|
||||
uint1 timersEnable;
|
||||
uint1 timersEnable = true;
|
||||
uint2 externalWaitStates;
|
||||
uint2 internalWaitStates;
|
||||
|
||||
//$00f1
|
||||
bool iplromEnable;
|
||||
uint1 iplromEnable = true;
|
||||
|
||||
//$00f2
|
||||
uint8 dspAddr;
|
||||
@ -53,30 +55,30 @@ private:
|
||||
uint8 aux5;
|
||||
} io;
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto ramRead(uint16 addr) -> uint8;
|
||||
auto ramWrite(uint16 addr, uint8 data) -> void;
|
||||
|
||||
auto busRead(uint16 addr) -> uint8;
|
||||
auto busWrite(uint16 addr, uint8 data) -> void;
|
||||
inline auto readRAM(uint16 address) -> uint8;
|
||||
inline auto writeRAM(uint16 address, uint8 data) -> void;
|
||||
|
||||
auto idle() -> void override;
|
||||
auto read(uint16 addr) -> uint8 override;
|
||||
auto write(uint16 addr, uint8 data) -> void override;
|
||||
auto read(uint16 address) -> uint8 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
|
||||
template<uint Frequency> struct Timer {
|
||||
uint8 stage0;
|
||||
uint8 stage1;
|
||||
uint8 stage2;
|
||||
uint4 stage3;
|
||||
bool line;
|
||||
bool enable;
|
||||
uint8 target;
|
||||
template<uint Frequency>
|
||||
struct Timer {
|
||||
uint8 stage0;
|
||||
uint8 stage1;
|
||||
uint8 stage2;
|
||||
uint4 stage3;
|
||||
boolean line;
|
||||
boolean enable;
|
||||
uint8 target;
|
||||
|
||||
auto step(uint clocks) -> void;
|
||||
auto synchronizeStage1() -> void;
|
||||
|
@ -16,6 +16,10 @@ struct Boolean {
|
||||
inline auto raise() { return data == 0 ? data = 1, 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); }
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user