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 {
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 License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -7,21 +7,116 @@ namespace Processor {
#include "instructions.cpp"
#include "serialization.cpp"
auto HG51B::exec(uint24 addr) -> void {
if(regs.halt) return;
addr = addr + regs.pc * 2;
opcode = read(addr++) << 0;
opcode |= read(addr++) << 8;
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
auto HG51B::lock() -> void {
io.lock = 1;
}
auto HG51B::halt() -> void {
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();
}
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 {
regs.halt = true;
regs.n = 0;
regs.z = 0;
regs.c = 0;
r = {};
io = {};
}
}

View File

@ -5,14 +5,27 @@
namespace Processor {
struct HG51B {
auto exec(uint24 addr) -> void;
virtual auto read(uint24 addr) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0;
virtual auto step(uint clocks) -> void;
virtual auto isROM(uint24 address) -> bool = 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 serialize(serializer&) -> void;
//uint16 programROM[2][256];
uint16 programRAM[2][256];
uint24 dataROM[1024];
uint8 dataRAM[3072];
@ -21,32 +34,76 @@ protected:
auto pull() -> void;
auto sa() -> uint;
auto ri() -> uint;
auto np() -> uint;
auto np() -> void;
auto instruction() -> void;
//registers.cpp
auto registerRead(uint8 addr) const -> uint24;
auto registerWrite(uint8 addr, uint24 data) -> void;
auto readRegister(uint7 address) -> uint24;
auto writeRegister(uint7 address, uint24 data) -> void;
struct Registers {
bool halt;
uint24 pc;
uint16 p;
bool n;
bool z;
bool c;
uint16 pb; //program bank
uint8 pc; //program counter
uint24 a;
boolean n; //negative
boolean z; //zero
boolean c; //carry
boolean i; //interrupt
uint24 a; //accumulator
uint24 acch;
uint24 accl;
uint24 busdata;
uint24 romdata;
uint24 ramdata;
uint24 busaddr;
uint24 ramaddr;
uint24 busData;
uint24 romData;
uint24 ramData;
uint24 busAddress;
uint24 ramAddress;
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];
uint16 opcode;

View File

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

View File

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

View File

@ -12,8 +12,8 @@ struct Cartridge {
auto serialize(serializer&) -> void;
MappedRAM rom;
MappedRAM ram;
ReadableMemory rom;
WritableMemory ram;
struct Information {
uint pathID = 0;
@ -67,8 +67,8 @@ private:
auto loadCartridgeSufamiTurboB(Markup::Node) -> void;
auto loadMemory(Memory&, Markup::Node, bool required) -> void;
auto loadMap(Markup::Node, SuperFamicom::Memory&) -> void;
auto loadMap(Markup::Node, const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&) -> void;
template<typename T> auto loadMap(Markup::Node, T&) -> uint;
auto loadMap(Markup::Node, const function<uint8 (uint24, uint8)>&, const function<void (uint24, uint8)>&) -> uint;
auto loadROM(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 size = map["size"].natural();
auto base = map["base"].natural();
auto mask = map["mask"].natural();
if(size == 0) size = memory.size();
if(size == 0) return;
bus.map({&SuperFamicom::Memory::read, &memory}, {&SuperFamicom::Memory::write, &memory}, addr, size, base, mask);
if(size == 0) return print("loadMap(): size=0\n"), 0; //does this ever actually occur?
return bus.map({&T::read, &memory}, {&T::write, &memory}, addr, size, base, mask);
}
auto Cartridge::loadMap(
Markup::Node map,
const function<uint8 (uint24, uint8)>& reader,
const function<void (uint24, uint8)>& writer
) -> void {
) -> uint {
auto addr = map["address"].text();
auto size = map["size"].natural();
auto base = map["base"].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)
@ -416,22 +417,23 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
hitachidsp.Frequency = 20'000'000;
}
hitachidsp.Roms = roms; //1 or 2
hitachidsp.Mapping = 0; //0 or 1
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)"]) {
loadMemory(hitachidsp.rom, memory, File::Required);
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)"]) {
loadMemory(hitachidsp.ram, memory, File::Optional);
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")) {
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 {
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
timer = (3 + dip.value.bits(0,3)) * 60; //in seconds
//DIP switches 4-5 serve an unknown purpose

View File

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

View File

@ -10,21 +10,17 @@ auto HitachiDSP::Enter() -> void {
while(true) scheduler.synchronize(), hitachidsp.main();
}
auto HitachiDSP::main() -> void {
if(mmio.dma) {
for(auto n : range(mmio.dmaLength)) {
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
step(2);
synchronize(cpu);
}
mmio.dma = false;
}
exec(mmio.programOffset);
step(1);
auto HitachiDSP::step(uint clocks) -> void {
HG51B::step(clocks);
Thread::step(clocks);
synchronize(cpu);
}
auto HitachiDSP::halt() -> void {
HG51B::halt();
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
}
auto HitachiDSP::unload() -> void {
rom.reset();
ram.reset();
@ -33,23 +29,6 @@ auto HitachiDSP::unload() -> void {
auto HitachiDSP::power() -> void {
HG51B::power();
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 {
MappedRAM rom;
MappedRAM ram;
ReadableMemory rom;
WritableMemory ram;
//hitachidsp.cpp
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void override;
auto halt() -> void override;
auto unload() -> void;
auto power() -> void;
auto isROM(uint24 address) -> bool override;
auto isRAM(uint24 address) -> bool override;
//HG51B read/write
auto read(uint24 addr) -> uint8 override;
auto write(uint24 addr, uint8 data) -> void override;
auto read(uint24 address) -> uint8 override;
auto write(uint24 address, uint8 data) -> void override;
//CPU ROM read/write
auto romRead(uint24 addr, uint8 data) -> uint8;
auto romWrite(uint24 addr, uint8 data) -> void;
auto addressROM(uint24 address) const -> maybe<uint24>;
auto readROM(uint24 address, uint8 data = 0) -> uint8;
auto writeROM(uint24 address, uint8 data) -> void;
//CPU RAM read/write
auto ramRead(uint24 addr, uint8 data) -> uint8;
auto ramWrite(uint24 addr, uint8 data) -> void;
auto addressRAM(uint24 address) const -> maybe<uint24>;
auto readRAM(uint24 address, uint8 data = 0) -> uint8;
auto writeRAM(uint24 address, uint8 data) -> void;
//HG51B data RAM read/write
auto dramRead(uint24 addr, uint8 data) -> uint8;
auto dramWrite(uint24 addr, uint8 data) -> void;
auto addressDRAM(uint24 address) const -> maybe<uint24>;
auto readDRAM(uint24 address, uint8 data = 0) -> uint8;
auto writeDRAM(uint24 address, uint8 data) -> void;
//CPU MMIO read/write
auto dspRead(uint24 addr, uint8 data) -> uint8;
auto dspWrite(uint24 addr, uint8 data) -> void;
//CPU IO read/write
auto addressIO(uint24 address) const -> maybe<uint24>;
auto readIO(uint24 address, uint8 data = 0) -> uint8;
auto writeIO(uint24 address, uint8 data) -> void;
auto firmware() const -> vector<uint8>;
auto serialize(serializer&) -> void;
uint Frequency;
uint Roms;
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;
bool Mapping;
};
extern HitachiDSP hitachidsp;

View File

@ -1,166 +1,264 @@
auto HitachiDSP::read(uint24 addr) -> uint8 {
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6cff,7c00-7cff
return dspRead(addr, 0x00);
}
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff
return dramRead(addr, 0x00);
}
if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff
if(rom.size() == 0) return 0x00;
addr = ((addr & 0x3f0000) >> 1) | (addr & 0x7fff);
addr = Bus::mirror(addr, rom.size());
return rom.read(addr, 0);
}
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);
}
auto HitachiDSP::isROM(uint24 address) -> bool {
return (bool)addressROM(address);
}
auto HitachiDSP::isRAM(uint24 address) -> bool {
return (bool)addressRAM(address);
}
auto HitachiDSP::read(uint24 address) -> uint8 {
if(auto linear = addressROM (address)) return readROM (*linear);
if(auto linear = addressRAM (address)) return readRAM (*linear);
if(auto linear = addressDRAM(address)) return readDRAM(*linear);
if(auto linear = addressIO (address)) return readIO (*linear);
return 0x00;
}
auto HitachiDSP::write(uint24 addr, uint8 data) -> void {
if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6fff,7c00-7fff
return dspWrite(addr, data);
}
if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff
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::write(uint24 address, uint8 data) -> void {
if(auto linear = addressROM (address)) return writeROM (*linear, data);
if(auto linear = addressRAM (address)) return writeRAM (*linear, data);
if(auto linear = addressDRAM(address)) return writeDRAM(*linear, data);
if(auto linear = addressIO (address)) return writeIO (*linear, data);
}
auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 {
if(hitachidsp.active() || regs.halt) {
addr = Bus::mirror(addr, rom.size());
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00;
return rom.read(addr, data);
//
auto HitachiDSP::addressROM(uint24 address) const -> maybe<uint24> {
if(Mapping == 0) {
//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;
}
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
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;
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;
return dataRAM[addr];
//
auto HitachiDSP::addressDRAM(uint24 address) const -> maybe<uint24> {
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 {
addr &= 0xfff;
if(addr >= 0xc00) return;
dataRAM[addr] = data;
auto HitachiDSP::readDRAM(uint24 address, uint8 data) -> uint8 {
address &= 0xfff;
if(address >= 0xc00) return data;
return dataRAM[address];
}
auto HitachiDSP::dspRead(uint24 addr, uint8) -> uint8 {
addr = 0x7c00 | (addr & 0x03ff);
auto HitachiDSP::writeDRAM(uint24 address, uint8 data) -> void {
address &= 0xfff;
if(address >= 0xc00) return;
dataRAM[address] = data;
}
//MMIO
switch(addr) {
case 0x7f40: return mmio.dmaSource >> 0;
case 0x7f41: return mmio.dmaSource >> 8;
case 0x7f42: return mmio.dmaSource >> 16;
case 0x7f43: return mmio.dmaLength >> 0;
case 0x7f44: return mmio.dmaLength >> 8;
case 0x7f45: return mmio.dmaTarget >> 0;
case 0x7f46: return mmio.dmaTarget >> 8;
case 0x7f47: return mmio.dmaTarget >> 16;
case 0x7f48: return mmio.r1f48;
case 0x7f49: return mmio.programOffset >> 0;
case 0x7f4a: return mmio.programOffset >> 8;
case 0x7f4b: return mmio.programOffset >> 16;
case 0x7f4c: return mmio.r1f4c;
case 0x7f4d: return mmio.pageNumber >> 0;
case 0x7f4e: return mmio.pageNumber >> 8;
case 0x7f4f: return mmio.programCounter;
case 0x7f50: return mmio.r1f50;
case 0x7f51: return mmio.r1f51;
case 0x7f52: return mmio.r1f52;
case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56:
case 0x7f57: case 0x7f58: case 0x7f59: case 0x7f5a:
case 0x7f5b: case 0x7f5c: case 0x7f5d: case 0x7f5e:
case 0x7f5f: return ((regs.halt == false) << 6) | ((regs.halt == true) << 1);
//
auto HitachiDSP::addressIO(uint24 address) const -> maybe<uint24> {
if(Mapping == 0) {
//00-3f,80-bf:6c00-6fff,7c00-7fff
if((address & 0x40ec00) == 0x006c00) {
return {address & 0x03ff};
}
} else {
//00-2f,80-af:6c00-6fff,7c00-7fff
if((address & 0x40ec00) == 0x006c00 && (address & 0x300000) != 0x300000) {
return {address & 0x03ff};
}
}
return {};
}
auto HitachiDSP::readIO(uint24 address, uint8 data) -> uint8 {
address = 0x7c00 | (address & 0x03ff);
//IO
switch(address) {
case 0x7f40: return io.dma.source.byte(0);
case 0x7f41: return io.dma.source.byte(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
if(addr >= 0x7f60 && addr <= 0x7f7f) {
return mmio.vector[addr & 0x1f];
//vectors
if(address >= 0x7f60 && address <= 0x7f7f) {
return io.vector[address & 0x1f];
}
//GPRs
if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) {
uint index = (addr & 0x3f) / 3; //0..15
uint shift = ((addr & 0x3f) % 3) * 8; //0, 8, 16
return regs.gpr[index] >> shift;
//registers
if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) {
address &= 0x3f;
return r.gpr[address / 3].byte(address % 3);
}
return 0x00;
}
auto HitachiDSP::dspWrite(uint24 addr, uint8 data) -> void {
addr = 0x7c00 | (addr & 0x03ff);
auto HitachiDSP::writeIO(uint24 address, uint8 data) -> void {
address = 0x7c00 | (address & 0x03ff);
//MMIO
switch(addr) {
case 0x7f40: mmio.dmaSource = (mmio.dmaSource & 0xffff00) | (data << 0); return;
case 0x7f41: mmio.dmaSource = (mmio.dmaSource & 0xff00ff) | (data << 8); return;
case 0x7f42: mmio.dmaSource = (mmio.dmaSource & 0x00ffff) | (data << 16); return;
case 0x7f43: mmio.dmaLength = (mmio.dmaLength & 0xff00) | (data << 0); return;
case 0x7f44: mmio.dmaLength = (mmio.dmaLength & 0x00ff) | (data << 8); return;
case 0x7f45: mmio.dmaTarget = (mmio.dmaTarget & 0xffff00) | (data << 0); return;
case 0x7f46: mmio.dmaTarget = (mmio.dmaTarget & 0xff00ff) | (data << 8); return;
case 0x7f47: mmio.dmaTarget = (mmio.dmaTarget & 0x00ffff) | (data << 16);
if(regs.halt) mmio.dma = true;
//IO
switch(address) {
case 0x7f40: io.dma.source.byte(0) = data; return;
case 0x7f41: io.dma.source.byte(1) = data; return;
case 0x7f42: io.dma.source.byte(2) = data; return;
case 0x7f43: io.dma.length.byte(0) = data; return;
case 0x7f44: io.dma.length.byte(1) = data; return;
case 0x7f45: io.dma.target.byte(0) = data; return;
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;
case 0x7f48: mmio.r1f48 = data & 0x01; return;
case 0x7f49: mmio.programOffset = (mmio.programOffset & 0xffff00) | (data << 0); return;
case 0x7f4a: mmio.programOffset = (mmio.programOffset & 0xff00ff) | (data << 8); return;
case 0x7f4b: mmio.programOffset = (mmio.programOffset & 0x00ffff) | (data << 16); return;
case 0x7f4c: mmio.r1f4c = data & 0x03; return;
case 0x7f4d: mmio.pageNumber = (mmio.pageNumber & 0x7f00) | ((data & 0xff) << 0); return;
case 0x7f4e: mmio.pageNumber = (mmio.pageNumber & 0x00ff) | ((data & 0x7f) << 8); return;
case 0x7f4f: mmio.programCounter = data;
if(regs.halt) {
regs.pc = mmio.pageNumber * 256 + mmio.programCounter;
regs.halt = false;
case 0x7f48:
io.cache.page = data.bit(0);
if(io.halt) io.cache.enable = 1;
return;
case 0x7f49: io.cache.base.byte(0) = data; return;
case 0x7f4a: io.cache.base.byte(1) = data; return;
case 0x7f4b: io.cache.base.byte(2) = data; return;
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;
case 0x7f50: mmio.r1f50 = data & 0x77; return;
case 0x7f51: mmio.r1f51 = data & 0x01; return;
case 0x7f52: mmio.r1f52 = data & 0x01; return;
}
//Vector
if(addr >= 0x7f60 && addr <= 0x7f7f) {
mmio.vector[addr & 0x1f] = data;
case 0x7f50:
io.wait.ram = data.bits(0,2);
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;
}
//GPRs
if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) {
uint index = (addr & 0x3f) / 3;
switch((addr & 0x3f) % 3) {
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;
}
//vectors
if(address >= 0x7f60 && address <= 0x7f7f) {
io.vector[address & 0x1f] = data;
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 {
HG51B::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 {
rom.writeProtect(true);
psram.writeProtect(false);
irq.flag = 0;
irq.enable = 0;
w.mapping = 1;

View File

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

View File

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

View File

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

View File

@ -1,4 +1,6 @@
auto SA1::BWRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) 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 & 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 {
if(!size()) return data;
address = bus.mirror(address, size());
return _data[address];
return WritableMemory::read(address, data);
}
auto SA1::BWRAM::write(uint24 address, uint8 data) -> void {
if(!size()) return;
address = bus.mirror(address, size());
_data[address] = data;
return WritableMemory::write(address, data);
}
//note: addresses are translated prior to invoking this function:

View File

@ -1,4 +1,6 @@
auto SA1::IRAM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) return false;
if(!cpu.r.rwb) return false;
if((cpu.r.mar & 0x40f800) == 0x003000) return true; //00-3f,80-bf:3000-37ff
return false;
@ -6,12 +8,14 @@ auto SA1::IRAM::conflict() const -> bool {
auto SA1::IRAM::read(uint24 address, uint8 data) -> uint8 {
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 {
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 {

View File

@ -1,4 +1,6 @@
auto SA1::ROM::conflict() const -> bool {
if(configuration.hacks.coprocessors.delayedSync) 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 & 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 {
address = bus.mirror(address, size());
return _data[address];
return ReadableMemory::read(address, data);
}
auto SA1::ROM::write(uint24 address, uint8 data) -> void {

View File

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

View File

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

View File

@ -12,8 +12,6 @@ auto SDD1::unload() -> void {
}
auto SDD1::power() -> void {
rom.writeProtect(true);
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
//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");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,6 +16,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
inline auto dmaStep(uint clocks) -> void;
inline auto dmaFlush() -> void;
inline auto dmaWrite() -> void;
auto dmaRun() -> void;
auto hdmaReset() -> void;
@ -166,11 +167,13 @@ private:
//dma.cpp
inline auto step(uint clocks) -> void;
inline auto edge() -> void;
inline auto flush() -> void;
inline auto write() -> void;
inline auto validA(uint24 address) -> bool;
inline auto readA(uint24 address) -> uint8;
inline auto readA(uint24 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, 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 {
for(auto& channel : channels) if(channel.dmaEnable) return true;
return false;
@ -24,10 +39,15 @@ auto CPU::dmaFlush() -> void {
bus.write(pipe.address, pipe.data);
}
auto CPU::dmaRun() -> void {
r.rwb = 0;
auto CPU::dmaWrite() -> void {
r.rwb = pipe.valid;
r.mar = pipe.address;
dmaStep(8);
dmaFlush();
}
auto CPU::dmaRun() -> void {
dmaWrite();
dmaEdge();
for(auto& channel : channels) channel.dmaRun();
dmaFlush();
@ -39,18 +59,14 @@ auto CPU::hdmaReset() -> void {
}
auto CPU::hdmaSetup() -> void {
r.rwb = 0;
dmaStep(8);
dmaFlush();
dmaWrite();
for(auto& channel : channels) channel.hdmaSetup();
dmaFlush();
status.irqLock = true;
}
auto CPU::hdmaRun() -> void {
r.rwb = 0;
dmaStep(8);
dmaFlush();
dmaWrite();
for(auto& channel : channels) channel.hdmaTransfer();
for(auto& channel : channels) channel.hdmaAdvance();
dmaFlush();
@ -59,13 +75,10 @@ auto CPU::hdmaRun() -> void {
//
auto CPU::Channel::step(uint clocks) -> void {
return cpu.dmaStep(clocks);
}
auto CPU::Channel::edge() -> void {
return cpu.dmaEdge();
}
auto CPU::Channel::step(uint clocks) -> void { 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::validA(uint24 address) -> bool {
//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;
}
auto CPU::Channel::flush() -> void {
return cpu.dmaFlush();
}
auto CPU::Channel::writeA(uint24 address, uint8 data) -> void {
return writeA(address, data, validA(address));
}
@ -148,9 +157,7 @@ auto CPU::Channel::dmaRun() -> void {
edge();
} while(dmaEnable && --transferSize);
cpu.r.rwb = 0;
step(8);
flush();
write();
edge();
dmaEnable = false;
}

View File

@ -23,14 +23,8 @@ auto CPU::step(uint clocks) -> void {
}
}
#if defined(DEBUGGER)
synchronize(smp);
synchronize(ppu);
#endif
#if defined(DEBUGGER) || defined(ACCURATE_SA1)
if(configuration.hacks.coprocessors.delayedSync) return;
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
#endif
}
//called by ppu.tick() when Hcounter=0
@ -94,10 +88,12 @@ auto CPU::dmaEdge() -> void {
status.hdmaPending = false;
if(hdmaEnable()) {
if(!dmaEnable()) {
r.rwb = 0;
dmaStep(8 - dmaCounter());
}
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnable()) {
r.rwb = 0; //unverified
step(status.clockCount - (status.dmaClocks % status.clockCount));
status.dmaActive = false;
}
@ -107,8 +103,10 @@ auto CPU::dmaEdge() -> void {
if(status.dmaPending) {
status.dmaPending = false;
if(dmaEnable()) {
r.rwb = 0;
dmaStep(8 - dmaCounter());
dmaRun();
r.rwb = 0; //unverified
step(status.clockCount - (status.dmaClocks % status.clockCount));
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/HiresMode7", hacks.ppuFast.hiresMode7);
bind(boolean, "Hacks/FastDSP/Enable", hacks.dspFast.enable);
bind(boolean, "Hacks/Coprocessors/DelayedSync", hacks.coprocessors.delayedSync);
#undef bind
}

View File

@ -33,6 +33,9 @@ struct Configuration {
struct DSPFast {
bool enable = false;
} dspFast;
struct Coprocessors {
bool delayedSync = false;
} coprocessors;
} hacks;
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 {
if(size == 0) return 0;
uint base = 0;

View File

@ -30,10 +30,10 @@ auto Bus::map(
const function<uint8 (uint24, uint8)>& read,
const function<void (uint24, uint8)>& write,
const string& addr, uint size, uint base, uint mask
) -> void {
) -> uint {
uint id = 1;
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;
@ -68,6 +68,8 @@ auto Bus::map(
}
}
}
return id;
}
auto Bus::unmap(const string& addr) -> void {

View File

@ -1,74 +1,46 @@
struct Memory {
virtual ~Memory() { reset(); }
inline explicit operator bool() const { return size() > 0; }
virtual auto reset() -> void {}
virtual auto allocate(uint) -> void {}
virtual auto allocate(uint, uint8 = 0xff) -> void {}
virtual auto data() -> uint8* = 0;
virtual auto size() const -> uint = 0;
virtual auto read(uint24 addr, uint8 data = 0) -> uint8 = 0;
virtual auto write(uint24 addr, uint8 data) -> void = 0;
virtual auto read(uint24 address, uint8 data = 0) -> uint8 = 0;
virtual auto write(uint24 address, uint8 data) -> void = 0;
uint id = 0;
};
struct StaticRAM : Memory {
inline StaticRAM(uint size);
inline ~StaticRAM();
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;
};
#include "readable.hpp"
#include "writable.hpp"
#include "protectable.hpp"
struct Bus {
alwaysinline static auto mirror(uint addr, uint size) -> uint;
alwaysinline static auto reduce(uint addr, uint mask) -> uint;
alwaysinline static auto mirror(uint address, uint size) -> uint;
alwaysinline static auto reduce(uint address, uint mask) -> uint;
~Bus();
alwaysinline auto read(uint24 addr, uint8 data) -> uint8;
alwaysinline auto write(uint24 addr, uint8 data) -> void;
alwaysinline auto read(uint24 address, uint8 data) -> uint8;
alwaysinline auto write(uint24 address, uint8 data) -> void;
auto reset() -> void;
auto map(
const function<uint8 (uint24, uint8)>& read,
const function<void (uint24, uint8)>& write,
const string& addr, uint size = 0, uint base = 0, uint mask = 0
) -> void;
auto unmap(const string& addr) -> void;
const string& address, uint size = 0, uint base = 0, uint mask = 0
) -> uint;
auto unmap(const string& address) -> void;
private:
uint8* lookup = nullptr;
uint32* target = nullptr;
function<auto (uint24, uint8) -> uint8> reader[256];
function<auto (uint24, uint8) -> void> writer[256];
function<uint8 (uint24, uint8)> reader[256];
function<void (uint24, uint8)> writer[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>
#endif
//#define ACCURATE_SA1
namespace SuperFamicom {
#define platform Emulator::platform
namespace File = Emulator::File;

View File

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

View File

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

View File

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

View File

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

View File

@ -3,17 +3,18 @@ auto Program::hackCompatibility() -> void {
bool fastPPUNoSpriteLimit = emulatorSettings.noSpriteLimit.checked();
bool fastPPUHiresMode7 = emulatorSettings.hiresMode7.checked();
bool fastDSP = emulatorSettings.fastDSPOption.checked();
bool coprocessorsDelayedSync = emulatorSettings.coprocessorsDelayedSyncOption.checked();
auto title = superFamicom.title;
if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false;
if(title == "KOUSHIEN_2") fastDSP = false;
if(title == "RENDERING RANGER R2") fastDSP = false;
//todo: update to new emulator->configuration API
emulator->set("Fast PPU", fastPPU);
emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit);
emulator->set("Fast PPU/Hires Mode 7", fastPPUHiresMode7);
emulator->set("Fast DSP", fastDSP);
emulator->configure("Hacks/FastPPU/Enable", fastPPU);
emulator->configure("Hacks/FastPPU/NoSpriteLimit", fastPPUNoSpriteLimit);
emulator->configure("Hacks/FastPPU/HiresMode7", fastPPUHiresMode7);
emulator->configure("Hacks/FastDSP/Enable", fastDSP);
emulator->configure("Hacks/Coprocessors/DelayedSync", coprocessorsDelayedSync);
}
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([&] {
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:");
superFXValue.setAlignment(0.5);
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/HiresMode7", hiresMode7.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/SufamiTurboB", path.recent.sufamiTurboB);
bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames);
bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable);
bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval);
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
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/HiresMode7", emulator.hack.fastPPU.hiresMode7);
bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable);
bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX);
bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable);
bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames);
bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable);
bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval);
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
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/HiresMode7", emulator.hack.fastPPU.hiresMode7);
bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable);
bind(boolean, "Emulator/Hack/Coprocessors/DelayedSync", emulator.hack.coprocessors.delayedSync);
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/ScreenSaver", general.screenSaver);

View File

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

View File

@ -64,11 +64,16 @@ struct inode {
stat(name, &data);
switch(mode) {
#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;
#else
//st_birthtime may return -1 or st_atime if it is not supported
#elif defined(PLATFORM_BSD) || defined(PLATFORM_MACOS)
//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
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
case time::modify: return data.st_mtime;
//for performance reasons, last access time is usually not enabled on various filesystems