mirror of https://github.com/bsnes-emu/bsnes.git
Update to v104r04 release.
byuu says: Changelog: - higan/emulator: added new Random class with three entropy settings: none, low, and high - md/vdp: corrected Vcounter readout in interlace mode [MoD] - sfc: updated core to use the new Random class; defaults to high entropy No entropy essentially returns 0, unless the random.bias(n) function is called, in which case, it returns n. In this case, n is meant to be the "logical/ideal" default value that maximizes compatibility with games. Low entropy is a very simple entropy modeled after RAM initialization striping patterns (eg 32 0x00s, followed by 32 0xFFs, repeating throughout.) It doesn't "glitch" like real hardware does on rare occasions (parts of the pattern being broken from time to time.) It also only really returns 0 or ~0. So the entropy is indeed extremely low, and not very useful at all for detecting bugs. Over time, we can try to improve this, of course. High entropy is PCG. This replaces the older, lower-entropy and more predictable, LFSR. PCG should be more than enough for emulator randomness, while still being quite fast. Unfortunately, the bad news ... both no entropy and low entropy fix the Konami logo popping sound in Prince of Persia, but all three entropy settings still cause the distortion in-game, especially evident at the title screen. So ... this may be a more serious bug than first suspected.
This commit is contained in:
parent
c0934b826c
commit
d621136d69
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "104.03";
|
||||
static const string Version = "104.04";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Random {
|
||||
enum class Entropy : uint { None, Low, High };
|
||||
|
||||
auto entropy(Entropy entropy) -> void {
|
||||
settings.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());
|
||||
}
|
||||
|
||||
auto operator()() -> uint64 {
|
||||
return iterate64();
|
||||
}
|
||||
|
||||
auto bias(uint64 bias) -> uint64 {
|
||||
if(settings.entropy == Entropy::None) return bias;
|
||||
return operator()();
|
||||
}
|
||||
|
||||
auto bound(uint64 bound) -> uint64 {
|
||||
uint64 threshold = -bound % bound;
|
||||
while(true) {
|
||||
uint64 result = iterate64();
|
||||
if(result >= threshold) return result % bound;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -14,8 +14,8 @@ auto VDP::read(uint24 addr) -> uint16 {
|
|||
//counter
|
||||
case 0xc00008: case 0xc0000a: case 0xc0000c: case 0xc0000e: {
|
||||
auto vcounter = state.vcounter;
|
||||
if(io.interlaceMode == 3) {
|
||||
vcounter = vcounter << 1 | state.field; //todo: unverified
|
||||
if(io.interlaceMode.bit(0)) {
|
||||
if(io.interlaceMode.bit(1)) vcounter <<= 1;
|
||||
vcounter.bit(0) = vcounter.bit(8);
|
||||
}
|
||||
return vcounter << 8 | (state.hdot >> 1) << 0;
|
||||
|
|
|
@ -103,7 +103,8 @@ auto ArmDSP::unload() -> void {
|
|||
}
|
||||
|
||||
auto ArmDSP::power() -> void {
|
||||
for(auto n : range(16 * 1024)) programRAM[n] = random(0x00);
|
||||
random.seed();
|
||||
for(auto n : range(16 * 1024)) programRAM[n] = random();
|
||||
bridge.reset = false;
|
||||
reset();
|
||||
}
|
||||
|
|
|
@ -86,7 +86,8 @@ auto CPU::power() -> void {
|
|||
bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000);
|
||||
bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000);
|
||||
|
||||
for(auto& byte : wram) byte = random(0x55);
|
||||
random.seed();
|
||||
for(auto& byte : wram) byte = random.bias(0x55);
|
||||
|
||||
//DMA
|
||||
for(auto& channel : this->channel) {
|
||||
|
|
|
@ -232,7 +232,8 @@ auto DSP::power() -> void {
|
|||
create(Enter, system.apuFrequency());
|
||||
stream = Emulator::audio.createStream(2, frequency() / 768.0);
|
||||
|
||||
for(auto& byte : apuram) byte = random(0x00);
|
||||
random.seed();
|
||||
for(auto& byte : apuram) byte = random();
|
||||
|
||||
memory::fill(&state, sizeof(State));
|
||||
state.noise = 0x4000;
|
||||
|
|
|
@ -207,17 +207,17 @@ auto PPU::Background::getTileColor() -> uint {
|
|||
}
|
||||
|
||||
auto PPU::Background::power() -> void {
|
||||
io.tiledataAddress = (random(0x0000) & 0x0f) << 12;
|
||||
io.screenAddress = (random(0x0000) & 0xfc) << 8;
|
||||
io.screenSize = random(0);
|
||||
io.mosaic = random(0);
|
||||
io.tileSize = random(0);
|
||||
io.tiledataAddress = (random() & 0x0f) << 12;
|
||||
io.screenAddress = (random() & 0xfc) << 8;
|
||||
io.screenSize = random();
|
||||
io.mosaic = random();
|
||||
io.tileSize = random();
|
||||
io.mode = 0;
|
||||
for(auto& p : io.priority) p = 0;
|
||||
io.aboveEnable = random(0);
|
||||
io.belowEnable = random(0);
|
||||
io.hoffset = random(0x0000);
|
||||
io.voffset = random(0x0000);
|
||||
io.aboveEnable = random();
|
||||
io.belowEnable = random();
|
||||
io.hoffset = random();
|
||||
io.voffset = random();
|
||||
|
||||
latch.hoffset = 0;
|
||||
latch.voffset = 0;
|
||||
|
|
|
@ -189,13 +189,13 @@ auto PPU::Object::power() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
io.aboveEnable = random(false);
|
||||
io.belowEnable = random(false);
|
||||
io.interlace = random(false);
|
||||
io.aboveEnable = random();
|
||||
io.belowEnable = random();
|
||||
io.interlace = random();
|
||||
|
||||
io.baseSize = random(0);
|
||||
io.nameselect = random(0);
|
||||
io.tiledataAddress = (random(0x0000) & 7) << 13;
|
||||
io.baseSize = random();
|
||||
io.nameselect = random();
|
||||
io.tiledataAddress = (random() & 7) << 13;
|
||||
io.firstSprite = 0;
|
||||
|
||||
for(auto& p : io.priority) p = 0;
|
||||
|
|
|
@ -95,17 +95,18 @@ auto PPU::power() -> void {
|
|||
function<auto (uint24, uint8) -> void> writer{&PPU::writeIO, this};
|
||||
bus.map(reader, writer, "00-3f,80-bf:2100-213f");
|
||||
|
||||
for(auto& n : vram.data) n = random(0x0000);
|
||||
random.seed();
|
||||
for(auto& n : vram.data) n = random();
|
||||
|
||||
ppu1.mdr = random(0xff);
|
||||
ppu2.mdr = random(0xff);
|
||||
ppu1.mdr = random.bias(0xff);
|
||||
ppu2.mdr = random.bias(0xff);
|
||||
|
||||
latch.vram = random(0x0000);
|
||||
latch.oam = random(0x00);
|
||||
latch.cgram = random(0x00);
|
||||
latch.bgofsPPU1 = random(0x00);
|
||||
latch.bgofsPPU2 = random(0x00);
|
||||
latch.mode7 = random(0x00);
|
||||
latch.vram = random();
|
||||
latch.oam = random();
|
||||
latch.cgram = random();
|
||||
latch.bgofsPPU1 = random();
|
||||
latch.bgofsPPU2 = random();
|
||||
latch.mode7 = random();
|
||||
latch.counters = false;
|
||||
latch.hcounter = 0;
|
||||
latch.vcounter = 0;
|
||||
|
@ -119,59 +120,59 @@ auto PPU::power() -> void {
|
|||
|
||||
//$2102 OAMADDL
|
||||
//$2103 OAMADDH
|
||||
io.oamBaseAddress = random(0x0000);
|
||||
io.oamAddress = random(0x0000);
|
||||
io.oamPriority = random(false);
|
||||
io.oamBaseAddress = random();
|
||||
io.oamAddress = random();
|
||||
io.oamPriority = random();
|
||||
|
||||
//$2105 BGMODE
|
||||
io.bgPriority = false;
|
||||
io.bgMode = 0;
|
||||
|
||||
//$210d BG1HOFS
|
||||
io.hoffsetMode7 = random(0x0000);
|
||||
io.hoffsetMode7 = random();
|
||||
|
||||
//$210e BG1VOFS
|
||||
io.voffsetMode7 = random(0x0000);
|
||||
io.voffsetMode7 = random();
|
||||
|
||||
//$2115 VMAIN
|
||||
io.vramIncrementMode = random(1);
|
||||
io.vramMapping = random(0);
|
||||
io.vramIncrementMode = random.bias(1);
|
||||
io.vramMapping = random();
|
||||
io.vramIncrementSize = 1;
|
||||
|
||||
//$2116 VMADDL
|
||||
//$2117 VMADDH
|
||||
io.vramAddress = random(0x0000);
|
||||
io.vramAddress = random();
|
||||
|
||||
//$211a M7SEL
|
||||
io.repeatMode7 = random(0);
|
||||
io.vflipMode7 = random(false);
|
||||
io.hflipMode7 = random(false);
|
||||
io.repeatMode7 = random();
|
||||
io.vflipMode7 = random();
|
||||
io.hflipMode7 = random();
|
||||
|
||||
//$211b M7A
|
||||
io.m7a = random(0x0000);
|
||||
io.m7a = random();
|
||||
|
||||
//$211c M7B
|
||||
io.m7b = random(0x0000);
|
||||
io.m7b = random();
|
||||
|
||||
//$211d M7C
|
||||
io.m7c = random(0x0000);
|
||||
io.m7c = random();
|
||||
|
||||
//$211e M7D
|
||||
io.m7d = random(0x0000);
|
||||
io.m7d = random();
|
||||
|
||||
//$211f M7X
|
||||
io.m7x = random(0x0000);
|
||||
io.m7x = random();
|
||||
|
||||
//$2120 M7Y
|
||||
io.m7y = random(0x0000);
|
||||
io.m7y = random();
|
||||
|
||||
//$2121 CGADD
|
||||
io.cgramAddress = random(0x00);
|
||||
io.cgramAddressLatch = random(0);
|
||||
io.cgramAddress = random();
|
||||
io.cgramAddressLatch = random();
|
||||
|
||||
//$2133 SETINI
|
||||
io.extbg = random(false);
|
||||
io.pseudoHires = random(false);
|
||||
io.extbg = random();
|
||||
io.pseudoHires = random();
|
||||
io.overscan = false;
|
||||
io.interlace = false;
|
||||
|
||||
|
|
|
@ -161,19 +161,20 @@ auto PPU::Screen::fixedColor() const -> uint15 {
|
|||
}
|
||||
|
||||
auto PPU::Screen::power() -> void {
|
||||
for(auto& n : cgram) n = random(0x0000);
|
||||
random.seed();
|
||||
for(auto& n : cgram) n = random();
|
||||
|
||||
io.blendMode = random(false);
|
||||
io.directColor = random(false);
|
||||
io.colorMode = random(false);
|
||||
io.colorHalve = random(false);
|
||||
io.bg1.colorEnable = random(false);
|
||||
io.bg2.colorEnable = random(false);
|
||||
io.bg3.colorEnable = random(false);
|
||||
io.bg4.colorEnable = random(false);
|
||||
io.obj.colorEnable = random(false);
|
||||
io.back.colorEnable = random(false);
|
||||
io.colorBlue = random(0);
|
||||
io.colorGreen = random(0);
|
||||
io.colorRed = random(0);
|
||||
io.blendMode = random();
|
||||
io.directColor = random();
|
||||
io.colorMode = random();
|
||||
io.colorHalve = random();
|
||||
io.bg1.colorEnable = random();
|
||||
io.bg2.colorEnable = random();
|
||||
io.bg3.colorEnable = random();
|
||||
io.bg4.colorEnable = random();
|
||||
io.obj.colorEnable = random();
|
||||
io.back.colorEnable = random();
|
||||
io.colorBlue = random();
|
||||
io.colorGreen = random();
|
||||
io.colorRed = random();
|
||||
}
|
||||
|
|
|
@ -47,58 +47,58 @@ auto PPU::Window::test(bool oneEnable, bool one, bool twoEnable, bool two, uint
|
|||
}
|
||||
|
||||
auto PPU::Window::power() -> void {
|
||||
io.bg1.oneEnable = random(false);
|
||||
io.bg1.oneInvert = random(false);
|
||||
io.bg1.twoEnable = random(false);
|
||||
io.bg1.twoInvert = random(false);
|
||||
io.bg1.mask = random(0);
|
||||
io.bg1.aboveEnable = random(false);
|
||||
io.bg1.belowEnable = random(false);
|
||||
io.bg1.oneEnable = random();
|
||||
io.bg1.oneInvert = random();
|
||||
io.bg1.twoEnable = random();
|
||||
io.bg1.twoInvert = random();
|
||||
io.bg1.mask = random();
|
||||
io.bg1.aboveEnable = random();
|
||||
io.bg1.belowEnable = random();
|
||||
|
||||
io.bg2.oneEnable = random(false);
|
||||
io.bg2.oneInvert = random(false);
|
||||
io.bg2.twoEnable = random(false);
|
||||
io.bg2.twoInvert = random(false);
|
||||
io.bg2.mask = random(0);
|
||||
io.bg2.aboveEnable = random(false);
|
||||
io.bg2.belowEnable = random(false);
|
||||
io.bg2.oneEnable = random();
|
||||
io.bg2.oneInvert = random();
|
||||
io.bg2.twoEnable = random();
|
||||
io.bg2.twoInvert = random();
|
||||
io.bg2.mask = random();
|
||||
io.bg2.aboveEnable = random();
|
||||
io.bg2.belowEnable = random();
|
||||
|
||||
io.bg3.oneEnable = random(false);
|
||||
io.bg3.oneInvert = random(false);
|
||||
io.bg3.twoEnable = random(false);
|
||||
io.bg3.twoInvert = random(false);
|
||||
io.bg3.mask = random(0);
|
||||
io.bg3.aboveEnable = random(false);
|
||||
io.bg3.belowEnable = random(false);
|
||||
io.bg3.oneEnable = random();
|
||||
io.bg3.oneInvert = random();
|
||||
io.bg3.twoEnable = random();
|
||||
io.bg3.twoInvert = random();
|
||||
io.bg3.mask = random();
|
||||
io.bg3.aboveEnable = random();
|
||||
io.bg3.belowEnable = random();
|
||||
|
||||
io.bg4.oneEnable = random(false);
|
||||
io.bg4.oneInvert = random(false);
|
||||
io.bg4.twoEnable = random(false);
|
||||
io.bg4.twoInvert = random(false);
|
||||
io.bg4.mask = random(0);
|
||||
io.bg4.aboveEnable = random(false);
|
||||
io.bg4.belowEnable = random(false);
|
||||
io.bg4.oneEnable = random();
|
||||
io.bg4.oneInvert = random();
|
||||
io.bg4.twoEnable = random();
|
||||
io.bg4.twoInvert = random();
|
||||
io.bg4.mask = random();
|
||||
io.bg4.aboveEnable = random();
|
||||
io.bg4.belowEnable = random();
|
||||
|
||||
io.obj.oneEnable = random(false);
|
||||
io.obj.oneInvert = random(false);
|
||||
io.obj.twoEnable = random(false);
|
||||
io.obj.twoInvert = random(false);
|
||||
io.obj.mask = random(0);
|
||||
io.obj.aboveEnable = random(false);
|
||||
io.obj.belowEnable = random(false);
|
||||
io.obj.oneEnable = random();
|
||||
io.obj.oneInvert = random();
|
||||
io.obj.twoEnable = random();
|
||||
io.obj.twoInvert = random();
|
||||
io.obj.mask = random();
|
||||
io.obj.aboveEnable = random();
|
||||
io.obj.belowEnable = random();
|
||||
|
||||
io.col.oneEnable = random(false);
|
||||
io.col.oneInvert = random(false);
|
||||
io.col.twoEnable = random(false);
|
||||
io.col.twoInvert = random(false);
|
||||
io.col.mask = random(0);
|
||||
io.col.aboveMask = random(0);
|
||||
io.col.belowMask = random(0);
|
||||
io.col.oneEnable = random();
|
||||
io.col.oneInvert = random();
|
||||
io.col.twoEnable = random();
|
||||
io.col.twoInvert = random();
|
||||
io.col.mask = random();
|
||||
io.col.aboveMask = random();
|
||||
io.col.belowMask = random();
|
||||
|
||||
io.oneLeft = random(0x00);
|
||||
io.oneRight = random(0x00);
|
||||
io.twoLeft = random(0x00);
|
||||
io.twoRight = random(0x00);
|
||||
io.oneLeft = random();
|
||||
io.oneRight = random();
|
||||
io.twoLeft = random();
|
||||
io.twoRight = random();
|
||||
|
||||
output.above.colorEnable = 0;
|
||||
output.below.colorEnable = 0;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/random.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/arm7tdmi/arm7tdmi.hpp>
|
||||
|
@ -23,8 +24,10 @@ namespace SuperFamicom {
|
|||
#define platform Emulator::platform
|
||||
namespace File = Emulator::File;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Random = Emulator::Random;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Random random;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Thread : Emulator::Thread {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
Random random;
|
||||
|
||||
auto Random::seed(uint seed) -> void {
|
||||
iter = seed;
|
||||
}
|
||||
|
||||
auto Random::operator()(uint result) -> uint {
|
||||
if(!settings.random) return result;
|
||||
return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320);
|
||||
}
|
||||
|
||||
auto Random::serialize(serializer& s) -> void {
|
||||
s.integer(iter);
|
||||
}
|
|
@ -42,9 +42,9 @@ auto System::serialize(serializer& s) -> void {
|
|||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
random.serialize(s);
|
||||
cartridge.serialize(s);
|
||||
system.serialize(s);
|
||||
random.serialize(s);
|
||||
cpu.serialize(s);
|
||||
smp.serialize(s);
|
||||
ppu.serialize(s);
|
||||
|
|
|
@ -4,9 +4,9 @@ namespace SuperFamicom {
|
|||
|
||||
System system;
|
||||
Scheduler scheduler;
|
||||
Random random;
|
||||
Cheat cheat;
|
||||
#include "video.cpp"
|
||||
#include "random.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto System::run() -> void {
|
||||
|
@ -141,7 +141,7 @@ auto System::power() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
random.seed((uint)time(0));
|
||||
random.entropy(Random::Entropy::High);
|
||||
|
||||
scheduler.reset();
|
||||
cpu.power();
|
||||
|
|
|
@ -44,17 +44,7 @@ private:
|
|||
friend class Cartridge;
|
||||
};
|
||||
|
||||
struct Random {
|
||||
auto seed(uint seed) -> void;
|
||||
auto operator()(uint result) -> uint;
|
||||
auto serialize(serializer& s) -> void;
|
||||
|
||||
private:
|
||||
uint iter = 0;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
extern Random random;
|
||||
|
||||
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }
|
||||
auto Region::PAL() -> bool { return system.region() == System::Region::PAL; }
|
||||
|
|
Loading…
Reference in New Issue