mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
random package can produce numbers faster for the running emulation
This commit is contained in:
parent
c767b1c1d5
commit
6311063b89
|
@ -124,12 +124,12 @@ func (mc *CPU) Reset() {
|
|||
// checking for instance == nil because it's possible for NewCPU to be
|
||||
// called with a nil instance (test package)
|
||||
if mc.instance != nil && mc.instance.Prefs.RandomState.Get().(bool) {
|
||||
mc.PC.Load(uint16(mc.instance.Random.Intn(0xffff)))
|
||||
mc.A.Load(uint8(mc.instance.Random.Intn(0xff)))
|
||||
mc.X.Load(uint8(mc.instance.Random.Intn(0xff)))
|
||||
mc.Y.Load(uint8(mc.instance.Random.Intn(0xff)))
|
||||
mc.SP.Load(uint8(mc.instance.Random.Intn(0xff)))
|
||||
mc.Status.FromValue(uint8(mc.instance.Random.Intn(0xff)))
|
||||
mc.PC.Load(uint16(mc.instance.Random.NoRewind(0xffff)))
|
||||
mc.A.Load(uint8(mc.instance.Random.NoRewind(0xff)))
|
||||
mc.X.Load(uint8(mc.instance.Random.NoRewind(0xff)))
|
||||
mc.Y.Load(uint8(mc.instance.Random.NoRewind(0xff)))
|
||||
mc.SP.Load(uint8(mc.instance.Random.NoRewind(0xff)))
|
||||
mc.Status.FromValue(uint8(mc.instance.Random.NoRewind(0xff)))
|
||||
} else {
|
||||
mc.PC.Load(0)
|
||||
mc.A.Load(0)
|
||||
|
|
|
@ -84,10 +84,10 @@ func (r Registers) String() string {
|
|||
func (r *Registers) reset(rand *random.Random) {
|
||||
for i := range r.Fetcher {
|
||||
if rand != nil {
|
||||
r.Fetcher[i].Low = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Hi = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Top = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Bottom = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Low = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Hi = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Top = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Bottom = byte(rand.NoRewind(0xff))
|
||||
} else {
|
||||
r.Fetcher[i].Low = 0
|
||||
r.Fetcher[i].Hi = 0
|
||||
|
@ -98,10 +98,10 @@ func (r *Registers) reset(rand *random.Random) {
|
|||
|
||||
for i := range r.FracFetcher {
|
||||
if rand != nil {
|
||||
r.FracFetcher[i].Low = byte(rand.Intn(0xff))
|
||||
r.FracFetcher[i].Hi = byte(rand.Intn(0xff))
|
||||
r.FracFetcher[i].Increment = byte(rand.Intn(0xff))
|
||||
r.FracFetcher[i].Count = byte(rand.Intn(0xff))
|
||||
r.FracFetcher[i].Low = byte(rand.NoRewind(0xff))
|
||||
r.FracFetcher[i].Hi = byte(rand.NoRewind(0xff))
|
||||
r.FracFetcher[i].Increment = byte(rand.NoRewind(0xff))
|
||||
r.FracFetcher[i].Count = byte(rand.NoRewind(0xff))
|
||||
} else {
|
||||
r.FracFetcher[i].Low = 0
|
||||
r.FracFetcher[i].Hi = 0
|
||||
|
@ -112,9 +112,9 @@ func (r *Registers) reset(rand *random.Random) {
|
|||
|
||||
for i := range r.MusicFetcher {
|
||||
if rand != nil {
|
||||
r.MusicFetcher[i].Waveform = uint32(rand.Intn(0xffffffff))
|
||||
r.MusicFetcher[i].Freq = uint32(rand.Intn(0xffffffff))
|
||||
r.MusicFetcher[i].Count = uint32(rand.Intn(0xffffffff))
|
||||
r.MusicFetcher[i].Waveform = uint32(rand.NoRewind(0xffffffff))
|
||||
r.MusicFetcher[i].Freq = uint32(rand.NoRewind(0xffffffff))
|
||||
r.MusicFetcher[i].Count = uint32(rand.NoRewind(0xffffffff))
|
||||
} else {
|
||||
r.MusicFetcher[i].Waveform = 0
|
||||
r.MusicFetcher[i].Freq = 0
|
||||
|
@ -123,7 +123,7 @@ func (r *Registers) reset(rand *random.Random) {
|
|||
}
|
||||
|
||||
if rand != nil {
|
||||
r.RNG.Value = uint32(rand.Intn(0xffffffff))
|
||||
r.RNG.Value = uint32(rand.NoRewind(0xffffffff))
|
||||
} else {
|
||||
r.RNG.Value = 0
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (cart *m3e) Reset() {
|
|||
for b := range cart.state.ram {
|
||||
for i := range cart.state.ram[b] {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram[b][i] = 0
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ func (cart *m3ePlus) Reset() {
|
|||
for b := range cart.state.ram {
|
||||
for i := range cart.state.ram[b] {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram[b][i] = 0
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func (cart *atari) ID() string {
|
|||
func (cart *atari) Reset() {
|
||||
for i := range cart.state.ram {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram[i] = 0
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func (cart *cbs) Plumb() {
|
|||
func (cart *cbs) Reset() {
|
||||
for i := range cart.state.ram {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram[i] = 0
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ func (cart *df) Plumb() {
|
|||
func (cart *df) Reset() {
|
||||
for i := range cart.state.ram {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram[i] = 0
|
||||
}
|
||||
|
|
|
@ -622,10 +622,10 @@ func (r DPCregisters) String() string {
|
|||
func (r *DPCregisters) reset(rand *random.Random) {
|
||||
for i := range r.Fetcher {
|
||||
if rand != nil {
|
||||
r.Fetcher[i].Low = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Hi = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Top = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Bottom = byte(rand.Intn(0xff))
|
||||
r.Fetcher[i].Low = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Hi = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Top = byte(rand.NoRewind(0xff))
|
||||
r.Fetcher[i].Bottom = byte(rand.NoRewind(0xff))
|
||||
} else {
|
||||
r.Fetcher[i].Low = 0
|
||||
r.Fetcher[i].Hi = 0
|
||||
|
@ -640,7 +640,7 @@ func (r *DPCregisters) reset(rand *random.Random) {
|
|||
}
|
||||
|
||||
if rand != nil {
|
||||
r.RNG = uint8(rand.Intn(0xff))
|
||||
r.RNG = uint8(rand.NoRewind(0xff))
|
||||
} else {
|
||||
r.RNG = 0
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ func (cart *mnetwork) Reset() {
|
|||
for b := range cart.state.ram256byte {
|
||||
for i := range cart.state.ram256byte[b] {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram256byte[b][i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram256byte[b][i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram256byte[b][i] = 0
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func (cart *mnetwork) Reset() {
|
|||
|
||||
for i := range cart.state.ram1k {
|
||||
if cart.instance.Prefs.RandomState.Get().(bool) {
|
||||
cart.state.ram1k[i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram1k[i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
cart.state.ram1k[i] = 0
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ func (cart *Supercharger) Plumb() {
|
|||
func (cart *Supercharger) Reset() {
|
||||
for b := range cart.state.ram {
|
||||
for i := range cart.state.ram[b] {
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.Intn(0xff))
|
||||
cart.state.ram[b][i] = uint8(cart.instance.Random.NoRewind(0xff))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ func (mem *Memory) read(address uint16) (uint8, error) {
|
|||
mem.LastCPUDrivenPins = vcs.TIADrivenPins
|
||||
data &= vcs.TIADrivenPins
|
||||
if mem.instance != nil && mem.instance.Prefs.RandomPins.Get().(bool) {
|
||||
data |= uint8(mem.instance.Random.Intn(0xff)) & ^vcs.TIADrivenPins
|
||||
data |= uint8(mem.instance.Random.Rewindable(0xff)) & ^vcs.TIADrivenPins
|
||||
} else {
|
||||
data |= mem.LastCPUData & ^vcs.TIADrivenPins
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func (ram *RAM) Snapshot() *RAM {
|
|||
func (ram *RAM) Reset() {
|
||||
for i := range ram.RAM {
|
||||
if ram.instance != nil && ram.instance.Prefs.RandomState.Get().(bool) {
|
||||
ram.RAM[i] = uint8(ram.instance.Random.Intn(0xff))
|
||||
ram.RAM[i] = uint8(ram.instance.Random.NoRewind(0xff))
|
||||
} else {
|
||||
ram.RAM[i] = 0
|
||||
}
|
||||
|
|
|
@ -100,8 +100,8 @@ func (tmr *Timer) Reset() {
|
|||
|
||||
if tmr.instance.Prefs.RandomState.Get().(bool) {
|
||||
tmr.divider = T1024T
|
||||
tmr.ticksRemaining = tmr.instance.Random.Intn(0xffff)
|
||||
tmr.mem.ChipWrite(chipbus.INTIM, uint8(tmr.instance.Random.Intn(0xff)))
|
||||
tmr.ticksRemaining = tmr.instance.Random.NoRewind(0xffff)
|
||||
tmr.mem.ChipWrite(chipbus.INTIM, uint8(tmr.instance.Random.NoRewind(0xff)))
|
||||
} else {
|
||||
tmr.divider = T1024T
|
||||
tmr.ticksRemaining = int(T1024T)
|
||||
|
|
|
@ -16,12 +16,20 @@
|
|||
// Package random should be used in preference to the math/rand package when a
|
||||
// random number is required inside the emulation.
|
||||
//
|
||||
// It ultimately uses the RNG from the standard library but adds an
|
||||
// understanding of how time is measured inside the emulation and ensures that
|
||||
// the same random number is generated at the same point in the emulation's
|
||||
// "timeline".
|
||||
// There are two functions belonging to the Rewind type that return random
|
||||
// numbers:
|
||||
//
|
||||
// An essential ingredient when generating random numbers in parallel
|
||||
// emulations, that are meant to be identical; and when rewinding and replaying
|
||||
// a single emulation.
|
||||
// Rewindable() returns numbers based on the current television coordinates.
|
||||
// The number will always return the same number for the same coordinates. As
|
||||
// such it is compatible with the emulator's rewind system.
|
||||
//
|
||||
// NoRewind() returns random numbers regardless of the current television
|
||||
// coordinates. It is therefore, not compatiable with the emulator's rewind
|
||||
// system.
|
||||
//
|
||||
// Parallel emulators should return the same sequence of random numbers even if
|
||||
// NoRewind() is used.
|
||||
//
|
||||
// If the same random numbers are required every single time then set ZeroSeed
|
||||
// to true. This is useful for testing purposes.
|
||||
package random
|
||||
|
|
|
@ -44,12 +44,16 @@ type Random struct {
|
|||
// use zero seed rather than the random base seed. this is only really
|
||||
// useful for normalised instances where random numbers must be predictable
|
||||
ZeroSeed bool
|
||||
|
||||
// standard Go random number generator for NoRewind()
|
||||
nonRewindable rand.Source
|
||||
}
|
||||
|
||||
// NewRandom is the preferred method of initialisation for the Random type.
|
||||
func NewRandom(tv TV) *Random {
|
||||
return &Random{
|
||||
tv: tv,
|
||||
tv: tv,
|
||||
nonRewindable: rand.NewSource(baseSeed),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,14 +62,43 @@ func coordsSum(c coords.TelevisionCoords) int64 {
|
|||
return int64(c.Frame*specification.AbsoluteMaxClks + c.Scanline*specification.ClksScanline + c.Clock)
|
||||
}
|
||||
|
||||
// new RNG from the standard library
|
||||
func (rnd *Random) rand() *rand.Rand {
|
||||
if rnd.ZeroSeed {
|
||||
return rand.New(rand.NewSource(coordsSum(rnd.tv.GetCoords())))
|
||||
// Rewindable generates a random number very quickly and based on the current
|
||||
// television coordinates. It's only really suitable for use in a running
|
||||
// emulation.
|
||||
//
|
||||
// It does however have the property of being predictable during a sesssion and
|
||||
// so is compatible with the rewind system.
|
||||
//
|
||||
// The returned number will between zero and value given in the n parameter.
|
||||
func (rnd *Random) Rewindable(n int) int {
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
return rand.New(rand.NewSource(baseSeed + coordsSum(rnd.tv.GetCoords())))
|
||||
|
||||
seed := coordsSum(rnd.tv.GetCoords())
|
||||
if !rnd.ZeroSeed {
|
||||
seed += baseSeed
|
||||
}
|
||||
seed *= seed
|
||||
b := seed >> 32
|
||||
if b != 0 {
|
||||
seed %= b
|
||||
}
|
||||
|
||||
return int(seed % int64(n))
|
||||
}
|
||||
|
||||
func (rnd *Random) Intn(n int) int {
|
||||
return rnd.rand().Intn(n)
|
||||
// NoRewind uses the standard Go library for generating random numbers. It can
|
||||
// be used to generate random numbers on a non-running emulation but it is not
|
||||
// therefore compatible with the rewind system.
|
||||
//
|
||||
// It is useful for generating a random state on startup.
|
||||
//
|
||||
// The returned number will between zero and value given in the n parameter.
|
||||
func (rnd *Random) NoRewind(n int) int {
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return int(rnd.nonRewindable.Int63() % int64(n))
|
||||
}
|
||||
|
|
|
@ -39,6 +39,6 @@ func TestRandom(t *testing.T) {
|
|||
b := random.NewRandom(&mockTV{})
|
||||
|
||||
for i := 1; i < 256; i++ {
|
||||
test.Equate(t, a.Intn(i), b.Intn(i))
|
||||
test.Equate(t, a.Rewindable(i), b.Rewindable(i))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue