Commit 3d34517f authored by Tim Allen's avatar Tim Allen

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 CPUcoprocessor 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.
parent a3e0f6da
Pipeline #29333127 failed with stage
in 13 minutes and 21 seconds
......@@ -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::power() -> void {
regs.halt = true;
auto HG51B::advance() -> void {
if(++r.pc == 0) {
if(io.cache.page == 1) return halt();
io.cache.page = 1;
r.pb = r.p;
}
}
regs.n = 0;
regs.z = 0;
regs.c = 0;
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 {
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
boolean n; //negative
boolean z; //zero
boolean c; //carry
boolean i; //interrupt
uint24 a;
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;