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);