Skip to main content

Crate baryuxn

Crate baryuxn 

Source
Expand description

§BaryUxn: the baremetal Uxn stack machine

An implementation of the Uxn stack machine designed to not rely on std.

This means it can run on baremetal, and is thoroughly adaptable to any plateform.

The source is also designed to be as readable as possible, while compiling down to efficient code regardless of the target plateform.

§STATUS

BaryUxn is still in early development, and as such is not thoroughly tested on real world examples. The API may change drastically to ease up implementation.

The stack machine itself is well tested, however.

Put simply, the library is in a usable state, but I would not be surprised that some things are missing or unergonomic.

§Features

As of now, there is a single feature, pretty_print, which includes string representations of Uxn opcodes for help debugging. It is disabled by default as it induces a huge increase in binary size.

§Quick start

To spin up a Uxn emulator, simply load a ROM by opening up a file and loading its byte contents into a form of storage that implements the UxnMemory trait. Rust’s slices implement this trait automatically.

let mut memory = [0; 0x10000];
// Examples devices that always read/write zeros
let mut devices = NullDevices;

// Creates a new machine state (stacks).
let mut machine = UxnMachineState::new();
// Vectors are the main way to execute code, they hold a mutable reference
// to everything needed, and keep running from the starting address until
// a BRK instruction is executed.
UxnVector::new(0x100, &mut machine, &mut memory, &mut devices).execute_till_end();

§Vectors

Uxn code is executed using vectors, a continuous stream of Uxn operations ending when it executes the BRK instruction.

Vectors in BaryUxn are implemented as iterators to allow flexibility. You can then:

  • log instruction counters
  • execute a vector step-by-step
  • or anything you can do with a standard iterator

Creating a UxnVector is as simple as:

// Creates an execution vector starting at address 0x110 and going until it
// executes a BRK instruction.
UxnVector::new(0x110, &mut machine, &mut memory, &mut devices).execute_till_end();

§Multiple vectors

Note that UxnVector takes ownership through a mutable reference to a UxnMachineState as well as memory and devices. If you need to keep track of multiple vectors (which can be needed to implement interrupt-like behaviors, like the Varvara computer does), you can use UxnVector::release:.

// Do stuff with the original vector...
let mut vector = UxnVector::new(0x100, &mut machine, &mut mem, &mut dev);

// Releasing the vector returns the current program counter
let next_instruction = vector.release();

// Do stuff with the other vector...
let mut second_vector = UxnVector::new(0x234, &mut machine, &mut mem, &mut dev);

// Once done, release it and recreate a new vector starting where the previous one
// ended
second_vector.release();
let mut vector = UxnVector::new(next_instruction, &mut machine, &mut mem, &mut dev);

§Memory

Due to wildly different possible memory implementations (shared memory, parallel access, etc), the implementation for memory components is user-defined. Anything implementing the UxnMemory trait, which allows you to access the storage using 16 bit integers, works.

A blanket implementation for usual arrays, slices, vectors and else is implemented.

§Devices

Uxn CPUs can interract with the outside world using devices. To execute instructions, a UxnMachineState requires a mutable reference to a value implementing the UxnDeviceBus trait.

This trait defines two methods: one to read bytes, and another to write them, all addressed by u8. Writing or reading from a so-called port can trigger specific behavior from your devices, like printing a character to the console or logging some information.

/// Simplified implementation of a CLI Varvara machine.
struct CliDeviceBus {
    storage: [u8; 0x100],
    debug: bool,
    should_quit: bool,
}
impl UxnDeviceBus for CliDeviceBus {
    fn read(&mut self, machine: &mut UxnMachineState, address: u8) -> u8 {
        let page = address & 0xf0;
        let port = address & 0x0f;
        match page {
            0x00 => match port {
                // System
                0x04 => machine.work_stack.pointer,
                0x05 => machine.return_stack.pointer,
                _ => self.storage[address as usize],
            },
            _ => self.storage[address as usize],
        }
    }
    fn write(&mut self, machine: &mut UxnMachineState, address: u8, byte: u8) {
        let page = address & 0xf0;
        let port = address & 0x0f;
        self.storage[address as usize] = byte;
        match page {
            0x00 => match port {
                // System
                0x04 => machine.work_stack.pointer = byte,
                0x05 => machine.return_stack.pointer = byte,
                0x0e if byte != 0 && self.debug => {
                    println!(
                        "WST ( {:?} )\nRST ( {:?} )",
                        machine.work_stack, machine.return_stack
                    );
                }
                0x0f if byte != 0 => self.should_quit = true,
                _ => {}
            },
            0x10 => match port {
                // Console
                0x08 => print!("{}", byte as char),
                0x09 => eprint!("{}", byte as char),
                _ => {}
            },
            _ => {}
        }
    }
}

Structs§

UxnMachineState
The Uxn machine, able to execute Uxntal instructions.
UxnStack
Circular stack used by the UxnMachineState to hold 256 bytes of data.
UxnStackIter
Iterator over the values of a UxnStack, starting from the current stack pointer and going back up the stack until it loops.
UxnStackIterMut
Iterator over the values of a UxnStack, starting from the current stack pointer and going back up the stack until it loops. Elements can be modified individually.
UxnVector
An instruction vector, implemented as an iterator that executes instructions in sequence until it encounters a BRK instruction.

Traits§

UxnDeviceBus
Uxn interacts with the outside world using devices.
UxnMemory
Uxn memory trait.

Functions§

execute_operation
Executes a single UxnTal operation given a program counter, RAM and devices.