Update to v104 release.

byuu says:

Changelog:

  - emulator/interface: removed unused Region struct
  - gba/cpu: optimized CPU::step() as much as I could for a slight
    speedup¹
  - gba/cpu: synchronize the APU better during FIFO updates
  - higan/md, icarus: add automatic region detection; make it the
    default option [hex\_usr]
      - picks NTSC-J if there's more than one match ... eventually, this
        will be a setting
  - higan/md, icarus: support all three combinations of SRAM (8-bit low,
    8-bit high, 16-bit)
  - processor/arm7tdmi: fix bug when changing to THUMB mode via MSR
    [MerryMage]
  - tomoko: redesigned crash detector to only occur once for all three
    ruby drivers
      - this will reduce disk thrashing since the configuration file
        only needs to be written out one extra time
      - technically, it's twice ... but we should've always been writing
        one out on first run in case it crashes then
  - tomoko: defaulted back to the safest ruby drivers, given the optimal
    drivers have some stability concerns

¹: minor errata: spotted a typo saying `synchronize(cpu)` when the CPU
is stopped, instead of `synchronize(ppu)`. This will be fixed in the v104
official 7zip archives.

I'm kind of rushing here but, it's really good timing for me to push out
a new official release. The blocking issues are resolved or close to it,
and we need lots of testing of the new major changes.

I'm going to consider this a semi-stable testing release and leave links
to v103 just in case.
This commit is contained in:
Tim Allen 2017-08-12 20:53:13 +10:00
parent 55f19c3e0d
commit ba384a7c48
13 changed files with 116 additions and 84 deletions

View File

@ -12,13 +12,13 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "103.32";
static const string Version = "104";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";
//incremented only when serialization format changes
static const string SerializerVersion = "103";
static const string SerializerVersion = "104";
namespace Constants {
namespace Colorburst {

View File

@ -9,11 +9,6 @@ struct Interface {
bool overscan;
} information;
struct Region {
string name;
};
vector<Region> regions;
struct Medium {
uint id;
string name;

View File

@ -20,12 +20,18 @@ auto CPU::main() -> void {
ARM7TDMI::irq = irq.ime && (irq.enable & irq.flag);
if(stopped()) {
if(!(irq.enable & irq.flag & Interrupt::Keypad)) return step(16);
if(!(irq.enable & irq.flag & Interrupt::Keypad)) {
Thread::step(16);
synchronize(cpu);
synchronize(apu);
}
context.stopped = false;
}
if(halted()) {
if(!(irq.enable & irq.flag)) return step(16);
if(!(irq.enable & irq.flag)) {
return step(16);
}
context.halted = false;
}
@ -33,22 +39,22 @@ auto CPU::main() -> void {
}
auto CPU::step(uint clocks) -> void {
for(auto& dma : this->dma) {
dma.waiting = max(0, dma.waiting - (int)clocks);
}
dma[0].waiting = max(0, dma[0].waiting - (int)clocks);
dma[1].waiting = max(0, dma[1].waiting - (int)clocks);
dma[2].waiting = max(0, dma[2].waiting - (int)clocks);
dma[3].waiting = max(0, dma[3].waiting - (int)clocks);
if(!context.dmaActive) {
context.dmaActive = true;
while(true) {
bool transferred = false;
for(auto& dma : this->dma) transferred |= dma.run();
if(!transferred) break;
}
while(dma[0].run() | dma[1].run() | dma[2].run() | dma[3].run());
context.dmaActive = false;
}
for(auto _ : range(clocks)) {
for(auto& timer : this->timer) timer.run();
timer[0].run();
timer[1].run();
timer[2].run();
timer[3].run();
context.clock++;
}

View File

@ -67,7 +67,7 @@ struct CPU : Processor::ARM7TDMI, Thread, IO {
//private:
struct DMA {
//dma.cpp
auto run() -> bool;
inline auto run() -> bool;
auto transfer() -> void;
uint2 id;
@ -98,7 +98,7 @@ struct CPU : Processor::ARM7TDMI, Thread, IO {
struct Timer {
//timer.cpp
auto run() -> void;
inline auto run() -> void;
auto step() -> void;
uint2 id;
@ -128,7 +128,7 @@ struct CPU : Processor::ARM7TDMI, Thread, IO {
} serial;
struct Keypad {
//auto keypad.cpp
//keypad.cpp
auto run() -> void;
uint1 enable;
@ -189,8 +189,8 @@ struct CPU : Processor::ARM7TDMI, Thread, IO {
uint32 load; //write location of slot buffer
integer wait = 1; //number of clocks before next slot load
auto empty() const { return addr == load; }
auto full() const { return load - addr == 16; }
inline auto empty() const { return addr == load; }
inline auto full() const { return load - addr == 16; }
} prefetch;
struct Context {

View File

@ -1,5 +1,5 @@
auto CPU::DMA::run() -> bool {
if(cpu.stopped() || !active || waiting) return false;
if(!active || waiting) return false;
transfer();
if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;

View File

@ -1,6 +1,4 @@
auto CPU::Timer::run() -> void {
if(cpu.stopped()) return;
if(pending) {
pending = false;
if(enable) period = reload;
@ -29,6 +27,7 @@ auto CPU::Timer::step() -> void {
}
auto CPU::runFIFO(uint n) -> void {
synchronize(apu);
apu.fifo[n].read();
if(apu.fifo[n].size > 16) return;

View File

@ -8,7 +8,7 @@ Cartridge cartridge;
auto Cartridge::load() -> bool {
information = {};
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"NTSC-J", "NTSC-U", "PAL"})) {
if(auto loaded = platform->load(ID::MegaDrive, "Mega Drive", "md", {"Auto", "NTSC-J", "NTSC-U", "PAL"})) {
information.pathID = loaded.pathID();
information.region = loaded.option();
} else return false;
@ -20,27 +20,43 @@ auto Cartridge::load() -> bool {
auto document = BML::unserialize(information.manifest);
information.title = document["information/title"].text();
if(information.region == "Auto") {
if(auto region = document["board/region"].text()) {
information.region = region.upcase();
} else {
information.region = "NTSC-J";
}
}
if(auto node = document["board/rom"]) {
rom.size = node["size"].natural();
rom.size = node["size"].natural() >> 1;
rom.mask = bit::round(rom.size) - 1;
if(rom.size) {
rom.data = new uint8[rom.mask + 1];
rom.data = new uint16[rom.mask + 1]();
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read, File::Required)) {
fp->read(rom.data, rom.size);
for(uint n : range(rom.size)) rom.data[n] = fp->readm(2);
}
}
}
}
if(auto node = document["board/ram"]) {
ram.size = node["size"].natural();
if(auto mode = node["mode"].text()) {
if(mode == "lo" ) ram.bits = 0x00ff;
if(mode == "hi" ) ram.bits = 0xff00;
if(mode == "word") ram.bits = 0xffff;
}
ram.size = node["size"].natural() >> (ram.bits == 0xffff);
ram.mask = bit::round(ram.size) - 1;
if(ram.size) {
ram.data = new uint8[ram.mask + 1];
ram.data = new uint16[ram.mask + 1]();
if(auto name = node["name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Read)) {
fp->read(ram.data, ram.size);
for(uint n : range(ram.size)) {
if(ram.bits != 0xffff) ram.data[n] = fp->readm(1) * 0x0101;
if(ram.bits == 0xffff) ram.data[n] = fp->readm(2);
}
}
}
}
@ -54,7 +70,10 @@ auto Cartridge::save() -> void {
if(auto name = document["board/ram/name"].text()) {
if(auto fp = platform->open(pathID(), name, File::Write)) {
fp->write(ram.data, ram.size);
for(uint n : range(ram.size)) {
if(ram.bits != 0xffff) fp->writem(ram.data[n], 1);
if(ram.bits == 0xffff) fp->writem(ram.data[n], 2);
}
}
}
}
@ -72,22 +91,21 @@ auto Cartridge::power() -> void {
for(auto n : range(8)) bank[n] = n;
}
auto Cartridge::read(uint24 addr) -> uint16 {
if(addr.bit(21) && ram.size && ramEnable) {
uint16 data = ram.data[addr + 0 & ram.mask] << 8;
return data | ram.data[addr + 1 & ram.mask] << 0;
auto Cartridge::read(uint24 address) -> uint16 {
if(address.bit(21) && ram.size && ramEnable) {
return ram.data[address >> 1 & ram.mask];
} else {
addr = bank[addr >> 19 & 7] << 19 | (addr & 0x7ffff);
uint16 data = rom.data[addr + 0 & rom.mask] << 8;
return data | rom.data[addr + 1 & rom.mask] << 0;
address = bank[address.bits(19,21)] << 19 | address.bits(0,18);
return rom.data[address >> 1 & rom.mask];
}
}
auto Cartridge::write(uint24 addr, uint16 data) -> void {
auto Cartridge::write(uint24 address, uint16 data) -> void {
//emulating RAM write protect bit breaks some commercial software
if(addr.bit(21) && ram.size && ramEnable /* && ramWritable */) {
ram.data[addr + 0 & ram.mask] = data >> 8;
ram.data[addr + 1 & ram.mask] = data >> 0;
if(address.bit(21) && ram.size && ramEnable /* && ramWritable */) {
if(ram.bits == 0x00ff) data = data.byte(0) * 0x0101;
if(ram.bits == 0xff00) data = data.byte(1) * 0x0101;
ram.data[address >> 1 & ram.mask] = data;
}
}

View File

@ -28,9 +28,10 @@ struct Cartridge {
} information;
struct Memory {
uint8* data = nullptr;
uint16* data = nullptr;
uint size = 0;
uint mask = 0;
uint bits = 0;
};
Memory rom;

View File

@ -9,11 +9,6 @@ Interface::Interface() {
information.name = "Mega Drive";
information.overscan = true;
regions.append({"Autodetect"});
regions.append({"NTSC-J"});
regions.append({"NTSC-U"});
regions.append({"PAL"});
media.append({ID::MegaDrive, "Mega Drive", "md"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};

View File

@ -35,6 +35,7 @@ auto ARM7TDMI::armMoveToStatus(uint4 field, uint1 mode, uint32 data) -> void {
psr.t = data.bit (5);
psr.f = data.bit (6);
psr.i = data.bit (7);
if(!mode && psr.t) r(15).data += 2;
}
}

View File

@ -14,8 +14,7 @@ Settings::Settings() {
set("Library/Location", {Path::user(), "Emulation/"});
set("Library/IgnoreManifests", false);
set("Video/Driver", ruby::Video::optimalDriver());
set("Video/Driver/Crashed", false);
set("Video/Driver", ruby::Video::safestDriver());
set("Video/Synchronize", false);
set("Video/Shader", "Blur");
set("Video/BlurEmulation", true);
@ -41,8 +40,7 @@ Settings::Settings() {
set("Video/Fullscreen/IntegralScaling", true);
set("Video/Fullscreen/Exclusive", false);
set("Audio/Driver", ruby::Audio::optimalDriver());
set("Audio/Driver/Crashed", false);
set("Audio/Driver", ruby::Audio::safestDriver());
set("Audio/Device", "");
set("Audio/Frequency", 48000);
set("Audio/Latency", 0);
@ -53,11 +51,12 @@ Settings::Settings() {
set("Audio/Balance", 50);
set("Audio/Reverb/Enable", false);
set("Input/Driver", ruby::Input::optimalDriver());
set("Input/Driver/Crashed", false);
set("Input/Driver", ruby::Input::safestDriver());
set("Input/Frequency", 5);
set("Input/FocusLoss/Pause", false);
set("Input/FocusLoss/AllowInput", false);
set("Crashed", false);
}
auto Settings::save() -> void {

View File

@ -33,26 +33,22 @@ Program::Program(string_vector args) {
new Presentation;
presentation->setVisible();
if(settings["Video/Driver/Crashed"].boolean()) {
if(settings["Crashed"].boolean()) {
MessageDialog().setText("Driver crash detected. Video/Audio/Input drivers have been disabled.").information();
settings["Video/Driver"].setValue("None");
MessageDialog().setText("Video driver crash detected. Driver has been reset to 'None'").information();
settings["Audio/Driver"].setValue("None");
settings["Input/Driver"].setValue("None");
}
settings["Video/Driver/Crashed"].setValue(true);
settings["Crashed"].setValue(true);
settings.save();
video = Video::create(settings["Video/Driver"].text());
video->setContext(presentation->viewport.handle());
video->setBlocking(settings["Video/Synchronize"].boolean());
if(!video->ready()) MessageDialog().setText("Failed to initialize video driver").warning();
settings["Video/Driver/Crashed"].setValue(false);
settings.save();
presentation->clearViewport();
if(settings["Audio/Driver/Crashed"].boolean()) {
settings["Audio/Driver"].setValue("None");
MessageDialog().setText("Audio driver crash detected. Driver has been reset to 'None'").information();
}
settings["Audio/Driver/Crashed"].setValue(true);
settings.save();
audio = Audio::create(settings["Audio/Driver"].text());
audio->setExclusive(settings["Audio/Exclusive"].boolean());
audio->setContext(presentation->viewport.handle());
@ -60,20 +56,13 @@ Program::Program(string_vector args) {
audio->setBlocking(settings["Audio/Synchronize"].boolean());
audio->setChannels(2);
if(!audio->ready()) MessageDialog().setText("Failed to initialize audio driver").warning();
settings["Audio/Driver/Crashed"].setValue(false);
settings.save();
if(settings["Input/Driver/Crashed"].boolean()) {
settings["Input/Driver"].setValue("None");
MessageDialog().setText("Input driver crash detected. Driver has been reset to 'None'").information();
}
settings["Input/Driver/Crashed"].setValue(true);
settings.save();
input = Input::create(settings["Input/Driver"].text());
input->setContext(presentation->viewport.handle());
input->onChange({&InputManager::onChange, &inputManager()});
if(!input->ready()) MessageDialog().setText("Failed to initialize input driver").warning();
settings["Input/Driver/Crashed"].setValue(false);
settings["Crashed"].setValue(false);
settings.save();
new InputManager;

View File

@ -11,12 +11,13 @@ struct MegaDriveCartridge {
MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size) {
if(size < 0x200) return;
string ramMode = "none";
uint32_t ramFrom = 0;
ramFrom |= data[0x01b4] << 24;
ramFrom |= data[0x01b5] << 16;
ramFrom |= data[0x01b6] << 8;
ramFrom |= data[0x01b7] << 0;
ramFrom &= ~1; //for some reason, most games specify 00200001 as RAM start offset
uint32_t ramTo = 0;
ramTo |= data[0x01b8] << 24;
@ -24,14 +25,42 @@ MegaDriveCartridge::MegaDriveCartridge(string location, uint8_t* data, uint size
ramTo |= data[0x01ba] << 8;
ramTo |= data[0x01bb] << 0;
uint32_t ramSize = ramTo - ramFrom;
if(ramSize > 0x020000) ramSize = 0; //sanity check
ramSize = bit::round(ramSize);
if(!(ramFrom & 1) && !(ramTo & 1)) ramMode = "lo";
if( (ramFrom & 1) && (ramTo & 1)) ramMode = "hi";
if(!(ramFrom & 1) && (ramTo & 1)) ramMode = "word";
manifest.append("board\n");
uint32_t ramSize = ramTo - ramFrom + 1;
if(ramMode == "lo") ramSize = (ramTo >> 1) - (ramFrom >> 1) + 1;
if(ramMode == "hi") ramSize = (ramTo >> 1) - (ramFrom >> 1) + 1;
if(ramMode == "word") ramSize = ramTo - ramFrom + 1;
if(ramMode != "none") ramSize = bit::round(min(0x20000, ramSize));
string_vector regions;
string region = slice((const char*)&data[0x1f0], 0, 16).trimRight(" ");
if(!regions) {
if(region == "JAPAN" ) regions.append("ntsc-j");
if(region == "EUROPE") regions.append("pal");
}
if(!regions) {
if(region.find("J")) regions.append("ntsc-j");
if(region.find("U")) regions.append("ntsc-u");
if(region.find("E")) regions.append("pal");
if(region.find("W")) regions.append("ntsc-j", "ntsc-u", "pal");
}
if(!regions && region.size() == 1) {
uint8_t field = region.hex();
if(field & 0x01) regions.append("ntsc-j");
if(field & 0x04) regions.append("ntsc-u");
if(field & 0x08) regions.append("pal");
}
if(!regions) {
regions.append("ntsc-j");
}
manifest.append("board region=", regions.left(), "\n");
manifest.append(" rom name=program.rom size=0x", hex(size), "\n");
if(ramSize)
manifest.append(" ram name=save.ram size=0x", hex(ramSize), " offset=0x", hex(ramFrom), "\n");
if(ramSize && ramMode != "none")
manifest.append(" ram name=save.ram size=0x", hex(ramSize), " offset=0x", hex(ramFrom), " mode=", ramMode, "\n");
manifest.append("\n");
manifest.append("information\n");
manifest.append(" title: ", Location::prefix(location), "\n");