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 #[allow(unused_variables)]
175 pub fn assemble(&mut self, source: &str) -> Result<Vec<u8>, Vec<AsmError>> {
176 let mut errors = vec![];
177 let lines: Vec<&str> = source.lines().collect();
178
179 let mut pc = 0usize;
181 for line in &lines {
182 let line = line.split(';').next().unwrap_or("").trim();
183 if line.is_empty() { continue; }
184 if line.to_uppercase().starts_with("LABEL ") || line.ends_with(':') {
185 let upper = line.to_uppercase();
187 let name = if upper.starts_with("LABEL ") {
188 line[6..].trim().trim_end_matches(':').trim()
189 } else {
190 line.trim_end_matches(':').trim()
191 };
192 if self.labels.contains_key(name) {
193 errors.push(AsmError::DuplicateLabel(name.to_string()));
194 }
195 self.labels.insert(name.to_string(), pc);
196 continue;
197 }
198 if line.to_uppercase().starts_with("DATA ") {
199 let bytes_str = &line[5..];
201 let count = bytes_str.split(',').filter(|s| !s.trim().is_empty()).count();
202 pc += count;
203 continue;
204 }
205 let size = self.instr_size(line);
206 if size == 0 && !line.is_empty() {
207 if errors.len() < 10 { errors.push(AsmError::ParseError(format!("unknown: {}", line))); }
208 }
209 pc += size;
210 }
211 if !errors.is_empty() { return Err(errors); }
212
213 self.data = vec![];
215 for line in &lines {
216 let line = line.split(';').next().unwrap_or("").trim();
217 if line.is_empty() { continue; }
218 if line.to_uppercase().starts_with("LABEL ") || line.ends_with(':') { continue; }
219 if line.to_uppercase().starts_with("DATA ") {
220 self.emit_data(&line[5..]);
221 continue;
222 }
223 self.emit_instruction(line, &mut errors);
224 }
225 if !errors.is_empty() { return Err(errors); }
226 Ok(self.data.clone())
227 }
228
229 fn instr_size(&self, line: &str) -> usize {
230 let parts: Vec<&str> = line.split_whitespace().collect();
231 if parts.is_empty() { return 0; }
232 let op_name = parts[0].to_uppercase();
233 let op = match Op::from_name(&op_name) {
234 Some(o) => o,
235 None => return 0,
236 };
237 match op {
238 Op::Nop | Op::Halt | Op::Ret | Op::Yield | Op::Swap => 1,
240 Op::CInc | Op::CDec | Op::CNeg | Op::CAbs | Op::Not |
242 Op::Push | Op::Pop | Op::Dup |
243 Op::InstinctQ | Op::AtpQ | Op::CircadianGet |
244 Op::ApoptosisChk | Op::ApoptosisTrigger | Op::Debug => 2,
245 Op::CAdd | Op::CSub | Op::CMul | Op::CDiv | Op::CMod |
247 Op::CMin | Op::CMax | Op::Cmp | Op::CLt | Op::CLe | Op::CEq | Op::CGt | Op::CGe |
248 Op::And | Op::Or | Op::Xor | Op::Shl | Op::Shr |
249 Op::FCvt | Op::FNeg | Op::FAdd | Op::FSub | Op::FMul | Op::FDiv |
250 Op::Load | Op::Store | Op::LoadF | Op::StoreF |
251 Op::Conf | Op::Fuse | Op::Trust | Op::Gate |
252 Op::GeneExpr | Op::EnzymeBind | Op::RnaTrans | Op::ProteinFold |
253 Op::MembraneChk | Op::Quarantine |
254 Op::AtpGen | Op::AtpConsume | Op::AtpTransfer |
255 Op::CircadianSet | Op::InstinctAct |
256 Op::Tell | Op::Ask | Op::Listen | Op::Delegate => 3,
257 Op::MovI | Op::AddI | Op::MAlloc | Op::Drop | Op::Jz | Op::Jnz |
259 Op::Je | Op::Jne => 4,
260 Op::Jmp | Op::Call => 4,
262 Op::Broadcast => 5,
264 Op::SysCall => 3,
266 Op::MFree => 2,
267 }
268 }
269
270 fn emit_data(&mut self, s: &str) {
271 for part in s.split(',') {
272 let trimmed = part.trim();
273 if let Ok(b) = trimmed.parse::<u8>() { self.data.push(b); }
274 }
275 }
276
277 fn emit_instruction(&mut self, line: &str, errors: &mut Vec<AsmError>) {
278 let parts: Vec<&str> = line.split_whitespace().collect();
279 if parts.is_empty() { return; }
280 let op = match Op::from_name(parts[0]) {
281 Some(o) => o,
282 None => { errors.push(AsmError::UnknownOpcode(parts[0].to_string())); return; }
283 };
284
285 let get_reg = |idx: usize, errs: &mut Vec<AsmError>| -> u8 {
286 if idx >= parts.len() { errs.push(AsmError::BadRegister("missing".into())); return 0; }
287 parse_reg(parts[idx]).unwrap_or_else(|| { errs.push(AsmError::BadRegister(parts[idx].to_string())); 0 })
288 };
289
290 let get_imm = |idx: usize, errs: &mut Vec<AsmError>| -> i16 {
291 if idx >= parts.len() { errs.push(AsmError::BadImmediate("missing".into())); return 0; }
292 if let Some(addr) = self.labels.get(parts[idx]) {
294 return 0;
296 }
297 let s = parts[idx].trim_end_matches(',');
299 s.parse::<i16>().unwrap_or_else(|_| {
300 if s.starts_with("0x") { i16::from_str_radix(&s[2..], 16).unwrap_or(0) }
302 else { 0 }
303 })
304 };
305
306 let get_imm_from_label = |idx: usize, errs: &mut Vec<AsmError>, labels: &HashMap<String,usize>, data_len: usize| -> i16 {
307 if idx >= parts.len() { return 0; }
308 let s = parts[idx].trim_end_matches(',');
309 if let Some(&addr) = labels.get(s) {
310 let instr_end = data_len + 4; let offset = addr as isize - instr_end as isize;
313 return offset as i16;
314 }
315 s.parse::<i16>().unwrap_or_else(|_| {
316 if s.starts_with("0x") { i16::from_str_radix(&s[2..], 16).unwrap_or(0) }
317 else { 0 }
318 })
319 };
320 let labels_clone = self.labels.clone();
321
322 self.data.push(op as u8);
323
324 match op {
325 Op::Nop | Op::Halt | Op::Ret | Op::Yield | Op::Swap => {}
326 Op::CInc | Op::CDec | Op::CNeg | Op::CAbs | Op::Not |
327 Op::Push | Op::Pop | Op::Dup | Op::MFree |
328 Op::InstinctQ | Op::AtpQ | Op::CircadianGet |
329 Op::ApoptosisChk | Op::ApoptosisTrigger | Op::Debug => {
330 self.data.push(get_reg(1, errors));
331 }
332 Op::CAdd | Op::CSub | Op::CMul | Op::CDiv | Op::CMod |
333 Op::CMin | Op::CMax | Op::Cmp | Op::CLt | Op::CLe | Op::CEq | Op::CGt | Op::CGe |
334 Op::And | Op::Or | Op::Xor | Op::Shl | Op::Shr |
335 Op::FCvt | Op::FNeg | Op::FAdd | Op::FSub | Op::FMul | Op::FDiv |
336 Op::Load | Op::Store | Op::LoadF | Op::StoreF |
337 Op::Conf | Op::Fuse | Op::Trust | Op::Gate |
338 Op::GeneExpr | Op::EnzymeBind | Op::RnaTrans | Op::ProteinFold |
339 Op::MembraneChk | Op::Quarantine |
340 Op::AtpGen | Op::AtpConsume | Op::AtpTransfer |
341 Op::CircadianSet | Op::InstinctAct |
342 Op::Tell | Op::Ask | Op::Listen | Op::Delegate => {
343 self.data.push(get_reg(1, errors));
344 self.data.push(get_reg(2, errors));
345 }
346 Op::MovI | Op::AddI | Op::MAlloc | Op::Drop => {
347 self.data.push(get_reg(1, errors));
348 let imm = get_imm_from_label(2, errors, &labels_clone, self.data.len());
349 self.data.push((imm & 0xFF) as u8);
350 self.data.push(((imm >> 8) & 0xFF) as u8);
351 }
352 Op::Jz | Op::Jnz | Op::Je | Op::Jne => {
353 self.data.push(get_reg(1, errors));
354 let imm = get_imm_from_label(2, errors, &labels_clone, self.data.len());
355 self.data.push((imm & 0xFF) as u8);
356 self.data.push(((imm >> 8) & 0xFF) as u8);
357 }
358 Op::Jmp | Op::Call => {
359 self.data.push(0); let imm = get_imm_from_label(1, errors, &labels_clone, self.data.len());
361 self.data.push((imm & 0xFF) as u8);
362 self.data.push(((imm >> 8) & 0xFF) as u8);
363 }
364 Op::Broadcast => {
365 self.data.push(get_reg(1, errors));
366 self.data.push(0); self.data.push(0); }
369 Op::SysCall => {
370 let n = get_imm_from_label(1, errors, &labels_clone, self.data.len());
371 self.data.push(n as u8);
372 self.data.push(0);
373 }
374 }
375 }
376
377 pub fn disassemble(&self, bytecode: &[u8]) -> String {
379 let mut out = String::new();
380 let mut pc = 0;
381 while pc < bytecode.len() {
382 let op_byte = bytecode[pc];
383 let op = Self::op_from_byte(op_byte);
385 let name = op.map(|o| o.format()).unwrap_or("???");
386 out.push_str(&format!("{:04x}: {}\n", pc, name));
387 pc += match op {
388 None => 1,
389 Some(o) => self.op_size(o),
390 };
391 }
392 out
393 }
394
395 fn op_from_byte(b: u8) -> Option<Op> {
396 match b {
398 0x00 => Some(Op::Nop), 0x01 => Some(Op::Halt), 0x02 => Some(Op::Jmp),
399 0x03 => Some(Op::Jz), 0x04 => Some(Op::Jnz), 0x05 => Some(Op::Je),
400 0x06 => Some(Op::Jne), 0x07 => Some(Op::Call), 0x08 => Some(Op::Ret),
401 0x10 => Some(Op::CAdd), 0x11 => Some(Op::CSub), 0x12 => Some(Op::CMul),
402 0x13 => Some(Op::CDiv), 0x14 => Some(Op::CMod), 0x15 => Some(Op::CNeg),
403 0x16 => Some(Op::CInc), 0x17 => Some(Op::CDec), 0x18 => Some(Op::CMin),
404 0x19 => Some(Op::CMax), 0x1A => Some(Op::CAbs),
405 0x20 => Some(Op::Cmp), 0x21 => Some(Op::CLt), 0x22 => Some(Op::CLe),
406 0x23 => Some(Op::CEq), 0x24 => Some(Op::CGt), 0x25 => Some(Op::CGe),
407 0x30 => Some(Op::And), 0x31 => Some(Op::Or), 0x32 => Some(Op::Xor),
408 0x33 => Some(Op::Not), 0x34 => Some(Op::Shl), 0x35 => Some(Op::Shr),
409 0x38 => Some(Op::FCvt), 0x39 => Some(Op::FNeg), 0x3A => Some(Op::FAdd),
410 0x3B => Some(Op::FSub), 0x3C => Some(Op::FMul), 0x3D => Some(Op::FDiv),
411 0x40 => Some(Op::Push), 0x41 => Some(Op::Pop), 0x42 => Some(Op::Dup),
412 0x43 => Some(Op::Swap),
413 0x48 => Some(Op::Load), 0x49 => Some(Op::Store), 0x4A => Some(Op::LoadF),
414 0x4B => Some(Op::StoreF), 0x4C => Some(Op::MAlloc), 0x4D => Some(Op::MFree),
415 0x50 => Some(Op::Conf), 0x51 => Some(Op::Fuse), 0x52 => Some(Op::Drop),
416 0x53 => Some(Op::Trust), 0x54 => Some(Op::Gate),
417 0x58 => Some(Op::MovI), 0x59 => Some(Op::AddI),
418 0x60 => Some(Op::Tell), 0x61 => Some(Op::Ask), 0x62 => Some(Op::Broadcast),
419 0x63 => Some(Op::Listen), 0x64 => Some(Op::Delegate),
420 0x68 => Some(Op::InstinctAct), 0x69 => Some(Op::InstinctQ),
421 0x6A => Some(Op::GeneExpr), 0x6B => Some(Op::EnzymeBind),
422 0x6C => Some(Op::RnaTrans), 0x6D => Some(Op::ProteinFold),
423 0x6E => Some(Op::MembraneChk), 0x6F => Some(Op::Quarantine),
424 0x70 => Some(Op::AtpGen), 0x71 => Some(Op::AtpConsume),
425 0x72 => Some(Op::AtpQ), 0x73 => Some(Op::AtpTransfer),
426 0x74 => Some(Op::ApoptosisChk), 0x75 => Some(Op::ApoptosisTrigger),
427 0x76 => Some(Op::CircadianSet), 0x77 => Some(Op::CircadianGet),
428 0x78 => Some(Op::SysCall), 0x79 => Some(Op::Debug), 0x7A => Some(Op::Yield),
429 _ => None,
430 }
431 }
432
433 fn op_size(&self, op: Op) -> usize {
434 let dummy = format!("{} R0", op.format());
436 self.instr_size(&dummy).max(1)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_simple_program() {
446 let mut asm = Assembler::new();
447 let src = "MOVI R0, 42\nCINC R0\nHALT";
448 let bc = asm.assemble(src).unwrap();
449 assert!(!bc.is_empty());
450 assert_eq!(bc[0], Op::MovI as u8);
451 assert_eq!(bc[4], Op::CInc as u8);
452 assert_eq!(bc[6], Op::Halt as u8);
453 }
454
455 #[test]
456 fn test_labels() {
457 let mut asm = Assembler::new();
458 let src = "LABEL start:\nMOVI R0, 10\nLABEL end:\nHALT";
459 let bc = asm.assemble(src).unwrap();
460 assert_eq!(asm.labels["start"], 0);
461 assert_eq!(asm.labels["end"], 4); assert_eq!(bc[4], Op::Halt as u8);
463 }
464
465 #[test]
466 fn test_jump_to_label() {
467 let mut asm = Assembler::new();
468 let src = "LABEL loop:\nCDEC R0\nJNZ R0, loop\nHALT";
469 let bc = asm.assemble(src).unwrap();
470 assert!(bc.len() > 0);
471 }
472
473 #[test]
474 fn test_comments() {
475 let mut asm = Assembler::new();
476 let src = "; this is a comment\nNOP ; inline comment\nHALT";
477 let bc = asm.assemble(src).unwrap();
478 assert_eq!(bc[0], Op::Nop as u8);
479 assert_eq!(bc[1], Op::Halt as u8);
480 assert_eq!(bc.len(), 2);
481 }
482
483 #[test]
484 fn test_data_directive() {
485 let mut asm = Assembler::new();
486 let src = "DATA 1, 2, 3, 42\nHALT";
487 let bc = asm.assemble(src).unwrap();
488 assert_eq!(&bc[0..4], &[1, 2, 3, 42]);
489 assert_eq!(bc[4], Op::Halt as u8);
490 }
491
492 #[test]
493 fn test_confidence_ops() {
494 let mut asm = Assembler::new();
495 let src = "CONF R0, R1\nFUSE R0, R1\nTRUST R0, R1";
496 let bc = asm.assemble(src).unwrap();
497 assert_eq!(bc[0], Op::Conf as u8);
498 assert_eq!(bc[3], Op::Fuse as u8);
499 assert_eq!(bc[6], Op::Trust as u8);
500 }
501
502 #[test]
503 fn test_biological_ops() {
504 let mut asm = Assembler::new();
505 let src = "INSTINCT_ACT R0, R1\nGENE_EXPR R0, R1\nATP_GEN R0, R1\nMEMBRANE_CHK R0, R1";
506 let bc = asm.assemble(src).unwrap();
507 assert_eq!(bc[0], 0x68); assert_eq!(bc[3], 0x6A); assert_eq!(bc[6], 0x70); assert_eq!(bc[9], 0x6E); }
512
513 #[test]
514 fn test_a2a_ops() {
515 let mut asm = Assembler::new();
516 let src = "TELL R0, R1\nASK R0, R1\nBROADCAST R0";
517 let bc = asm.assemble(src).unwrap();
518 assert_eq!(bc[0], 0x60);
519 assert_eq!(bc[3], 0x61);
520 assert_eq!(bc[6], 0x62);
521 }
522
523 #[test]
524 fn test_arithmetic() {
525 let mut asm = Assembler::new();
526 let src = "MOVI R0, 10\nMOVI R1, 3\nCADD R0, R1\nCDIV R0, R1";
527 let bc = asm.assemble(src).unwrap();
528 assert_eq!(bc[0], Op::MovI as u8);
529 assert_eq!(bc[4], Op::MovI as u8);
530 assert_eq!(bc[8], Op::CAdd as u8);
531 assert_eq!(bc[11], Op::CDiv as u8);
532 }
533
534 #[test]
535 fn test_case_insensitive() {
536 let mut asm = Assembler::new();
537 let bc1 = asm.assemble("NOP\nhalt").unwrap();
538 let mut asm2 = Assembler::new();
539 let bc2 = asm2.assemble("nop\nHALT").unwrap();
540 assert_eq!(bc1, bc2);
541 }
542
543 #[test]
544 fn test_unknown_opcode() {
545 let mut asm = Assembler::new();
546 let result = asm.assemble("FOOBAR R0, R1");
547 assert!(result.is_err());
548 }
549
550 #[test]
551 fn test_disassemble() {
552 let mut asm = Assembler::new();
553 let bc = asm.assemble("NOP\nMOVI R0, 42\nHALT").unwrap();
554 let out = asm.disassemble(&bc);
555 assert!(out.contains("NOP"));
556 assert!(out.contains("MOVI"));
557 assert!(out.contains("HALT"));
558 }
559
560 #[test]
561 fn test_stack_ops() {
562 let mut asm = Assembler::new();
563 let src = "PUSH R0\nPUSH R1\nSWAP\nPOP R0";
564 let bc = asm.assemble(src).unwrap();
565 assert_eq!(bc[0], 0x40); assert_eq!(bc[2], 0x40); assert_eq!(bc[4], 0x43); assert_eq!(bc[5], 0x41); }
570
571 #[test]
572 fn test_memory_ops() {
573 let mut asm = Assembler::new();
574 let src = "MOVI R0, 0\nMOVI R1, 42\nSTORE R1, R0";
575 let bc = asm.assemble(src).unwrap();
576 assert_eq!(bc[8], Op::Store as u8);
577 }
578
579 #[test]
580 fn test_call_ret() {
581 let mut asm = Assembler::new();
582 let src = "LABEL func:\nRET\nCALL func\nHALT";
583 let bc = asm.assemble(src).unwrap();
584 assert_eq!(bc[0], Op::Ret as u8);
585 assert_eq!(bc[1], Op::Call as u8);
586 }
587}