use crate::TibToken;
use anyhow::{Context, Result};
use itertools::Itertools;
fn pad_bytes_42(input: &[u8]) -> [u8; 42] {
let mut buf: [u8; 42] = [0; 42];
let cpy_len = input.len().min(24);
buf[..cpy_len].copy_from_slice(&input[..cpy_len]);
buf
}
fn pad_bytes_8(input: &[u8]) -> [u8; 8] {
let mut buf: [u8; 8] = [0; 8];
let cpy_len = input.len().min(8);
buf[..cpy_len].copy_from_slice(&input[..cpy_len]);
buf
}
struct TIVariableSection {
name: [u8; 8],
data: Box<[u8]>,
}
impl TIVariableSection {
fn get_checksum(&self) -> u16 {
let sum: u64 = self.data.iter().fold(0, |sum, cur| sum + (*cur as u64));
(sum & 0xFFFF).try_into().unwrap() }
}
impl From<TIVariableSection> for Box<[u8]> {
fn from(value: TIVariableSection) -> Self {
let mut buf = Vec::<u8>::new();
buf.extend_from_slice(&[0x0D, 0x0]); buf.extend_from_slice(&(value.data.len() as u16 + 2).to_le_bytes());
buf.extend_from_slice(&[0x05]); buf.extend_from_slice(&value.name);
buf.extend_from_slice(&[0x0]); buf.extend_from_slice(&[0x0]); buf.extend_from_slice(&(value.data.len() as u16 + 2).to_le_bytes());
buf.extend_from_slice(&(value.data.len() as u16).to_le_bytes());
buf.extend_from_slice(&value.data);
buf.into_boxed_slice()
}
}
pub fn compile(tokens: Vec<TibToken>, prgm_name: &[u8]) -> Result<Vec<u8>> {
let comment: [u8; 42] = pad_bytes_42(b"Generated by https://docs.rs/tibrs");
let data_section = TIVariableSection {
name: pad_bytes_8(prgm_name),
data: {
let mut buf = Vec::<u8>::new();
for tok in tokens {
buf.extend_from_slice(
hex::decode(
tok.hex_str
.strip_prefix("0x")
.with_context(|| format!("Malformated hex: '{}'", tok.hex_str))?,
)?
.into_iter()
.rev()
.collect_vec()
.as_slice(),
);
}
buf.into()
},
};
let mut buf = Vec::<u8>::new();
buf.extend_from_slice("**TI83F*".as_bytes()); buf.extend_from_slice(&[0x1A, 0x0A, 0x0]); buf.extend_from_slice(&comment);
let checksum = data_section.get_checksum();
let data_buf: Box<[u8]> = data_section.into();
buf.extend_from_slice(&(data_buf.len() as u16).to_le_bytes());
buf.extend_from_slice(&data_buf);
buf.extend_from_slice(&checksum.to_le_bytes());
Ok(buf)
}