bsnes-plus/bsnes/snes/chip/cx4/cx4.cpp

180 lines
3.7 KiB
C++

#include <snes.hpp>
#define CX4_CPP
namespace SNES {
#include "bus.cpp"
#include "memory.cpp"
#include "registers.cpp"
#include "instructions.cpp"
#include "data.cpp"
#include "serialization.cpp"
Cx4 cx4;
void Cx4::Enter() { cx4.enter(); }
void Cx4::enter() {
while (true) {
scheduler.synchronize();
bool wasBusy = busy();
// currently performing DMA
if (mmio.dma) {
for (unsigned n = 0; n < mmio.dmaLength; n++) {
uint8 data = op_read(mmio.dmaSource + n);
op_write(mmio.dmaTarget + n, data);
add_clocks(1);
}
mmio.dma = false;
}
// currently performing cache preload
else if (mmio.cacheLoading) {
load_page(mmio.cachePreload, mmio.pageNumber);
}
// currently executing code
else if (!regs.halt) {
if (cache[regs.cachePage].pageNumber != regs.pc >> 8) {
// cache new program page
load_page(regs.cachePage, regs.pc >> 8);
}
opcode = cache[regs.cachePage].data[regs.pc & 0xff];
nextpc();
instruction();
}
// currently idle
else {
add_clocks(1);
}
regs.irqPending |= wasBusy && !busy();
if (regs.irqPending && !mmio.irqDisable) {
cpu.regs.irq = 1;
}
}
}
void Cx4::add_clocks(unsigned clocks) {
while (clocks--) {
step(1);
synchronize_cpu();
if(regs.rwbustime && --regs.rwbustime) {
if (regs.writebus) {
regs.mdr = regs.writebusdata;
cx4bus.write(regs.rwbusaddr, regs.writebusdata);
} else {
regs.busdata = cx4bus.read(regs.rwbusaddr);
regs.mdr = regs.busdata;
}
}
while (mmio.suspend) {
step(1);
synchronize_cpu();
if (mmio.suspendCycles && !--mmio.suspendCycles)
mmio.suspend = false;
}
}
}
void Cx4::nextpc() {
regs.pc++;
if ((regs.pc & 0xff) == 0) {
// continue to next page or stop
if (!regs.cachePage) {
regs.pc = regs.p << 8; // ?
regs.cachePage = 1;
} else {
regs.halt = true;
}
}
}
void Cx4::change_page() {
bool otherPage = regs.cachePage ^ 1;
uint16 programPage = regs.pc >> 8;
if (cache[regs.cachePage].pageNumber == programPage) {
// still in same page
}
// another locked page can be executed if it's the correct program page
else if (!cache[otherPage].lock || cache[otherPage].pageNumber == programPage) {
regs.cachePage = otherPage;
}
// if both pages are locked (and invalid), halt
else if (cache[regs.cachePage].lock) {
regs.halt = true;
}
}
void Cx4::load_page(uint8 cachePage, uint16 programPage) {
uint24 addr = mmio.programOffset + (programPage << 9);
// used for busy flag
mmio.cacheLoading = true;
for (unsigned i = 0; i < 256; i++) {
cache[cachePage].data[i] = op_read(addr++);
cache[cachePage].data[i] |= (op_read(addr++) << 8);
add_clocks(1);
}
cache[cachePage].pageNumber = programPage;
mmio.cacheLoading = false;
}
void Cx4::init() {
}
void Cx4::enable() {
}
void Cx4::power() {
cx4bus.init();
reset();
}
void Cx4::reset() {
create(Cx4::Enter, frequency);
memset(dataRAM, 0, sizeof(dataRAM));
regs.halt = true;
regs.cachePage = 0;
regs.irqPending = false;
regs.rwbustime = 0;
regs.mdr = 0;
regs.n = 0;
regs.z = 0;
regs.v = 0;
regs.c = 0;
mmio.suspend = false;
mmio.cacheLoading = false;
mmio.dmaSource = 0x000000;
mmio.dmaLength = 0x0000;
mmio.dmaTarget = 0x000000;
mmio.cachePreload = 0x00;
mmio.programOffset = 0x000000;
mmio.pageNumber = 0x0000;
mmio.programCounter = 0x00;
mmio.romSpeed = 0x3;
mmio.ramSpeed = 0x3;
mmio.irqDisable = 0x00;
mmio.r1f52 = 0x01;
for (auto& cachePage : cache) {
cachePage.lock = false;
cachePage.pageNumber = 0xffff;
memset(cachePage.data, 0, sizeof(cachePage.data));
}
}
}