1use ternaria_arith::{Tryte, Word};
17use ternaria_isa::asm;
18
19pub const RADIX: i64 = 243;
21
22pub const RADIX_TRITS: u32 = 5;
24
25pub const BIAS: i64 = 9_841;
27
28pub const BYTES_PER_TRYTE: usize = 2;
30
31pub const BYTES_PER_INSTRUCTION: usize = 3 * BYTES_PER_TRYTE;
33
34pub const HEADER_WORDS: usize = 5;
36
37pub const HEADER_BYTES: usize = HEADER_WORDS * 3;
40
41pub const MAGIC: i64 = 19_683 + 243 + 3;
43
44pub mod hdr {
46 pub const MAGIC: usize = 0;
48 pub const LINK: usize = 1;
50 pub const ENTRY: usize = 2;
52 pub const CODE: usize = 3;
54 pub const BSS: usize = 4;
56}
57
58#[derive(Clone, PartialEq, Eq, Debug)]
60pub enum ExecError {
61 Assembly(String),
63 NotAnExecutable,
65 Truncated,
67}
68
69impl std::fmt::Display for ExecError {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 match self {
72 ExecError::Assembly(e) => write!(f, "{e}"),
73 ExecError::NotAnExecutable => write!(f, "not an executable"),
74 ExecError::Truncated => write!(f, "truncated executable"),
75 }
76 }
77}
78
79impl std::error::Error for ExecError {}
80
81fn put_word(out: &mut Vec<u8>, value: i64) {
83 let mut v = value;
84 for _ in 0..3 {
85 out.push((v % RADIX) as u8);
86 v /= RADIX;
87 }
88}
89
90fn get_word(bytes: &[u8], at: usize) -> i64 {
92 let mut acc = 0i64;
93 let mut weight = 1i64;
94 for k in 0..3 {
95 acc += bytes[at + k] as i64 * weight;
96 weight *= RADIX;
97 }
98 acc
99}
100
101fn put_tryte(out: &mut Vec<u8>, t: Tryte) {
103 let v = t.value() as i64 + BIAS;
104 out.push((v % RADIX) as u8);
105 out.push((v / RADIX) as u8);
106}
107
108fn get_tryte(bytes: &[u8], at: usize) -> Tryte {
110 let v = bytes[at] as i64 + bytes[at + 1] as i64 * RADIX - BIAS;
111 Tryte::from_value(v as i32)
112}
113
114pub fn build(source: &str, link: i64, bss: i64) -> Result<Vec<u8>, ExecError> {
119 let program = asm::assemble_at(source, link).map_err(|e| ExecError::Assembly(e.to_string()))?;
120 let entry = program
121 .labels
122 .get("_start")
123 .map(|i| link + *i as i64 * 3)
124 .unwrap_or(link);
125
126 let code_trytes = program.instructions.len() as i64 * 3;
127 let mut out = Vec::with_capacity(HEADER_BYTES + program.instructions.len() * 6);
128 put_word(&mut out, MAGIC);
129 put_word(&mut out, link);
130 put_word(&mut out, entry);
131 put_word(&mut out, code_trytes);
132 put_word(&mut out, bss);
133
134 for instr in &program.instructions {
135 let w = instr.encode();
136 for k in 0..3 {
137 put_tryte(&mut out, w.tryte(k));
138 }
139 }
140 Ok(out)
141}
142
143#[derive(Clone, Copy, PartialEq, Eq, Debug)]
145pub struct Header {
146 pub link: i64,
148 pub entry: i64,
150 pub code_trytes: i64,
152 pub bss_trytes: i64,
154}
155
156pub fn header(image: &[u8]) -> Result<Header, ExecError> {
158 if image.len() < HEADER_BYTES || get_word(image, hdr::MAGIC * 3) != MAGIC {
159 return Err(ExecError::NotAnExecutable);
160 }
161 let h = Header {
162 link: get_word(image, hdr::LINK * 3),
163 entry: get_word(image, hdr::ENTRY * 3),
164 code_trytes: get_word(image, hdr::CODE * 3),
165 bss_trytes: get_word(image, hdr::BSS * 3),
166 };
167 let need = HEADER_BYTES + (h.code_trytes as usize) * BYTES_PER_TRYTE;
168 if image.len() < need {
169 return Err(ExecError::Truncated);
170 }
171 Ok(h)
172}
173
174pub fn words(image: &[u8]) -> Result<Vec<Word>, ExecError> {
176 let h = header(image)?;
177 let mut out = Vec::new();
178 let mut at = HEADER_BYTES;
179 for _ in 0..(h.code_trytes / 3) {
180 let trytes = [
181 get_tryte(image, at),
182 get_tryte(image, at + 2),
183 get_tryte(image, at + 4),
184 ];
185 out.push(Word::from_trytes(trytes));
186 at += BYTES_PER_INSTRUCTION;
187 }
188 Ok(out)
189}
190
191pub fn prelude() -> String {
193 let entries: [(&str, i64); 9] = [
194 ("EXE_MAGIC", MAGIC),
195 ("EXE_RADIX", RADIX),
196 ("EXE_RADIX_TRITS", RADIX_TRITS as i64),
197 ("EXE_BIAS", BIAS),
198 ("EXE_HEADER_TRYTES", HEADER_BYTES as i64),
199 ("HDR_LINK", hdr::LINK as i64),
200 ("HDR_ENTRY", hdr::ENTRY as i64),
201 ("HDR_CODE", hdr::CODE as i64),
202 ("HDR_BSS", hdr::BSS as i64),
203 ];
204 let mut out = String::new();
205 for (name, value) in entries {
206 out.push_str(&format!(".equ {name}, {value}\n"));
207 }
208 out
209}