1use std::collections::HashMap;
22use std::fmt;
23
24#[derive(Clone, Copy, Debug, PartialEq)]
26pub enum Op {
27 Nop=0x00, Halt=0x01, Jmp=0x02, Jz=0x03, Jnz=0x04, Je=0x05, Jne=0x06,
29 Call=0x07, Ret=0x08,
30 CAdd=0x10, CSub=0x11, CMul=0x12, CDiv=0x13, CMod=0x14, CNeg=0x15,
32 CInc=0x16, CDec=0x17, CMin=0x18, CMax=0x19, CAbs=0x1A,
33 Cmp=0x20, CLt=0x21, CLe=0x22, CEq=0x23, CGt=0x24, CGe=0x25,
35 And=0x30, Or=0x31, Xor=0x32, Not=0x33, Shl=0x34, Shr=0x35,
37 FCvt=0x38, FNeg=0x39, FAdd=0x3A, FSub=0x3B, FMul=0x3C, FDiv=0x3D,
39 Push=0x40, Pop=0x41, Dup=0x42, Swap=0x43,
41 Load=0x48, Store=0x49, LoadF=0x4A, StoreF=0x4B, MAlloc=0x4C, MFree=0x4D,
43 Conf=0x50, Fuse=0x51, Drop=0x52, Trust=0x53, Gate=0x54,
45 MovI=0x58, AddI=0x59,
47 Tell=0x60, Ask=0x61, Broadcast=0x62, Listen=0x63, Delegate=0x64,
49 InstinctAct=0x68, InstinctQ=0x69,
51 GeneExpr=0x6A, EnzymeBind=0x6B, RnaTrans=0x6C, ProteinFold=0x6D,
53 MembraneChk=0x6E, Quarantine=0x6F,
55 AtpGen=0x70, AtpConsume=0x71, AtpQ=0x72, AtpTransfer=0x73,
57 ApoptosisChk=0x74, ApoptosisTrigger=0x75,
58 CircadianSet=0x76, CircadianGet=0x77,
59 SysCall=0x78, Debug=0x79, Yield=0x7A,
61}
62
63impl Op {
64 fn from_name(name: &str) -> Option<Self> {
65 let m = [
66 ("NOP", Op::Nop), ("HALT", Op::Halt), ("JMP", Op::Jmp), ("JZ", Op::Jz),
67 ("JNZ", Op::Jnz), ("JE", Op::Je), ("JNE", Op::Jne), ("CALL", Op::Call),
68 ("RET", Op::Ret),
69 ("CADD", Op::CAdd), ("CSUB", Op::CSub), ("CMUL", Op::CMul), ("CDIV", Op::CDiv),
70 ("CMOD", Op::CMod), ("CNEG", Op::CNeg), ("CINC", Op::CInc), ("CDEC", Op::CDec),
71 ("CMIN", Op::CMin), ("CMAX", Op::CMax), ("CABS", Op::CAbs),
72 ("CMP", Op::Cmp), ("CLT", Op::CLt), ("CLE", Op::CLe), ("CEQ", Op::CEq),
73 ("CGT", Op::CGt), ("CGE", Op::CGe),
74 ("AND", Op::And), ("OR", Op::Or), ("XOR", Op::Xor), ("NOT", Op::Not),
75 ("SHL", Op::Shl), ("SHR", Op::Shr),
76 ("FCVT", Op::FCvt), ("FNEG", Op::FNeg), ("FADD", Op::FAdd), ("FSUB", Op::FSub),
77 ("FMUL", Op::FMul), ("FDIV", Op::FDiv),
78 ("PUSH", Op::Push), ("POP", Op::Pop), ("DUP", Op::Dup), ("SWAP", Op::Swap),
79 ("LOAD", Op::Load), ("STORE", Op::Store), ("LOADF", Op::LoadF),
80 ("STOREF", Op::StoreF), ("MALLOC", Op::MAlloc), ("MFREE", Op::MFree),
81 ("CONF", Op::Conf), ("FUSE", Op::Fuse), ("DROP", Op::Drop), ("TRUST", Op::Trust),
82 ("GATE", Op::Gate),
83 ("MOVI", Op::MovI), ("ADDI", Op::AddI),
84 ("TELL", Op::Tell), ("ASK", Op::Ask), ("BROADCAST", Op::Broadcast),
85 ("LISTEN", Op::Listen), ("DELEGATE", Op::Delegate),
86 ("INSTINCT_ACT", Op::InstinctAct), ("INSTINCT_Q", Op::InstinctQ),
87 ("GENE_EXPR", Op::GeneExpr), ("ENZYME_BIND", Op::EnzymeBind),
88 ("RNA_TRANS", Op::RnaTrans), ("PROTEIN_FOLD", Op::ProteinFold),
89 ("MEMBRANE_CHK", Op::MembraneChk), ("QUARANTINE", Op::Quarantine),
90 ("ATP_GEN", Op::AtpGen), ("ATP_CONSUME", Op::AtpConsume), ("ATP_Q", Op::AtpQ),
91 ("ATP_TRANSFER", Op::AtpTransfer),
92 ("APOPTOSIS_CHK", Op::ApoptosisChk), ("APOPTOSIS_TRIGGER", Op::ApoptosisTrigger),
93 ("CIRCADIAN_SET", Op::CircadianSet), ("CIRCADIAN_GET", Op::CircadianGet),
94 ("SYSCALL", Op::SysCall), ("DEBUG", Op::Debug), ("YIELD", Op::Yield),
95 ];
96 m.iter().find(|(n,_)| n.eq_ignore_ascii_case(name)).map(|(_,o)| *o)
97 }
98
99 fn format(self) -> &'static str {
100 match self {
101 Op::Nop => "NOP", Op::Halt => "HALT", Op::Jmp => "JMP", Op::Jz => "JZ",
102 Op::Jnz => "JNZ", Op::Je => "JE", Op::Jne => "JNE", Op::Call => "CALL",
103 Op::Ret => "RET", Op::CAdd => "CADD", Op::CSub => "CSUB", Op::CMul => "CMUL",
104 Op::CDiv => "CDIV", Op::CMod => "CMOD", Op::CNeg => "CNEG", Op::CInc => "CINC",
105 Op::CDec => "CDEC", Op::CMin => "CMIN", Op::CMax => "CMAX", Op::CAbs => "CABS",
106 Op::Cmp => "CMP", Op::CLt => "CLT", Op::CLe => "CLE", Op::CEq => "CEQ",
107 Op::CGt => "CGT", Op::CGe => "CGE", Op::And => "AND", Op::Or => "OR",
108 Op::Xor => "XOR", Op::Not => "NOT", Op::Shl => "SHL", Op::Shr => "SHR",
109 Op::FCvt => "FCVT", Op::FNeg => "FNEG", Op::FAdd => "FADD", Op::FSub => "FSUB",
110 Op::FMul => "FMUL", Op::FDiv => "FDIV", Op::Push => "PUSH", Op::Pop => "POP",
111 Op::Dup => "DUP", Op::Swap => "SWAP", Op::Load => "LOAD", Op::Store => "STORE",
112 Op::LoadF => "LOADF", Op::StoreF => "STOREF", Op::MAlloc => "MALLOC",
113 Op::MFree => "MFREE", Op::Conf => "CONF", Op::Fuse => "FUSE", Op::Drop => "DROP",
114 Op::Trust => "TRUST", Op::Gate => "GATE", Op::MovI => "MOVI", Op::AddI => "ADDI",
115 Op::Tell => "TELL", Op::Ask => "ASK", Op::Broadcast => "BROADCAST",
116 Op::Listen => "LISTEN", Op::Delegate => "DELEGATE",
117 Op::InstinctAct => "INSTINCT_ACT", Op::InstinctQ => "INSTINCT_Q",
118 Op::GeneExpr => "GENE_EXPR", Op::EnzymeBind => "ENZYME_BIND",
119 Op::RnaTrans => "RNA_TRANS", Op::ProteinFold => "PROTEIN_FOLD",
120 Op::MembraneChk => "MEMBRANE_CHK", Op::Quarantine => "QUARANTINE",
121 Op::AtpGen => "ATP_GEN", Op::AtpConsume => "ATP_CONSUME", Op::AtpQ => "ATP_Q",
122 Op::AtpTransfer => "ATP_TRANSFER", Op::ApoptosisChk => "APOPTOSIS_CHK",
123 Op::ApoptosisTrigger => "APOPTOSIS_TRIGGER", Op::CircadianSet => "CIRCADIAN_SET",
124 Op::CircadianGet => "CIRCADIAN_GET", Op::SysCall => "SYSCALL",
125 Op::Debug => "DEBUG", Op::Yield => "YIELD",
126 }
127 }
128}
129
130fn parse_reg(s: &str) -> Option<u8> {
132 let s = s.trim().trim_end_matches(',');
133 if s.starts_with('R') || s.starts_with('r') {
134 s[1..].parse::<u8>().ok().filter(|&r| r <= 15)
135 } else {
136 None
137 }
138}
139
140#[derive(Debug)]
142pub enum AsmError {
143 UnknownOpcode(String),
144 BadRegister(String),
145 BadImmediate(String),
146 UndefinedLabel(String),
147 DuplicateLabel(String),
148 ParseError(String),
149}
150
151impl fmt::Display for AsmError {
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 match self {
154 AsmError::UnknownOpcode(s) => write!(f, "unknown opcode: {}", s),
155 AsmError::BadRegister(s) => write!(f, "bad register: {}", s),
156 AsmError::BadImmediate(s) => write!(f, "bad immediate: {}", s),
157 AsmError::UndefinedLabel(s) => write!(f, "undefined label: {}", s),
158 AsmError::DuplicateLabel(s) => write!(f, "duplicate label: {}", s),
159 AsmError::ParseError(s) => write!(f, "parse error: {}", s),
160 }
161 }
162}
163
164pub struct Assembler {
166 labels: HashMap<String, usize>,
167 data: Vec<u8>,
168}
169
170impl Assembler {
171 pub fn new() -> Self { Assembler { labels: HashMap::new(), data: vec![] } }
172
173 pub fn assemble(&mut self, source: &str) -> Result<Vec<u8>, Vec<AsmError>> {
175 let mut errors = vec![];
176 let lines: Vec<&str> = source.lines().collect();
177
178 let mut pc = 0usize;
180 for line in &lines {
181 let line = line.split(';').next().unwrap_or("").trim();
182 if line.is_empty() { continue; }
183 if line.to_uppercase().starts_with("LABEL ") || line.ends_with(':') {
184 let upper = line.to_uppercase();
186 let name = if upper.starts_with("LABEL ") {
187 line[6..].trim().trim_end_matches(':').trim()
188 } else {
189 line.trim_end_matches(':').trim()
190 };
191 if self.labels.contains_key(name) {
192 errors.push(AsmError::DuplicateLabel(name.to_string()));
193 }
194 self.labels.insert(name.to_string(), pc);
195 continue;
196 }
197 if line.to_uppercase().starts_with("DATA ") {
198 let bytes_str = &line[5..];
200 let count = bytes_str.split(',').filter(|s| !s.trim().is_empty()).count();
201 pc += count;
202 continue;
203 }
204 let size = self.instr_size(line);
205 if size == 0 && !line.is_empty() {
206 if errors.len() < 10 { errors.push(AsmError::ParseError(format!("unknown: {}", line))); }
207 }
208 pc += size;
209 }
210 if !errors.is_empty() { return Err(errors); }
211
212 self.data = vec![];
214 for line in &lines {
215 let line = line.split(';').next().unwrap_or("").trim();
216 if line.is_empty() { continue; }
217 if line.to_uppercase().starts_with("LABEL ") || line.ends_with(':') { continue; }
218 if line.to_uppercase().starts_with("DATA ") {
219 self.emit_data(&line[5..]);
220 continue;
221 }
222 self.emit_instruction(line, &mut errors);
223 }
224 if !errors.is_empty() { return Err(errors); }
225 Ok(self.data.clone())
226 }
227
228 fn instr_size(&self, line: &str) -> usize {
229 let parts: Vec<&str> = line.split_whitespace().collect();
230 if parts.is_empty() { return 0; }
231 let op_name = parts[0].to_uppercase();
232 let op = match Op::from_name(&op_name) {
233 Some(o) => o,
234 None => return 0,
235 };
236 match op {
237 Op::Nop | Op::Halt | Op::Ret | Op::Yield | Op::Swap => 1,
239 Op::CInc | Op::CDec | Op::CNeg | Op::CAbs | Op::Not |
241 Op::Push | Op::Pop | Op::Dup |
242 Op::InstinctQ | Op::AtpQ | Op::CircadianGet |
243 Op::ApoptosisChk | Op::ApoptosisTrigger | Op::Debug => 2,
244 Op::CAdd | Op::CSub | Op::CMul | Op::CDiv | Op::CMod |
246 Op::CMin | Op::CMax | Op::Cmp | Op::CLt | Op::CLe | Op::CEq | Op::CGt | Op::CGe |
247 Op::And | Op::Or | Op::Xor | Op::Shl | Op::Shr |
248 Op::FCvt | Op::FNeg | Op::FAdd | Op::FSub | Op::FMul | Op::FDiv |
249 Op::Load | Op::Store | Op::LoadF | Op::StoreF |
250 Op::Conf | Op::Fuse | Op::Trust | Op::Gate |
251 Op::GeneExpr | Op::EnzymeBind | Op::RnaTrans | Op::ProteinFold |
252 Op::MembraneChk | Op::Quarantine |
253 Op::AtpGen | Op::AtpConsume | Op::AtpTransfer |
254 Op::CircadianSet | Op::InstinctAct |
255 Op::Tell | Op::Ask | Op::Listen | Op::Delegate => 3,
256 Op::MovI | Op::AddI | Op::MAlloc | Op::Drop | Op::Jz | Op::Jnz |
258 Op::Je | Op::Jne => 4,
259 Op::Jmp | Op::Call => 4,
261 Op::Broadcast => 5,
263 Op::SysCall => 3,
265 Op::MFree => 2,
266 }
267 }
268
269 fn emit_data(&mut self, s: &str) {
270 for part in s.split(',') {
271 let trimmed = part.trim();
272 if let Ok(b) = trimmed.parse::<u8>() { self.data.push(b); }
273 }
274 }
275
276 fn emit_instruction(&mut self, line: &str, errors: &mut Vec<AsmError>) {
277 let parts: Vec<&str> = line.split_whitespace().collect();
278 if parts.is_empty() { return; }
279 let op = match Op::from_name(parts[0]) {
280 Some(o) => o,
281 None => { errors.push(AsmError::UnknownOpcode(parts[0].to_string())); return; }
282 };
283
284 let get_reg = |idx: usize, errs: &mut Vec<AsmError>| -> u8 {
285 if idx >= parts.len() { errs.push(AsmError::BadRegister("missing".into())); return 0; }
286 parse_reg(parts[idx]).unwrap_or_else(|| { errs.push(AsmError::BadRegister(parts[idx].to_string())); 0 })
287 };
288
289 let get_imm = |idx: usize, errs: &mut Vec<AsmError>| -> i16 {
290 if idx >= parts.len() { errs.push(AsmError::BadImmediate("missing".into())); return 0; }
291 if let Some(&addr) = self.labels.get(parts[idx]) {
293 return 0;
295 }
296 let s = parts[idx].trim_end_matches(',');
298 s.parse::<i16>().unwrap_or_else(|_| {
299 if s.starts_with("0x") { i16::from_str_radix(&s[2..], 16).unwrap_or(0) }
301 else { 0 }
302 })
303 };
304
305 let get_imm_from_label = |idx: usize, errs: &mut Vec<AsmError>, labels: &HashMap<String,usize>, data_len: usize| -> i16 {
306 if idx >= parts.len() { return 0; }
307 let s = parts[idx].trim_end_matches(',');
308 if let Some(&addr) = labels.get(s) {
309 let instr_end = data_len + 4; let offset = addr as isize - instr_end as isize;
312 return offset as i16;
313 }
314 s.parse::<i16>().unwrap_or_else(|_| {
315 if s.starts_with("0x") { i16::from_str_radix(&s[2..], 16).unwrap_or(0) }
316 else { 0 }
317 })
318 };
319 let labels_clone = self.labels.clone();
320
321 self.data.push(op as u8);
322
323 match op {
324 Op::Nop | Op::Halt | Op::Ret | Op::Yield | Op::Swap => {}
325 Op::CInc | Op::CDec | Op::CNeg | Op::CAbs | Op::Not |
326 Op::Push | Op::Pop | Op::Dup | Op::MFree |
327 Op::InstinctQ | Op::AtpQ | Op::CircadianGet |
328 Op::ApoptosisChk | Op::ApoptosisTrigger | Op::Debug => {
329 self.data.push(get_reg(1, errors));
330 }
331 Op::CAdd | Op::CSub | Op::CMul | Op::CDiv | Op::CMod |
332 Op::CMin | Op::CMax | Op::Cmp | Op::CLt | Op::CLe | Op::CEq | Op::CGt | Op::CGe |
333 Op::And | Op::Or | Op::Xor | Op::Shl | Op::Shr |
334 Op::FCvt | Op::FNeg | Op::FAdd | Op::FSub | Op::FMul | Op::FDiv |
335 Op::Load | Op::Store | Op::LoadF | Op::StoreF |
336 Op::Conf | Op::Fuse | Op::Trust | Op::Gate |
337 Op::GeneExpr | Op::EnzymeBind | Op::RnaTrans | Op::ProteinFold |
338 Op::MembraneChk | Op::Quarantine |
339 Op::AtpGen | Op::AtpConsume | Op::AtpTransfer |
340 Op::CircadianSet | Op::InstinctAct |
341 Op::Tell | Op::Ask | Op::Listen | Op::Delegate => {
342 self.data.push(get_reg(1, errors));
343 self.data.push(get_reg(2, errors));
344 }
345 Op::MovI | Op::AddI | Op::MAlloc | Op::Drop => {
346 self.data.push(get_reg(1, errors));
347 let imm = get_imm_from_label(2, errors, &labels_clone, self.data.len());
348 self.data.push((imm & 0xFF) as u8);
349 self.data.push(((imm >> 8) & 0xFF) as u8);
350 }
351 Op::Jz | Op::Jnz | Op::Je | Op::Jne => {
352 self.data.push(get_reg(1, errors));
353 let imm = get_imm_from_label(2, errors, &labels_clone, self.data.len());
354 self.data.push((imm & 0xFF) as u8);
355 self.data.push(((imm >> 8) & 0xFF) as u8);
356 }
357 Op::Jmp | Op::Call => {
358 self.data.push(0); let imm = get_imm_from_label(1, errors, &labels_clone, self.data.len());
360 self.data.push((imm & 0xFF) as u8);
361 self.data.push(((imm >> 8) & 0xFF) as u8);
362 }
363 Op::Broadcast => {
364 self.data.push(get_reg(1, errors));
365 self.data.push(0); self.data.push(0); }
368 Op::SysCall => {
369 let n = get_imm_from_label(1, errors, &labels_clone, self.data.len());
370 self.data.push(n as u8);
371 self.data.push(0);
372 }
373 }
374 }
375
376 pub fn disassemble(&self, bytecode: &[u8]) -> String {
378 let mut out = String::new();
379 let mut pc = 0;
380 while pc < bytecode.len() {
381 let op_byte = bytecode[pc];
382 let op = Self::op_from_byte(op_byte);
384 let name = op.map(|o| o.format()).unwrap_or("???");
385 out.push_str(&format!("{:04x}: {}\n", pc, name));
386 pc += match op {
387 None => 1,
388 Some(o) => self.op_size(o),
389 };
390 }
391 out
392 }
393
394 fn op_from_byte(b: u8) -> Option<Op> {
395 match b {
397 0x00 => Some(Op::Nop), 0x01 => Some(Op::Halt), 0x02 => Some(Op::Jmp),
398 0x03 => Some(Op::Jz), 0x04 => Some(Op::Jnz), 0x05 => Some(Op::Je),
399 0x06 => Some(Op::Jne), 0x07 => Some(Op::Call), 0x08 => Some(Op::Ret),
400 0x10 => Some(Op::CAdd), 0x11 => Some(Op::CSub), 0x12 => Some(Op::CMul),
401 0x13 => Some(Op::CDiv), 0x14 => Some(Op::CMod), 0x15 => Some(Op::CNeg),
402 0x16 => Some(Op::CInc), 0x17 => Some(Op::CDec), 0x18 => Some(Op::CMin),
403 0x19 => Some(Op::CMax), 0x1A => Some(Op::CAbs),
404 0x20 => Some(Op::Cmp), 0x21 => Some(Op::CLt), 0x22 => Some(Op::CLe),
405 0x23 => Some(Op::CEq), 0x24 => Some(Op::CGt), 0x25 => Some(Op::CGe),
406 0x30 => Some(Op::And), 0x31 => Some(Op::Or), 0x32 => Some(Op::Xor),
407 0x33 => Some(Op::Not), 0x34 => Some(Op::Shl), 0x35 => Some(Op::Shr),
408 0x38 => Some(Op::FCvt), 0x39 => Some(Op::FNeg), 0x3A => Some(Op::FAdd),
409 0x3B => Some(Op::FSub), 0x3C => Some(Op::FMul), 0x3D => Some(Op::FDiv),
410 0x40 => Some(Op::Push), 0x41 => Some(Op::Pop), 0x42 => Some(Op::Dup),
411 0x43 => Some(Op::Swap),
412 0x48 => Some(Op::Load), 0x49 => Some(Op::Store), 0x4A => Some(Op::LoadF),
413 0x4B => Some(Op::StoreF), 0x4C => Some(Op::MAlloc), 0x4D => Some(Op::MFree),
414 0x50 => Some(Op::Conf), 0x51 => Some(Op::Fuse), 0x52 => Some(Op::Drop),
415 0x53 => Some(Op::Trust), 0x54 => Some(Op::Gate),
416 0x58 => Some(Op::MovI), 0x59 => Some(Op::AddI),
417 0x60 => Some(Op::Tell), 0x61 => Some(Op::Ask), 0x62 => Some(Op::Broadcast),
418 0x63 => Some(Op::Listen), 0x64 => Some(Op::Delegate),
419 0x68 => Some(Op::InstinctAct), 0x69 => Some(Op::InstinctQ),
420 0x6A => Some(Op::GeneExpr), 0x6B => Some(Op::EnzymeBind),
421 0x6C => Some(Op::RnaTrans), 0x6D => Some(Op::ProteinFold),
422 0x6E => Some(Op::MembraneChk), 0x6F => Some(Op::Quarantine),
423 0x70 => Some(Op::AtpGen), 0x71 => Some(Op::AtpConsume),
424 0x72 => Some(Op::AtpQ), 0x73 => Some(Op::AtpTransfer),
425 0x74 => Some(Op::ApoptosisChk), 0x75 => Some(Op::ApoptosisTrigger),
426 0x76 => Some(Op::CircadianSet), 0x77 => Some(Op::CircadianGet),
427 0x78 => Some(Op::SysCall), 0x79 => Some(Op::Debug), 0x7A => Some(Op::Yield),
428 _ => None,
429 }
430 }
431
432 fn op_size(&self, op: Op) -> usize {
433 let dummy = format!("{} R0", op.format());
435 self.instr_size(&dummy).max(1)
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn test_simple_program() {
445 let mut asm = Assembler::new();
446 let src = "MOVI R0, 42\nCINC R0\nHALT";
447 let bc = asm.assemble(src).unwrap();
448 assert!(!bc.is_empty());
449 assert_eq!(bc[0], Op::MovI as u8);
450 assert_eq!(bc[4], Op::CInc as u8);
451 assert_eq!(bc[6], Op::Halt as u8);
452 }
453
454 #[test]
455 fn test_labels() {
456 let mut asm = Assembler::new();
457 let src = "LABEL start:\nMOVI R0, 10\nLABEL end:\nHALT";
458 let bc = asm.assemble(src).unwrap();
459 assert_eq!(asm.labels["start"], 0);
460 assert_eq!(asm.labels["end"], 4); assert_eq!(bc[4], Op::Halt as u8);
462 }
463
464 #[test]
465 fn test_jump_to_label() {
466 let mut asm = Assembler::new();
467 let src = "LABEL loop:\nCDEC R0\nJNZ R0, loop\nHALT";
468 let bc = asm.assemble(src).unwrap();
469 assert!(bc.len() > 0);
470 }
471
472 #[test]
473 fn test_comments() {
474 let mut asm = Assembler::new();
475 let src = "; this is a comment\nNOP ; inline comment\nHALT";
476 let bc = asm.assemble(src).unwrap();
477 assert_eq!(bc[0], Op::Nop as u8);
478 assert_eq!(bc[1], Op::Halt as u8);
479 assert_eq!(bc.len(), 2);
480 }
481
482 #[test]
483 fn test_data_directive() {
484 let mut asm = Assembler::new();
485 let src = "DATA 1, 2, 3, 42\nHALT";
486 let bc = asm.assemble(src).unwrap();
487 assert_eq!(&bc[0..4], &[1, 2, 3, 42]);
488 assert_eq!(bc[4], Op::Halt as u8);
489 }
490
491 #[test]
492 fn test_confidence_ops() {
493 let mut asm = Assembler::new();
494 let src = "CONF R0, R1\nFUSE R0, R1\nTRUST R0, R1";
495 let bc = asm.assemble(src).unwrap();
496 assert_eq!(bc[0], Op::Conf as u8);
497 assert_eq!(bc[3], Op::Fuse as u8);
498 assert_eq!(bc[6], Op::Trust as u8);
499 }
500
501 #[test]
502 fn test_biological_ops() {
503 let mut asm = Assembler::new();
504 let src = "INSTINCT_ACT R0, R1\nGENE_EXPR R0, R1\nATP_GEN R0, R1\nMEMBRANE_CHK R0, R1";
505 let bc = asm.assemble(src).unwrap();
506 assert_eq!(bc[0], 0x68); assert_eq!(bc[3], 0x6A); assert_eq!(bc[6], 0x70); assert_eq!(bc[9], 0x6E); }
511
512 #[test]
513 fn test_a2a_ops() {
514 let mut asm = Assembler::new();
515 let src = "TELL R0, R1\nASK R0, R1\nBROADCAST R0";
516 let bc = asm.assemble(src).unwrap();
517 assert_eq!(bc[0], 0x60);
518 assert_eq!(bc[3], 0x61);
519 assert_eq!(bc[6], 0x62);
520 }
521
522 #[test]
523 fn test_arithmetic() {
524 let mut asm = Assembler::new();
525 let src = "MOVI R0, 10\nMOVI R1, 3\nCADD R0, R1\nCDIV R0, R1";
526 let bc = asm.assemble(src).unwrap();
527 assert_eq!(bc[0], Op::MovI as u8);
528 assert_eq!(bc[4], Op::MovI as u8);
529 assert_eq!(bc[8], Op::CAdd as u8);
530 assert_eq!(bc[11], Op::CDiv as u8);
531 }
532
533 #[test]
534 fn test_case_insensitive() {
535 let mut asm = Assembler::new();
536 let bc1 = asm.assemble("NOP\nhalt").unwrap();
537 let mut asm2 = Assembler::new();
538 let bc2 = asm2.assemble("nop\nHALT").unwrap();
539 assert_eq!(bc1, bc2);
540 }
541
542 #[test]
543 fn test_unknown_opcode() {
544 let mut asm = Assembler::new();
545 let result = asm.assemble("FOOBAR R0, R1");
546 assert!(result.is_err());
547 }
548
549 #[test]
550 fn test_disassemble() {
551 let mut asm = Assembler::new();
552 let bc = asm.assemble("NOP\nMOVI R0, 42\nHALT").unwrap();
553 let out = asm.disassemble(&bc);
554 assert!(out.contains("NOP"));
555 assert!(out.contains("MOVI"));
556 assert!(out.contains("HALT"));
557 }
558
559 #[test]
560 fn test_stack_ops() {
561 let mut asm = Assembler::new();
562 let src = "PUSH R0\nPUSH R1\nSWAP\nPOP R0";
563 let bc = asm.assemble(src).unwrap();
564 assert_eq!(bc[0], 0x40); assert_eq!(bc[2], 0x40); assert_eq!(bc[4], 0x43); assert_eq!(bc[5], 0x41); }
569
570 #[test]
571 fn test_memory_ops() {
572 let mut asm = Assembler::new();
573 let src = "MOVI R0, 0\nMOVI R1, 42\nSTORE R1, R0";
574 let bc = asm.assemble(src).unwrap();
575 assert_eq!(bc[8], Op::Store as u8);
576 }
577
578 #[test]
579 fn test_call_ret() {
580 let mut asm = Assembler::new();
581 let src = "LABEL func:\nRET\nCALL func\nHALT";
582 let bc = asm.assemble(src).unwrap();
583 assert_eq!(bc[0], Op::Ret as u8);
584 assert_eq!(bc[1], Op::Call as u8);
585 }
586}