| Crates.io | uxn-tal |
| lib.rs | uxn-tal |
| version | 0.1.8 |
| created_at | 2025-08-02 03:21:20.565328+00 |
| updated_at | 2025-08-24 06:12:20.402356+00 |
| description | A Rust library for assembling TAL (Tal Assembly Language) files into UXN ROM files |
| homepage | |
| repository | https://github.com/yourusername/uxn-tal |
| max_upload_size | |
| id | 1778127 |
| size | 667,805 |
A fast and comprehensive Rust library for assembling TAL (Tal Assembly Language) files into UXN ROM files.
This library provides functionality to parse TAL source code and generate bytecode compatible with the UXN virtual machine, with full symbol generation support for debugging.
2), return mode (r), and keep mode (k)uxncli, uxnemu, and other UXN emulators#12, #1234), character ('A'), decimal, and binary@main) and sublabels (&loop);main), relative (,loop), and sublabel references|0100) and byte skipping ($10)( like this )Add this to your Cargo.toml:
[dependencies]
uxn-tal = "0.1.0"
use uxn_tal::Assembler;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let tal_source = r#"
|0100 @main
#48 #65 #6c #6c #6f ( "Hello" )
#20 #57 #6f #72 #6c #64 #21 #0a ( " World!\n" )
BRK
"#;
let mut assembler = Assembler::new();
let rom = assembler.assemble(tal_source)?;
// Save ROM file
std::fs::write("hello.rom", rom)?;
// Generate symbol file
let symbols = assembler.generate_symbol_file();
std::fs::write("hello.sym", symbols)?;
Ok(())
}
use uxn_tal::assemble_file_with_symbols;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Assembles hello.tal -> hello.rom + hello.sym
let (rom_path, sym_path, size) = assemble_file_with_symbols("hello.tal")?;
println!("Generated {} bytes: {} + {}",
size,
rom_path.display(),
sym_path.display()
);
Ok(())
}
use uxn_tal::assemble_directory;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Process all .tal files in a directory
let results = assemble_directory("examples/", true)?; // true = generate symbols
for (tal_path, rom_path, sym_path, size) in results {
println!("✅ {} -> {} ({} bytes)",
tal_path.file_name().unwrap().to_string_lossy(),
rom_path.file_name().unwrap().to_string_lossy(),
size
);
if let Some(sym_path) = sym_path {
println!(" + {}", sym_path.file_name().unwrap().to_string_lossy());
}
}
Ok(())
}
#12 ( hex byte literal )
#1234 ( hex short literal )
'A ( character literal )
42 ( decimal literal )
#b101010 ( binary literal )
@main ( define main label )
&loop ( define sublabel under current main label )
;main ( absolute reference to main label )
,loop ( relative reference to sublabel )
/target ( relative reference to any label )
ADD ( basic instruction )
ADD2 ( short mode - operates on 16-bit values )
ADDr ( return mode - operates on return stack )
ADDk ( keep mode - keeps operands on stack )
ADD2rk ( combined modes )
|0100 ( pad to absolute address 0x0100 )
$10 ( skip 16 bytes, filling with zeros )
"Hello World! 00 ( raw string with explicit null terminator )
0100 main
010A main/loop
0115 data
0125 end
Compact binary format with:
u16AssemblerMain assembler instance for processing TAL source code.
impl Assembler {
pub fn new() -> Self
pub fn assemble(&mut self, source: &str) -> Result<Vec<u8>, AssemblerError>
pub fn generate_symbol_file(&self) -> String
pub fn generate_symbol_file_binary(&self) -> Vec<u8>
pub fn symbols(&self) -> &HashMap<String, Symbol>
}
AssemblerErrorComprehensive error types with detailed messages:
SyntaxError { line: usize, message: String }UnknownOpcode { opcode: String }UndefinedLabel { label: String }DuplicateLabel { label: String }InvalidNumber { value: String }Io(std::io::Error)// Assemble single file
pub fn assemble_file<P: AsRef<Path>>(input_path: P) -> Result<Vec<u8>, AssemblerError>
// Assemble file to ROM
pub fn assemble_file_to_rom<P: AsRef<Path>, Q: AsRef<Path>>(
input_path: P,
output_path: Q
) -> Result<usize, AssemblerError>
// Auto-generate ROM with same basename
pub fn assemble_file_auto<P: AsRef<Path>>(
input_path: P
) -> Result<(PathBuf, usize), AssemblerError>
// Assemble with symbol generation
pub fn assemble_file_with_symbols<P: AsRef<Path>>(
input_path: P
) -> Result<(PathBuf, PathBuf, usize), AssemblerError>
// Batch process directory
pub fn assemble_directory<P: AsRef<Path>>(
dir_path: P,
generate_symbols: bool
) -> Result<Vec<(PathBuf, PathBuf, Option<PathBuf>, usize)>, AssemblerError>
The examples/ directory contains comprehensive demonstrations:
comprehensive_demo.rs - Complete feature showcasebatch_assembler.rs - Batch processing with CLI optionstest_symbols.rs - Symbol generation testinghello_world.rs - Basic usage exampleRun examples with:
cargo run --example comprehensive_demo
cargo run --example batch_assembler -- --symbols
The library includes comprehensive tests covering all functionality:
# Run all tests
cargo test
# Run specific test
cargo test test_skip_directive
# Run with verbose output
cargo test -- --nocapture
uxncli - Command-line UXN emulatoruxnemu - UXN emulator with GUIImportant: This assembler currently supports core TAL syntax but cannot compile most existing TAL programs that use advanced features. The following are not yet implemented:
%MACRO { ... }) - Preprocessor-style definitions (used in most demos).Screen/width) - Direct device port access syntax (ubiquitous in UXN programs)[ LIT2 01 -Screen/auto ]) - Raw bytecode blocks (common optimization)Reality Check: The demo TAL files in uxn/projects/examples/demos all failed to assemble because they extensively use these features. This assembler is currently best suited for:
For production UXN development, consider using the official uxnasm or ruxnasm assemblers until these features are implemented.
cargo testThis project is licensed under the MIT License - see the LICENSE file for details.
#b10101010'A, 'B"Hello World"@main, @loop;main, ;data&loop, &end,loop, ,end/loop (for short jumps)All UXN instructions are supported with mode flags:
ADD, SUB, MUL, DIV, JMP, JSR, etc.ADD2, LDA2, STA2 (operates on 16-bit values)ADDr, LDAr (uses return stack)ADDk, LDAk (doesn't consume stack values)ADD2rk, LDA2k, etc.|0100 (pad to address 0x0100)( this is a comment )( Hello World Program )
|0100 @reset
;hello-world print-string
BRK
@print-string ( string* -- )
&loop
LDAk #18 DEO INC2
LDAk ,&loop JCN
POP2 JMP2r
@hello-world
"Hello 20 "World! 0a 00
( Counter Program )
|0100 @reset
#00 ;counter STA
&main-loop
;counter LDA
DUP #30 ADD #18 DEO ( print digit )
#20 #18 DEO ( print space )
INC DUP ;counter STA
#0a LTH ,&main-loop JCN
#0a #18 DEO ( newline )
BRK
@counter $1
# Run the hello world example
cargo run --example hello_world
# Run the counter example
cargo run --example counter
Run the test suite:
cargo test
The assembler uses a two-pass approach:
lexer: Tokenizes TAL source codeparser: Converts tokens into Abstract Syntax Treeopcodes: UXN instruction definitions and mode handlingassembler: Main assembly logic with symbol table managementrom: ROM file generation and binary outputerror: Comprehensive error types with detailed messagesThe library provides detailed error messages for common issues:
Update: This assembler now supports core TAL syntax and advanced features found in most production TAL programs. The following features are now implemented:
%MACRO { ... }): Preprocessor-style definitions.Screen/width): Direct device port access syntax[ ... ]): Raw bytecode blocksYou can now assemble most existing TAL programs, including those from official UXN projects and demos.
All errors include file name, line number, position, and source line for actionable debugging. Common error types:
AssemblerMain assembler instance for processing TAL source code.
impl Assembler {
pub fn new() -> Self
pub fn assemble(&mut self, source: &str, context: Option<String>) -> Result<Vec<u8>, AssemblerError>
pub fn generate_symbol_file(&self) -> String
pub fn generate_symbol_file_binary(&self) -> Vec<u8>
pub fn symbols(&self) -> &HashMap<String, Symbol>
}
AssemblerErrorComprehensive error types with detailed messages:
SyntaxError { path: String, line: usize, position: usize, message: String, source_line: String }UnknownOpcode { opcode: String }UndefinedLabel { label: String }DuplicateLabel { label: String }InvalidNumber { value: String }Io(std::io::Error)This project is licensed under the MIT License - see the LICENSE file for details.
The repository includes ROMs and TAL files from the uxn reference
implementation, which are © Devine Lu Linvega and released under the MIT
license
Contributions are welcome! Please feel free to submit a Pull Request.