mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-01-17 12:48:23 +01:00
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:
parent
a8f2bfc533
commit
b38a657192
@ -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/";
|
||||
|
@ -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<uint32> seed = nothing, maybe<uint32> 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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<uint16>;
|
||||
auto read() -> maybe<uint32>;
|
||||
auto write(uint2 addr, uint8 byte) -> void;
|
||||
|
||||
auto serialize(serializer& s) -> void;
|
||||
};
|
||||
|
||||
extern Player player;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
scheduler.synchronize(player);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<uint8>;
|
||||
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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -95,8 +95,7 @@ auto PPU::power() -> void {
|
||||
function<auto (uint24, uint8) -> 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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -49,7 +49,7 @@ struct InputJoypadUdev {
|
||||
set<JoypadInput> hats;
|
||||
set<JoypadInput> buttons;
|
||||
bool rumble = false;
|
||||
uint effectID = 0;
|
||||
int effectID = -1;
|
||||
};
|
||||
vector<Joypad> 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user