Krolyxon
Commit 8d47920

Branches

Tags

Refs →

bench51srcassembler.rs

127 lines · 3.2 KB · Rust

1use crate::instructions::Instruction;
2use std::collections::HashMap;
3
4fn tokenize(line: &str) -> Vec<String> {
5 line.split(|c| c == ' ' || c == ',' || c == '\t')
6 .filter(|s| !s.is_empty())
7 .map(|s| s.to_lowercase())
8 .collect()
9}
10
11fn parse_reg(s: &str) -> u8 {
12 match s {
13 "a" => 0,
14 "b" => 1,
15 "c" => 2,
16 "d" => 3,
17 _ => panic!("Unknown register {}", s),
18 }
19}
20
21fn instr_size(tokens: &[String]) -> u16 {
22 match tokens[0].as_str() {
23 "mov" | "add" | "sub" | "jmp" | "jz" | "jnz" => 3,
24 "hlt" => 1,
25 _ => panic!("Unknown instruction {}", tokens[0])
26
27 }
28}
29
30
31fn first_pass(source: &str) -> HashMap<String, u16> {
32 let mut symbols = HashMap::new();
33 let mut addr: u16 = 0;
34
35 for line in source.lines() {
36 let line = line.trim();
37
38 // Ignoring comments and empty lines
39 if line.is_empty() || line.starts_with(";") {
40 continue;
41 }
42
43 // Labels (ends with ":")
44 if line.ends_with(":") {
45 let label = line.trim_end_matches(":").to_string();
46 symbols.insert(label, addr);
47 continue;
48 }
49
50 let tokens = tokenize(line);
51 addr += instr_size(&tokens);
52 }
53
54 symbols
55}
56
57pub fn assembler(source: &str) -> Vec<u8> {
58 let symbols = first_pass(source);
59 let mut bytes = Vec::new();
60
61 for (line_no, line) in source.lines().enumerate() {
62 let line = line.trim();
63
64 // Comments in assembly start with ";"
65 if line.is_empty() || line.starts_with(';') || line.ends_with(":") {
66 continue;
67 }
68
69 let tokens = tokenize(line);
70
71 match tokens[0].as_str() {
72 "mov" => {
73 // mov reg, imm
74 let reg = parse_reg(&tokens[1]);
75 let imm: u8 = tokens[2].parse().unwrap();
76
77 bytes.push(Instruction::MOV as u8);
78 bytes.push(reg);
79 bytes.push(imm);
80 }
81
82 "add" => {
83 // add a, b
84 let r1 = parse_reg(&tokens[1]);
85 let r2 = parse_reg(&tokens[2]);
86
87 bytes.push(Instruction::ADD as u8);
88 bytes.push(r1);
89 bytes.push(r2);
90 }
91
92 "sub" => {
93 // sub a, b
94 let r1 = parse_reg(&tokens[1]);
95 let r2 = parse_reg(&tokens[2]);
96
97 bytes.push(Instruction::SUB as u8);
98 bytes.push(r1);
99 bytes.push(r2);
100 }
101
102 "jmp" | "jz" | "jnz" => {
103 let opcode = match tokens[0].as_str() {
104 "jmp" => Instruction::JMP,
105 "jz" => Instruction::JZ,
106 "jnz" => Instruction::JNZ,
107 _ => unreachable!()
108 };
109
110 let label = &tokens[1];
111 let addr = *symbols.get(label).expect("Uknown label");
112
113 bytes.push(opcode as u8);
114 bytes.push((addr & 0xFF) as u8); // low
115 bytes.push((addr >> 8) as u8); // high
116 }
117
118 "hlt" => {
119 bytes.push(Instruction::HLT as u8);
120 }
121
122 _ => panic!("Line {}: unknown instruction", line_no + 1),
123 }
124 }
125
126 bytes
127}