lucia-lasm

Crates.iolucia-lasm
lib.rslucia-lasm
version1.0.4
created_at2025-12-25 18:04:31.698916+00
updated_at2025-12-26 17:38:00.929669+00
descriptionLASM - Lucia Assembly Language
homepagehttps://github.com/SirPigari/lasm
repositoryhttps://github.com/SirPigari/lasm
max_upload_size
id2004811
size195,220
SirPigari (SirPigari)

documentation

README

LASM - Lucia Assembly Language

LASM (Lucia Assembly) is a Rust crate that provides JIT (Just-In-Time) compilation of a custom assembly language to native machine code via Cranelift. Its key features are high-performance JIT execution and seamless variable interaction between Rust and LASM code.

Why LASM?

JIT Compilation: Unlike interpreted languages, LASM compiles to native machine code at runtime using Cranelift, providing near-native performance with minimal overhead.

Variable Bridge: Seamlessly pass data between high-level Rust code and low-level assembly, enabling performance-critical sections while maintaining safety.

Modern Assembly: Register-based architecture with convenient syscalls, eliminating the boilerplate of traditional assembly programming.

Rust Integration: Drop LASM code anywhere in your Rust application for instant performance boosts.

Purpose: LASM was developed as the low-level execution backend for the Lucia Programming Language. It powers the lasm module, allowing performance-critical sections of an otherwise interpreted language to execute at near-native speed through JIT compilation.

Installation

As a Cargo Crate

Add to your Cargo.toml:

[dependencies]
lasm = { version = "1", package = "lucia-lasm" }

Or use the latest version from crates.io:

cargo add lucia-lasm

Building from Source

git clone https://github.com/SirPigari/lasm
cd lasm
cargo build --release

Usage

As a Library

use lasm::{compile, call, Target};
use std::collections::HashMap;

fn main() {
    let code = r#"
        mov 42, r0
        syscall print_int
        mov "\n", r1
        mov 1, r0
        mov 1, r2
        syscall write
        mov 0, r0
        syscall exit
    "#;
    
    let target = Target::Native;
    let compiled = compile(code, target).expect("Compilation failed");
    
    let variables = HashMap::new();
    let (exit_code, _) = call(&compiled, &variables).expect("Execution failed");
    
    assert_eq!(exit_code, 0);
}

Command Line Tool

The crate includes a CLI binary for testing and development:

cargo run --bin lasm -- examples/hello.lasm

Or after building:

./target/release/lasm examples/hello.lasm

Library API

Basic Usage

use lasm::{compile, call, Target};
use std::collections::HashMap;

fn main() {
    let code = r#"
        mov 42, r0
        syscall print_int
        mov "\n", r1
        mov 1, r0
        mov 1, r2
        syscall write
        mov 0, r0
        syscall exit
    "#;
    
    let target = Target::Native;
    let compiled = compile(code, target).expect("Compilation failed");
    
    let variables = HashMap::new();
    let (exit_code, _) = call(&compiled, &variables).expect("Execution failed");
    
    assert_eq!(exit_code, 0);
}

Variable Interaction

One of LASM's most powerful features is seamless variable passing between Rust and LASM code:

use lasm::{compile, call, Target, Value};
use std::collections::HashMap;

fn main() {
    let code = r#"
        ; Load input variables
        load input, r0    ; Load 'input' variable into r0
        add r0, 10        ; Add 10 to it
        push r0, output   ; Store result in 'output' variable
        
        mov 0, r0
        syscall exit
    "#;
    
    let target = Target::Native;
    let compiled = compile(code, target).expect("Compiling failed");
    
    let mut variables = HashMap::new();
    variables.insert("input".to_string(), Value::Int(42));
    
    let (exit_code, updated_vars) = call(&compiled, &variables).expect("Execution failed");
    
    if let Some(Value::Int(result)) = updated_vars.get("output") {
        assert_eq!(*result, 52);
    }
}

Variable Instructions

  • load "var_name", reg - Load a variable from Rust into a register
  • push reg, "var_name" - Store a register value back to a Rust variable

Variables can be of type Int, Float, String, Ptr, or Null.

Architecture

Registers

  • General Purpose: r0-r15 (64-bit)
  • Stack Pointer: rsp
  • Base Pointer: rbp

Instructions

Data Movement

  • mov <src>, <dst> - Move value from source to destination
  • ldr <reg>, [<reg>] - Load from memory address in register
  • str <reg>, [<reg>] - Store to memory address in register
  • load "var", <reg> - Load Rust variable into register
  • push <reg>, "var" - Store register value to Rust variable

Arithmetic

  • add <dst>, <src> - Add
  • sub <dst>, <src> - Subtract
  • mul <dst>, <src> - Multiply
  • div <dst>, <src> - Divide
  • mod <dst>, <src> - Modulo

Floating Point Arithmetic

  • fadd <dst>, <src> - Add floats
  • fsub <dst>, <src> - Subtract floats
  • fmul <dst>, <src> - Multiply floats
  • fdiv <dst>, <src> - Divide floats
  • fmod <dst>, <src> - Modulo floats

Logic

  • and <dst>, <src> - Bitwise AND
  • or <dst>, <src> - Bitwise OR
  • xor <dst>, <src> - Bitwise XOR
  • not <dst> - Bitwise NOT
  • shl <dst>, <src> - Shift left
  • shr <dst>, <src> - Shift right

Control Flow

  • cmp <left>, <right> - Compare registers/values
  • beq <label> - Branch if equal
  • bne <label> - Branch if not equal
  • blt <label> - Branch if less than
  • bgt <label> - Branch if greater than
  • ble <label> - Branch if less or equal
  • bge <label> - Branch if greater or equal
  • jmp <label> - Unconditional jump
  • call <label> - Call subroutine
  • ret - Return from subroutine

Stack Operations

  • pushr <reg> - Push register onto stack
  • popr <reg> - Pop from stack into register

Operands

Instructions can take:

  • Immediate values: Numbers like 42 or strings like "hello"
  • Registers: r0, r1, etc.
  • Labels: For jumps and calls

Syscalls

LASM provides high-level syscalls that abstract away low-level system calls:

I/O

  • syscall write - Write to file descriptor (fd in r0, buffer in r1, count in r2)
  • syscall read - Read from file descriptor
  • syscall print_int - Print integer (value in r0)
  • syscall print_float - Print float (bits in r0)
  • syscall print_str - Print null-terminated string (pointer in r0)
  • syscall flush - Flush output stream (fd in r0)

Memory

  • syscall malloc - Allocate memory (size in r0, returns pointer in r0)
  • syscall free - Free memory (pointer in r0)
  • syscall memcpy - Copy memory (dst in r0, src in r1, len in r2)
  • syscall memset - Set memory (dst in r0, val in r1, len in r2)

Strings

  • syscall strcmp - Compare strings (str1 in r0, str2 in r1, returns comparison in r0)
  • syscall strcpy - Copy string (dst in r0, src in r1, returns dst in r0)
  • syscall strlen - Get string length (str in r0, returns length in r0)
  • syscall starts_with - Check if string starts with prefix (str in r0, prefix in r1, returns 1/0 in r0)
  • syscall streq - Check if strings are equal (str1 in r0, str2 in r1, returns 1/0 in r0)
  • syscall ends_with - Check if string ends with suffix (str in r0, suffix in r1, returns 1/0 in r0)
  • syscall trim - Trim whitespace (str in r0, out in r1, returns length in r0)
  • syscall trim_start - Trim leading whitespace (str in r0, out in r1, returns length in r0)
  • syscall trim_end - Trim trailing whitespace (str in r0, out in r1, returns length in r0)
  • syscall isws - Check if string is all whitespace (str in r0, returns 1/0 in r0)

Conversion

  • syscall atoi - String to integer (str in r0, returns int in r0)
  • syscall atof - String to float (str in r0, returns float bits in r0)
  • syscall itoa - Integer to string (int in r0, buffer in r1, size in r2, returns length in r0)
  • syscall ftoa - Float to string (bits in r0, buffer in r1, size in r2, returns length in r0)
  • syscall itof - Integer to float (int in r0, returns float bits in r0)
  • syscall ftoi - Float to integer (bits in r0, returns int in r0)

Math

  • syscall fadd - Add floats (a bits in r0, b bits in r1, returns result bits in r0)
  • syscall fsub - Subtract floats
  • syscall fmul - Multiply floats
  • syscall fdiv - Divide floats
  • syscall fmod - Modulo floats

Time & Random

  • syscall time - Get current time (returns nanoseconds in r0)
  • syscall fmt_time - Format time (total_nanos in r0, format in r1, buffer in r2, returns length in r0)
  • syscall rand - Generate random float (seed in r0, returns float bits in r0)

Utilities

  • syscall sleep - Sleep for milliseconds (ms in r0)
  • syscall system - Execute system command (cmd in r0, returns exit code in r0)

System

  • syscall exit - Exit program (status in r0)

Examples

Hello World

; Hello World
mov "Hello, World!\n", r1
mov 1, r0
mov 14, r2
syscall write

mov 0, r0
syscall exit

Calculator

; Simple calculator
mov 10, r0
mov 20, r1
add r0, r1  ; r0 = 30

syscall print_int
mov "\n", r1
mov 1, r0
mov 1, r2
syscall write

mov 0, r0
syscall exit

Running Examples

The repository includes example LASM programs in the examples/ directory:

# Hello World
cargo run -- examples/hello.lasm

# Calculator
cargo run -- examples/calculator.lasm

# Time display
cargo run -- examples/time.lasm

# Interactive shell
cargo run -- examples/shell.lasm

Or after building the release binary:

./target/release/lasm examples/hello.lasm

Syntax Details

  • Comments: Start with ; and continue to end of line
  • Labels: End with : and can be jumped to
  • Strings: Enclosed in double quotes, null-terminated automatically
  • Numbers: Decimal integers, can be used as immediates
  • Case Sensitive: All keywords and register names are case sensitive

Error Handling

LASM provides clear error messages for:

  • Syntax errors
  • Undefined labels
  • Invalid register names
  • Unsupported syscalls
  • File I/O errors

Performance

LASM's JIT compilation provides significant performance advantages for compute-intensive tasks:

use lasm::{compile, call, Target, Value};
use std::collections::HashMap;

fn main() {
    // Simple variable manipulation example
    let simple_lasm = r#"
        load "input", r0     ; Load input value
        add r0, 100          ; Add 100
        push r0, "output"    ; Store result
        mov 0, r0
        syscall exit
    "#;
    
    // Compile once, reuse for multiple runs
    let target = Target::Native;
    let compiled = compile(simple_lasm, target).expect("Compile failed");
    
    // Execute with different inputs
    for input in 0..10 {
        let mut vars = HashMap::new();
        vars.insert("input".to_string(), Value::Int(input));
        
        let (exit_code, result_vars) = call(&compiled, &vars).expect("Execution failed");
        
        if let Some(Value::Int(result)) = result_vars.get("output") {
            println!("Input: {}, Output: {}", input, result);
        }
    }
}

JIT compilation enables dynamic code generation while maintaining high performance.

Limitations

  • 64-bit architecture only (x86_64, AArch64)
  • No floating point registers (floats stored as bits in general registers)
  • No direct hardware access
  • Stack operations are simulated

Testing

Run the test suite:

cargo test

The tests include various LASM programs demonstrating different features and syscalls.

Contributing

Contributions are welcome! This is a Rust crate that can be extended in several ways:

  • Additional Syscalls: Add new high-level operations
  • Optimization: Improve code generation and performance
  • Error Handling: Better error messages and recovery
  • Documentation: More examples and tutorials
  • Cross-Platform: Support for additional operating systems
  • Features: New instructions or language constructs

Development Setup

git clone https://github.com/SirPigari/lasm
cd lasm
cargo build
cargo test

Publishing

To publish to crates.io:

cargo publish

Make sure to update the version in Cargo.toml and tag the release.

License

MIT LICENSE

Commit count: 0

cargo fmt