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.
This commit is contained in:
Tim Allen 2017-08-25 00:24:34 +10:00
parent a8f2bfc533
commit b38a657192
15 changed files with 141 additions and 134 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -5,110 +5,92 @@ namespace Emulator {
struct Random { struct Random {
enum class Entropy : uint { None, Low, High }; enum class Entropy : uint { None, Low, High };
auto operator()() -> uint64 {
return random();
}
auto entropy(Entropy entropy) -> void { auto entropy(Entropy entropy) -> void {
settings.entropy = entropy; _entropy = entropy;
seed(); seed();
} }
auto seed(maybe<uint32> seed = nothing, maybe<uint32> sequence = nothing) -> void { auto seed(maybe<uint32> seed = nothing, maybe<uint32> sequence = nothing) -> void {
if(!seed) seed = (uint32)clock(); if(!seed) seed = (uint32)clock();
if(!sequence) sequence = 0; if(!sequence) sequence = 0;
seed32(seed(), sequence());
_state = 0;
_increment = sequence() << 1 | 1;
step();
_state += seed();
step();
} }
auto operator()() -> uint64 { auto random() -> uint64 {
return iterate64(); if(_entropy == Entropy::None) return 0;
return (uint64)step() << 32 | (uint64)step() << 0;
} }
auto bias(uint64 bias) -> uint64 { auto bias(uint64 bias) -> uint64 {
if(settings.entropy == Entropy::None) return bias; if(_entropy == Entropy::None) return bias;
return operator()(); return random();
} }
auto bound(uint64 bound) -> uint64 { auto bound(uint64 bound) -> uint64 {
uint64 threshold = -bound % bound; uint64 threshold = -bound % bound;
while(true) { while(true) {
uint64 result = iterate64(); uint64 result = random();
if(result >= threshold) return result % bound; 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 { auto serialize(serializer& s) -> void {
s.integer((uint&)settings.entropy); s.integer((uint&)_entropy);
s.integer(ram.state); s.integer(_state);
s.integer(ram.increment); s.integer(_increment);
s.integer(pcg.state);
s.integer(pcg.increment);
} }
private: private:
auto seed32(uint32 seed, uint32 sequence) -> void { auto step() -> uint32 {
switch(settings.entropy) { uint64 state = _state;
_state = state * 6364136223846793005ull + _increment;
case Entropy::None: { uint32 xorshift = (state >> 18 ^ state) >> 27;
break; uint32 rotate = state >> 59;
} return xorshift >> rotate | xorshift << (-rotate & 31);
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 iterate32() -> uint32 { Entropy _entropy = Entropy::High;
switch(settings.entropy) { uint64 _state;
uint64 _increment;
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;
}; };
} }

View File

@ -24,6 +24,7 @@ auto CPU::main() -> void {
Thread::step(16); Thread::step(16);
synchronize(ppu); synchronize(ppu);
synchronize(apu); synchronize(apu);
synchronize(player);
} }
context.stopped = false; context.stopped = false;
} }
@ -61,6 +62,7 @@ auto CPU::step(uint clocks) -> void {
Thread::step(clocks); Thread::step(clocks);
synchronize(ppu); synchronize(ppu);
synchronize(apu); synchronize(apu);
synchronize(player);
} }
auto CPU::power() -> void { auto CPU::power() -> void {

View File

@ -7,7 +7,26 @@ namespace GameBoyAdvance {
Player player; Player player;
#include "serialization.cpp" #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 { auto Player::power() -> void {
create(Player::Enter, 1'000.0);
status.enable = false; status.enable = false;
status.rumble = false; status.rumble = false;
@ -17,9 +36,12 @@ auto Player::power() -> void {
status.packet = 0; status.packet = 0;
status.send = 0; status.send = 0;
status.recv = 0; status.recv = 0;
status.timeout = 0;
} }
auto Player::frame() -> void { 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(); uint32 hash = Hash::CRC32(ppu.output, 240 * 160 * sizeof(uint32)).value();
status.logoDetected = (hash == 0x7776eb55); status.logoDetected = (hash == 0x7776eb55);
@ -101,6 +123,7 @@ auto Player::write(uint2 addr, uint8 byte) -> void {
if(addr == 3 && status.packet == 15) { if(addr == 3 && status.packet == 15) {
status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04 status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04
platform->inputRumble(0, 0, 10, status.rumble); platform->inputRumble(0, 0, 10, status.rumble);
if(status.rumble) status.timeout = 500; //stop rumble manually after 500ms
} }
} }

View File

@ -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<uint16>;
auto read() -> maybe<uint32>;
auto write(uint2 addr, uint8 byte) -> void;
auto serialize(serializer& s) -> void;
private:
struct Status { struct Status {
bool enable; bool enable;
bool rumble; bool rumble;
@ -9,16 +23,9 @@ struct Player {
uint packet; uint packet;
uint32 send; uint32 send;
uint32 recv; uint32 recv;
uint timeout;
} status; } status;
auto power() -> void;
auto frame() -> void;
auto keyinput() -> maybe<uint16>;
auto read() -> maybe<uint32>;
auto write(uint2 addr, uint8 byte) -> void;
auto serialize(serializer& s) -> void;
}; };
extern Player player; extern Player player;

View File

@ -8,4 +8,6 @@ auto Player::serialize(serializer& s) -> void {
s.integer(status.packet); s.integer(status.packet);
s.integer(status.send); s.integer(status.send);
s.integer(status.recv); s.integer(status.recv);
s.integer(status.timeout);
} }

View File

@ -72,6 +72,7 @@ auto System::runToSave() -> void {
scheduler.synchronize(cpu); scheduler.synchronize(cpu);
scheduler.synchronize(ppu); scheduler.synchronize(ppu);
scheduler.synchronize(apu); scheduler.synchronize(apu);
scheduler.synchronize(player);
} }
} }

View File

@ -6,18 +6,6 @@ namespace SuperFamicom {
#include "serialization.cpp" #include "serialization.cpp"
ArmDSP armdsp; 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 { auto ArmDSP::Enter() -> void {
armdsp.boot(); armdsp.boot();
while(true) scheduler.synchronize(), armdsp.main(); while(true) scheduler.synchronize(), armdsp.main();
@ -103,8 +91,7 @@ auto ArmDSP::unload() -> void {
} }
auto ArmDSP::power() -> void { auto ArmDSP::power() -> void {
random.seed(); random.array((uint8*)programRAM, sizeof(programRAM));
for(auto n : range(16 * 1024)) programRAM[n] = random();
bridge.reset = false; bridge.reset = false;
reset(); reset();
} }

View File

@ -7,9 +7,6 @@
struct ArmDSP : Processor::ARM7TDMI, Thread { struct ArmDSP : Processor::ARM7TDMI, Thread {
#include "registers.hpp" #include "registers.hpp"
ArmDSP();
~ArmDSP();
static auto Enter() -> void; static auto Enter() -> void;
auto boot() -> void; auto boot() -> void;
auto main() -> void; auto main() -> void;
@ -31,9 +28,9 @@ struct ArmDSP : Processor::ARM7TDMI, Thread {
auto firmware() const -> nall::vector<uint8>; auto firmware() const -> nall::vector<uint8>;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
uint8* programROM; uint8 programROM[128 * 1024];
uint8* dataROM; uint8 dataROM[32 * 1024];
uint8* programRAM; uint8 programRAM[16 * 1024];
}; };
extern ArmDSP armdsp; extern ArmDSP armdsp;

View File

@ -86,8 +86,7 @@ auto CPU::power() -> void {
bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000); bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000);
bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000); bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000);
random.seed(); random.array(wram, sizeof(wram));
for(auto& byte : wram) byte = random.bias(0x55);
//DMA //DMA
for(auto& channel : this->channel) { for(auto& channel : this->channel) {

View File

@ -232,8 +232,7 @@ auto DSP::power() -> void {
create(Enter, system.apuFrequency()); create(Enter, system.apuFrequency());
stream = Emulator::audio.createStream(2, frequency() / 768.0); stream = Emulator::audio.createStream(2, frequency() / 768.0);
random.seed(); random.array(apuram, sizeof(apuram));
for(auto& byte : apuram) byte = random();
memory::fill(&state, sizeof(State)); memory::fill(&state, sizeof(State));
state.noise = 0x4000; state.noise = 0x4000;

View File

@ -95,8 +95,7 @@ auto PPU::power() -> void {
function<auto (uint24, uint8) -> void> writer{&PPU::writeIO, this}; function<auto (uint24, uint8) -> void> writer{&PPU::writeIO, this};
bus.map(reader, writer, "00-3f,80-bf:2100-213f"); bus.map(reader, writer, "00-3f,80-bf:2100-213f");
random.seed(); random.array((uint8*)vram.data, sizeof(vram.data));
for(auto& n : vram.data) n = random();
ppu1.mdr = random.bias(0xff); ppu1.mdr = random.bias(0xff);
ppu2.mdr = random.bias(0xff); ppu2.mdr = random.bias(0xff);

View File

@ -161,8 +161,8 @@ auto PPU::Screen::fixedColor() const -> uint15 {
} }
auto PPU::Screen::power() -> void { auto PPU::Screen::power() -> void {
random.seed(); random.array((uint8*)cgram, sizeof(cgram));
for(auto& n : cgram) n = random(); for(auto& word : cgram) word &= 0x7fff;
io.blendMode = random(); io.blendMode = random();
io.directColor = random(); io.directColor = random();

View File

@ -141,7 +141,7 @@ auto System::power() -> void {
Emulator::audio.reset(); Emulator::audio.reset();
Emulator::audio.setInterface(interface); Emulator::audio.setInterface(interface);
random.entropy(Random::Entropy::High); random.entropy(Random::Entropy::Low);
scheduler.reset(); scheduler.reset();
cpu.power(); cpu.power();

View File

@ -49,7 +49,7 @@ struct InputJoypadUdev {
set<JoypadInput> hats; set<JoypadInput> hats;
set<JoypadInput> buttons; set<JoypadInput> buttons;
bool rumble = false; bool rumble = false;
uint effectID = 0; int effectID = -1;
}; };
vector<Joypad> joypads; vector<Joypad> joypads;
@ -102,12 +102,31 @@ struct InputJoypadUdev {
if(jp.hid->id() != id) continue; if(jp.hid->id() != id) continue;
if(!jp.hid->rumble()) continue; if(!jp.hid->rumble()) continue;
input_event play; if(!enable) {
memset(&play, 0, sizeof(input_event)); if(jp.effectID == -1) return true; //already stopped?
play.type = EV_FF;
play.code = jp.effectID; ioctl(jp.fd, EVIOCRMFF, jp.effectID);
play.value = enable; jp.effectID = -1;
auto unused = write(jp.fd, &play, sizeof(input_event)); } 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; return true;
} }
@ -237,16 +256,6 @@ private:
} }
} }
jp.rumble = jp.effects >= 2 && testBit(jp.ffbit, FF_RUMBLE); 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); createJoypadHID(jp);
joypads.append(jp); joypads.append(jp);