From b38a657192a50aa045c3fdba6424e8eaa885e415 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 25 Aug 2017 00:24:34 +1000 Subject: [PATCH] Update to v104r05 release. byuu says: Changelog: - emulator/random: new array function with more realistic RAM initializations - emulator/random: both low and high entropy register initializations now use PCG - gba/player: rumble will time out and disable after being left on for 500ms; fixes Pokemon Pinball issue - ruby/input/udev: fixed rumble effects [ma\_rysia] - sfc/system: default to low-entropy randomization of memory The low-entropy memory randomization is modeled after one of my SHVC 2/1/3 systems. It generates striped patterns in memory, using random inputs (biased to 0x00/0xff), and has a random chance of corrupting 1-2 bits of random values in the pool of memory (to prevent easy emulator detection and to match observed results on hardware.) The reasoning for using PCG on register initializations, is that I don't believe they're going to have repeating patterns like RAM does anyway. And register initializations are way more vital. I want to have the new low-entropy RAM mode tested, so at least for the next few WIPs, I've set the SNES randomization over to low-entropy. We'll have to have a long discussion and decide whether we want official releases to use high-entropy or low-entropy. Also, I figured out the cause of the Prince of Persia distortion ... I had the volume under the audio settings tab set to 200%. I didn't realize there were SNES games that clipped so easily, given how incredibly weak SNES audio is compared to every other sound source on my PC. So with no entropy or low-entropy, indeed the game now sounds just fine. I can't actually test the udev fixes, so I guess we'll see how that goes for Screwtape and ma\_rysia. --- higan/emulator/emulator.hpp | 2 +- higan/emulator/random.hpp | 136 ++++++++++-------------- higan/gba/cpu/cpu.cpp | 2 + higan/gba/player/player.cpp | 23 ++++ higan/gba/player/player.hpp | 27 +++-- higan/gba/player/serialization.cpp | 2 + higan/gba/system/system.cpp | 1 + higan/sfc/coprocessor/armdsp/armdsp.cpp | 15 +-- higan/sfc/coprocessor/armdsp/armdsp.hpp | 9 +- higan/sfc/cpu/cpu.cpp | 3 +- higan/sfc/dsp/dsp.cpp | 3 +- higan/sfc/ppu/ppu.cpp | 3 +- higan/sfc/ppu/screen/screen.cpp | 4 +- higan/sfc/system/system.cpp | 2 +- ruby/input/joypad/udev.cpp | 43 +++++--- 15 files changed, 141 insertions(+), 134 deletions(-) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 08df3032..01aa0cec 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "104.04"; + static const string Version = "104.05"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/random.hpp b/higan/emulator/random.hpp index ff02c08a..71c26569 100644 --- a/higan/emulator/random.hpp +++ b/higan/emulator/random.hpp @@ -5,110 +5,92 @@ namespace Emulator { struct Random { enum class Entropy : uint { None, Low, High }; + auto operator()() -> uint64 { + return random(); + } + auto entropy(Entropy entropy) -> void { - settings.entropy = entropy; + _entropy = entropy; seed(); } auto seed(maybe seed = nothing, maybe sequence = nothing) -> void { if(!seed) seed = (uint32)clock(); if(!sequence) sequence = 0; - seed32(seed(), sequence()); + + _state = 0; + _increment = sequence() << 1 | 1; + step(); + _state += seed(); + step(); } - auto operator()() -> uint64 { - return iterate64(); + auto random() -> uint64 { + if(_entropy == Entropy::None) return 0; + return (uint64)step() << 32 | (uint64)step() << 0; } auto bias(uint64 bias) -> uint64 { - if(settings.entropy == Entropy::None) return bias; - return operator()(); + if(_entropy == Entropy::None) return bias; + return random(); } auto bound(uint64 bound) -> uint64 { uint64 threshold = -bound % bound; while(true) { - uint64 result = iterate64(); + uint64 result = random(); if(result >= threshold) return result % bound; } } + auto array(uint8* data, uint32 size) -> void { + if(_entropy == Entropy::None) { + memory::fill(data, size); + return; + } + + if(_entropy == Entropy::High) { + for(uint32 address : range(size)) { + data[address] = random(); + } + return; + } + + //Entropy::Low + uint lobit = random() & 3; + uint hibit = (lobit + 8 + (random() & 3)) & 15; + uint lovalue = random() & 255; + uint hivalue = random() & 255; + if((random() & 3) == 0) lovalue = 0; + if((random() & 1) == 0) hivalue = ~lovalue; + + for(uint32 address : range(size)) { + uint8 value = address.bit(lobit) ? lovalue : hivalue; + if(address.bit(hibit)) value = ~value; + if((random() & 511) == 0) value.bit(random() & 7) ^= 1; + if((random() & 2047) == 0) value.bit(random() & 7) ^= 1; + data[address] = value; + } + } + auto serialize(serializer& s) -> void { - s.integer((uint&)settings.entropy); - s.integer(ram.state); - s.integer(ram.increment); - s.integer(pcg.state); - s.integer(pcg.increment); + s.integer((uint&)_entropy); + s.integer(_state); + s.integer(_increment); } private: - auto seed32(uint32 seed, uint32 sequence) -> void { - switch(settings.entropy) { - - case Entropy::None: { - break; - } - - case Entropy::Low: { - ram.state = seed; - ram.increment = 0; - break; - } - - case Entropy::High: { - pcg.state = 0; - pcg.increment = sequence << 1 | 1; - iterate32(); - pcg.state += seed; - iterate32(); - break; - } - - } + auto step() -> uint32 { + uint64 state = _state; + _state = state * 6364136223846793005ull + _increment; + uint32 xorshift = (state >> 18 ^ state) >> 27; + uint32 rotate = state >> 59; + return xorshift >> rotate | xorshift << (-rotate & 31); } - auto iterate32() -> uint32 { - switch(settings.entropy) { - - case Entropy::None: { - return 0; - } - - case Entropy::Low: { - uint64 result = 0; - if(ram.increment.bit(4 + ram.state.bits(0,1))) result = ~0; - ram.increment++; - return result; - } - - case Entropy::High: { - uint64 state = pcg.state; - pcg.state = state * 6364136223846793005ull + pcg.increment; - uint32 xorshift = (state >> 18 ^ state) >> 27; - uint32 rotate = state >> 59; - return xorshift >> rotate | xorshift << (-rotate & 31); - } - - } - } - - auto iterate64() -> uint64 { - return (uint64)iterate32() << 32 | (uint64)iterate32() << 0; - } - - struct Settings { - Entropy entropy = Entropy::High; - } settings; - - struct RAM { //Entropy::Low - uint64 state; - uint64 increment; - } ram; - - struct PCG { //Entropy::High - uint64 state; - uint64 increment; - } pcg; + Entropy _entropy = Entropy::High; + uint64 _state; + uint64 _increment; }; } diff --git a/higan/gba/cpu/cpu.cpp b/higan/gba/cpu/cpu.cpp index 4bcef922..4ec99674 100644 --- a/higan/gba/cpu/cpu.cpp +++ b/higan/gba/cpu/cpu.cpp @@ -24,6 +24,7 @@ auto CPU::main() -> void { Thread::step(16); synchronize(ppu); synchronize(apu); + synchronize(player); } context.stopped = false; } @@ -61,6 +62,7 @@ auto CPU::step(uint clocks) -> void { Thread::step(clocks); synchronize(ppu); synchronize(apu); + synchronize(player); } auto CPU::power() -> void { diff --git a/higan/gba/player/player.cpp b/higan/gba/player/player.cpp index 35e65555..bcbd4ea9 100644 --- a/higan/gba/player/player.cpp +++ b/higan/gba/player/player.cpp @@ -7,7 +7,26 @@ namespace GameBoyAdvance { Player player; #include "serialization.cpp" +auto Player::Enter() -> void { + while(true) scheduler.synchronize(), player.main(); +} + +auto Player::main() -> void { + if(status.timeout && !--status.timeout) { + platform->inputRumble(0, 0, 10, false); + } + + step(1); +} + +auto Player::step(uint clocks) -> void { + Thread::step(clocks); + synchronize(cpu); +} + auto Player::power() -> void { + create(Player::Enter, 1'000.0); + status.enable = false; status.rumble = false; @@ -17,9 +36,12 @@ auto Player::power() -> void { status.packet = 0; status.send = 0; status.recv = 0; + + status.timeout = 0; } auto Player::frame() -> void { + //todo: this is not a very performant way of detecting the GBP logo ... uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value(); status.logoDetected = (hash == 0x7776eb55); @@ -101,6 +123,7 @@ auto Player::write(uint2 addr, uint8 byte) -> void { if(addr == 3 && status.packet == 15) { status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04 platform->inputRumble(0, 0, 10, status.rumble); + if(status.rumble) status.timeout = 500; //stop rumble manually after 500ms } } diff --git a/higan/gba/player/player.hpp b/higan/gba/player/player.hpp index 8e65cd0e..0628ddde 100644 --- a/higan/gba/player/player.hpp +++ b/higan/gba/player/player.hpp @@ -1,4 +1,18 @@ -struct Player { +struct Player : Thread { + static auto Enter() -> void; + auto main() -> void; + auto step(uint clocks) -> void; + + auto power() -> void; + auto frame() -> void; + + auto keyinput() -> maybe; + auto read() -> maybe; + auto write(uint2 addr, uint8 byte) -> void; + + auto serialize(serializer& s) -> void; + +private: struct Status { bool enable; bool rumble; @@ -9,16 +23,9 @@ struct Player { uint packet; uint32 send; uint32 recv; + + uint timeout; } status; - - auto power() -> void; - auto frame() -> void; - - auto keyinput() -> maybe; - auto read() -> maybe; - auto write(uint2 addr, uint8 byte) -> void; - - auto serialize(serializer& s) -> void; }; extern Player player; diff --git a/higan/gba/player/serialization.cpp b/higan/gba/player/serialization.cpp index ba74d24e..a04a26af 100644 --- a/higan/gba/player/serialization.cpp +++ b/higan/gba/player/serialization.cpp @@ -8,4 +8,6 @@ auto Player::serialize(serializer& s) -> void { s.integer(status.packet); s.integer(status.send); s.integer(status.recv); + + s.integer(status.timeout); } diff --git a/higan/gba/system/system.cpp b/higan/gba/system/system.cpp index 57645e61..f6eb9e11 100644 --- a/higan/gba/system/system.cpp +++ b/higan/gba/system/system.cpp @@ -72,6 +72,7 @@ auto System::runToSave() -> void { scheduler.synchronize(cpu); scheduler.synchronize(ppu); scheduler.synchronize(apu); + scheduler.synchronize(player); } } diff --git a/higan/sfc/coprocessor/armdsp/armdsp.cpp b/higan/sfc/coprocessor/armdsp/armdsp.cpp index 9c4c396f..e9dabfce 100644 --- a/higan/sfc/coprocessor/armdsp/armdsp.cpp +++ b/higan/sfc/coprocessor/armdsp/armdsp.cpp @@ -6,18 +6,6 @@ namespace SuperFamicom { #include "serialization.cpp" ArmDSP armdsp; -ArmDSP::ArmDSP() { - programROM = new uint8[128 * 1024]; - dataROM = new uint8[32 * 1024]; - programRAM = new uint8[16 * 1024]; -} - -ArmDSP::~ArmDSP() { - delete[] programROM; - delete[] dataROM; - delete[] programRAM; -} - auto ArmDSP::Enter() -> void { armdsp.boot(); while(true) scheduler.synchronize(), armdsp.main(); @@ -103,8 +91,7 @@ auto ArmDSP::unload() -> void { } auto ArmDSP::power() -> void { - random.seed(); - for(auto n : range(16 * 1024)) programRAM[n] = random(); + random.array((uint8*)programRAM, sizeof(programRAM)); bridge.reset = false; reset(); } diff --git a/higan/sfc/coprocessor/armdsp/armdsp.hpp b/higan/sfc/coprocessor/armdsp/armdsp.hpp index 06826cff..428ca2a9 100644 --- a/higan/sfc/coprocessor/armdsp/armdsp.hpp +++ b/higan/sfc/coprocessor/armdsp/armdsp.hpp @@ -7,9 +7,6 @@ struct ArmDSP : Processor::ARM7TDMI, Thread { #include "registers.hpp" - ArmDSP(); - ~ArmDSP(); - static auto Enter() -> void; auto boot() -> void; auto main() -> void; @@ -31,9 +28,9 @@ struct ArmDSP : Processor::ARM7TDMI, Thread { auto firmware() const -> nall::vector; auto serialize(serializer&) -> void; - uint8* programROM; - uint8* dataROM; - uint8* programRAM; + uint8 programROM[128 * 1024]; + uint8 dataROM[32 * 1024]; + uint8 programRAM[16 * 1024]; }; extern ArmDSP armdsp; diff --git a/higan/sfc/cpu/cpu.cpp b/higan/sfc/cpu/cpu.cpp index 00a0ed63..66e280ad 100644 --- a/higan/sfc/cpu/cpu.cpp +++ b/higan/sfc/cpu/cpu.cpp @@ -86,8 +86,7 @@ auto CPU::power() -> void { bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000); bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000); - random.seed(); - for(auto& byte : wram) byte = random.bias(0x55); + random.array(wram, sizeof(wram)); //DMA for(auto& channel : this->channel) { diff --git a/higan/sfc/dsp/dsp.cpp b/higan/sfc/dsp/dsp.cpp index 44167c73..8f738572 100644 --- a/higan/sfc/dsp/dsp.cpp +++ b/higan/sfc/dsp/dsp.cpp @@ -232,8 +232,7 @@ auto DSP::power() -> void { create(Enter, system.apuFrequency()); stream = Emulator::audio.createStream(2, frequency() / 768.0); - random.seed(); - for(auto& byte : apuram) byte = random(); + random.array(apuram, sizeof(apuram)); memory::fill(&state, sizeof(State)); state.noise = 0x4000; diff --git a/higan/sfc/ppu/ppu.cpp b/higan/sfc/ppu/ppu.cpp index d4bed152..d98b771b 100644 --- a/higan/sfc/ppu/ppu.cpp +++ b/higan/sfc/ppu/ppu.cpp @@ -95,8 +95,7 @@ auto PPU::power() -> void { function void> writer{&PPU::writeIO, this}; bus.map(reader, writer, "00-3f,80-bf:2100-213f"); - random.seed(); - for(auto& n : vram.data) n = random(); + random.array((uint8*)vram.data, sizeof(vram.data)); ppu1.mdr = random.bias(0xff); ppu2.mdr = random.bias(0xff); diff --git a/higan/sfc/ppu/screen/screen.cpp b/higan/sfc/ppu/screen/screen.cpp index 769dba2f..193b0c3b 100644 --- a/higan/sfc/ppu/screen/screen.cpp +++ b/higan/sfc/ppu/screen/screen.cpp @@ -161,8 +161,8 @@ auto PPU::Screen::fixedColor() const -> uint15 { } auto PPU::Screen::power() -> void { - random.seed(); - for(auto& n : cgram) n = random(); + random.array((uint8*)cgram, sizeof(cgram)); + for(auto& word : cgram) word &= 0x7fff; io.blendMode = random(); io.directColor = random(); diff --git a/higan/sfc/system/system.cpp b/higan/sfc/system/system.cpp index 62c8c3e6..c965d882 100644 --- a/higan/sfc/system/system.cpp +++ b/higan/sfc/system/system.cpp @@ -141,7 +141,7 @@ auto System::power() -> void { Emulator::audio.reset(); Emulator::audio.setInterface(interface); - random.entropy(Random::Entropy::High); + random.entropy(Random::Entropy::Low); scheduler.reset(); cpu.power(); diff --git a/ruby/input/joypad/udev.cpp b/ruby/input/joypad/udev.cpp index 580c6270..3cfe9a05 100644 --- a/ruby/input/joypad/udev.cpp +++ b/ruby/input/joypad/udev.cpp @@ -49,7 +49,7 @@ struct InputJoypadUdev { set hats; set buttons; bool rumble = false; - uint effectID = 0; + int effectID = -1; }; vector joypads; @@ -102,12 +102,31 @@ struct InputJoypadUdev { if(jp.hid->id() != id) continue; if(!jp.hid->rumble()) continue; - input_event play; - memset(&play, 0, sizeof(input_event)); - play.type = EV_FF; - play.code = jp.effectID; - play.value = enable; - auto unused = write(jp.fd, &play, sizeof(input_event)); + if(!enable) { + if(jp.effectID == -1) return true; //already stopped? + + ioctl(jp.fd, EVIOCRMFF, jp.effectID); + jp.effectID = -1; + } else { + if(jp.effectID != -1) return true; //already started? + + ff_effect effect; + memory::fill(&effect, sizeof(ff_effect)); + effect.type = FF_RUMBLE; + effect.id = -1; + effect.u.rumble.strong_magnitude = 65535; + effect.u.rumble.weak_magnitude = 65535; + ioctl(jp.fd, EVIOCSFF, &effect); + jp.effectID = effect.id; + + input_event play; + memory::fill(&play, sizeof(input_event)); + play.type = EV_FF; + play.code = jp.effectID; + play.value = enable; + auto unused = write(jp.fd, &play, sizeof(input_event)); + } + return true; } @@ -237,16 +256,6 @@ private: } } jp.rumble = jp.effects >= 2 && testBit(jp.ffbit, FF_RUMBLE); - if(jp.rumble) { - ff_effect effect; - memset(&effect, 0, sizeof(ff_effect)); - effect.type = FF_RUMBLE; - effect.id = -1; - effect.u.rumble.strong_magnitude = 65535; - effect.u.rumble.weak_magnitude = 65535; - ioctl(jp.fd, EVIOCSFF, &effect); - jp.effectID = effect.id; - } createJoypadHID(jp); joypads.append(jp);