2个版本
0.1.1 | 2024年5月12日 |
---|---|
0.1.0 | 2024年5月12日 |
#321 在 模拟器
每月32次下载
64KB
1K SLoC
BaryUxn
不依赖标准库设计的Uxn堆栈机的实现
lib.rs
:
BaryUxn:裸机Uxn堆栈机
不依赖 std
设计的Uxn堆栈机的实现
这意味着它可以在裸机上运行,并且可以完全适应任何平台。
源代码也被设计得尽可能易读,同时编译成高效代码,不论目标平台。
状态
BaryUxn仍处于早期开发阶段,因此未在真实世界示例上进行彻底测试。API可能会发生巨大变化以简化实现。
堆栈机本身经过了充分测试,但由于测试依赖于baryasm,这是一个具有相同目标的UxnTal汇编器,因此某些指令无法完美测试。在这个早期实现中,某些测试失败是正常的。
简单来说,这个库处于可使用状态,但我不奇怪有些东西缺失。
快速入门
要启动Uxn模拟器,只需打开文件,将ROM的字内容加载到可以按 [u16
] 索引的存储形式中。
# use baryuxn::*;
# use baryuxn::machine::*;
# fn main() -> Result<(), Box<dyn std::error::Error>> {
# let rom = UxnArrayRom([0; 0x10000]);
# let mut devices = ();
// Creates a new machine and executes a given ROM, starting at address 0x100.
let mut machine = UxnMachine::new(rom);
machine.execute_vector(0x100, &mut devices);
# }
向量
使用向量执行Uxn代码,它是一系列连续的Uxn操作,直到执行BRK
指令。
BaryUxn中的向量被实现为迭代器以提供灵活性。然后您可以
- 记录执行的指令
- 逐步执行向量
- 或者任何花哨的操作
创建一个 UxnVector
如下所示
// Creates an execution vector starting at address 0x110 and going until it
// executes a BRK instruction.
let mut vector = machine.vector(0x110, &mut devices);
多个向量
请注意,`UxnVector
` 通过对 `UxnMachine
` 的可变引用以及一些设备来获取所有权。如果您需要跟踪多个向量(这可能是实现类似于 Varvara 计算机 的中断行为时所需的),可以使用 `InactiveUxnVector
`。
let mut vector = machine.vector(0x100, &mut devices);
// Do stuff with the original vector...
let inactive = vector.make_inactive();
let mut second_vector = machine.vector(0x234, &mut devices);
// Do stuff with the other vector...
second_vector.make_inactive();
let mut vector = inactive.make_active(&mut machine, &mut devices);
设备
Uxn CPU 可以通过设备与外部世界交互。要执行指令,`UxnMachine
` 需要一个实现了 `UxnDeviceBus
` 特性的值的可变引用。
此特性定义了两个方法:一个用于读取字节,另一个用于写入字节,所有操作均通过 [u8
] 进行。从所谓的端口写入或读取可以触发设备的特定行为,如将字符打印到控制台或记录某些信息。
/// Simplified implementation of a CLI Varvara machine.
struct CliDeviceBus {
storage: [u8; 0x100],
debug: bool,
should_quit: bool,
}
impl<T> UxnDeviceBus<T> for CliDeviceBus {
fn read(&mut self, machine: &mut UxnMachine<T>, 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 UxnMachine<T>, 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),
_ => {}
},
_ => {}
}
}
}