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
impl MMix
Sourcepub fn execute_instruction(&mut self) -> bool
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
impl MMix
pub fn display_with(&self, format: ValueFormat) -> MMixDisplay<'_>
Source§impl MMix
impl MMix
Sourcepub fn read_byte(&self, addr: u64) -> u8
pub fn read_byte(&self, addr: u64) -> u8
Read a byte from memory at the given address. Uninitialized memory reads as zero.
Sourcepub fn write_byte(&mut self, addr: u64, value: u8)
pub fn write_byte(&mut self, addr: u64, value: u8)
Write a byte to memory at the given address.
Sourcepub fn occupied(&self) -> impl Iterator<Item = (u64, u8)>
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.
Sourcepub fn loaded_extent(&self) -> impl Iterator<Item = (u64, u8)> + '_
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.
Sourcepub fn set_journal(&mut self, on: bool)
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.
Sourcepub fn take_journal(&mut self) -> Vec<u64>
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.
Sourcepub fn read_wyde(&self, addr: u64) -> u16
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.
Sourcepub fn write_wyde(&mut self, addr: u64, value: u16)
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)].
Sourcepub fn read_tetra(&self, addr: u64) -> u32
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)].
Sourcepub fn write_tetra(&mut self, addr: u64, value: u32)
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)].
Sourcepub fn read_octa(&self, addr: u64) -> u64
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.
Sourcepub fn write_octa(&mut self, addr: u64, value: u64)
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§impl MMix
impl MMix
Sourcepub fn call_depth(&self) -> usize
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
impl MMix
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new MMIX computer with all registers and memory initialized
to zero, and process I/O routed through StdHost.
Sourcepub fn with_host<H: Host + 'static>(host: H) -> Self
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.
Sourcepub fn reset(&mut self)
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.
Sourcepub fn host_mut(&mut self) -> &mut dyn Host
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();Sourcepub fn into_host(self) -> Box<dyn Host>
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.
Sourcepub fn set_debug_strings(&mut self, strings: Vec<Vec<u8>>)
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.
Sourcepub fn get_register(&self, reg: u8) -> u64
pub fn get_register(&self, reg: u8) -> u64
Get the value of a general-purpose register.
Sourcepub fn set_register(&mut self, reg: u8, value: u64)
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.
Sourcepub fn get_special(&self, reg: SpecialReg) -> u64
pub fn get_special(&self, reg: SpecialReg) -> u64
Get the value of a special-purpose register.
Sourcepub fn set_special(&mut self, reg: SpecialReg, value: u64)
pub fn set_special(&mut self, reg: SpecialReg, value: u64)
Set the value of a special-purpose register.
Sourcepub fn advance_pc(&mut self)
pub fn advance_pc(&mut self)
Advance the program counter by 4 bytes (one instruction).
Sourcepub fn get_exit_code(&self) -> u64
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.
Sourcepub fn run(&mut self) -> usize
pub fn run(&mut self) -> usize
Execute instructions starting from the current PC until a halt condition. Returns the number of instructions executed.
Sourcepub fn run_bounded(&mut self, budget: usize) -> (usize, Stop)
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.