29 releases (16 breaking)

0.20.0 Dec 18, 2023
0.19.0 Sep 22, 2023
0.18.1 May 6, 2023
0.16.0 Aug 10, 2022
0.7.0 Jul 1, 2021

#97 in Programming languages


Used in cambridge-asm-cli

MPL-2.0 license

94KB
2.5K SLoC

cambridge-asm

GitHub Workflow Status Crates.io

Extending the instruction set

With macros

#[macro_use]
extern crate cambridge_asm;

// Import `Core` instruction set
use cambridge_asm::parse::Core;

// Custom instruction
inst! {
    ext (ctx) {
        // I/O accessed with ctx.io
        // Output is ctx.io.write
        // Use with write! or writeln! macros
        writeln!(ctx.io.write, "This is a custom instruction").unwrap();

        // Set r0 to detect custom instruction call
        ctx.gprs[0] = 20;
    }
}

// Extend `Core` instruction set
extend! {
    Ext extends Core {
        EXT => ext,
    }
}

Without macros

// Import `Core` instruction set
use cambridge_asm::parse::Core;

// Imports of essential types
use cambridge_asm::{
    exec::{Context, ExecFunc, PasmResult},
    inst::{InstSet, Op},
};

pub fn ext(ctx: &mut Context, _: &Op) -> PasmResult {
    // I/O accessed with ctx.io
    // Output is ctx.io.write
    // Use with write! or writeln! macros
    writeln!(ctx.io.write, "This is a custom instruction").unwrap();

    // Set r0 to detect custom instruction call
    ctx.gprs[0] = 20;

    // Return Ok
    Ok(())
}

enum Ext {
    EXT,
    Parent(Core),
}

impl std::str::FromStr for Ext {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_uppercase().as_str() {
            "EXT" => Ok(Self::EXT),
            s => Ok(Self::Parent(s.parse::<Core>()?)),
        }
    }
}

impl std::fmt::Display for Ext {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::EXT => f.write_str("EXT"),
            Self::Parent(p) => write!(f, "{}", p),
        }
    }
}

impl InstSet for Ext {
    fn as_func_ptr(&self) -> ExecFunc {
        match self {
            Self::EXT => ext,
            Self::Parent(p) => p.as_func_ptr(),
        }
    }

    fn from_func_ptr(f: ExecFunc) -> Result<Self, String> {
        const EXT: ExecFunc = ext;

        match f {
            EXT => Ok(Self::EXT),
            f => Ok(Self::Parent(Core::from_func_ptr(f)?)),
        }
    }
}

Usage

fn main() {
    use cambridge_asm::{exec::Io, parse::jit};

    const PROG: &str = r#"EXT
END

NONE:
"#;
    let out = TestStdout::new(vec![]);

    let mut e = jit::<Ext>(PROG, Io::default()).unwrap();
    e.exec::<Ext>();

    // Check if r0 == 20
    assert_eq!(e.ctx.gprs[0], 20);
}

Refer to src/parse.rs to see how the Extended instruction set is made using the macro

Using a completely custom instruction set

#[macro_use]
extern crate cambridge_asm;

inst! {
    h (ctx) {
        write!(ctx.io.write, "H").unwrap();
    }
}

inst! {
    e (ctx) {
        write!(ctx.io.write, "E").unwrap();
    }
}

inst! {
    l (ctx) {
        write!(ctx.io.write, "L").unwrap();
    }
}

inst! {
    o (ctx) {
        write!(ctx.io.write, "O").unwrap();
    }
}

// Define a new instruction set
inst_set! {
    Custom {
        H => h,
        E => e,
        L => l,
        O => o,
        END => cambridge_asm::exec::io::end,
    }
}

// Using it
fn main() {
    use cambridge_asm::{exec::Io, parse::jit};

    // Outputs "HELLO"
    const PROG: &str = r#"H
E
L
L
O
END

NONE:
"#;

    let out = TestStdout::new(vec![]);

    let mut e = jit::<Custom>(PROG, Io::default()).unwrap();
    e.exec::<Custom>();
}

Dependencies

~1.9–2.5MB
~25K SLoC