#emulator #uxn #no-std #no-alloc #varvara

no-std baryuxn

作为no_std库实现的Uxn堆栈机的实现

2个版本

0.1.1 2024年5月12日
0.1.0 2024年5月12日

#321模拟器

每月32次下载

Unlicense

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),
                _ => {}
            },
            _ => {}
        }
    }
}

无运行时依赖