mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
a3e0f6da25
commit
3d34517f3e
|
@ -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/";
|
||||
|
|
|
@ -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 = {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -98,8 +98,6 @@ auto Cartridge::load() -> bool {
|
|||
information.sha256 = sha.digest();
|
||||
}
|
||||
|
||||
rom.writeProtect(true);
|
||||
ram.writeProtect(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,19 +10,15 @@ 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);
|
||||
auto HitachiDSP::step(uint clocks) -> void {
|
||||
HG51B::step(clocks);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
mmio.dma = false;
|
||||
}
|
||||
}
|
||||
|
||||
exec(mmio.programOffset);
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
auto HitachiDSP::halt() -> void {
|
||||
HG51B::halt();
|
||||
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
|
||||
}
|
||||
|
||||
auto HitachiDSP::unload() -> void {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
if((addr & 0x40ffe0) == 0x00ffe0) return mmio.vector[addr & 0x1f];
|
||||
} else {
|
||||
//00-3f,80-bf:8000-ffff; c0-ff:0000-ffff
|
||||
if((address & 0x408000) == 0x008000 || (address & 0xc00000) == 0xc00000) {
|
||||
return {address & 0x3fffff};
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -7,7 +7,7 @@ struct OBC1 {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
MappedRAM ram;
|
||||
WritableMemory ram;
|
||||
|
||||
private:
|
||||
auto ramRead(uint addr) -> uint8;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -15,7 +15,7 @@ struct SDD1 {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
MappedRAM rom;
|
||||
ReadableMemory rom;
|
||||
|
||||
private:
|
||||
uint8 r4800; //hard enable
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct SuperFX : Processor::GSU, Thread {
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
ReadableMemory rom;
|
||||
WritableMemory ram;
|
||||
|
||||
//superfx.cpp
|
||||
static auto Enter() -> void;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ struct Configuration {
|
|||
struct DSPFast {
|
||||
bool enable = false;
|
||||
} dspFast;
|
||||
struct Coprocessors {
|
||||
bool delayedSync = false;
|
||||
} coprocessors;
|
||||
} hacks;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -20,8 +20,6 @@
|
|||
#include <gb/gb.hpp>
|
||||
#endif
|
||||
|
||||
//#define ACCURATE_SA1
|
||||
|
||||
namespace SuperFamicom {
|
||||
#define platform Emulator::platform
|
||||
namespace File = Emulator::File;
|
||||
|
|
|
@ -21,7 +21,7 @@ auto BSMemory::power() -> void {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ struct BSMemory : Memory {
|
|||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint pathID = 0;
|
||||
MappedRAM memory;
|
||||
ProtectableMemory memory;
|
||||
bool readonly;
|
||||
|
||||
private:
|
||||
|
|
|
@ -12,8 +12,6 @@ auto SufamiTurboCartridge::unload() -> void {
|
|||
}
|
||||
|
||||
auto SufamiTurboCartridge::power() -> void {
|
||||
rom.writeProtect(true);
|
||||
ram.writeProtect(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ struct SufamiTurboCartridge {
|
|||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint pathID = 0;
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
ReadableMemory rom;
|
||||
WritableMemory ram;
|
||||
};
|
||||
|
||||
extern SufamiTurboCartridge sufamiturboA;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ auto Settings::process(bool load) -> void {
|
|||
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);
|
||||
|
||||
|
|
|
@ -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}};
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue