Update to v094r34 release.

byuu says:

Fixes SuperFX fmult, lmult timings; rambr, bramr and clsr assignment
masking. Implements true GBA ROM prefetch (buggy, lower test score, but
runs Mario & Luigi without crashing on battles anymore.)
This commit is contained in:
Tim Allen 2015-06-28 18:44:56 +10:00
parent 4c9266d18f
commit 7ff7f64482
27 changed files with 164 additions and 139 deletions

View File

@ -8,7 +8,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "094.33";
static const string Version = "094.34";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";
@ -33,7 +33,7 @@ template<typename R, typename... P> struct hook<R (P...)> {
auto operator()(P... p) const -> R {
#if defined(DEBUGGER)
if(callback) return callback(std::forward<P>(p)...);
if(callback) return callback(forward<P>(p)...);
#endif
return R();
}

View File

@ -33,7 +33,7 @@ struct Interface {
unsigned id;
unsigned type; //0 = digital, 1 = analog (relative), 2 = rumble
string name;
uintptr_t guid;
uintptr_t guid; //user data field
};
vector<Input> input;
vector<unsigned> order;
@ -57,7 +57,6 @@ struct Interface {
virtual auto inputRumble(unsigned, unsigned, unsigned, bool) -> void {}
virtual auto dipSettings(const Markup::Node&) -> unsigned { return 0; }
virtual auto path(unsigned) -> string { return ""; }
virtual auto server() -> string { return ""; }
virtual auto notify(string text) -> void { print(text, "\n"); }
};
Bind* bind = nullptr;
@ -73,7 +72,6 @@ struct Interface {
auto inputRumble(unsigned port, unsigned device, unsigned input, bool enable) -> void { return bind->inputRumble(port, device, input, enable); }
auto dipSettings(const Markup::Node& node) -> unsigned { return bind->dipSettings(node); }
auto path(unsigned group) -> string { return bind->path(group); }
auto server() -> string { return bind->server(); }
template<typename... P> auto notify(P&&... p) -> void { return bind->notify({forward<P>(p)...}); }
//information

View File

@ -63,6 +63,72 @@ auto CPU::step(unsigned clocks) -> void {
sync_step(clocks);
}
auto CPU::bus_idle(uint32 addr) -> void {
step(1);
prefetch_step(1);
}
auto CPU::bus_read(uint32 addr, uint32 size, bool mode) -> uint32 {
unsigned wait = bus.wait(addr, size, mode);
if(addr < 0x0800'0000) {
unsigned word = bus.read(addr, size);
step(wait);
prefetch_step(wait);
return word;
}
if(addr < 0x0e00'0000) {
if(regs.wait.control.prefetch) {
if(mode == Nonsequential) prefetch_start(addr);
unsigned word = prefetch_take();
if(size == Byte) word = (addr & 1) ? (word >> 8) : (word & 0xff);
if(size == Word) word |= prefetch_take() << 16;
return word;
}
unsigned word = cartridge.read(addr, size);
step(wait);
return word;
}
if(addr < 0x1000'0000) {
prefetch_stall();
unsigned word = bus.read(addr, size);
step(wait);
return word;
}
step(wait);
prefetch_step(wait);
return 0x0000'0000; //open bus?
}
auto CPU::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
unsigned wait = bus.wait(addr, size, mode);
if(addr < 0x0800'0000) {
step(wait);
prefetch_step(wait);
return bus.write(addr, size, word);
}
if(addr < 0x0e00'0000) {
prefetch_stall();
step(wait);
return bus.write(addr, size, word);
}
if(addr < 0x1000'0000) {
prefetch_stall();
step(wait);
return bus.write(addr, size, word);
}
step(wait);
prefetch_step(wait);
}
auto CPU::sync_step(unsigned clocks) -> void {
ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread);
@ -71,43 +137,6 @@ auto CPU::sync_step(unsigned clocks) -> void {
if(apu.clock < 0) co_switch(apu.thread);
}
auto CPU::bus_idle(uint32 addr) -> void {
if(regs.wait.control.prefetch) prefetch_run();
step(1);
}
auto CPU::bus_read(uint32 addr, uint32 size, bool mode) -> uint32 {
if(regs.wait.control.prefetch) {
if((addr & 0x0fffffff) >= 0x08000000 && (addr & 0x0fffffff) <= 0x0dffffff) {
if(auto word = prefetch_read(addr, size)) {
step(1 + (size == Word));
return *word;
}
}
}
unsigned wait = bus.wait(addr, size, mode);
unsigned word = bus.read(addr, size);
step(wait);
return word;
}
auto CPU::bus_load(uint32 addr, uint32 size, bool mode) -> uint32 {
if(regs.wait.control.prefetch) prefetch_run();
return bus_read(addr, size, mode);
}
auto CPU::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
unsigned wait = bus.wait(addr, size, mode);
step(wait);
bus.write(addr, size, word);
}
auto CPU::bus_store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
if(regs.wait.control.prefetch) prefetch_run();
return bus_write(addr, size, mode, word);
}
auto CPU::keypad_run() -> void {
if(regs.keypad.control.enable == false) return;
@ -122,7 +151,7 @@ auto CPU::keypad_run() -> void {
}
auto CPU::power() -> void {
create(CPU::Enter, 16777216);
create(CPU::Enter, 16'777'216);
ARM::power();
for(auto n : range( 32 * 1024)) iwram[n] = 0;
@ -152,7 +181,7 @@ auto CPU::power() -> void {
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
regs.clock = 0;
regs.memory.control = 0x0d000020;
regs.memory.control = 0x0d00'0020;
pending.dma.vblank = 0;
pending.dma.hblank = 0;

View File

@ -12,15 +12,13 @@ struct CPU : Processor::ARM, Thread, MMIO {
static auto Enter() -> void;
auto main() -> void;
auto step(unsigned clocks) -> void;
auto step(unsigned clocks) -> void override;
auto bus_idle(uint32 addr) -> void override;
auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 override;
auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void override;
auto sync_step(unsigned clocks) -> void;
auto bus_idle(uint32 addr) -> void;
auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32;
auto bus_load(uint32 addr, uint32 size, bool mode) -> uint32;
auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto bus_store(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto keypad_run() -> void;
auto power() -> void;

View File

@ -1,17 +1,43 @@
auto CPU::prefetch_run() -> void {
if(prefetch.slots == 8) return;
prefetch.slots++;
auto CPU::prefetch_stall() -> void {
prefetch.stalled = true;
}
auto CPU::prefetch_read(uint32 addr, uint32 size) -> maybe<uint32> {
if(prefetch.slots >= (size == Word ? 2 : 1)) {
prefetch.slots -= (size == Word ? 2 : 1);
return cartridge.read(addr, size);
auto CPU::prefetch_start(uint32 addr) -> void {
prefetch.stalled = false;
prefetch.slots = 0;
prefetch.input = 0;
prefetch.output = 0;
prefetch.addr = addr;
prefetch.wait = bus.wait(addr, Half, Nonsequential);
}
auto CPU::prefetch_step(unsigned clocks) -> void {
if(!regs.wait.control.prefetch || prefetch.stalled) return;
prefetch.wait -= clocks;
while(prefetch.wait <= 0) {
if(prefetch.slots < 8) {
prefetch.slot[prefetch.output++] = cartridge.read(prefetch.addr, Half);
prefetch.slots++;
prefetch.addr += 2;
}
prefetch.wait += bus.wait(prefetch.addr, Half, Sequential);
}
prefetch.slots = 0;
return nothing;
}
auto CPU::prefetch_wait() -> void {
step(prefetch.wait);
prefetch_step(prefetch.wait);
}
auto CPU::prefetch_take() -> uint16 {
return 0;
if(prefetch.slots) {
step(1);
prefetch_step(1);
} else {
prefetch_wait();
}
prefetch.slots--;
return prefetch.slot[prefetch.input++];
}

View File

@ -1,13 +1,16 @@
struct Prefetch {
struct Slot {
uint32 addr;
uint16 half;
} slot[8];
uint16 slot[8] = {0};
uint3 input = 0;
uint3 output = 0;
bool stalled = true;
unsigned slots = 0;
signed wait = 0;
uint32 addr = 0;
} prefetch;
auto prefetch_run() -> void;
auto prefetch_read(uint32 addr, uint32 size) -> maybe<uint32>;
auto prefetch_stall() -> void;
auto prefetch_start(uint32 addr) -> void;
auto prefetch_step(unsigned clocks) -> void;
auto prefetch_wait() -> void;
auto prefetch_take() -> uint16;

View File

@ -221,6 +221,9 @@ auto CPU::Registers::WaitControl::operator=(uint16 source) -> uint16 {
prefetch = (source >> 14) & 1;
gametype = (source >> 15) & 1;
swait[3] = nwait[3];
cpu.prefetch.stalled = true;
return operator uint16();
}

View File

@ -41,6 +41,7 @@ auto mMenuRadioItem::setChecked() -> type& {
auto mMenuRadioItem::setGroup(sGroup group) -> type& {
state.group = group;
signal(setGroup, group);
if(group && group->objects() == 1) setChecked();
return *this;
}

View File

@ -59,6 +59,7 @@ auto mRadioButton::setChecked() -> type& {
auto mRadioButton::setGroup(sGroup group) -> type& {
state.group = group;
signal(setGroup, group);
if(group && group->objects() == 1) setChecked();
return *this;
}

View File

@ -41,6 +41,7 @@ auto mRadioLabel::setChecked() -> type& {
auto mRadioLabel::setGroup(sGroup group) -> type& {
state.group = group;
signal(setGroup, group);
if(group && group->objects() == 1) setChecked();
return *this;
}

View File

@ -40,7 +40,7 @@ auto ARM::read(uint32 addr, uint32 size, bool mode) -> uint32 {
auto ARM::load(uint32 addr, uint32 size, bool mode) -> uint32 {
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
uint32 word = bus_load(addr, size, mode);
uint32 word = bus_read(addr, size, mode);
if(size == Half) { word &= 0xffff; word |= word << 16; }
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
@ -63,7 +63,7 @@ auto ARM::store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
bus_store(addr, size, mode, word);
bus_write(addr, size, mode, word);
processor.nonsequential = true;
}

View File

@ -19,9 +19,7 @@ struct ARM {
virtual auto step(unsigned clocks) -> void = 0;
virtual auto bus_idle(uint32 addr) -> void = 0;
virtual auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 = 0;
virtual auto bus_load(uint32 addr, uint32 size, bool mode) -> uint32 = 0;
virtual auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void = 0;
virtual auto bus_store(uint32 addr, uint32 size, bool mode, uint32 word) -> void = 0;
//arm.cpp
auto power() -> void;

View File

@ -1,27 +1,16 @@
#include <processor/processor.hpp>
#include "gsu.hpp"
//note: multiplication results *may* sometimes be invalid when both CLSR and MS0 are set
//the product of multiplication in this mode (21mhz + fast-multiply) has not been analyzed;
//however, the timing of this mode has been confirmed to work as specified below
namespace Processor {
#include "instructions.cpp"
#include "table.cpp"
#include "serialization.cpp"
//note: multiplication results *may* sometimes be invalid when both CLSR and MS0 are set
//the product of multiplication in this mode (21mhz + fast-multiply) has not been analyzed;
//however, the timing of this mode has been confirmed to work as specified below
auto GSU::cache_access_speed() -> unsigned {
if(clockmode == 1) return 2;
if(clockmode == 2) return 1;
return regs.clsr ? 1 : 2;
}
auto GSU::memory_access_speed() -> unsigned {
if(clockmode == 1) return 6;
if(clockmode == 2) return 5;
return regs.clsr ? 5 : 6;
}
auto GSU::power() -> void {
}

View File

@ -4,7 +4,6 @@
namespace Processor {
struct GSU {
unsigned clockmode; //0 = selectable; 1 = force 10.74mhz; 2 = force 21.48mhz
#include "registers.hpp"
virtual auto step(unsigned clocks) -> void = 0;
@ -23,9 +22,6 @@ struct GSU {
virtual auto cache_flush() -> void = 0;
//gsu.cpp
auto cache_access_speed() -> unsigned;
auto memory_access_speed() -> unsigned;
auto power() -> void;
auto reset() -> void;

View File

@ -383,7 +383,7 @@ auto GSU::op_mult_r() {
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(cache_access_speed());
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt1): umult rN
@ -393,7 +393,7 @@ auto GSU::op_umult_r() {
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(cache_access_speed());
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt2): mult #N
@ -403,7 +403,7 @@ auto GSU::op_mult_i() {
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(cache_access_speed());
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$80-8f(alt3): umult #N
@ -413,7 +413,7 @@ auto GSU::op_umult_i() {
regs.sfr.s = (regs.dr() & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
if(!regs.cfgr.ms0) step(cache_access_speed());
if(!regs.cfgr.ms0) step(regs.clsr ? 1 : 2);
}
//$90: sbk
@ -499,7 +499,7 @@ auto GSU::op_fmult() {
regs.sfr.cy = (result & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
step(4 + (regs.cfgr.ms0 << 2));
step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2));
}
//$9f(alt1): lmult
@ -511,7 +511,7 @@ auto GSU::op_lmult() {
regs.sfr.cy = (result & 0x8000);
regs.sfr.z = (regs.dr() == 0);
regs.reset();
step(4 + (regs.cfgr.ms0 << 2));
step((regs.cfgr.ms0 ? 3 : 7) * (regs.clsr ? 1 : 2));
}
//$a0-af(alt0): ibt rN,#pp
@ -618,7 +618,7 @@ auto GSU::op_getc() {
//$df(alt2): ramb
auto GSU::op_ramb() {
rambuffer_sync();
regs.rambr = regs.sr();
regs.rambr = regs.sr() & 0x01;
regs.reset();
}

View File

@ -31,6 +31,7 @@ struct reg16_t {
inline auto operator = (const reg16_t& i) { return assign(i); }
reg16_t() = default;
reg16_t(const reg16_t&) = delete;
};

View File

@ -1,6 +1,4 @@
auto GSU::serialize(serializer& s) -> void {
s.integer(clockmode);
s.integer(regs.pipeline);
s.integer(regs.ramaddr);

View File

@ -10,12 +10,10 @@ struct ArmDSP : Processor::ARM, Coprocessor {
static void Enter();
void enter();
void step(unsigned clocks);
void bus_idle(uint32 addr);
uint32 bus_read(uint32 addr, uint32 size, bool mode);
uint32 bus_load(uint32 addr, uint32 size, bool mode);
void bus_write(uint32 addr, uint32 size, bool mode, uint32 word);
void bus_store(uint32 addr, uint32 size, bool mode, uint32 word);
void step(unsigned clocks) override;
void bus_idle(uint32 addr) override;
uint32 bus_read(uint32 addr, uint32 size, bool mode) override;
void bus_write(uint32 addr, uint32 size, bool mode, uint32 word) override;
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);

View File

@ -1,5 +1,8 @@
#ifdef ARMDSP_CPP
//note: timings are completely unverified
//due to the ST018 chip design (on-die ROM), testing is nearly impossible
void ArmDSP::bus_idle(uint32 addr) {
step(1);
}
@ -44,10 +47,6 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
return 0u;
}
uint32 ArmDSP::bus_load(uint32 addr, uint32 size, bool mode) {
return bus_read(addr, size, mode);
}
void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
step(1);
@ -102,8 +101,4 @@ void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
}
}
void ArmDSP::bus_store(uint32 addr, uint32 size, bool mode, uint32 word) {
return bus_write(addr, size, mode, word);
}
#endif

View File

@ -60,7 +60,7 @@ void Event::submitScore() {
data.append("ba:", ba[0], ",", ba[1], "\n");
}
lstring side = interface->server().split<1>("@");
/*lstring side = interface->server().split<1>("@");
string username = side(0).split<1>(":")(0);
string password = side(0).split<1>(":")(1);
side(1).ltrim("http://");
@ -71,7 +71,7 @@ void Event::submitScore() {
string hostport = side(1);
if(hostport.empty()) hostport = "80";
/*http server;
http server;
if(server.connect(hostname, decimal(hostport))) {
string content = {
"username:", username, "\n",

View File

@ -66,7 +66,7 @@ auto SuperFX::rpix(uint8 x, uint8 y) -> uint8 {
for(unsigned n = 0; n < bpp; n++) {
unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 };
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
data |= ((bus_read(addr + byte) >> x) & 1) << n;
}
@ -94,11 +94,11 @@ auto SuperFX::pixelcache_flush(pixelcache_t& cache) -> void {
uint8 data = 0x00;
for(unsigned x = 0; x < 8; x++) data |= ((cache.data[x] >> n) & 1) << x;
if(cache.bitpend != 0xff) {
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
data &= cache.bitpend;
data |= bus_read(addr + byte) & ~cache.bitpend;
}
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
bus_write(addr + byte, data);
}

View File

@ -43,12 +43,12 @@ auto SuperFX::op_read(uint16 addr) -> uint8 {
unsigned dp = offset & 0xfff0;
unsigned sp = (regs.pbr << 16) + ((regs.cbr + dp) & 0xfff0);
for(unsigned n = 0; n < 16; n++) {
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
cache.buffer[dp++] = bus_read(sp++);
}
cache.valid[offset >> 4] = true;
} else {
step(cache_access_speed());
step(regs.clsr ? 1 : 2);
}
return cache.buffer[offset];
}
@ -56,12 +56,12 @@ auto SuperFX::op_read(uint16 addr) -> uint8 {
if(regs.pbr <= 0x5f) {
//$[00-5f]:[0000-ffff] ROM
rombuffer_sync();
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
return bus_read((regs.pbr << 16) + addr);
} else {
//$[60-7f]:[0000-ffff] RAM
rambuffer_sync();
step(memory_access_speed());
step(regs.clsr ? 5 : 6);
return bus_read((regs.pbr << 16) + addr);
}
}

View File

@ -87,7 +87,7 @@ auto SuperFX::mmio_write(unsigned addr, uint8 data) -> void {
} break;
case 0x3033: {
regs.bramr = data;
regs.bramr = data & 0x01;
} break;
case 0x3034: {
@ -104,7 +104,7 @@ auto SuperFX::mmio_write(unsigned addr, uint8 data) -> void {
} break;
case 0x3039: {
regs.clsr = data;
regs.clsr = data & 0x01;
} break;
case 0x303a: {

View File

@ -5,7 +5,6 @@ auto SuperFX::serialize(serializer& s) -> void {
Thread::serialize(s);
s.array(ram.data(), ram.size());
s.integer(instruction_counter);
s.integer(r15_modified);
}

View File

@ -25,17 +25,12 @@ auto SuperFX::enter() -> void {
if(regs.sfr.g == 0) {
step(6);
synchronize_cpu();
continue;
}
(this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])();
unsigned opcode = (regs.sfr & 0x0300) + peekpipe();
(this->*opcode_table[opcode])();
if(r15_modified == false) regs.r[15]++;
if(++instruction_counter >= 128) {
instruction_counter = 0;
synchronize_cpu();
}
}
}
@ -60,7 +55,6 @@ auto SuperFX::power() -> void {
auto SuperFX::reset() -> void {
GSU::reset();
create(SuperFX::Enter, system.cpu_frequency());
instruction_counter = 0;
memory_reset();
timing_reset();
}

View File

@ -21,9 +21,6 @@ struct SuperFX : Processor::GSU, Coprocessor {
//serialization.cpp
auto serialize(serializer&) -> void;
privileged:
unsigned instruction_counter = 0;
};
extern SuperFX superfx;

View File

@ -26,7 +26,7 @@ auto SuperFX::rombuffer_sync() -> void {
auto SuperFX::rombuffer_update() -> void {
regs.sfr.r = 1;
regs.romcl = memory_access_speed();
regs.romcl = regs.clsr ? 5 : 6;
}
auto SuperFX::rombuffer_read() -> uint8 {
@ -45,7 +45,7 @@ auto SuperFX::rambuffer_read(uint16 addr) -> uint8 {
auto SuperFX::rambuffer_write(uint16 addr, uint8 data) -> void {
rambuffer_sync();
regs.ramcl = memory_access_speed();
regs.ramcl = regs.clsr ? 5 : 6;
regs.ramar = addr;
regs.ramdr = data;
}