Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ default-members = ["redox-terminal", "redox-core"]
[workspace.dependencies]
criterion = "0.5.1"
enumflags2 = "0.7.10"
float-cmp = "0.9.0"
hashbrown = "0.14.5"
itertools = "0.13.0"
float-cmp = "0.10.0"
hashbrown = "0.15.2"
itertools = "0.14.0"
num-traits = "0.2.19"
num-derive = "0.4.2"
prettytable = "0.10.0"
rand = "0.8.5"
rand_xoshiro = "0.6.0"
rand = "0.9.0"
rand_core = "0.9.0"
rand_xoshiro = "0.7.0"
strum = "0.26.3"
strum_macros = "0.26.4"
thiserror = "1.0.61"
thiserror = "2.0.11"
unescape = "0.1.0"
unicode-segmentation = "1.11.0"
unicode-segmentation = "1.12.0"
winres = "0.1.12"

[profile.dev]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# redox
# Redox
A new toy virtual machine, assembler and compiler, written in Rust.
3 changes: 2 additions & 1 deletion redox-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
authors = ["Ryan Jones-Ward <sciguyryan@gmail.com>"]
edition = "2021"
readme = "README.md"
license = "LGPL-2.1-only"
description = "A new toy virtual machine, assembler and compiler, written in Rust."
license_file = "LICENSE"
repository = "https://github.com/sciguyryan/Redox"
homepage = "https://github.com/sciguyryan/Redox"

Expand All @@ -20,6 +20,7 @@ num-traits.workspace = true
num-derive.workspace = true
prettytable.workspace = true
rand.workspace = true
rand_core.workspace = true
rand_xoshiro.workspace = true
strum.workspace = true
strum_macros.workspace = true
Expand Down
6 changes: 3 additions & 3 deletions redox-core/src/com_bus/com_bus_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use super::device_error::DeviceResult;

pub trait ComBusIO {
/// Read a u8 value from the device.
fn read_u8(&self) -> DeviceResult<u8>;
fn read_u8(&mut self) -> DeviceResult<u8>;
/// Read a u32 value from the device.
fn read_u32(&self) -> DeviceResult<u32>;
fn read_u32(&mut self) -> DeviceResult<u32>;
/// Read a uf32 value from the device.
fn read_f32(&self) -> DeviceResult<f32>;
fn read_f32(&mut self) -> DeviceResult<f32>;

/// Write a u8 value to the device.
fn write_u8(&mut self, value: u8) -> DeviceResult<()>;
Expand Down
94 changes: 51 additions & 43 deletions redox-core/src/com_bus/random_device.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
use rand::{rngs::OsRng, Rng, SeedableRng};
use rand::{rngs::OsRng, Rng, SeedableRng, TryRngCore};
use rand_xoshiro::Xoshiro256Plus;
use std::cell::RefCell;

use super::{
com_bus_io::ComBusIO,
device_error::{DeviceError, DeviceResult},
};

thread_local! {
static PRNG: RefCell<Xoshiro256Plus> = RefCell::new(Xoshiro256Plus::from_entropy());
static CRNG: RefCell<OsRng> = const { RefCell::new(OsRng) };
}

#[repr(u8)]
#[derive(PartialEq, Eq)]
enum RandomMode {
Crypto,
Seeded,
Standard,
Pseudorandom,
}

/// The seedable random generator chip!
/// The random number generator device!
pub struct RandomDevice {
/// Has the random number generator been initialized?
is_initialized: bool,
/// The random generator mode currently being used.
mode: RandomMode,
/// The pseudorandom number generator.
prng: Xoshiro256Plus,
/// The Cryptographically secure RNG generator.
crng: OsRng,
}

impl RandomDevice {
pub fn new() -> Self {
Self {
// By default we initialize the random number generator using entropy.
is_initialized: true,
mode: RandomMode::Standard,
mode: RandomMode::Crypto,
prng: Xoshiro256Plus::from_os_rng(),
crng: OsRng,
}
}
}
Expand All @@ -44,39 +44,51 @@ impl Default for RandomDevice {
}

impl ComBusIO for RandomDevice {
fn read_u8(&self) -> DeviceResult<u8> {
fn read_u8(&mut self) -> DeviceResult<u8> {
if !self.is_initialized {
return DeviceResult::Err(DeviceError::Misconfigured);
}

Ok(match self.mode {
RandomMode::Crypto => CRNG.with(|r| r.borrow_mut().gen::<u8>()),
RandomMode::Seeded => PRNG.with(|r| r.borrow_mut().gen::<u8>()),
RandomMode::Standard => rand::thread_rng().gen::<u8>(),
RandomMode::Crypto => {
let mut arr: [u8; 1] = [0; 1];
self.crng
.try_fill_bytes(&mut arr)
.expect("failed to get u8");
arr[0]
}
RandomMode::Pseudorandom => self.prng.random::<u8>(),
})
}

fn read_u32(&self) -> DeviceResult<u32> {
fn read_u32(&mut self) -> DeviceResult<u32> {
if !self.is_initialized {
return DeviceResult::Err(DeviceError::Misconfigured);
}

Ok(match self.mode {
RandomMode::Crypto => CRNG.with(|r| r.borrow_mut().gen::<u32>()),
RandomMode::Seeded => PRNG.with(|r| r.borrow_mut().gen::<u32>()),
RandomMode::Standard => rand::thread_rng().gen::<u32>(),
RandomMode::Crypto => self.crng.try_next_u32().expect("failed to get u32"),
RandomMode::Pseudorandom => self.prng.random::<u32>(),
})
}

fn read_f32(&self) -> DeviceResult<f32> {
fn read_f32(&mut self) -> DeviceResult<f32> {
if !self.is_initialized {
return DeviceResult::Err(DeviceError::Misconfigured);
}

Ok(match self.mode {
RandomMode::Crypto => CRNG.with(|r| r.borrow_mut().gen::<f32>()),
RandomMode::Seeded => PRNG.with(|r| r.borrow_mut().gen::<f32>()),
RandomMode::Standard => rand::thread_rng().gen::<f32>(),
RandomMode::Crypto => {
// We want to weed out any potential infinities or NaN's,
// and we'll iterate until we find one.
let mut value = f32::INFINITY;
while !value.is_finite() {
let v = self.crng.try_next_u32().expect("failed to get u32");
value = f32::from_bits(v);
}
value
}
RandomMode::Pseudorandom => self.prng.random::<f32>(),
})
}

Expand All @@ -86,19 +98,15 @@ impl ComBusIO for RandomDevice {
// The instruction indicating the type of random number generator to be used.
match value {
0 => {
// Standard mode - a PRNG derived from entropy.
PRNG.with(|r| *r.borrow_mut() = Xoshiro256Plus::from_entropy());
self.mode = RandomMode::Standard;
// Cryptographic random mode - a cryptographically secure random number generator.
// This is the default.
self.mode = RandomMode::Crypto;
self.is_initialized = true;
}
1 => {
// Seeded random mode - the seed must be set via sending a u32 command after this one.
self.mode = RandomMode::Seeded;
self.is_initialized = false;
}
2 => {
// Cryptographic random mode - a cryptographically secure random number generator.
self.mode = RandomMode::Crypto;
// Standard mode - a PRNG derived from entropy.
self.prng = Xoshiro256Plus::from_os_rng();
self.mode = RandomMode::Pseudorandom;
self.is_initialized = true;
}
_ => {
Expand All @@ -112,8 +120,8 @@ impl ComBusIO for RandomDevice {
fn write_u32(&mut self, value: u32) -> DeviceResult<()> {
let mut result = DeviceResult::Ok(());

if self.mode == RandomMode::Seeded {
PRNG.with(|r| *r.borrow_mut() = Xoshiro256Plus::seed_from_u64(value as u64));
if self.mode == RandomMode::Pseudorandom {
self.prng = Xoshiro256Plus::seed_from_u64(value as u64);

// Indicate that the PRNG has been seeded and initialized.
self.is_initialized = true;
Expand All @@ -131,21 +139,21 @@ impl ComBusIO for RandomDevice {

#[cfg(test)]
mod tests_random_device {
use crate::com_bus::com_bus_io::ComBusIO;
use crate::com_bus::{com_bus_io::ComBusIO, random_device::RandomMode};

use super::RandomDevice;

/// Test the PRNG to ensure it is non-repeating.
/// Test the cryptographic RNG to ensure it is non-repeating.
#[test]
fn test_prng() {
// By default, the generator should use an entropy-seeded PRNG.
let device = RandomDevice::default();
fn test_crng() {
// By default, the generator should use an entropy-seeded RNG.
let mut device = RandomDevice::default();
let mut sequence_1 = vec![];
for _ in 0..1000 {
sequence_1.push(device.read_u32().expect(""));
}

let device = RandomDevice::default();
let mut device = RandomDevice::default();
let mut sequence_2 = vec![];
for _ in 0..1000 {
sequence_2.push(device.read_u32().expect(""));
Expand All @@ -158,7 +166,7 @@ mod tests_random_device {
#[test]
fn test_seeded_prng() {
let mut device = RandomDevice::default();
_ = device.write_u8(0x1);
_ = device.write_u8(RandomMode::Pseudorandom as u8);
_ = device.write_u32(0xdeadbeef);

let mut sequence_1 = vec![];
Expand All @@ -167,7 +175,7 @@ mod tests_random_device {
}

let mut device = RandomDevice::default();
_ = device.write_u8(0x1);
_ = device.write_u8(RandomMode::Pseudorandom as u8);
_ = device.write_u32(0xdeadbeef);

let mut sequence_2 = vec![];
Expand Down
6 changes: 3 additions & 3 deletions redox-core/src/com_bus/test_debug_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,23 @@ impl Default for TestDebugDevice {
}

impl ComBusIO for TestDebugDevice {
fn read_u8(&self) -> DeviceResult<u8> {
fn read_u8(&mut self) -> DeviceResult<u8> {
if self.should_fail[0] {
return DeviceResult::Err(DeviceError::OperationNotSupported);
}

Ok(u8::MAX)
}

fn read_u32(&self) -> DeviceResult<u32> {
fn read_u32(&mut self) -> DeviceResult<u32> {
if self.should_fail[1] {
return DeviceResult::Err(DeviceError::OperationNotSupported);
}

Ok(u32::MAX)
}

fn read_f32(&self) -> DeviceResult<f32> {
fn read_f32(&mut self) -> DeviceResult<f32> {
if self.should_fail[2] {
return DeviceResult::Err(DeviceError::OperationNotSupported);
}
Expand Down
48 changes: 29 additions & 19 deletions redox-core/src/compiling/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl Compiler {
}
}

#[inline]
fn assert_label_does_not_exist(&self, label: &String) {
assert!(
!self.labels.contains_key(label),
Expand Down Expand Up @@ -73,19 +74,16 @@ impl Compiler {
// Now load the code labels.
self.load_code_labels(&instructions);

//println!("labels = {:#?}", self.labels);

if optimize {
// TODO - optimizations should go here.
// TODO - an optimization here might be to use a short call/jump instruction
// TODO - if the call address is close enough, for example.

// TODO - the calculate label positions method should be called here again.
// TODO - this is because the instructions may have changed, altering the relative positions of the labels.
}

// TODO - do we care enough to do this?
if optimize {
// TODO - a second optimization pass should go here to optimize any labels
// TODO - related to the data section.
// TODO - the calculate code label positions method should be called here again.
// TODO - this is because the instructions may have changed, altering the relative
// TODO - positions of the code labels.
}

self.handle_labelled_instructions(&mut instructions);
Expand Down Expand Up @@ -137,7 +135,7 @@ impl Compiler {

self.labels.insert(
d.label.clone(),
LabelEntry::new(LabelType::Code, d.label.clone(), position),
LabelEntry::new(LabelType::Data, d.label.clone(), position),
);

position += d.bytes.len();
Expand All @@ -159,21 +157,18 @@ impl Compiler {

// Attempt to get the label entry.
// If a label has been used but has not been defined then that is a syntax error.
let label_entry = if let Some(l) = self.labels.get(label) {
l
} else {
let Some(label_entry) = self.labels.get(label) else {
panic!("syntax error - no label with the name of {label} has been defined.");
};

let position = label_entry.position as u32;

match label_entry.label_type {
LabelType::Code => {
*ins = match ins {
Instruction::CallAbsU32Imm(_, label) => Instruction::CallRelCSU32Offset(
label_entry.position as u32,
label.clone(),
),
Instruction::CallRelCSU32Offset(_, _) => continue,
_ => unreachable!("unexpected labelled instruction instance - {ins}"),
if let Some(i) =
Compiler::maybe_rewrite_code_labelled_instruction(ins, position)
{
*ins = i;
}
}
LabelType::Data => todo!(),
Expand All @@ -182,6 +177,21 @@ impl Compiler {
}
}

#[inline]
fn maybe_rewrite_code_labelled_instruction(
ins: &Instruction,
label_position: u32,
) -> Option<Instruction> {
match ins {
Instruction::CallAbsU32Imm(_, label) => Some(Instruction::CallRelCSU32Offset(
label_position,
label.clone(),
)),
Instruction::CallRelCSU32Offset(_, _) => None,
_ => unreachable!("unexpected labelled instruction - {ins}"),
}
}

/// Compile a single instruction into bytecode.
///
/// # Arguments
Expand Down
Loading