From ba384a7c48f315994b34187f05bd3af31a99461e Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 12 Aug 2017 20:53:13 +1000 Subject: [PATCH] Update to v104 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- higan/emulator/emulator.hpp | 4 +- higan/emulator/interface.hpp | 5 -- higan/gba/cpu/cpu.cpp | 28 ++++++---- higan/gba/cpu/cpu.hpp | 10 ++-- higan/gba/cpu/dma.cpp | 2 +- higan/gba/cpu/timer.cpp | 3 +- higan/md/cartridge/cartridge.cpp | 56 ++++++++++++------- higan/md/cartridge/cartridge.hpp | 3 +- higan/md/interface/interface.cpp | 5 -- higan/processor/arm7tdmi/instructions-arm.cpp | 1 + .../configuration/configuration.cpp | 11 ++-- higan/target-tomoko/program/program.cpp | 29 +++------- icarus/heuristics/mega-drive.cpp | 43 +++++++++++--- 13 files changed, 116 insertions(+), 84 deletions(-) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 936ec043..fa5073cf 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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 { diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 17645885..9c294291 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -9,11 +9,6 @@ struct Interface { bool overscan; } information; - struct Region { - string name; - }; - vector regions; - struct Medium { uint id; string name; diff --git a/higan/gba/cpu/cpu.cpp b/higan/gba/cpu/cpu.cpp index be1f35fb..495af66a 100644 --- a/higan/gba/cpu/cpu.cpp +++ b/higan/gba/cpu/cpu.cpp @@ -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++; } diff --git a/higan/gba/cpu/cpu.hpp b/higan/gba/cpu/cpu.hpp index c276b60b..a8bf79ad 100644 --- a/higan/gba/cpu/cpu.hpp +++ b/higan/gba/cpu/cpu.hpp @@ -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 { diff --git a/higan/gba/cpu/dma.cpp b/higan/gba/cpu/dma.cpp index 2fe7a8e2..6ae1dfa1 100644 --- a/higan/gba/cpu/dma.cpp +++ b/higan/gba/cpu/dma.cpp @@ -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; diff --git a/higan/gba/cpu/timer.cpp b/higan/gba/cpu/timer.cpp index a75ca137..f38678ab 100644 --- a/higan/gba/cpu/timer.cpp +++ b/higan/gba/cpu/timer.cpp @@ -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; diff --git a/higan/md/cartridge/cartridge.cpp b/higan/md/cartridge/cartridge.cpp index c51ab4d1..65894d1d 100644 --- a/higan/md/cartridge/cartridge.cpp +++ b/higan/md/cartridge/cartridge.cpp @@ -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; } } diff --git a/higan/md/cartridge/cartridge.hpp b/higan/md/cartridge/cartridge.hpp index d3705c95..22d2bea4 100644 --- a/higan/md/cartridge/cartridge.hpp +++ b/higan/md/cartridge/cartridge.hpp @@ -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; diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index ca7860f2..afef5658 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -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"}; diff --git a/higan/processor/arm7tdmi/instructions-arm.cpp b/higan/processor/arm7tdmi/instructions-arm.cpp index dadfb3b6..470fae3e 100644 --- a/higan/processor/arm7tdmi/instructions-arm.cpp +++ b/higan/processor/arm7tdmi/instructions-arm.cpp @@ -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; } } diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index ece53405..b5f58fb7 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -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 { diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 9f88e643..b1665457 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -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; diff --git a/icarus/heuristics/mega-drive.cpp b/icarus/heuristics/mega-drive.cpp index 24baf7c3..0072f88b 100644 --- a/icarus/heuristics/mega-drive.cpp +++ b/icarus/heuristics/mega-drive.cpp @@ -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");