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:
Tim Allen 2017-08-24 12:45:24 +10:00
parent c0934b826c
commit d621136d69
16 changed files with 236 additions and 138 deletions

View File

@ -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/";

114
higan/emulator/random.hpp Normal file
View File

@ -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;
};
}

View File

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

View File

@ -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();
}

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

@ -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; }