ready to begin PR

This commit is contained in:
Lokathor 2018-11-29 00:15:41 -07:00
parent 6ae1374412
commit b40ee565d0
2 changed files with 185 additions and 30 deletions

View file

@ -219,16 +219,100 @@ The Control registers are also pretty simple compared to most IO registers:
an effect when used with timer 0.
* 3 bits that do nothing
* 1 bit for **Interrupt:** Whenever this timer overflows it will signal an
interrupt.
interrupt. We still haven't gotten into interrupts yet (since you have to hand
write some ASM for that, it's annoying), but when we cover them this is how
you do them with timers.
* 1 bit to **Enable** the timer. When you disable a timer it retains the current
value, but when you enable it again the value jumps to whatever its currently
assigned default value is.
TODO timer control struct / methods
```rust
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct TimerControl(u16);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerFrequency {
One = 0,
SixFour = 1,
TwoFiveSix = 2,
OneZeroTwoFour = 3,
}
impl TimerControl {
pub fn frequency(self) -> TimerFrequency {
match self.0 & 0b11 {
0 => TimerFrequency::One,
1 => TimerFrequency::SixFour,
2 => TimerFrequency::TwoFiveSix,
3 => TimerFrequency::OneZeroTwoFour,
_ => unreachable!(),
}
}
pub fn cascade_mode(self) -> bool {
self.0 & 0b100 > 0
}
pub fn interrupt(self) -> bool {
self.0 & 0b100_0000 > 0
}
pub fn enabled(self) -> bool {
self.0 & 0b1000_0000 > 0
}
//
pub fn set_frequency(&mut self, frequency: TimerFrequency) {
self.0 &= !0b11;
self.0 |= frequency as u16;
}
pub fn set_cascade_mode(&mut self, bit: bool) {
if bit {
self.0 |= 0b100;
} else {
self.0 &= !0b100;
}
}
pub fn set_interrupt(&mut self, bit: bool) {
if bit {
self.0 |= 0b100_0000;
} else {
self.0 &= !0b100_0000;
}
}
pub fn set_enabled(&mut self, bit: bool) {
if bit {
self.0 |= 0b1000_0000;
} else {
self.0 &= !0b1000_0000;
}
}
}
```
### A Timer Based Seed
TODO turn on 2+ timers with cascading when the game turns on and wait for a key press
Okay so how do we turns some timers into a PRNG seed? Well, usually our seed is
a `u32`. So we'll take two timers, string them together with that cascade deal,
and then set them off. Then we wait until the user presses any key. We probably
do this as our first thing at startup, but we might show the title and like a
"press any key to continue" message, or something.
```rust
/// Mucks with the settings of Timers 0 and 1.
fn u32_from_user_wait() -> u32 {
let mut t = TimerControl::default();
t.set_enabled(true);
t.set_cascading(true);
TM1CNT.write(t.0);
t.set_cascading(false);
TM0CNT.write(t.0);
while key_input().0 == 0 {}
t.set_enabled(false);
TM0CNT.write(t.0);
TM1CNT.write(t.0);
let low = TM0D.read() as u32;
let high = TM1D.read() as u32;
(high << 32) | low
}
```
## Various Generators
@ -314,13 +398,6 @@ pub fn lcg32(seed: u32) -> u32 {
[Compiler Explorer](https://rust.godbolt.org/z/k5n_jJ)
What's this `wrapping_mul` stuff? Well, in Rust's debug builds a numeric
overflow will panic, and then overflows are unchecked in `--release` mode. If
you want things to always wrap without problems you can either use a compiler
flag to change how debug mode works, or (for more "portable" code) you can just
make the call to `wrapping_mul`. All the same goes for add and subtract and so
on.
#### Multi-stream Generators
Note that you don't have to add a compile time constant, you could add a runtime
@ -471,39 +548,37 @@ Paper](http://www.pcg-random.org/paper.html), but here's the bullet points:
then use the single lowest bit, if it's 4 then use the lowest 2 bits, etc.
* Every time you run the generator, XOR the output with the selected value from
the array.
* Every time the generator state lands on 0, cycle every element of the array.
* Every time the generator state lands on 0, cycle the array. We want to be
careful with what we mean here by "cycle". We want the _entire_ pattern of
possible array bits to occur eventually. However, we obviously can't do
arbitrary adds for as many bits as we like, so we'll have to "carry the 1"
between the portions of the array by hand.
Here's an example using an 8 slot array and `pcg16_xsh_rs`:
```rust
// uses pcg16_xsh_rs from above
// I asked ubsan and they said this is the best way to absolutely ensure that
// our extension array is aligned so that we can pretend it's a `u32` array
// later. When it comes to memory safety, you always do what ubsan says.
#[repr(align(4))]
struct AlignedU16Array([u16; 8]);
pub struct PCG16_EXT8 {
pub struct PCG16Ext8 {
state: u32,
ext: AlignedU16Array,
ext: [u16; 8],
}
impl PCG16_EXT8 {
impl PCG16Ext8 {
pub fn next_u16(&mut self) -> u16 {
// PCG as normal.
let mut out = pcg16_xsh_rs(&mut self.state);
// XOR with a selected extension array value
out ^= unsafe { self.ext.0.get_unchecked((self.state & !0b111) as usize) };
// if state == 0 we cycle the array by sending each u16 pair though the
// normal LCG process.
out ^= unsafe { self.ext.get_unchecked((self.state & !0b111) as usize) };
// if state == 0 we cycle the array with a series of overflowing adds
if self.state == 0 {
unsafe {
let mut ptr = self.ext.0.as_mut_ptr() as *mut u16 as *mut u32;
for _ in 0..4 {
*ptr = (*ptr).wrapping_mul(32310901).wrapping_add(5);
ptr = ptr.offset(1);
}
let mut carry = true;
let mut index = 0;
while carry && index < self.ext.len() {
let (add_output, next_carry) = self.ext[index].overflowing_add(1);
self.ext[index] = add_output;
carry = next_carry;
index += 1;
}
}
out
@ -517,6 +592,10 @@ The period gained from using an extension array is quite impressive. For a b-bit
generator giving r-bit outputs, and k array slots, the period goes from 2^b to
2^(k*r+b). So our 2^32 period generator has been extended to 2^160.
Of course, we might care to seed the array itself so that it's not all 0 bits
all the way though, but that's not strictly necessary. All 0s is a legitimate
part of the extension cycle, so we have to pass through it at some point.
### Xoshiro128** (128-bit state, 32-bit output, non-uniform)
The [Xoshiro128**](http://xoshiro.di.unimi.it/xoshiro128starstar.c) generator is
@ -1032,6 +1111,6 @@ That was a whole lot. Let's put them in a table:
| lcg32 | 4 | u16 | 2^32 | 1 |
| pcg16_xsh_rs | 4 | u16 | 2^32 | 1 |
| pcg32_rxs_m_xs | 4 | u32 | 2^32 | 1 |
| PCG16_EXT8 | 20 | u16 | 2^160 | 8 |
| PCG16Ext8 | 20 | u16 | 2^160 | 8 |
| xoshiro128** | 16 | u32 | 2^128-1| 0 |
| jsf32 | 16 | u32 | ~2^126 | 0 |

View file

@ -573,3 +573,79 @@ pub const TM2CNT: VolatilePtr<u16> = VolatilePtr(0x400_010A as *mut u16);
pub const TM3D: VolatilePtr<u16> = VolatilePtr(0x400_010C as *mut u16);
pub const TM3CNT: VolatilePtr<u16> = VolatilePtr(0x400_010E as *mut u16);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct TimerControl(u16);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerFrequency {
One = 0,
SixFour = 1,
TwoFiveSix = 2,
OneZeroTwoFour = 3,
}
impl TimerControl {
pub fn frequency(self) -> TimerFrequency {
match self.0 & 0b11 {
0 => TimerFrequency::One,
1 => TimerFrequency::SixFour,
2 => TimerFrequency::TwoFiveSix,
3 => TimerFrequency::OneZeroTwoFour,
_ => unreachable!(),
}
}
pub fn cascading(self) -> bool {
self.0 & 0b100 > 0
}
pub fn interrupt(self) -> bool {
self.0 & 0b100_0000 > 0
}
pub fn enabled(self) -> bool {
self.0 & 0b1000_0000 > 0
}
//
pub fn set_frequency(&mut self, frequency: TimerFrequency) {
self.0 &= !0b11;
self.0 |= frequency as u16;
}
pub fn set_cascading(&mut self, bit: bool) {
if bit {
self.0 |= 0b100;
} else {
self.0 &= !0b100;
}
}
pub fn set_interrupt(&mut self, bit: bool) {
if bit {
self.0 |= 0b100_0000;
} else {
self.0 &= !0b100_0000;
}
}
pub fn set_enabled(&mut self, bit: bool) {
if bit {
self.0 |= 0b1000_0000;
} else {
self.0 &= !0b1000_0000;
}
}
}
/// Mucks with the settings of Timers 0 and 1.
fn u32_from_user_wait() -> u32 {
let mut t = TimerControl::default();
t.set_enabled(true);
t.set_cascading(true);
TM1CNT.write(t.0);
t.set_cascading(false);
TM0CNT.write(t.0);
while key_input().0 == 0 {}
t.set_enabled(false);
TM0CNT.write(t.0);
TM1CNT.write(t.0);
let low = TM0D.read() as u32;
let high = TM1D.read() as u32;
(high << 32) | low
}