Update to v106r61 release.

byuu says:

This release adds ikari's Cx4 notes to bsnes. It fixes the MMX2 intro's
boss fight sequence to be frame perfect to real hardware. It's also very
slightly faster than before.

I've also added an option to toggle the CPU↔coprocessor cycle
synchronization to the emulation settings panel, so you don't have to
recompile to get the more accurate SA1 timings. I'm most likely going to
default this to disabled in bsnes, and *maybe* enabled in higan out of
the box.

StaticRAM (wasn't used) and MappedRAM are gone from the Super Famicom
core. Instead, there's now ReadableMemory, WritableMemory, and
ProtectedMemory (WritableMemory with a toggle for write protection.)
Cartridge::loadMap now takes a template Memory object, which bypasses an
extra virtual function call on memory accesses, but it doesn't really
impact speed much. Whatever.
This commit is contained in:
Tim Allen 2018-09-04 15:44:35 +10:00
parent a3e0f6da25
commit 3d34517f3e
52 changed files with 958 additions and 600 deletions

View File

@ -28,7 +28,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.60"; static const string Version = "106.61";
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

@ -7,21 +7,116 @@ namespace Processor {
#include "instructions.cpp" #include "instructions.cpp"
#include "serialization.cpp" #include "serialization.cpp"
auto HG51B::exec(uint24 addr) -> void { auto HG51B::lock() -> void {
if(regs.halt) return; io.lock = 1;
addr = addr + regs.pc * 2; }
opcode = read(addr++) << 0;
opcode |= read(addr++) << 8; auto HG51B::halt() -> void {
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff); io.halt = 1;
}
auto HG51B::wait(uint24 address) -> uint {
if(isROM(address)) return 1 + io.wait.rom;
if(isRAM(address)) return 1 + io.wait.ram;
return 1;
}
auto HG51B::main() -> void {
if(io.lock) return step(1);
if(io.suspend.enable) return suspend();
if(io.cache.enable) return cache();
if(io.dma.enable) return dma();
if(io.halt) return step(1);
return execute();
}
auto HG51B::step(uint clocks) -> void {
if(io.bus.enable) {
if(io.bus.pending > clocks) {
io.bus.pending -= clocks;
} else {
io.bus.enable = 0;
io.bus.pending = 0;
if(io.bus.reading) io.bus.reading = 0, r.busData = read(io.bus.address);
if(io.bus.writing) io.bus.writing = 0, write(io.bus.address, r.busData);
}
}
}
auto HG51B::execute() -> void {
uint24 address = io.cache.base + r.pb * 512;
if(io.cache.address[io.cache.page] != address) {
io.cache.page ^= 1;
if(io.cache.address[io.cache.page] != address) {
if(io.cache.lock[io.cache.page]) {
io.cache.page ^= 1;
if(io.cache.lock[io.cache.page]) return halt();
}
cache();
}
}
opcode = programRAM[io.cache.page][r.pc];
advance();
step(1);
instruction(); instruction();
} }
auto HG51B::advance() -> void {
if(++r.pc == 0) {
if(io.cache.page == 1) return halt();
io.cache.page = 1;
r.pb = r.p;
}
}
auto HG51B::suspend() -> void {
if(!io.suspend.duration) return step(1); //indefinite
step(io.suspend.duration);
io.suspend.enable = 0;
io.suspend.duration = 0;
}
auto HG51B::cache() -> void {
uint24 address = io.cache.base + r.pb * 512;
io.cache.address[io.cache.page] = address;
for(uint offset : range(256)) {
step(wait(address)); programRAM[io.cache.page][offset].byte(0) = read(address++);
step(wait(address)); programRAM[io.cache.page][offset].byte(1) = read(address++);
}
io.cache.enable = 0;
}
auto HG51B::dma() -> void {
for(uint offset : range(io.dma.length)) {
uint24 source = io.dma.source + offset;
uint24 target = io.dma.target + offset;
if(isROM(source) && isROM(target)) return lock();
if(isRAM(source) && isRAM(target)) return lock();
step(wait(source));
auto data = read(source);
step(wait(target));
write(target, data);
}
io.dma.enable = 0;
}
auto HG51B::running() const -> bool {
return io.cache.enable || io.dma.enable || io.bus.pending || !io.halt;
}
auto HG51B::busy() const -> bool {
return io.cache.enable || io.dma.enable || io.bus.pending;
}
auto HG51B::power() -> void { auto HG51B::power() -> void {
regs.halt = true; r = {};
io = {};
regs.n = 0;
regs.z = 0;
regs.c = 0;
} }
} }

View File

@ -5,14 +5,27 @@
namespace Processor { namespace Processor {
struct HG51B { struct HG51B {
auto exec(uint24 addr) -> void; virtual auto step(uint clocks) -> void;
virtual auto read(uint24 addr) -> uint8 = 0; virtual auto isROM(uint24 address) -> bool = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0; virtual auto isRAM(uint24 address) -> bool = 0;
virtual auto read(uint24 address) -> uint8 = 0;
virtual auto write(uint24 address, uint8 data) -> void = 0;
virtual auto lock() -> void;
virtual auto halt() -> void;
auto wait(uint24 address) -> uint;
auto main() -> void;
auto execute() -> void;
auto advance() -> void;
auto suspend() -> void;
auto cache() -> void;
auto dma() -> void;
auto running() const -> bool;
auto busy() const -> bool;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
//uint16 programROM[2][256]; uint16 programRAM[2][256];
uint24 dataROM[1024]; uint24 dataROM[1024];
uint8 dataRAM[3072]; uint8 dataRAM[3072];
@ -21,32 +34,76 @@ protected:
auto pull() -> void; auto pull() -> void;
auto sa() -> uint; auto sa() -> uint;
auto ri() -> uint; auto ri() -> uint;
auto np() -> uint; auto np() -> void;
auto instruction() -> void; auto instruction() -> void;
//registers.cpp //registers.cpp
auto registerRead(uint8 addr) const -> uint24; auto readRegister(uint7 address) -> uint24;
auto registerWrite(uint8 addr, uint24 data) -> void; auto writeRegister(uint7 address, uint24 data) -> void;
struct Registers { struct Registers {
bool halt;
uint24 pc;
uint16 p; uint16 p;
bool n; uint16 pb; //program bank
bool z; uint8 pc; //program counter
bool c;
uint24 a; boolean n; //negative
boolean z; //zero
boolean c; //carry
boolean i; //interrupt
uint24 a; //accumulator
uint24 acch; uint24 acch;
uint24 accl; uint24 accl;
uint24 busdata; uint24 busData;
uint24 romdata; uint24 romData;
uint24 ramdata; uint24 ramData;
uint24 busaddr; uint24 busAddress;
uint24 ramaddr; uint24 ramAddress;
uint24 gpr[16]; uint24 gpr[16];
} regs; } r;
struct IO {
uint1 lock;
uint1 halt = 1;
uint1 irq; //0 = enable, 1 = disable
uint1 rom = 1; //0 = 2 ROMs, 1 = 1 ROM
uint8 vector[32];
struct Wait {
uint3 rom = 3;
uint3 ram = 3;
} wait;
struct Suspend {
uint1 enable;
uint8 duration;
} suspend;
struct Cache {
uint1 enable;
uint1 page;
uint1 lock[2];
uint24 address[2];
uint24 base;
uint16 pb;
uint8 pc;
} cache;
struct DMA {
uint1 enable;
uint24 source;
uint24 target;
uint16 length;
} dma;
struct Bus {
uint1 enable;
uint1 reading;
uint1 writing;
uint4 pending;
uint24 address;
} bus;
} io;
uint24 stack[8]; uint24 stack[8];
uint16 opcode; uint16 opcode;

View File

@ -6,11 +6,11 @@ auto HG51B::push() -> void {
stack[3] = stack[2]; stack[3] = stack[2];
stack[2] = stack[1]; stack[2] = stack[1];
stack[1] = stack[0]; stack[1] = stack[0];
stack[0] = regs.pc; stack[0] = r.pb << 8 | r.pc << 0;
} }
auto HG51B::pull() -> void { auto HG51B::pull() -> void {
regs.pc = stack[0]; auto pc = stack[0];
stack[0] = stack[1]; stack[0] = stack[1];
stack[1] = stack[2]; stack[1] = stack[2];
stack[2] = stack[3]; stack[2] = stack[3];
@ -19,28 +19,27 @@ auto HG51B::pull() -> void {
stack[5] = stack[6]; stack[5] = stack[6];
stack[6] = stack[7]; stack[6] = stack[7];
stack[7] = 0x0000; stack[7] = 0x0000;
r.pb = pc >> 8;
r.pc = pc >> 0;
} }
//Shift-A: math opcodes can shift A register prior to ALU operation //Shift-A: math opcodes can shift A register prior to ALU operation
auto HG51B::sa() -> uint { auto HG51B::sa() -> uint {
switch(opcode & 0x0300) { default: static const uint shift[] = {0, 1, 8, 16};
case 0x0000: return regs.a << 0; return r.a << shift[opcode.bits(8,9)];
case 0x0100: return regs.a << 1;
case 0x0200: return regs.a << 8;
case 0x0300: return regs.a << 16;
}
} }
//Register-or-Immediate: most opcodes can load from a register or immediate //Register-or-Immediate: most opcodes can load from a register or immediate
auto HG51B::ri() -> uint { auto HG51B::ri() -> uint {
if(opcode & 0x0400) return opcode & 0xff; if(opcode.bit(10)) return opcode.bits(0,7);
return registerRead(opcode & 0xff); return readRegister(opcode.bits(0,7));
} }
//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes) //New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes)
auto HG51B::np() -> uint { auto HG51B::np() -> void {
if(opcode & 0x0200) return (regs.p << 8) | (opcode & 0xff); if(opcode.bit(9)) r.pb = r.p;
return (regs.pc & 0xffff00) | (opcode & 0xff); r.pc = opcode.bits(0,7);
} }
auto HG51B::instruction() -> void { auto HG51B::instruction() -> void {
@ -53,300 +52,322 @@ auto HG51B::instruction() -> void {
//00.0 10.0 .... .... //00.0 10.0 .... ....
//jump i //jump i
if(opcode & 0x2000) push(); if(opcode & 0x2000) push();
regs.pc = np(); np();
step(2);
} }
else if((opcode & 0xdd00) == 0x0c00) { else if((opcode & 0xdd00) == 0x0c00) {
//00.0 11.0 .... .... //00.0 11.0 .... ....
//jumpeq i //jumpeq i
if(regs.z) { if(r.z) {
if(opcode & 0x2000) push(); if(opcode & 0x2000) push();
regs.pc = np(); np();
step(2);
} }
} }
else if((opcode & 0xdd00) == 0x1000) { else if((opcode & 0xdd00) == 0x1000) {
//00.1 00.0 .... .... //00.1 00.0 .... ....
//jumpge i //jumpge i
if(regs.c) { if(r.c) {
if(opcode & 0x2000) push(); if(opcode & 0x2000) push();
regs.pc = np(); np();
step(2);
} }
} }
else if((opcode & 0xdd00) == 0x1400) { else if((opcode & 0xdd00) == 0x1400) {
//00.1 01.0 .... .... //00.1 01.0 .... ....
//jumpmi i //jumpmi i
if(regs.n) { if(r.n) {
if(opcode & 0x2000) push(); if(opcode & 0x2000) push();
regs.pc = np(); np();
step(2);
} }
} }
else if((opcode & 0xffff) == 0x1c00) { else if((opcode & 0xffff) == 0x1c00) {
//0001 1100 0000 0000 //0001 1100 0000 0000
//loop? //wait
if(io.bus.enable) step(io.bus.pending);
} }
else if((opcode & 0xfffe) == 0x2500) { else if((opcode & 0xfffe) == 0x2500) {
//0010 0101 0000 000. //0010 0101 0000 000.
//skiplt/skipge //skiplt/skipge
if(regs.c == (opcode & 1)) regs.pc++; if(r.c == (opcode & 1)) {
advance();
step(1);
}
} }
else if((opcode & 0xfffe) == 0x2600) { else if((opcode & 0xfffe) == 0x2600) {
//0010 0110 0000 000. //0010 0110 0000 000.
//skipne/skipeq //skipne/skipeq
if(regs.z == (opcode & 1)) regs.pc++; if(r.z == (opcode & 1)) {
advance();
step(1);
}
} }
else if((opcode & 0xfffe) == 0x2700) { else if((opcode & 0xfffe) == 0x2700) {
//0010 0111 0000 000. //0010 0111 0000 000.
//skipmi/skippl //skipmi/skippl
if(regs.n == (opcode & 1)) regs.pc++; if(r.n == (opcode & 1)) {
advance();
step(1);
}
} }
else if((opcode & 0xffff) == 0x3c00) { else if((opcode & 0xffff) == 0x3c00) {
//0011 1100 0000 0000 //0011 1100 0000 0000
//ret //ret
pull(); pull();
step(2);
} }
else if((opcode & 0xffff) == 0x4000) { else if((opcode & 0xffff) == 0x4000) {
//0100 0000 0000 0000 //0100 0000 0000 0000
//rdbus //???
regs.busdata = read(regs.busaddr++); r.busAddress++;
} }
else if((opcode & 0xf800) == 0x4800) { else if((opcode & 0xf800) == 0x4800) {
//0100 1... .... .... //0100 1... .... ....
//cmpr a<<n,ri //cmpr a<<n,ri
int result = ri() - sa(); int result = ri() - sa();
regs.n = result & 0x800000; r.n = result & 0x800000;
regs.z = (uint24)result == 0; r.z = (uint24)result == 0;
regs.c = result >= 0; r.c = result >= 0;
} }
else if((opcode & 0xf800) == 0x5000) { else if((opcode & 0xf800) == 0x5000) {
//0101 0... .... .... //0101 0... .... ....
//cmp a<<n,ri //cmp a<<n,ri
int result = sa() - ri(); int result = sa() - ri();
regs.n = result & 0x800000; r.n = result & 0x800000;
regs.z = (uint24)result == 0; r.z = (uint24)result == 0;
regs.c = result >= 0; r.c = result >= 0;
} }
else if((opcode & 0xfb00) == 0x5900) { else if((opcode & 0xfb00) == 0x5900) {
//0101 1.01 .... .... //0101 1.01 .... ....
//sxb //sxb
regs.a = (int8)ri(); r.a = (int8)ri();
} }
else if((opcode & 0xfb00) == 0x5a00) { else if((opcode & 0xfb00) == 0x5a00) {
//0101 1.10 .... .... //0101 1.10 .... ....
//sxw //sxw
regs.a = (int16)ri(); r.a = (int16)ri();
} }
else if((opcode & 0xfb00) == 0x6000) { else if((opcode & 0xfb00) == 0x6000) {
//0110 0.00 .... .... //0110 0.00 .... ....
//ld a,ri //ld a,ri
regs.a = ri(); r.a = ri();
} }
else if((opcode & 0xfb00) == 0x6100) { else if((opcode & 0xfb00) == 0x6100) {
//0110 0.01 .... .... //0110 0.01 .... ....
//ld ?,ri //ld bus,ri
r.busData = ri();
} }
else if((opcode & 0xfb00) == 0x6300) { else if((opcode & 0xfb00) == 0x6300) {
//0110 0.11 .... .... //0110 0.11 .... ....
//ld p,ri //ld p,ri
regs.p = ri(); r.p = ri();
} }
else if((opcode & 0xfb00) == 0x6800) { else if((opcode & 0xfb00) == 0x6800) {
//0110 1.00 .... .... //0110 1.00 .... ....
//rdraml //rdraml
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xffff00) | (dataRAM[target] << 0); if(target < 0xc00) r.ramData.byte(0) = dataRAM[target];
} }
else if((opcode & 0xfb00) == 0x6900) { else if((opcode & 0xfb00) == 0x6900) {
//0110 1.01 .... .... //0110 1.01 .... ....
//rdramh //rdramh
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xff00ff) | (dataRAM[target] << 8); if(target < 0xc00) r.ramData.byte(1) = dataRAM[target];
} }
else if((opcode & 0xfb00) == 0x6a00) { else if((opcode & 0xfb00) == 0x6a00) {
//0110 1.10 .... .... //0110 1.10 .... ....
//rdramb //rdramb
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0x00ffff) | (dataRAM[target] << 16); if(target < 0xc00) r.ramData.byte(2) = dataRAM[target];
} }
else if((opcode & 0xffff) == 0x7000) { else if((opcode & 0xffff) == 0x7000) {
//0111 0000 0000 0000 //0111 0000 0000 0000
//rdrom //rdrom
regs.romdata = dataROM[regs.a & 0x3ff]; r.romData = dataROM[(uint10)r.a];
} }
else if((opcode & 0xff00) == 0x7c00) { else if((opcode & 0xff00) == 0x7c00) {
//0111 1100 .... .... //0111 1100 .... ....
//ld pl,i //ld pl,i
regs.p = (regs.p & 0xff00) | ((opcode & 0xff) << 0); r.p.byte(0) = opcode.bits(0,7);
} }
else if((opcode & 0xff00) == 0x7d00) { else if((opcode & 0xff00) == 0x7d00) {
//0111 1101 .... .... //0111 1101 .... ....
//ld ph,i //ld ph,i
regs.p = (regs.p & 0x00ff) | ((opcode & 0xff) << 8); r.p.byte(1) = opcode.bits(0,7);
} }
else if((opcode & 0xf800) == 0x8000) { else if((opcode & 0xf800) == 0x8000) {
//1000 0... .... .... //1000 0... .... ....
//add a<<n,ri //add a<<n,ri
int result = sa() + ri(); int result = sa() + ri();
regs.a = result; r.a = result;
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
regs.c = result > 0xffffff; r.c = result > 0xffffff;
} }
else if((opcode & 0xf800) == 0x8800) { else if((opcode & 0xf800) == 0x8800) {
//1000 1... .... .... //1000 1... .... ....
//subr a<<n,ri //subr a<<n,ri
int result = ri() - sa(); int result = ri() - sa();
regs.a = result; r.a = result;
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
regs.c = result >= 0; r.c = result >= 0;
} }
else if((opcode & 0xf800) == 0x9000) { else if((opcode & 0xf800) == 0x9000) {
//1001 0... .... .... //1001 0... .... ....
//sub a<<n,ri //sub a<<n,ri
int result = sa() - ri(); int result = sa() - ri();
regs.a = result; r.a = result;
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
regs.c = result >= 0; r.c = result >= 0;
} }
else if((opcode & 0xfb00) == 0x9800) { else if((opcode & 0xfb00) == 0x9800) {
//1001 1.00 .... .... //1001 1.00 .... ....
//mul a,ri //mul a,ri
int64 x = (int24)regs.a; int64 x = (int24)r.a;
int64 y = (int24)ri(); int64 y = (int24)ri();
x *= y; x *= y;
regs.accl = x >> 0ull; r.accl = x >> 0ull;
regs.acch = x >> 24ull; r.acch = x >> 24ull;
regs.n = regs.acch & 0x800000; r.n = r.acch & 0x800000;
regs.z = x == 0; r.z = x == 0;
} }
else if((opcode & 0xf800) == 0xa800) { else if((opcode & 0xf800) == 0xa800) {
//1010 1... .... .... //1010 1... .... ....
//xor a<<n,ri //xor a<<n,ri
regs.a = sa() ^ ri(); r.a = sa() ^ ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xf800) == 0xb000) { else if((opcode & 0xf800) == 0xb000) {
//1011 0... .... .... //1011 0... .... ....
//and a<<n,ri //and a<<n,ri
regs.a = sa() & ri(); r.a = sa() & ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xf800) == 0xb800) { else if((opcode & 0xf800) == 0xb800) {
//1011 1... .... .... //1011 1... .... ....
//or a<<n,ri //or a<<n,ri
regs.a = sa() | ri(); r.a = sa() | ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xfb00) == 0xc000) { else if((opcode & 0xfb00) == 0xc000) {
//1100 0.00 .... .... //1100 0.00 .... ....
//shr a,ri //shr a,ri
regs.a = regs.a >> ri(); r.a = r.a >> ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xfb00) == 0xc800) { else if((opcode & 0xfb00) == 0xc800) {
//1100 1.00 .... .... //1100 1.00 .... ....
//asr a,ri //asr a,ri
regs.a = (int24)regs.a >> ri(); r.a = (int24)r.a >> ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xfb00) == 0xd000) { else if((opcode & 0xfb00) == 0xd000) {
//1101 0.00 .... .... //1101 0.00 .... ....
//ror a,ri //ror a,ri
uint24 length = ri(); uint24 length = ri();
regs.a = (regs.a >> length) | (regs.a << (24 - length)); r.a = (r.a >> length) | (r.a << (24 - length));
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xfb00) == 0xd800) { else if((opcode & 0xfb00) == 0xd800) {
//1101 1.00 .... .... //1101 1.00 .... ....
//shl a,ri //shl a,ri
regs.a = regs.a << ri(); r.a = r.a << ri();
regs.n = regs.a & 0x800000; r.n = r.a & 0x800000;
regs.z = regs.a == 0; r.z = r.a == 0;
} }
else if((opcode & 0xff00) == 0xe000) { else if((opcode & 0xff00) == 0xe000) {
//1110 0000 .... .... //1110 0000 .... ....
//st r,a //st r,a
registerWrite(opcode & 0xff, regs.a); writeRegister(opcode & 0xff, r.a);
}
else if((opcode & 0xff00) == 0xe100) {
//1110 0001 .... ....
//st r,bus
writeRegister(opcode & 0xff, r.busData);
} }
else if((opcode & 0xfb00) == 0xe800) { else if((opcode & 0xfb00) == 0xe800) {
//1110 1.00 .... .... //1110 1.00 .... ....
//wrraml //wrraml
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 0; if(target < 0xc00) dataRAM[target] = r.ramData.byte(0);
} }
else if((opcode & 0xfb00) == 0xe900) { else if((opcode & 0xfb00) == 0xe900) {
//1110 1.01 .... .... //1110 1.01 .... ....
//wrramh //wrramh
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 8; if(target < 0xc00) dataRAM[target] = r.ramData.byte(1);
} }
else if((opcode & 0xfb00) == 0xea00) { else if((opcode & 0xfb00) == 0xea00) {
//1110 1.10 .... .... //1110 1.10 .... ....
//wrramb //wrramb
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0);
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 16; if(target < 0xc00) dataRAM[target] = r.ramData.byte(2);
} }
else if((opcode & 0xff00) == 0xf000) { else if((opcode & 0xff00) == 0xf000) {
//1111 0000 .... .... //1111 0000 .... ....
//swap a,r //swap a,r
uint24 source = registerRead(opcode & 0xff); uint24 source = readRegister(opcode & 0xff);
uint24 target = regs.a; uint24 target = r.a;
regs.a = source; r.a = source;
registerWrite(opcode & 0xff, target); writeRegister(opcode & 0xff, target);
} }
else if((opcode & 0xffff) == 0xfc00) { else if((opcode & 0xffff) == 0xfc00) {
//1111 1100 0000 0000 //1111 1100 0000 0000
//halt //halt
regs.halt = true; halt();
} }
else { else {
print("Hitachi DSP: unknown opcode @ ", hex(regs.pc - 1, 4L), " = ", hex(opcode, 4L), "\n"); print("Hitachi DSP: unknown opcode @ ", hex((r.pb << 8 | r.pc << 0) - 1, 6L), " = ", hex(opcode, 4L), "\n");
regs.halt = true; halt();
} }
} }

View File

@ -1,13 +1,26 @@
auto HG51B::registerRead(uint8 addr) const -> uint24 { auto HG51B::readRegister(uint7 address) -> uint24 {
switch(addr) { switch(address) {
case 0x00: return regs.a; case 0x00: return r.a;
case 0x01: return regs.acch; case 0x01: return r.acch;
case 0x02: return regs.accl; case 0x02: return r.accl;
case 0x03: return regs.busdata; case 0x03: return r.busData;
case 0x08: return regs.romdata; case 0x08: return r.romData;
case 0x0c: return regs.ramdata; case 0x0c: return r.ramData;
case 0x13: return regs.busaddr; case 0x13: return r.busAddress;
case 0x1c: return regs.ramaddr; case 0x1c: return r.ramAddress;
case 0x20: return r.pc;
case 0x2e:
io.bus.enable = 1;
io.bus.reading = 1;
io.bus.pending = 1 + io.wait.rom;
io.bus.address = r.busAddress;
return 0x000000;
case 0x2f:
io.bus.enable = 1;
io.bus.reading = 1;
io.bus.pending = 1 + io.wait.ram;
io.bus.address = r.busAddress;
return 0x000000;
case 0x50: return 0x000000; case 0x50: return 0x000000;
case 0x51: return 0xffffff; case 0x51: return 0xffffff;
case 0x52: return 0x00ff00; case 0x52: return 0x00ff00;
@ -24,51 +37,64 @@ auto HG51B::registerRead(uint8 addr) const -> uint24 {
case 0x5d: return 0xfeffff; case 0x5d: return 0xfeffff;
case 0x5e: return 0x000100; case 0x5e: return 0x000100;
case 0x5f: return 0x00feff; case 0x5f: return 0x00feff;
case 0x60: return regs.gpr[ 0]; case 0x60: case 0x70: return r.gpr[ 0];
case 0x61: return regs.gpr[ 1]; case 0x61: case 0x71: return r.gpr[ 1];
case 0x62: return regs.gpr[ 2]; case 0x62: case 0x72: return r.gpr[ 2];
case 0x63: return regs.gpr[ 3]; case 0x63: case 0x73: return r.gpr[ 3];
case 0x64: return regs.gpr[ 4]; case 0x64: case 0x74: return r.gpr[ 4];
case 0x65: return regs.gpr[ 5]; case 0x65: case 0x75: return r.gpr[ 5];
case 0x66: return regs.gpr[ 6]; case 0x66: case 0x76: return r.gpr[ 6];
case 0x67: return regs.gpr[ 7]; case 0x67: case 0x77: return r.gpr[ 7];
case 0x68: return regs.gpr[ 8]; case 0x68: case 0x78: return r.gpr[ 8];
case 0x69: return regs.gpr[ 9]; case 0x69: case 0x79: return r.gpr[ 9];
case 0x6a: return regs.gpr[10]; case 0x6a: case 0x7a: return r.gpr[10];
case 0x6b: return regs.gpr[11]; case 0x6b: case 0x7b: return r.gpr[11];
case 0x6c: return regs.gpr[12]; case 0x6c: case 0x7c: return r.gpr[12];
case 0x6d: return regs.gpr[13]; case 0x6d: case 0x7d: return r.gpr[13];
case 0x6e: return regs.gpr[14]; case 0x6e: case 0x7e: return r.gpr[14];
case 0x6f: return regs.gpr[15]; case 0x6f: case 0x7f: return r.gpr[15];
} }
return 0x000000; return 0x000000;
} }
auto HG51B::registerWrite(uint8 addr, uint24 data) -> void { auto HG51B::writeRegister(uint7 address, uint24 data) -> void {
switch(addr) { switch(address) {
case 0x00: regs.a = data; return; case 0x00: r.a = data; return;
case 0x01: regs.acch = data; return; case 0x01: r.acch = data; return;
case 0x02: regs.accl = data; return; case 0x02: r.accl = data; return;
case 0x03: regs.busdata = data; return; case 0x03: r.busData = data; return;
case 0x08: regs.romdata = data; return; case 0x08: r.romData = data; return;
case 0x0c: regs.ramdata = data; return; case 0x0c: r.ramData = data; return;
case 0x13: regs.busaddr = data; return; case 0x13: r.busAddress = data; return;
case 0x1c: regs.ramaddr = data; return; case 0x1c: r.ramAddress = data; return;
case 0x60: regs.gpr[ 0] = data; return; case 0x20: r.pc = data; return;
case 0x61: regs.gpr[ 1] = data; return; case 0x2e:
case 0x62: regs.gpr[ 2] = data; return; io.bus.enable = 1;
case 0x63: regs.gpr[ 3] = data; return; io.bus.writing = 1;
case 0x64: regs.gpr[ 4] = data; return; io.bus.pending = 1 + io.wait.rom;
case 0x65: regs.gpr[ 5] = data; return; io.bus.address = r.busAddress;
case 0x66: regs.gpr[ 6] = data; return; return;
case 0x67: regs.gpr[ 7] = data; return; case 0x2f:
case 0x68: regs.gpr[ 8] = data; return; io.bus.enable = 1;
case 0x69: regs.gpr[ 9] = data; return; io.bus.writing = 1;
case 0x6a: regs.gpr[10] = data; return; io.bus.pending = 1 + io.wait.ram;
case 0x6b: regs.gpr[11] = data; return; io.bus.address = r.busAddress;
case 0x6c: regs.gpr[12] = data; return; return;
case 0x6d: regs.gpr[13] = data; return; case 0x60: case 0x70: r.gpr[ 0] = data; return;
case 0x6e: regs.gpr[14] = data; return; case 0x61: case 0x71: r.gpr[ 1] = data; return;
case 0x6f: regs.gpr[15] = data; return; case 0x62: case 0x72: r.gpr[ 2] = data; return;
case 0x63: case 0x73: r.gpr[ 3] = data; return;
case 0x64: case 0x74: r.gpr[ 4] = data; return;
case 0x65: case 0x75: r.gpr[ 5] = data; return;
case 0x66: case 0x76: r.gpr[ 6] = data; return;
case 0x67: case 0x77: r.gpr[ 7] = data; return;
case 0x68: case 0x78: r.gpr[ 8] = data; return;
case 0x69: case 0x79: r.gpr[ 9] = data; return;
case 0x6a: case 0x7a: r.gpr[10] = data; return;
case 0x6b: case 0x7b: r.gpr[11] = data; return;
case 0x6c: case 0x7c: r.gpr[12] = data; return;
case 0x6d: case 0x7d: r.gpr[13] = data; return;
case 0x6e: case 0x7e: r.gpr[14] = data; return;
case 0x6f: case 0x7f: r.gpr[15] = data; return;
} }
} }

View File

@ -1,23 +1,58 @@
auto HG51B::serialize(serializer& s) -> void { auto HG51B::serialize(serializer& s) -> void {
s.array(programRAM[0]);
s.array(programRAM[1]);
s.array(dataRAM); s.array(dataRAM);
for(auto& n : stack) s.integer(n);
s.integer(r.p);
s.integer(r.pb);
s.integer(r.pc);
s.boolean(r.n);
s.boolean(r.z);
s.boolean(r.c);
s.boolean(r.i);
s.integer(r.a);
s.integer(r.acch);
s.integer(r.accl);
s.integer(r.busData);
s.integer(r.romData);
s.integer(r.ramData);
s.integer(r.busAddress);
s.integer(r.ramAddress);
s.array(r.gpr);
s.integer(io.lock);
s.integer(io.halt);
s.integer(io.irq);
s.integer(io.rom);
s.array(io.vector);
s.integer(io.wait.rom);
s.integer(io.wait.ram);
s.integer(io.suspend.enable);
s.integer(io.suspend.duration);
s.integer(io.cache.enable);
s.integer(io.cache.page);
s.array(io.cache.lock);
s.array(io.cache.address);
s.integer(io.cache.base);
s.integer(io.cache.pb);
s.integer(io.cache.pc);
s.integer(io.dma.enable);
s.integer(io.dma.source);
s.integer(io.dma.target);
s.integer(io.dma.length);
s.integer(io.bus.enable);
s.integer(io.bus.reading);
s.integer(io.bus.writing);
s.integer(io.bus.pending);
s.integer(io.bus.address);
s.array(stack);
s.integer(opcode); s.integer(opcode);
s.integer(regs.halt);
s.integer(regs.pc);
s.integer(regs.p);
s.integer(regs.n);
s.integer(regs.z);
s.integer(regs.c);
s.integer(regs.a);
s.integer(regs.acch);
s.integer(regs.accl);
s.integer(regs.busdata);
s.integer(regs.romdata);
s.integer(regs.ramdata);
s.integer(regs.busaddr);
s.integer(regs.ramaddr);
for(auto& n : regs.gpr) s.integer(n);
} }

View File

@ -10,6 +10,7 @@ N push(db(PC));
lo(PC) = read(r.vector + 0); lo(PC) = read(r.vector + 0);
hi(PC) = read(r.vector + 1); hi(PC) = read(r.vector + 1);
db(PC) = 0x00; db(PC) = 0x00;
idleJump();
} }
//both the accumulator and index registers can independently be in either 8-bit or 16-bit mode. //both the accumulator and index registers can independently be in either 8-bit or 16-bit mode.

View File

@ -98,8 +98,6 @@ auto Cartridge::load() -> bool {
information.sha256 = sha.digest(); information.sha256 = sha.digest();
} }
rom.writeProtect(true);
ram.writeProtect(false);
return true; return true;
} }

View File

@ -12,8 +12,8 @@ struct Cartridge {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
MappedRAM rom; ReadableMemory rom;
MappedRAM ram; WritableMemory ram;
struct Information { struct Information {
uint pathID = 0; uint pathID = 0;
@ -67,8 +67,8 @@ private:
auto loadCartridgeSufamiTurboB(Markup::Node) -> void; auto loadCartridgeSufamiTurboB(Markup::Node) -> void;
auto loadMemory(Memory&, Markup::Node, bool required) -> void; auto loadMemory(Memory&, Markup::Node, bool required) -> void;
auto loadMap(Markup::Node, SuperFamicom::Memory&) -> void; template<typename T> auto loadMap(Markup::Node, T&) -> uint;
auto loadMap(Markup::Node, const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&) -> void; auto loadMap(Markup::Node, const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&) -> uint;
auto loadROM(Markup::Node) -> void; auto loadROM(Markup::Node) -> void;
auto loadRAM(Markup::Node) -> void; auto loadRAM(Markup::Node) -> void;

View File

@ -130,26 +130,27 @@ auto Cartridge::loadMemory(Memory& ram, Markup::Node node, bool required) -> voi
} }
} }
auto Cartridge::loadMap(Markup::Node map, SuperFamicom::Memory& memory) -> void { template<typename T> //T = ReadableMemory, WritableMemory, ProtectableMemory
auto Cartridge::loadMap(Markup::Node map, T& memory) -> uint {
auto addr = map["address"].text(); auto addr = map["address"].text();
auto size = map["size"].natural(); auto size = map["size"].natural();
auto base = map["base"].natural(); auto base = map["base"].natural();
auto mask = map["mask"].natural(); auto mask = map["mask"].natural();
if(size == 0) size = memory.size(); if(size == 0) size = memory.size();
if(size == 0) return; if(size == 0) return print("loadMap(): size=0\n"), 0; //does this ever actually occur?
bus.map({&SuperFamicom::Memory::read, &memory}, {&SuperFamicom::Memory::write, &memory}, addr, size, base, mask); return bus.map({&T::read, &memory}, {&T::write, &memory}, addr, size, base, mask);
} }
auto Cartridge::loadMap( auto Cartridge::loadMap(
Markup::Node map, Markup::Node map,
const function<uint8 (uint24, uint8)>& reader, const function<uint8 (uint24, uint8)>& reader,
const function<void (uint24, uint8)>& writer const function<void (uint24, uint8)>& writer
) -> void { ) -> uint {
auto addr = map["address"].text(); auto addr = map["address"].text();
auto size = map["size"].natural(); auto size = map["size"].natural();
auto base = map["base"].natural(); auto base = map["base"].natural();
auto mask = map["mask"].natural(); auto mask = map["mask"].natural();
bus.map(reader, writer, addr, size, base, mask); return bus.map(reader, writer, addr, size, base, mask);
} }
//memory(type=ROM,content=Program) //memory(type=ROM,content=Program)
@ -416,22 +417,23 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
hitachidsp.Frequency = 20'000'000; hitachidsp.Frequency = 20'000'000;
} }
hitachidsp.Roms = roms; //1 or 2 hitachidsp.Roms = roms; //1 or 2
hitachidsp.Mapping = 0; //0 or 1
for(auto map : node.find("map")) { for(auto map : node.find("map")) {
loadMap(map, {&HitachiDSP::dspRead, &hitachidsp}, {&HitachiDSP::dspWrite, &hitachidsp}); loadMap(map, {&HitachiDSP::readIO, &hitachidsp}, {&HitachiDSP::writeIO, &hitachidsp});
} }
if(auto memory = node["memory(type=ROM,content=Program)"]) { if(auto memory = node["memory(type=ROM,content=Program)"]) {
loadMemory(hitachidsp.rom, memory, File::Required); loadMemory(hitachidsp.rom, memory, File::Required);
for(auto map : memory.find("map")) { for(auto map : memory.find("map")) {
loadMap(map, {&HitachiDSP::romRead, &hitachidsp}, {&HitachiDSP::romWrite, &hitachidsp}); loadMap(map, {&HitachiDSP::readROM, &hitachidsp}, {&HitachiDSP::writeROM, &hitachidsp});
} }
} }
if(auto memory = node["memory(type=RAM,content=Save)"]) { if(auto memory = node["memory(type=RAM,content=Save)"]) {
loadMemory(hitachidsp.ram, memory, File::Optional); loadMemory(hitachidsp.ram, memory, File::Optional);
for(auto map : memory.find("map")) { for(auto map : memory.find("map")) {
loadMap(map, {&HitachiDSP::ramRead, &hitachidsp}, {&HitachiDSP::ramWrite, &hitachidsp}); loadMap(map, {&HitachiDSP::readRAM, &hitachidsp}, {&HitachiDSP::writeRAM, &hitachidsp});
} }
} }
@ -450,7 +452,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
} }
} }
for(auto map : memory.find("map")) { for(auto map : memory.find("map")) {
loadMap(map, {&HitachiDSP::dramRead, &hitachidsp}, {&HitachiDSP::dramWrite, &hitachidsp}); loadMap(map, {&HitachiDSP::readDRAM, &hitachidsp}, {&HitachiDSP::writeDRAM, &hitachidsp});
} }
} }
} }

View File

@ -39,11 +39,6 @@ auto Event::unload() -> void {
auto Event::power() -> void { auto Event::power() -> void {
create(Event::Enter, 1); create(Event::Enter, 1);
rom[0].writeProtect(true);
rom[1].writeProtect(true);
rom[2].writeProtect(true);
rom[3].writeProtect(true);
//DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes //DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes
timer = (3 + dip.value.bits(0,3)) * 60; //in seconds timer = (3 + dip.value.bits(0,3)) * 60; //in seconds
//DIP switches 4-5 serve an unknown purpose //DIP switches 4-5 serve an unknown purpose

View File

@ -30,7 +30,7 @@ struct Event : Thread {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
public: public:
MappedRAM rom[4]; ReadableMemory rom[4];
enum class Board : uint { Unknown, CampusChallenge92, PowerFest94 } board; enum class Board : uint { Unknown, CampusChallenge92, PowerFest94 } board;
uint timer; uint timer;

View File

@ -10,21 +10,17 @@ auto HitachiDSP::Enter() -> void {
while(true) scheduler.synchronize(), hitachidsp.main(); while(true) scheduler.synchronize(), hitachidsp.main();
} }
auto HitachiDSP::main() -> void { auto HitachiDSP::step(uint clocks) -> void {
if(mmio.dma) { HG51B::step(clocks);
for(auto n : range(mmio.dmaLength)) { Thread::step(clocks);
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
step(2);
synchronize(cpu);
}
mmio.dma = false;
}
exec(mmio.programOffset);
step(1);
synchronize(cpu); synchronize(cpu);
} }
auto HitachiDSP::halt() -> void {
HG51B::halt();
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
}
auto HitachiDSP::unload() -> void { auto HitachiDSP::unload() -> void {
rom.reset(); rom.reset();
ram.reset(); ram.reset();
@ -33,23 +29,6 @@ auto HitachiDSP::unload() -> void {
auto HitachiDSP::power() -> void { auto HitachiDSP::power() -> void {
HG51B::power(); HG51B::power();
create(HitachiDSP::Enter, Frequency); create(HitachiDSP::Enter, Frequency);
rom.writeProtect(true);
ram.writeProtect(false);
mmio.dma = false;
mmio.dmaSource = 0x000000;
mmio.dmaLength = 0x0000;
mmio.dmaTarget = 0x000000;
mmio.r1f48 = 0x00;
mmio.programOffset = 0x000000;
mmio.r1f4c = 0x00;
mmio.pageNumber = 0x0000;
mmio.programCounter = 0x00;
mmio.r1f50 = 0x33;
mmio.r1f51 = 0x00;
mmio.r1f52 = 0x01;
} }
} }

View File

@ -1,55 +1,48 @@
struct HitachiDSP : Processor::HG51B, Thread { struct HitachiDSP : Processor::HG51B, Thread {
MappedRAM rom; ReadableMemory rom;
MappedRAM ram; WritableMemory ram;
//hitachidsp.cpp
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto step(uint clocks) -> void override;
auto halt() -> void override;
auto unload() -> void; auto unload() -> void;
auto power() -> void; auto power() -> void;
auto isROM(uint24 address) -> bool override;
auto isRAM(uint24 address) -> bool override;
//HG51B read/write //HG51B read/write
auto read(uint24 addr) -> uint8 override; auto read(uint24 address) -> uint8 override;
auto write(uint24 addr, uint8 data) -> void override; auto write(uint24 address, uint8 data) -> void override;
//CPU ROM read/write //CPU ROM read/write
auto romRead(uint24 addr, uint8 data) -> uint8; auto addressROM(uint24 address) const -> maybe<uint24>;
auto romWrite(uint24 addr, uint8 data) -> void; auto readROM(uint24 address, uint8 data = 0) -> uint8;
auto writeROM(uint24 address, uint8 data) -> void;
//CPU RAM read/write //CPU RAM read/write
auto ramRead(uint24 addr, uint8 data) -> uint8; auto addressRAM(uint24 address) const -> maybe<uint24>;
auto ramWrite(uint24 addr, uint8 data) -> void; auto readRAM(uint24 address, uint8 data = 0) -> uint8;
auto writeRAM(uint24 address, uint8 data) -> void;
//HG51B data RAM read/write //HG51B data RAM read/write
auto dramRead(uint24 addr, uint8 data) -> uint8; auto addressDRAM(uint24 address) const -> maybe<uint24>;
auto dramWrite(uint24 addr, uint8 data) -> void; auto readDRAM(uint24 address, uint8 data = 0) -> uint8;
auto writeDRAM(uint24 address, uint8 data) -> void;
//CPU MMIO read/write //CPU IO read/write
auto dspRead(uint24 addr, uint8 data) -> uint8; auto addressIO(uint24 address) const -> maybe<uint24>;
auto dspWrite(uint24 addr, uint8 data) -> void; auto readIO(uint24 address, uint8 data = 0) -> uint8;
auto writeIO(uint24 address, uint8 data) -> void;
auto firmware() const -> vector<uint8>; auto firmware() const -> vector<uint8>;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint Frequency; uint Frequency;
uint Roms; uint Roms;
bool Mapping;
struct MMIO {
bool dma; //true during DMA transfers
uint24 dmaSource; //$1f40-$1f42
uint24 dmaLength; //$1f43-$1f44
uint24 dmaTarget; //$1f45-$1f47
uint8 r1f48; //$1f48
uint24 programOffset; //$1f49-$1f4b
uint8 r1f4c; //$1f4c
uint16 pageNumber; //$1f4d-$1f4e
uint8 programCounter; //$1f4f
uint8 r1f50; //$1f50
uint8 r1f51; //$1f51
uint8 r1f52; //$1f52
uint8 vector[32]; //$1f60-$1f7f
} mmio;
}; };
extern HitachiDSP hitachidsp; extern HitachiDSP hitachidsp;

View File

@ -1,166 +1,264 @@
auto HitachiDSP::read(uint24 addr) -> uint8 { auto HitachiDSP::isROM(uint24 address) -> bool {
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6cff,7c00-7cff return (bool)addressROM(address);
return dspRead(addr, 0x00); }
}
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff auto HitachiDSP::isRAM(uint24 address) -> bool {
return dramRead(addr, 0x00); return (bool)addressRAM(address);
} }
if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff
if(rom.size() == 0) return 0x00; auto HitachiDSP::read(uint24 address) -> uint8 {
addr = ((addr & 0x3f0000) >> 1) | (addr & 0x7fff); if(auto linear = addressROM (address)) return readROM (*linear);
addr = Bus::mirror(addr, rom.size()); if(auto linear = addressRAM (address)) return readRAM (*linear);
return rom.read(addr, 0); if(auto linear = addressDRAM(address)) return readDRAM(*linear);
} if(auto linear = addressIO (address)) return readIO (*linear);
if((addr & 0xf88000) == 0x700000) { //$70-77:0000-7fff
if(ram.size() == 0) return 0x00;
addr = ((addr & 0x070000) >> 1) | (addr & 0x7fff);
addr = Bus::mirror(addr, ram.size());
return ram.read(addr);
}
return 0x00; return 0x00;
} }
auto HitachiDSP::write(uint24 addr, uint8 data) -> void { auto HitachiDSP::write(uint24 address, uint8 data) -> void {
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6fff,7c00-7fff if(auto linear = addressROM (address)) return writeROM (*linear, data);
return dspWrite(addr, data); if(auto linear = addressRAM (address)) return writeRAM (*linear, data);
} if(auto linear = addressDRAM(address)) return writeDRAM(*linear, data);
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff if(auto linear = addressIO (address)) return writeIO (*linear, data);
return dramWrite(addr, data);
}
if((addr & 0xf88000) == 0x700000) { //$70-77:0000-7fff
if(ram.size() == 0) return;
addr = ((addr & 0x070000) >> 1) | (addr & 0x7fff);
addr = Bus::mirror(addr, ram.size());
return ram.write(addr, data);
}
} }
auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 { //
if(hitachidsp.active() || regs.halt) {
addr = Bus::mirror(addr, rom.size()); auto HitachiDSP::addressROM(uint24 address) const -> maybe<uint24> {
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00; if(Mapping == 0) {
return rom.read(addr, data); //00-3f,80-bf:8000-ffff; c0-ff:0000-ffff
if((address & 0x408000) == 0x008000 || (address & 0xc00000) == 0xc00000) {
address = (address & 0x3f0000) >> 1 | (address & 0x7fff);
return {address & 0x1fffff};
}
} else {
//00-3f,80-bf:8000-ffff; c0-ff:0000-ffff
if((address & 0x408000) == 0x008000 || (address & 0xc00000) == 0xc00000) {
return {address & 0x3fffff};
}
} }
if((addr & 0x40ffe0) == 0x00ffe0) return mmio.vector[addr & 0x1f]; return {};
}
auto HitachiDSP::readROM(uint24 address, uint8 data) -> uint8 {
if(hitachidsp.active() || !busy()) {
address = bus.mirror(address, rom.size());
//if(Roms == 2 && mmio.r1f52 == 1 && address >= (bit::round(rom.size()) >> 1)) return 0x00;
return rom.read(address, data);
}
//DSP has the bus acquired: CPU reads from 00:ffc0-ffff return IO registers (including reset vector overrides)
if(Mapping == 0 && (address & 0xbfffc0) == 0x007fc0) return readIO(0x7f40 | address & 0x3f);
if(Mapping == 1 && (address & 0xbfffc0) == 0x00ffc0) return readIO(0x7f40 | address & 0x3f);
return data; return data;
} }
auto HitachiDSP::romWrite(uint24 addr, uint8 data) -> void { auto HitachiDSP::writeROM(uint24 address, uint8 data) -> void {
} }
auto HitachiDSP::ramRead(uint24 addr, uint8 data) -> uint8 { //
auto HitachiDSP::addressRAM(uint24 address) const -> maybe<uint24> {
if(Mapping == 0) {
//70-77:0000-7fff
if((address & 0xf88000) == 0x700000) {
address = (address & 0x070000) >> 1 | (address & 0x7fff);
return {address & 0x03ffff};
}
} else {
//30-3f,b0-bf:6000-7fff
if((address & 0x70e000) == 0x306000) {
address = (address & 0x0f0000) >> 3 | (address & 0x1fff);
return {address & 0x01ffff};
}
}
return {};
}
auto HitachiDSP::readRAM(uint24 address, uint8 data) -> uint8 {
if(ram.size() == 0) return 0x00; //not open bus if(ram.size() == 0) return 0x00; //not open bus
return ram.read(Bus::mirror(addr, ram.size()), data); return ram.read(bus.mirror(address, ram.size()), data);
} }
auto HitachiDSP::ramWrite(uint24 addr, uint8 data) -> void { auto HitachiDSP::writeRAM(uint24 address, uint8 data) -> void {
if(ram.size() == 0) return; if(ram.size() == 0) return;
return ram.write(Bus::mirror(addr, ram.size()), data); return ram.write(bus.mirror(address, ram.size()), data);
} }
auto HitachiDSP::dramRead(uint24 addr, uint8 data) -> uint8 { //
addr &= 0xfff;
if(addr >= 0xc00) return data; auto HitachiDSP::addressDRAM(uint24 address) const -> maybe<uint24> {
return dataRAM[addr]; if(Mapping == 0) {
//00-3f,80-bf:6000-6bff,7000-7bff
if((address & 0x40e000) == 0x006000 && (address & 0x0c00) != 0x0c00) {
return {address & 0x0fff};
}
} else {
//00-2f,80-af:6000-6bff,7000-7bff
if((address & 0x40e000) == 0x006000 && (address & 0x0c00) != 0x0c00 && (address & 0x300000) != 0x300000) {
return {address & 0x0fff};
}
}
return {};
} }
auto HitachiDSP::dramWrite(uint24 addr, uint8 data) -> void { auto HitachiDSP::readDRAM(uint24 address, uint8 data) -> uint8 {
addr &= 0xfff; address &= 0xfff;
if(addr >= 0xc00) return; if(address >= 0xc00) return data;
dataRAM[addr] = data; return dataRAM[address];
} }
auto HitachiDSP::dspRead(uint24 addr, uint8) -> uint8 { auto HitachiDSP::writeDRAM(uint24 address, uint8 data) -> void {
addr = 0x7c00 | (addr & 0x03ff); address &= 0xfff;
if(address >= 0xc00) return;
dataRAM[address] = data;
}
//MMIO //
switch(addr) {
case 0x7f40: return mmio.dmaSource >> 0; auto HitachiDSP::addressIO(uint24 address) const -> maybe<uint24> {
case 0x7f41: return mmio.dmaSource >> 8; if(Mapping == 0) {
case 0x7f42: return mmio.dmaSource >> 16; //00-3f,80-bf:6c00-6fff,7c00-7fff
case 0x7f43: return mmio.dmaLength >> 0; if((address & 0x40ec00) == 0x006c00) {
case 0x7f44: return mmio.dmaLength >> 8; return {address & 0x03ff};
case 0x7f45: return mmio.dmaTarget >> 0; }
case 0x7f46: return mmio.dmaTarget >> 8; } else {
case 0x7f47: return mmio.dmaTarget >> 16; //00-2f,80-af:6c00-6fff,7c00-7fff
case 0x7f48: return mmio.r1f48; if((address & 0x40ec00) == 0x006c00 && (address & 0x300000) != 0x300000) {
case 0x7f49: return mmio.programOffset >> 0; return {address & 0x03ff};
case 0x7f4a: return mmio.programOffset >> 8; }
case 0x7f4b: return mmio.programOffset >> 16; }
case 0x7f4c: return mmio.r1f4c; return {};
case 0x7f4d: return mmio.pageNumber >> 0; }
case 0x7f4e: return mmio.pageNumber >> 8;
case 0x7f4f: return mmio.programCounter; auto HitachiDSP::readIO(uint24 address, uint8 data) -> uint8 {
case 0x7f50: return mmio.r1f50; address = 0x7c00 | (address & 0x03ff);
case 0x7f51: return mmio.r1f51;
case 0x7f52: return mmio.r1f52; //IO
case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56: switch(address) {
case 0x7f57: case 0x7f58: case 0x7f59: case 0x7f5a: case 0x7f40: return io.dma.source.byte(0);
case 0x7f5b: case 0x7f5c: case 0x7f5d: case 0x7f5e: case 0x7f41: return io.dma.source.byte(1);
case 0x7f5f: return ((regs.halt == false) << 6) | ((regs.halt == true) << 1); case 0x7f42: return io.dma.source.byte(2);
case 0x7f43: return io.dma.length.byte(0);
case 0x7f44: return io.dma.length.byte(1);
case 0x7f45: return io.dma.target.byte(0);
case 0x7f46: return io.dma.target.byte(1);
case 0x7f47: return io.dma.target.byte(2);
case 0x7f48: return io.cache.page;
case 0x7f49: return io.cache.base.byte(0);
case 0x7f4a: return io.cache.base.byte(1);
case 0x7f4b: return io.cache.base.byte(2);
case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1;
case 0x7f4d: return io.cache.pb.byte(0);
case 0x7f4e: return io.cache.pb.byte(1);
case 0x7f4f: return io.cache.pc;
case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4;
case 0x7f51: return io.irq;
case 0x7f52: return io.rom;
case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56: case 0x7f57:
case 0x7f59: case 0x7f5b: case 0x7f5c: case 0x7f5d: case 0x7f5e:
case 0x7f5f: return io.suspend.enable << 0 | r.i << 1 | running() << 6 | busy() << 7;
} }
//Vector //vectors
if(addr >= 0x7f60 && addr <= 0x7f7f) { if(address >= 0x7f60 && address <= 0x7f7f) {
return mmio.vector[addr & 0x1f]; return io.vector[address & 0x1f];
} }
//GPRs //registers
if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) { if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
uint index = (addr & 0x3f) / 3; //0..15 address &= 0x3f;
uint shift = ((addr & 0x3f) % 3) * 8; //0, 8, 16 return r.gpr[address / 3].byte(address % 3);
return regs.gpr[index] >> shift;
} }
return 0x00; return 0x00;
} }
auto HitachiDSP::dspWrite(uint24 addr, uint8 data) -> void { auto HitachiDSP::writeIO(uint24 address, uint8 data) -> void {
addr = 0x7c00 | (addr & 0x03ff); address = 0x7c00 | (address & 0x03ff);
//MMIO //IO
switch(addr) { switch(address) {
case 0x7f40: mmio.dmaSource = (mmio.dmaSource & 0xffff00) | (data << 0); return; case 0x7f40: io.dma.source.byte(0) = data; return;
case 0x7f41: mmio.dmaSource = (mmio.dmaSource & 0xff00ff) | (data << 8); return; case 0x7f41: io.dma.source.byte(1) = data; return;
case 0x7f42: mmio.dmaSource = (mmio.dmaSource & 0x00ffff) | (data << 16); return; case 0x7f42: io.dma.source.byte(2) = data; return;
case 0x7f43: mmio.dmaLength = (mmio.dmaLength & 0xff00) | (data << 0); return;
case 0x7f44: mmio.dmaLength = (mmio.dmaLength & 0x00ff) | (data << 8); return; case 0x7f43: io.dma.length.byte(0) = data; return;
case 0x7f45: mmio.dmaTarget = (mmio.dmaTarget & 0xffff00) | (data << 0); return; case 0x7f44: io.dma.length.byte(1) = data; return;
case 0x7f46: mmio.dmaTarget = (mmio.dmaTarget & 0xff00ff) | (data << 8); return;
case 0x7f47: mmio.dmaTarget = (mmio.dmaTarget & 0x00ffff) | (data << 16); case 0x7f45: io.dma.target.byte(0) = data; return;
if(regs.halt) mmio.dma = true; case 0x7f46: io.dma.target.byte(1) = data; return;
case 0x7f47: io.dma.target.byte(2) = data;
if(io.halt) io.dma.enable = 1;
return; return;
case 0x7f48: mmio.r1f48 = data & 0x01; return;
case 0x7f49: mmio.programOffset = (mmio.programOffset & 0xffff00) | (data << 0); return; case 0x7f48:
case 0x7f4a: mmio.programOffset = (mmio.programOffset & 0xff00ff) | (data << 8); return; io.cache.page = data.bit(0);
case 0x7f4b: mmio.programOffset = (mmio.programOffset & 0x00ffff) | (data << 16); return; if(io.halt) io.cache.enable = 1;
case 0x7f4c: mmio.r1f4c = data & 0x03; return; return;
case 0x7f4d: mmio.pageNumber = (mmio.pageNumber & 0x7f00) | ((data & 0xff) << 0); return;
case 0x7f4e: mmio.pageNumber = (mmio.pageNumber & 0x00ff) | ((data & 0x7f) << 8); return; case 0x7f49: io.cache.base.byte(0) = data; return;
case 0x7f4f: mmio.programCounter = data; case 0x7f4a: io.cache.base.byte(1) = data; return;
if(regs.halt) { case 0x7f4b: io.cache.base.byte(2) = data; return;
regs.pc = mmio.pageNumber * 256 + mmio.programCounter;
regs.halt = false; case 0x7f4c:
io.cache.lock[0] = data.bit(0);
io.cache.lock[1] = data.bit(1);
return;
case 0x7f4d: io.cache.pb.byte(0) = data; return;
case 0x7f4e: io.cache.pb.byte(1) = data; return;
case 0x7f4f:
io.cache.pc = data;
if(io.halt) {
io.halt = 0;
r.pb = io.cache.pb;
r.pc = io.cache.pc;
} }
return; return;
case 0x7f50: mmio.r1f50 = data & 0x77; return;
case 0x7f51: mmio.r1f51 = data & 0x01; return;
case 0x7f52: mmio.r1f52 = data & 0x01; return;
}
//Vector case 0x7f50:
if(addr >= 0x7f60 && addr <= 0x7f7f) { io.wait.ram = data.bits(0,2);
mmio.vector[addr & 0x1f] = data; io.wait.rom = data.bits(4,6);
return;
case 0x7f51:
io.irq = data.bit(0);
if(io.irq == 1) r.i = 0, cpu.r.irq = 0;
return;
case 0x7f52:
io.rom = data.bit(0);
return;
case 0x7f53:
io.lock = 0;
io.halt = 1;
return;
case 0x7f55: io.suspend.enable = 1; io.suspend.duration = 0; return; //indefinite
case 0x7f56: io.suspend.enable = 1; io.suspend.duration = 32; return;
case 0x7f57: io.suspend.enable = 1; io.suspend.duration = 64; return;
case 0x7f58: io.suspend.enable = 1; io.suspend.duration = 96; return;
case 0x7f59: io.suspend.enable = 1; io.suspend.duration = 128; return;
case 0x7f5a: io.suspend.enable = 1; io.suspend.duration = 160; return;
case 0x7f5b: io.suspend.enable = 1; io.suspend.duration = 192; return;
case 0x7f5c: io.suspend.enable = 1; io.suspend.duration = 224; return;
case 0x7f5d: io.suspend.enable = 0; return; //resume
case 0x7f5e:
r.i = 0; //does not deassert CPU IRQ line
return; return;
} }
//GPRs //vectors
if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) { if(address >= 0x7f60 && address <= 0x7f7f) {
uint index = (addr & 0x3f) / 3; io.vector[address & 0x1f] = data;
switch((addr & 0x3f) % 3) { return;
case 0: regs.gpr[index] = (regs.gpr[index] & 0xffff00) | (data << 0); return; }
case 1: regs.gpr[index] = (regs.gpr[index] & 0xff00ff) | (data << 8); return;
case 2: regs.gpr[index] = (regs.gpr[index] & 0x00ffff) | (data << 16); return; //registers
} if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
address &= 0x3f;
r.gpr[address / 3].byte(address % 3) = data;
} }
} }

View File

@ -13,18 +13,4 @@ auto HitachiDSP::firmware() const -> vector<uint8> {
auto HitachiDSP::serialize(serializer& s) -> void { auto HitachiDSP::serialize(serializer& s) -> void {
HG51B::serialize(s); HG51B::serialize(s);
Thread::serialize(s); Thread::serialize(s);
s.integer(mmio.dma);
s.integer(mmio.dmaSource);
s.integer(mmio.dmaLength);
s.integer(mmio.dmaTarget);
s.integer(mmio.r1f48);
s.integer(mmio.programOffset);
s.integer(mmio.r1f4c);
s.integer(mmio.pageNumber);
s.integer(mmio.programCounter);
s.integer(mmio.r1f50);
s.integer(mmio.r1f51);
s.integer(mmio.r1f52);
s.array(mmio.vector);
} }

View File

@ -11,9 +11,6 @@ auto MCC::unload() -> void {
} }
auto MCC::power() -> void { auto MCC::power() -> void {
rom.writeProtect(true);
psram.writeProtect(false);
irq.flag = 0; irq.flag = 0;
irq.enable = 0; irq.enable = 0;
w.mapping = 1; w.mapping = 1;

View File

@ -2,8 +2,8 @@
//Custom logic chip inside the BS-X Satellaview base cartridge //Custom logic chip inside the BS-X Satellaview base cartridge
struct MCC { struct MCC {
MappedRAM rom; ReadableMemory rom;
MappedRAM psram; WritableMemory psram;
//mcc.cpp //mcc.cpp
auto unload() -> void; auto unload() -> void;

View File

@ -10,8 +10,6 @@ auto OBC1::unload() -> void {
} }
auto OBC1::power() -> void { auto OBC1::power() -> void {
ram.writeProtect(false);
status.baseptr = (ramRead(0x1ff5) & 1) ? 0x1800 : 0x1c00; status.baseptr = (ramRead(0x1ff5) & 1) ? 0x1800 : 0x1c00;
status.address = (ramRead(0x1ff6) & 0x7f); status.address = (ramRead(0x1ff6) & 0x7f);
status.shift = (ramRead(0x1ff6) & 3) << 1; status.shift = (ramRead(0x1ff6) & 3) << 1;

View File

@ -7,7 +7,7 @@ struct OBC1 {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
MappedRAM ram; WritableMemory ram;
private: private:
auto ramRead(uint addr) -> uint8; auto ramRead(uint addr) -> uint8;

View File

@ -1,4 +1,6 @@
auto SA1::BWRAM::conflict() const -> bool { auto SA1::BWRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(!cpu.r.rwb) return false; if(!cpu.r.rwb) return false;
if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff
if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff
@ -8,13 +10,13 @@ auto SA1::BWRAM::conflict() const -> bool {
auto SA1::BWRAM::read(uint24 address, uint8 data) -> uint8 { auto SA1::BWRAM::read(uint24 address, uint8 data) -> uint8 {
if(!size()) return data; if(!size()) return data;
address = bus.mirror(address, size()); address = bus.mirror(address, size());
return _data[address]; return WritableMemory::read(address, data);
} }
auto SA1::BWRAM::write(uint24 address, uint8 data) -> void { auto SA1::BWRAM::write(uint24 address, uint8 data) -> void {
if(!size()) return; if(!size()) return;
address = bus.mirror(address, size()); address = bus.mirror(address, size());
_data[address] = data; return WritableMemory::write(address, data);
} }
//note: addresses are translated prior to invoking this function: //note: addresses are translated prior to invoking this function:

View File

@ -1,4 +1,6 @@
auto SA1::IRAM::conflict() const -> bool { auto SA1::IRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(!cpu.r.rwb) return false; if(!cpu.r.rwb) return false;
if((cpu.r.mar & 0x40f800) == 0x003000) return true; //00-3f,80-bf:3000-37ff if((cpu.r.mar & 0x40f800) == 0x003000) return true; //00-3f,80-bf:3000-37ff
return false; return false;
@ -6,12 +8,14 @@ auto SA1::IRAM::conflict() const -> bool {
auto SA1::IRAM::read(uint24 address, uint8 data) -> uint8 { auto SA1::IRAM::read(uint24 address, uint8 data) -> uint8 {
if(!size()) return data; if(!size()) return data;
return _data[address & size() - 1]; address = bus.mirror(address, size());
return WritableMemory::read(address, data);
} }
auto SA1::IRAM::write(uint24 address, uint8 data) -> void { auto SA1::IRAM::write(uint24 address, uint8 data) -> void {
if(!size()) return; if(!size()) return;
_data[address & size() - 1] = data; address = bus.mirror(address, size());
return WritableMemory::write(address, data);
} }
auto SA1::IRAM::readCPU(uint24 address, uint8 data) -> uint8 { auto SA1::IRAM::readCPU(uint24 address, uint8 data) -> uint8 {

View File

@ -1,4 +1,6 @@
auto SA1::ROM::conflict() const -> bool { auto SA1::ROM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(!cpu.r.rwb) return false; if(!cpu.r.rwb) return false;
if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff
if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff
@ -7,7 +9,7 @@ auto SA1::ROM::conflict() const -> bool {
auto SA1::ROM::read(uint24 address, uint8 data) -> uint8 { auto SA1::ROM::read(uint24 address, uint8 data) -> uint8 {
address = bus.mirror(address, size()); address = bus.mirror(address, size());
return _data[address]; return ReadableMemory::read(address, data);
} }
auto SA1::ROM::write(uint24 address, uint8 data) -> void { auto SA1::ROM::write(uint24 address, uint8 data) -> void {

View File

@ -128,10 +128,6 @@ auto SA1::power() -> void {
WDC65816::power(); WDC65816::power();
create(SA1::Enter, system.cpuFrequency()); create(SA1::Enter, system.cpuFrequency());
rom.writeProtect(true);
bwram.writeProtect(false);
iram.writeProtect(false);
bwram.dma = false; bwram.dma = false;
for(auto addr : range(iram.size())) { for(auto addr : range(iram.size())) {
iram.write(addr, 0x00); iram.write(addr, 0x00);

View File

@ -47,7 +47,7 @@ struct SA1 : Processor::WDC65816, Thread {
//serialization.cpp //serialization.cpp
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
struct ROM : MappedRAM { struct ROM : ReadableMemory {
//rom.cpp //rom.cpp
alwaysinline auto conflict() const -> bool; alwaysinline auto conflict() const -> bool;
@ -61,7 +61,7 @@ struct SA1 : Processor::WDC65816, Thread {
auto writeSA1(uint24 address, uint8 data) -> void; auto writeSA1(uint24 address, uint8 data) -> void;
} rom; } rom;
struct BWRAM : MappedRAM { struct BWRAM : WritableMemory {
//bwram.cpp //bwram.cpp
alwaysinline auto conflict() const -> bool; alwaysinline auto conflict() const -> bool;
@ -83,7 +83,7 @@ struct SA1 : Processor::WDC65816, Thread {
bool dma; bool dma;
} bwram; } bwram;
struct IRAM : MappedRAM { struct IRAM : WritableMemory {
//iram.cpp //iram.cpp
alwaysinline auto conflict() const -> bool; alwaysinline auto conflict() const -> bool;

View File

@ -12,8 +12,6 @@ auto SDD1::unload() -> void {
} }
auto SDD1::power() -> void { auto SDD1::power() -> void {
rom.writeProtect(true);
//hook S-CPU DMA MMIO registers to gather information for struct dma[]; //hook S-CPU DMA MMIO registers to gather information for struct dma[];
//buffer address and transfer size information for use in SDD1::mcu_read() //buffer address and transfer size information for use in SDD1::mcu_read()
bus.map({&SDD1::dmaRead, &sdd1}, {&SDD1::dmaWrite, &sdd1}, "00-3f,80-bf:4300-437f"); bus.map({&SDD1::dmaRead, &sdd1}, {&SDD1::dmaWrite, &sdd1}, "00-3f,80-bf:4300-437f");

View File

@ -15,7 +15,7 @@ struct SDD1 {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
MappedRAM rom; ReadableMemory rom;
private: private:
uint8 r4800; //hard enable uint8 r4800; //hard enable

View File

@ -41,10 +41,6 @@ auto SPC7110::unload() -> void {
auto SPC7110::power() -> void { auto SPC7110::power() -> void {
create(SPC7110::Enter, 21'477'272); create(SPC7110::Enter, 21'477'272);
prom.writeProtect(true);
drom.writeProtect(true);
ram.writeProtect(false);
r4801 = 0x00; r4801 = 0x00;
r4802 = 0x00; r4802 = 0x00;
r4803 = 0x00; r4803 = 0x00;

View File

@ -52,9 +52,9 @@ struct SPC7110 : Thread {
auto aluMultiply() -> void; auto aluMultiply() -> void;
auto aluDivide() -> void; auto aluDivide() -> void;
MappedRAM prom; //program ROM ReadableMemory prom; //program ROM
MappedRAM drom; //data ROM ReadableMemory drom; //data ROM
MappedRAM ram; WritableMemory ram;
private: private:
//decompression unit //decompression unit

View File

@ -40,9 +40,6 @@ auto SuperFX::power() -> void {
GSU::power(); GSU::power();
create(SuperFX::Enter, Frequency); create(SuperFX::Enter, Frequency);
rom.writeProtect(true);
ram.writeProtect(false);
romMask = rom.size() - 1; romMask = rom.size() - 1;
ramMask = ram.size() - 1; ramMask = ram.size() - 1;

View File

@ -1,6 +1,6 @@
struct SuperFX : Processor::GSU, Thread { struct SuperFX : Processor::GSU, Thread {
MappedRAM rom; ReadableMemory rom;
MappedRAM ram; WritableMemory ram;
//superfx.cpp //superfx.cpp
static auto Enter() -> void; static auto Enter() -> void;

View File

@ -16,6 +16,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
inline auto dmaStep(uint clocks) -> void; inline auto dmaStep(uint clocks) -> void;
inline auto dmaFlush() -> void; inline auto dmaFlush() -> void;
inline auto dmaWrite() -> void;
auto dmaRun() -> void; auto dmaRun() -> void;
auto hdmaReset() -> void; auto hdmaReset() -> void;
@ -166,11 +167,13 @@ private:
//dma.cpp //dma.cpp
inline auto step(uint clocks) -> void; inline auto step(uint clocks) -> void;
inline auto edge() -> void; inline auto edge() -> void;
inline auto flush() -> void;
inline auto write() -> void;
inline auto validA(uint24 address) -> bool; inline auto validA(uint24 address) -> bool;
inline auto readA(uint24 address) -> uint8; inline auto readA(uint24 address) -> uint8;
inline auto readA(uint24 address, bool valid) -> uint8; inline auto readA(uint24 address, bool valid) -> uint8;
inline auto readB(uint8 address, bool valid) -> uint8; inline auto readB(uint8 address, bool valid) -> uint8;
inline auto flush() -> void;
inline auto writeA(uint24 address, uint8 data) -> void; inline auto writeA(uint24 address, uint8 data) -> void;
inline auto writeA(uint24 address, uint8 data, bool valid) -> void; inline auto writeA(uint24 address, uint8 data, bool valid) -> void;
inline auto writeB(uint8 address, uint8 data, bool valid) -> void; inline auto writeB(uint8 address, uint8 data, bool valid) -> void;

View File

@ -1,3 +1,18 @@
/* DMA timing:
The general idea is that DMA moves values between the A-bus and B-bus in parallel.
Obviously, a write can't happen at the same time as a read, as the read needs 8 cycles to complete.
My theory is that the accesses are staggered, like so:
Cycle 0: read 0
Cycle 1: read 1, write 0
Cycle 2: read 2, write 1
...
Cycle n: read n, write n-1
Cycle n+1: write n
The staggered writes are implemented below using the pipe/flush concept.
*/
auto CPU::dmaEnable() -> bool { auto CPU::dmaEnable() -> bool {
for(auto& channel : channels) if(channel.dmaEnable) return true; for(auto& channel : channels) if(channel.dmaEnable) return true;
return false; return false;
@ -24,10 +39,15 @@ auto CPU::dmaFlush() -> void {
bus.write(pipe.address, pipe.data); bus.write(pipe.address, pipe.data);
} }
auto CPU::dmaRun() -> void { auto CPU::dmaWrite() -> void {
r.rwb = 0; r.rwb = pipe.valid;
r.mar = pipe.address;
dmaStep(8); dmaStep(8);
dmaFlush(); dmaFlush();
}
auto CPU::dmaRun() -> void {
dmaWrite();
dmaEdge(); dmaEdge();
for(auto& channel : channels) channel.dmaRun(); for(auto& channel : channels) channel.dmaRun();
dmaFlush(); dmaFlush();
@ -39,18 +59,14 @@ auto CPU::hdmaReset() -> void {
} }
auto CPU::hdmaSetup() -> void { auto CPU::hdmaSetup() -> void {
r.rwb = 0; dmaWrite();
dmaStep(8);
dmaFlush();
for(auto& channel : channels) channel.hdmaSetup(); for(auto& channel : channels) channel.hdmaSetup();
dmaFlush(); dmaFlush();
status.irqLock = true; status.irqLock = true;
} }
auto CPU::hdmaRun() -> void { auto CPU::hdmaRun() -> void {
r.rwb = 0; dmaWrite();
dmaStep(8);
dmaFlush();
for(auto& channel : channels) channel.hdmaTransfer(); for(auto& channel : channels) channel.hdmaTransfer();
for(auto& channel : channels) channel.hdmaAdvance(); for(auto& channel : channels) channel.hdmaAdvance();
dmaFlush(); dmaFlush();
@ -59,13 +75,10 @@ auto CPU::hdmaRun() -> void {
// //
auto CPU::Channel::step(uint clocks) -> void { auto CPU::Channel::step(uint clocks) -> void { return cpu.dmaStep(clocks); }
return cpu.dmaStep(clocks); auto CPU::Channel::edge() -> void { return cpu.dmaEdge(); }
} auto CPU::Channel::flush() -> void { return cpu.dmaFlush(); }
auto CPU::Channel::write() -> void { return cpu.dmaWrite(); }
auto CPU::Channel::edge() -> void {
return cpu.dmaEdge();
}
auto CPU::Channel::validA(uint24 address) -> bool { auto CPU::Channel::validA(uint24 address) -> bool {
//A-bus cannot access the B-bus or CPU I/O registers //A-bus cannot access the B-bus or CPU I/O registers
@ -96,10 +109,6 @@ auto CPU::Channel::readB(uint8 address, bool valid) -> uint8 {
return cpu.r.mdr; return cpu.r.mdr;
} }
auto CPU::Channel::flush() -> void {
return cpu.dmaFlush();
}
auto CPU::Channel::writeA(uint24 address, uint8 data) -> void { auto CPU::Channel::writeA(uint24 address, uint8 data) -> void {
return writeA(address, data, validA(address)); return writeA(address, data, validA(address));
} }
@ -148,9 +157,7 @@ auto CPU::Channel::dmaRun() -> void {
edge(); edge();
} while(dmaEnable && --transferSize); } while(dmaEnable && --transferSize);
cpu.r.rwb = 0; write();
step(8);
flush();
edge(); edge();
dmaEnable = false; dmaEnable = false;
} }

View File

@ -23,14 +23,8 @@ auto CPU::step(uint clocks) -> void {
} }
} }
#if defined(DEBUGGER) if(configuration.hacks.coprocessors.delayedSync) return;
synchronize(smp);
synchronize(ppu);
#endif
#if defined(DEBUGGER) || defined(ACCURATE_SA1)
for(auto coprocessor : coprocessors) synchronize(*coprocessor); for(auto coprocessor : coprocessors) synchronize(*coprocessor);
#endif
} }
//called by ppu.tick() when Hcounter=0 //called by ppu.tick() when Hcounter=0
@ -94,10 +88,12 @@ auto CPU::dmaEdge() -> void {
status.hdmaPending = false; status.hdmaPending = false;
if(hdmaEnable()) { if(hdmaEnable()) {
if(!dmaEnable()) { if(!dmaEnable()) {
r.rwb = 0;
dmaStep(8 - dmaCounter()); dmaStep(8 - dmaCounter());
} }
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun(); status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnable()) { if(!dmaEnable()) {
r.rwb = 0; //unverified
step(status.clockCount - (status.dmaClocks % status.clockCount)); step(status.clockCount - (status.dmaClocks % status.clockCount));
status.dmaActive = false; status.dmaActive = false;
} }
@ -107,8 +103,10 @@ auto CPU::dmaEdge() -> void {
if(status.dmaPending) { if(status.dmaPending) {
status.dmaPending = false; status.dmaPending = false;
if(dmaEnable()) { if(dmaEnable()) {
r.rwb = 0;
dmaStep(8 - dmaCounter()); dmaStep(8 - dmaCounter());
dmaRun(); dmaRun();
r.rwb = 0; //unverified
step(status.clockCount - (status.dmaClocks % status.clockCount)); step(status.clockCount - (status.dmaClocks % status.clockCount));
status.dmaActive = false; status.dmaActive = false;
} }

View File

@ -20,6 +20,7 @@ auto Configuration::process(Markup::Node document, bool load) -> void {
bind(boolean, "Hacks/FastPPU/NoSpriteLimit", hacks.ppuFast.noSpriteLimit); bind(boolean, "Hacks/FastPPU/NoSpriteLimit", hacks.ppuFast.noSpriteLimit);
bind(boolean, "Hacks/FastPPU/HiresMode7", hacks.ppuFast.hiresMode7); bind(boolean, "Hacks/FastPPU/HiresMode7", hacks.ppuFast.hiresMode7);
bind(boolean, "Hacks/FastDSP/Enable", hacks.dspFast.enable); bind(boolean, "Hacks/FastDSP/Enable", hacks.dspFast.enable);
bind(boolean, "Hacks/Coprocessors/DelayedSync", hacks.coprocessors.delayedSync);
#undef bind #undef bind
} }

View File

@ -33,6 +33,9 @@ struct Configuration {
struct DSPFast { struct DSPFast {
bool enable = false; bool enable = false;
} dspFast; } dspFast;
struct Coprocessors {
bool delayedSync = false;
} coprocessors;
} hacks; } hacks;
private: private:

View File

@ -1,41 +1,3 @@
//StaticRAM
StaticRAM::StaticRAM(uint size) : _size(size) { _data = new uint8[_size]; }
StaticRAM::~StaticRAM() { delete[] _data; }
auto StaticRAM::data() -> uint8* { return _data; }
auto StaticRAM::size() const -> uint { return _size; }
auto StaticRAM::read(uint24 addr, uint8) -> uint8 { return _data[addr]; }
auto StaticRAM::write(uint24 addr, uint8 data) -> void { _data[addr] = data; }
auto StaticRAM::operator[](uint24 addr) -> uint8& { return _data[addr]; }
auto StaticRAM::operator[](uint24 addr) const -> const uint8& { return _data[addr]; }
//MappedRAM
auto MappedRAM::reset() -> void {
delete[] _data;
_data = nullptr;
_size = 0;
_writeProtect = false;
}
auto MappedRAM::allocate(uint size) -> void {
reset();
_data = new uint8[_size = size];
memory::fill<uint8>(_data, _size, 0xff);
}
auto MappedRAM::writeProtect(bool writeProtect) -> void { _writeProtect = writeProtect; }
auto MappedRAM::data() -> uint8* { return _data; }
auto MappedRAM::size() const -> uint { return _size; }
auto MappedRAM::read(uint24 addr, uint8) -> uint8 { return _data[addr]; }
auto MappedRAM::write(uint24 addr, uint8 data) -> void { if(!_writeProtect) _data[addr] = data; }
auto MappedRAM::operator[](uint24 addr) const -> const uint8& { return _data[addr]; }
//Bus
auto Bus::mirror(uint addr, uint size) -> uint { auto Bus::mirror(uint addr, uint size) -> uint {
if(size == 0) return 0; if(size == 0) return 0;
uint base = 0; uint base = 0;

View File

@ -30,10 +30,10 @@ auto Bus::map(
const function<uint8 (uint24, uint8)>& read, const function<uint8 (uint24, uint8)>& read,
const function<void (uint24, uint8)>& write, const function<void (uint24, uint8)>& write,
const string& addr, uint size, uint base, uint mask const string& addr, uint size, uint base, uint mask
) -> void { ) -> uint {
uint id = 1; uint id = 1;
while(counter[id]) { while(counter[id]) {
if(++id >= 256) return print("SFC error: bus map exhausted\n"); if(++id >= 256) return print("SFC error: bus map exhausted\n"), 0;
} }
reader[id] = read; reader[id] = read;
@ -68,6 +68,8 @@ auto Bus::map(
} }
} }
} }
return id;
} }
auto Bus::unmap(const string& addr) -> void { auto Bus::unmap(const string& addr) -> void {

View File

@ -1,74 +1,46 @@
struct Memory { struct Memory {
virtual ~Memory() { reset(); }
inline explicit operator bool() const { return size() > 0; } inline explicit operator bool() const { return size() > 0; }
virtual auto reset() -> void {} virtual auto reset() -> void {}
virtual auto allocate(uint) -> void {} virtual auto allocate(uint, uint8 = 0xff) -> void {}
virtual auto data() -> uint8* = 0; virtual auto data() -> uint8* = 0;
virtual auto size() const -> uint = 0; virtual auto size() const -> uint = 0;
virtual auto read(uint24 addr, uint8 data = 0) -> uint8 = 0; virtual auto read(uint24 address, uint8 data = 0) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0; virtual auto write(uint24 address, uint8 data) -> void = 0;
uint id = 0;
}; };
struct StaticRAM : Memory { #include "readable.hpp"
inline StaticRAM(uint size); #include "writable.hpp"
inline ~StaticRAM(); #include "protectable.hpp"
inline auto data() -> uint8*;
inline auto size() const -> uint;
inline auto read(uint24 addr, uint8 data = 0) -> uint8;
inline auto write(uint24 addr, uint8 data) -> void;
inline auto operator[](uint24 addr) -> uint8&;
inline auto operator[](uint24 addr) const -> const uint8&;
protected:
uint8* _data = nullptr;
uint _size = 0;
};
struct MappedRAM : Memory {
inline auto reset() -> void;
inline auto allocate(uint) -> void;
inline auto writeProtect(bool writeProtect) -> void;
inline auto data() -> uint8*;
inline auto size() const -> uint;
inline auto read(uint24 addr, uint8 data = 0) -> uint8;
inline auto write(uint24 addr, uint8 data) -> void;
inline auto operator[](uint24 addr) const -> const uint8&;
protected:
uint8* _data = nullptr;
uint _size = 0;
bool _writeProtect = false;
};
struct Bus { struct Bus {
alwaysinline static auto mirror(uint addr, uint size) -> uint; alwaysinline static auto mirror(uint address, uint size) -> uint;
alwaysinline static auto reduce(uint addr, uint mask) -> uint; alwaysinline static auto reduce(uint address, uint mask) -> uint;
~Bus(); ~Bus();
alwaysinline auto read(uint24 addr, uint8 data) -> uint8; alwaysinline auto read(uint24 address, uint8 data) -> uint8;
alwaysinline auto write(uint24 addr, uint8 data) -> void; alwaysinline auto write(uint24 address, uint8 data) -> void;
auto reset() -> void; auto reset() -> void;
auto map( auto map(
const function<uint8 (uint24, uint8)>& read, const function<uint8 (uint24, uint8)>& read,
const function<void (uint24, uint8)>& write, const function<void (uint24, uint8)>& write,
const string& addr, uint size = 0, uint base = 0, uint mask = 0 const string& address, uint size = 0, uint base = 0, uint mask = 0
) -> void; ) -> uint;
auto unmap(const string& addr) -> void; auto unmap(const string& address) -> void;
private: private:
uint8* lookup = nullptr; uint8* lookup = nullptr;
uint32* target = nullptr; uint32* target = nullptr;
function<auto (uint24, uint8) -> uint8> reader[256]; function<uint8 (uint24, uint8)> reader[256];
function<auto (uint24, uint8) -> void> writer[256]; function<void (uint24, uint8)> writer[256];
uint24 counter[256]; uint24 counter[256];
}; };

View File

@ -0,0 +1,48 @@
struct ProtectableMemory : Memory {
inline auto reset() -> void override {
delete[] self.data;
self.data = nullptr;
}
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
delete[] self.data;
self.data = new uint8[self.size = size];
for(uint address : range(size)) self.data[address] = fill;
}
inline auto data() -> uint8* override {
return self.data;
}
inline auto size() const -> uint override {
return self.size;
}
inline auto writable() const -> bool {
return self.writable;
}
inline auto writable(bool writable) -> void {
self.writable = writable;
}
inline auto read(uint24 address, uint8 data = 0) -> uint8 override {
return self.data[address];
}
inline auto write(uint24 address, uint8 data) -> void override {
if(!self.writable) return;
self.data[address] = data;
}
inline auto operator[](uint24 address) const -> uint8 {
return self.data[address];
}
private:
struct {
uint8* data = nullptr;
uint size = 0;
bool writable = false;
} self;
};

View File

@ -0,0 +1,37 @@
struct ReadableMemory : Memory {
inline auto reset() -> void override {
delete[] self.data;
self.data = nullptr;
}
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
delete[] self.data;
self.data = new uint8[self.size = size];
for(uint address : range(size)) self.data[address] = fill;
}
inline auto data() -> uint8* override {
return self.data;
}
inline auto size() const -> uint override {
return self.size;
}
inline auto read(uint24 address, uint8 data = 0) -> uint8 override {
return self.data[address];
}
inline auto write(uint24 address, uint8 data) -> void override {
}
inline auto operator[](uint24 address) const -> uint8 {
return self.data[address];
}
private:
struct {
uint8* data = nullptr;
uint size = 0;
} self;
};

View File

@ -0,0 +1,38 @@
struct WritableMemory : Memory {
inline auto reset() -> void override {
delete[] self.data;
self.data = nullptr;
}
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
delete[] self.data;
self.data = new uint8[self.size = size];
for(uint address : range(size)) self.data[address] = fill;
}
inline auto data() -> uint8* override {
return self.data;
}
inline auto size() const -> uint override {
return self.size;
}
inline auto read(uint24 address, uint8 data = 0) -> uint8 override {
return self.data[address];
}
inline auto write(uint24 address, uint8 data) -> void override {
self.data[address] = data;
}
inline auto operator[](uint24 address) -> uint8& {
return self.data[address];
}
private:
struct {
uint8* data = nullptr;
uint size = 0;
} self;
};

View File

@ -20,8 +20,6 @@
#include <gb/gb.hpp> #include <gb/gb.hpp>
#endif #endif
//#define ACCURATE_SA1
namespace SuperFamicom { namespace SuperFamicom {
#define platform Emulator::platform #define platform Emulator::platform
namespace File = Emulator::File; namespace File = Emulator::File;

View File

@ -14,14 +14,14 @@ auto BSMemory::unload() -> void {
} }
auto BSMemory::power() -> void { auto BSMemory::power() -> void {
regs.command = 0; regs.command = 0;
regs.writeOld = 0x00; regs.writeOld = 0x00;
regs.writeNew = 0x00; regs.writeNew = 0x00;
regs.flashEnable = false; regs.flashEnable = false;
regs.readEnable = false; regs.readEnable = false;
regs.writeEnable = false; regs.writeEnable = false;
memory.writeProtect(!regs.writeEnable); memory.writable(regs.writeEnable);
} }
auto BSMemory::data() -> uint8* { auto BSMemory::data() -> uint8* {
@ -117,7 +117,7 @@ auto BSMemory::write(uint24 addr, uint8 data) -> void {
regs.writeEnable = false; regs.writeEnable = false;
} }
memory.writeProtect(!regs.writeEnable); memory.writable(regs.writeEnable);
} }
} }

View File

@ -13,7 +13,7 @@ struct BSMemory : Memory {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint pathID = 0; uint pathID = 0;
MappedRAM memory; ProtectableMemory memory;
bool readonly; bool readonly;
private: private:

View File

@ -12,8 +12,6 @@ auto SufamiTurboCartridge::unload() -> void {
} }
auto SufamiTurboCartridge::power() -> void { auto SufamiTurboCartridge::power() -> void {
rom.writeProtect(true);
ram.writeProtect(false);
} }
} }

View File

@ -4,8 +4,8 @@ struct SufamiTurboCartridge {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint pathID = 0; uint pathID = 0;
MappedRAM rom; ReadableMemory rom;
MappedRAM ram; WritableMemory ram;
}; };
extern SufamiTurboCartridge sufamiturboA; extern SufamiTurboCartridge sufamiturboA;

View File

@ -3,17 +3,18 @@ auto Program::hackCompatibility() -> void {
bool fastPPUNoSpriteLimit = emulatorSettings.noSpriteLimit.checked(); bool fastPPUNoSpriteLimit = emulatorSettings.noSpriteLimit.checked();
bool fastPPUHiresMode7 = emulatorSettings.hiresMode7.checked(); bool fastPPUHiresMode7 = emulatorSettings.hiresMode7.checked();
bool fastDSP = emulatorSettings.fastDSPOption.checked(); bool fastDSP = emulatorSettings.fastDSPOption.checked();
bool coprocessorsDelayedSync = emulatorSettings.coprocessorsDelayedSyncOption.checked();
auto title = superFamicom.title; auto title = superFamicom.title;
if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false; if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false;
if(title == "KOUSHIEN_2") fastDSP = false; if(title == "KOUSHIEN_2") fastDSP = false;
if(title == "RENDERING RANGER R2") fastDSP = false; if(title == "RENDERING RANGER R2") fastDSP = false;
//todo: update to new emulator->configuration API emulator->configure("Hacks/FastPPU/Enable", fastPPU);
emulator->set("Fast PPU", fastPPU); emulator->configure("Hacks/FastPPU/NoSpriteLimit", fastPPUNoSpriteLimit);
emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit); emulator->configure("Hacks/FastPPU/HiresMode7", fastPPUHiresMode7);
emulator->set("Fast PPU/Hires Mode 7", fastPPUHiresMode7); emulator->configure("Hacks/FastDSP/Enable", fastDSP);
emulator->set("Fast DSP", fastDSP); emulator->configure("Hacks/Coprocessors/DelayedSync", coprocessorsDelayedSync);
} }
auto Program::hackPatchMemory(vector<uint8_t>& data) -> void { auto Program::hackPatchMemory(vector<uint8_t>& data) -> void {

View File

@ -57,6 +57,9 @@ auto EmulatorSettings::create() -> void {
fastDSPOption.setText("Fast DSP").setChecked(settings.emulator.hack.fastDSP.enable).onToggle([&] { fastDSPOption.setText("Fast DSP").setChecked(settings.emulator.hack.fastDSP.enable).onToggle([&] {
settings.emulator.hack.fastDSP.enable = fastDSPOption.checked(); settings.emulator.hack.fastDSP.enable = fastDSPOption.checked();
}); });
coprocessorsDelayedSyncOption.setText("Fast coprocessors (delayed sync)").setChecked(settings.emulator.hack.coprocessors.delayedSync).onToggle([&] {
settings.emulator.hack.coprocessors.delayedSync = coprocessorsDelayedSyncOption.checked();
});
superFXLabel.setText("SuperFX clock speed:"); superFXLabel.setText("SuperFX clock speed:");
superFXValue.setAlignment(0.5); superFXValue.setAlignment(0.5);
superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] { superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] {
@ -71,4 +74,5 @@ auto EmulatorSettings::updateConfiguration() -> void {
emulator->configure("Hacks/FastPPU/NoSpriteLimit", noSpriteLimit.checked()); emulator->configure("Hacks/FastPPU/NoSpriteLimit", noSpriteLimit.checked());
emulator->configure("Hacks/FastPPU/HiresMode7", hiresMode7.checked()); emulator->configure("Hacks/FastPPU/HiresMode7", hiresMode7.checked());
emulator->configure("Hacks/FastDSP/Enable", fastDSPOption.checked()); emulator->configure("Hacks/FastDSP/Enable", fastDSPOption.checked());
emulator->configure("Hacks/Coprocessor/DelayedSync", coprocessorsDelayedSyncOption.checked());
} }

View File

@ -90,17 +90,18 @@ auto Settings::process(bool load) -> void {
bind(text, "Path/Recent/SufamiTurboA", path.recent.sufamiTurboA); bind(text, "Path/Recent/SufamiTurboA", path.recent.sufamiTurboA);
bind(text, "Path/Recent/SufamiTurboB", path.recent.sufamiTurboB); bind(text, "Path/Recent/SufamiTurboB", path.recent.sufamiTurboB);
bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames); bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames);
bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable); bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable);
bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval); bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval);
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload); bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad); bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
bind(boolean, "Emulator/Hack/FastPPU/Enable", emulator.hack.fastPPU.enable); bind(boolean, "Emulator/Hack/FastPPU/Enable", emulator.hack.fastPPU.enable);
bind(boolean, "Emulator/Hack/FastPPU/NoSpriteLimit", emulator.hack.fastPPU.noSpriteLimit); bind(boolean, "Emulator/Hack/FastPPU/NoSpriteLimit", emulator.hack.fastPPU.noSpriteLimit);
bind(boolean, "Emulator/Hack/FastPPU/HiresMode7", emulator.hack.fastPPU.hiresMode7); bind(boolean, "Emulator/Hack/FastPPU/HiresMode7", emulator.hack.fastPPU.hiresMode7);
bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable); bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable);
bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX); bind(boolean, "Emulator/Hack/Coprocessors/DelayedSync", emulator.hack.coprocessors.delayedSync);
bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable); bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX);
bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable);
bind(boolean, "General/StatusBar", general.statusBar); bind(boolean, "General/StatusBar", general.statusBar);
bind(boolean, "General/ScreenSaver", general.screenSaver); bind(boolean, "General/ScreenSaver", general.screenSaver);

View File

@ -81,6 +81,9 @@ struct Settings : Markup::Node {
struct FastDSP { struct FastDSP {
bool enable = true; bool enable = true;
} fastDSP; } fastDSP;
struct Coprocessors {
bool delayedSync = true;
} coprocessors;
uint fastSuperFX = 100; uint fastSuperFX = 100;
} hack; } hack;
struct Cheats { struct Cheats {
@ -256,6 +259,7 @@ public:
CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}}; CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}};
CheckLabel hiresMode7{&fastPPULayout, Size{0, 0}}; CheckLabel hiresMode7{&fastPPULayout, Size{0, 0}};
CheckLabel fastDSPOption{&layout, Size{~0, 0}}; CheckLabel fastDSPOption{&layout, Size{~0, 0}};
CheckLabel coprocessorsDelayedSyncOption{&layout, Size{~0, 0}};
HorizontalLayout superFXLayout{&layout, Size{~0, 0}}; HorizontalLayout superFXLayout{&layout, Size{~0, 0}};
Label superFXLabel{&superFXLayout, Size{0, 0}}; Label superFXLabel{&superFXLayout, Size{0, 0}};
Label superFXValue{&superFXLayout, Size{50, 0}}; Label superFXValue{&superFXLayout, Size{50, 0}};

View File

@ -64,11 +64,16 @@ struct inode {
stat(name, &data); stat(name, &data);
switch(mode) { switch(mode) {
#if defined(PLATFORM_WINDOWS) #if defined(PLATFORM_WINDOWS)
//on Windows, the last status change time (ctime) holds the file creation time instead
case time::create: return data.st_ctime; case time::create: return data.st_ctime;
#else #elif defined(PLATFORM_BSD) || defined(PLATFORM_MACOS)
//st_birthtime may return -1 or st_atime if it is not supported //st_birthtime may return -1 or st_atime if it is not supported by the file system
//the best that can be done in this case is to return st_mtime if it's older //the best that can be done in this case is to return st_mtime if it's older
case time::create: return min((uint)data.st_birthtime, (uint)data.st_mtime); case time::create: return min((uint)data.st_birthtime, (uint)data.st_mtime);
#else
//Linux simply doesn't support file creation time at all
//this is also our fallback case for unsupported operating systems
case time::create: return data.st_mtime;
#endif #endif
case time::modify: return data.st_mtime; case time::modify: return data.st_mtime;
//for performance reasons, last access time is usually not enabled on various filesystems //for performance reasons, last access time is usually not enabled on various filesystems