braintal/src/uxn/mod.rs

78 lines
2.3 KiB
Rust

//! Turns (optimised) bf into uxntal.
//!
//! We could, at some point, integrate a very simple tal assembler into the program, so that it can work standalone.
//! The array of bf cells begins at `ffff` ond grows down, because if it started at `0000` then you would only have 0x100 cells untill you start reading program memory (!), and I can't be bothered to make it work out how big the program will be.
mod asm;
use crate::bf::Op;
pub fn from_ops(s: &[Op]) -> Vec<u8> {
let mut r = init();
let mut uxn_index = 0;
for (i, o) in s.iter().enumerate() {
let new = match_op(o, i, uxn_index);
uxn_index += new.split_whitespace().count();
r.push_str(&new);
r.push('\n');
}
r.push_str("#0f DEO");
dbg!(&r);
let (bin, _) = ruxnasm::assemble(r.as_bytes()).unwrap();
bin
}
fn match_op(s: &Op, bf_index: usize, uxn_index: usize) -> String {
use Op::*;
let mut prog = String::new();
// Each addition MUST
// - Have each whitespace-seperated word equate to 1 byte of uxn
// - Leave the stack in the same format it was found
match s {
Add(1) => prog.push_str("DUP2 LDAk #01 ADD ROT ROT STA"), //INC is slightly smaller
Add(amt) if amt > &0 => prog.push_str(&format!("DUP2 LDAk LIT {:02} ADD ROT ROT STA", amt)),
Add(amt) if amt < &0 => {
prog.push_str(&format!("DUP2 LDAk LIT {:02} SUB ROT ROT STA", -amt))
}
Seek(-1) => prog.push_str("#0001 ADD2"),
Seek(amt) if amt > &0 => prog.push_str(&format!("LIT 00 LIT {:02} SUB2", amt)),
Seek(amt) if amt < &0 => prog.push_str(&format!("LIT 00 LIT {:02} ADD2", -amt)),
LoopStart(adr) => prog.push_str(&format!(
"LDAk LIT 00 EQU ;{} JCN2 @{}",
sanitize_num(*adr),
sanitize_num(bf_index)
)),
LoopEnd(adr) => prog.push_str(&format!(
"LDAk ;{} JCN2 @{}",
sanitize_num(*adr),
sanitize_num(bf_index)
)),
Output => prog.push_str("LDAk #18 DEO"),
Input => prog.push_str(&format!(
";{0} LIT 10 DEO2 BRK @{0} DUP2 LIT 12 DEI ROT ROT STA",
sanitize_num(bf_index)
)),
_ => panic!("Instruction used but not supported, please file a bug!"),
}
prog
}
/// Load the pointer to the stack
fn init() -> String {
"|100\n#ffff\n".to_string()
}
/// In tal you can't have raw decimals as symbol names, so we do a crude conversion.
fn sanitize_num(mut x: usize) -> String {
let mut r = String::from("_");
while x > 0 {
r.push((0x41 + (x % 25)) as u8 as char);
x /= 25;
}
r
}