Skip to main content

MMix

Struct MMix 

Source
pub struct MMix { /* private fields */ }
Expand description

The MMIX computer architecture.

MMIX has:

  • 256 general-purpose registers ($0-$255), each holding 64 bits (an octabyte)
  • 32 special-purpose registers (rA-rZ, rBB, rTT, rWW, rXX, rYY, rZZ)
  • 2^64 bytes of virtual memory

Instructions are tetrybytes (4 bytes) with format: OP X Y Z where OP is the opcode and X, Y, Z are operands.

§Thread safety

MMix owns a Host as Box<dyn Host> and is therefore none of Send, Sync, UnwindSafe, or RefUnwindSafe. This is deliberate: the intended embedders are single-threaded and capture into Rc<RefCell<_>>, which a Send bound would forbid. Move the program, not the machine — construct an MMix on the thread that runs it, and wrap it in std::panic::AssertUnwindSafe to put one through catch_unwind.

Implementations§

Source§

impl MMix

Source

pub fn execute_instruction(&mut self) -> bool

Execute a single instruction at the current program counter. Returns true if execution should continue, false if halted.

Source§

impl MMix

Source

pub fn display_with(&self, format: ValueFormat) -> MMixDisplay<'_>

Source§

impl MMix

Source

pub fn read_byte(&self, addr: u64) -> u8

Read a byte from memory at the given address. Uninitialized memory reads as zero.

Source

pub fn write_byte(&mut self, addr: u64, value: u8)

Write a byte to memory at the given address.

Source

pub fn occupied(&self) -> impl Iterator<Item = (u64, u8)>

Every address with a nonzero byte, in ascending address order — the sparse memory’s contents for a caller that wants to display or diff them, since the underlying map’s iteration order is unspecified.

Source

pub fn loaded_extent(&self) -> impl Iterator<Item = (u64, u8)> + '_

Every address write_image loaded, in ascending order, paired with its current byte value (a later overwrite during execution shows the new value here, same as MMix::occupied would if that value is nonzero).

Unlike MMix::occupied, this includes a byte that the loaded program set to zero — sparse memory drops a zero-write from self.memory, so occupied can’t tell “loaded and zero” from “never written.” This can, so it is what a caller wants for the real loaded extent instead of a set with silent holes.

Source

pub fn set_journal(&mut self, on: bool)

Enable or disable the write journal. While enabled, every MMix::write_byte call records its address (including a zero-write, which removes the address from MMix::occupied — that removal is itself a state change worth journaling). Survives MMix::reset; the accumulated addresses do not.

Source

pub fn take_journal(&mut self) -> Vec<u64>

Drain the journal, returning every address written since the last call, deduplicated and in ascending order — matching MMix::occupied’s ordering.

Source

pub fn read_wyde(&self, addr: u64) -> u16

Read a wyde (2 bytes) from memory at the address rounded down to a multiple of 2: M_2[A] = M_2[2*floor(A/2)]. Example: a wyde at #103 reads bytes #102..#103.

Source

pub fn write_wyde(&mut self, addr: u64, value: u16)

Write a wyde (2 bytes) to memory at the address rounded down to a multiple of 2: M_2[A] = M_2[2*floor(A/2)].

Source

pub fn read_tetra(&self, addr: u64) -> u32

Read a tetra (4 bytes) from memory at the address rounded down to a multiple of 4: M_4[A] = M_4[4*floor(A/4)].

Source

pub fn write_tetra(&mut self, addr: u64, value: u32)

Write a tetra (4 bytes) to memory at the address rounded down to a multiple of 4: M_4[A] = M_4[4*floor(A/4)].

Source

pub fn read_octa(&self, addr: u64) -> u64

Read an octa (8 bytes) from memory at the address rounded down to a multiple of 8: M_8[A] = M_8[8*floor(A/8)]. Example: any address in #100..#107 reads bytes #100..#107.

Source

pub fn write_octa(&mut self, addr: u64, value: u64)

Write an octa (8 bytes) to memory at the address rounded down to a multiple of 8: M_8[A] = M_8[8*floor(A/8)].

Source

pub fn fetch_instruction(&self) -> (u8, u8, u8, u8)

Fetch the next instruction from memory and decode it. Returns (OP, X, Y, Z) where:

  • OP is the opcode
  • X, Y, Z are the operand bytes
Source§

impl MMix

Source

pub fn call_depth(&self) -> usize

Current call depth: the number of PUSHJ/PUSHGO frames not yet popped. Walks the register stack down from rO to [STACK_SEGMENT_START]: a marker octa below 256 closes out a frame and counts; a SAVE context — its topmost octa never below 256 — is skipped whole and does not count. Stops, returning the count so far, if rO is ever misaligned or outside the stack segment, or if a step’s target does not lie strictly below where it started — the walk trusts address arithmetic, never a value read from memory, to bound itself, so no stack content can make it loop.

Source§

impl MMix

Source

pub fn new() -> Self

Create a new MMIX computer with all registers and memory initialized to zero, and process I/O routed through StdHost.

Source

pub fn with_host<H: Host + 'static>(host: H) -> Self

Create a new MMIX computer with all registers and memory initialized to zero, routing every process-level write, the clock, diagnostics, and trap events through host instead of the process.

The resulting MMix is none of Send, Sync, UnwindSafe, or RefUnwindSafe — see the MMix docs.

Source

pub fn reset(&mut self)

Return the machine to its freshly-constructed state — every register, all of memory, the program counter, the call-frame stack, open file handles, and the exit code — while keeping the installed Host.

A caller that injected a host to capture output needs this: dropping the machine and building another would take the host with it, and only the machine’s state is stale between runs. The host’s own buffers are untouched, so a host that accumulates sees successive runs appended; clear them through your own handle if that is not what you want.

The journal’s enabled flag (see MMix::set_journal) survives; the accumulated buffer does not, since a fresh machine has written nothing yet.

Source

pub fn host_mut(&mut self) -> &mut dyn Host

The installed Host, for a caller that needs to reach it after construction rather than keeping a shared handle.

Host requires [Any], so an embedder can recover its concrete host type:

let mut mmix = MMix::with_host(Capture(Vec::new()));
let host: &mut dyn Any = mmix.host_mut();
let capture = host.downcast_mut::<Capture>().expect("our own host type");
capture.0.clear();
Source

pub fn into_host(self) -> Box<dyn Host>

Consume the machine and return its Host, for a caller that wants what the host captured once the program is done.

Source

pub fn set_debug_strings(&mut self, strings: Vec<Vec<u8>>)

Install the string table every debug directive’s TRAP 0,Debug,K reads from, K-indexed. The loader (write_image, run_mmo) calls this once, before the program runs.

Source

pub fn get_register(&self, reg: u8) -> u64

Get the value of a general-purpose register.

Source

pub fn set_register(&mut self, reg: u8, value: u64)

Set the value of a general-purpose register.

Writing to a local register $i with i >= rL grows rL to i+1 and zeroes every register the growth exposes, so a register between the old and new rL never reads a stale value.

Source

pub fn get_special(&self, reg: SpecialReg) -> u64

Get the value of a special-purpose register.

Source

pub fn set_special(&mut self, reg: SpecialReg, value: u64)

Set the value of a special-purpose register.

Source

pub fn get_pc(&self) -> u64

Get the current program counter.

Source

pub fn set_pc(&mut self, pc: u64)

Set the program counter.

Source

pub fn advance_pc(&mut self)

Advance the program counter by 4 bytes (one instruction).

Source

pub fn get_exit_code(&self) -> u64

Get the process exit code: $255, unreduced, after a Halt trap — process::exit takes it mod 256 — or 1 after any other halt.

Source

pub fn run(&mut self) -> usize

Execute instructions starting from the current PC until a halt condition. Returns the number of instructions executed.

Source

pub fn run_bounded(&mut self, budget: usize) -> (usize, Stop)

Execute instructions starting from the current PC until the machine halts or budget instructions have run, whichever comes first. Returns the instruction count and which condition stopped it.

Trait Implementations§

Source§

impl Default for MMix

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Display for MMix

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for MMix

§

impl !Send for MMix

§

impl !Sync for MMix

§

impl !UnwindSafe for MMix

§

impl Freeze for MMix

§

impl Unpin for MMix

§

impl UnsafeUnpin for MMix

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more