Krolyxon
Commit 467ec78

Branches

Tags

Refs →

bench51srcassembler.rs

205 lines · 5.9 KB · Rust

1use crate::instructions::Instruction;
2use std::collections::HashMap;
3
4fn tokenize(line: &str) -> Vec<String> {
5 line.split([' ', ',', '\t'])
6 .filter(|s| !s.is_empty())
7 .map(|s| s.to_lowercase())
8 .collect()
9}
10
11// Returns corrensponding u8 value of the register
12fn parse_reg(s: &str) -> u8 {
13 match s {
14 "a" => 0,
15 "b" => 1,
16 "c" => 2,
17 "d" => 3,
18 _ => panic!("Unknown register {}", s),
19 }
20}
21
22fn is_reg(s: &str) -> bool {
23 matches!(s, "a" | "b" | "c" | "d")
24}
25
26fn instr_size(tokens: &[String]) -> u16 {
27 match tokens[0].as_str() {
28 "mov" | "add" | "sub" | "jmp" | "jz" | "jnz" | "cmp" | "mul" | "div" | "call" => 3,
29 "sys" => 2,
30 "hlt" | "ret" => 1,
31 _ => panic!("Unknown instruction {}", tokens[0]),
32 }
33}
34
35fn first_pass(source: &str) -> HashMap<String, u16> {
36 let mut symbols = HashMap::new();
37 let mut addr: u16 = 0;
38
39 for line in source.lines() {
40 let line = line.trim();
41
42 // Ignoring comments and empty lines
43 if line.is_empty() || line.starts_with(";") {
44 continue;
45 }
46
47 // Labels (ends with ":")
48 if line.ends_with(":") {
49 let label = line.trim_end_matches(":").to_string();
50 symbols.insert(label, addr);
51 continue;
52 }
53
54 let tokens = tokenize(line);
55 addr += instr_size(&tokens);
56 }
57
58 symbols
59}
60
61pub fn assembler(source: &str) -> Vec<u8> {
62 let symbols = first_pass(source);
63 let mut bytes = Vec::new();
64
65 for (line_no, line) in source.lines().enumerate() {
66 let line = line.trim();
67
68 // Comments in assembly start with ";"
69 if line.is_empty() || line.starts_with(';') || line.ends_with(":") {
70 continue;
71 }
72
73 let tokens = tokenize(line);
74
75 match tokens[0].as_str() {
76 "mov" => {
77 // mov reg, imm
78 let r1= parse_reg(&tokens[1]);
79 if is_reg(&tokens[2]) {
80 let r2 = parse_reg(&tokens[2]);
81 bytes.push(Instruction::MOV_RR as u8);
82 bytes.push(r1);
83 bytes.push(r2);
84 } else {
85 let imm: u8 = tokens[2].parse().unwrap();
86 bytes.push(Instruction::MOV_RI as u8);
87 bytes.push(r1);
88 bytes.push(imm);
89 }
90 }
91
92 "add" => {
93 // add a, b
94 let r1 = parse_reg(&tokens[1]);
95
96 if is_reg(&tokens[2]) {
97 let r2 = parse_reg(&tokens[2]);
98 bytes.push(Instruction::ADD_RR as u8);
99 bytes.push(r1);
100 bytes.push(r2);
101 } else {
102 let imm: u8 = tokens[2].parse().unwrap();
103 bytes.push(Instruction::ADD_RI as u8);
104 bytes.push(r1);
105 bytes.push(imm);
106 }
107
108 }
109
110 "sub" => {
111 // sub a, b
112 let r1 = parse_reg(&tokens[1]);
113 if is_reg(&tokens[2]) {
114 let r2 = parse_reg(&tokens[2]);
115 bytes.push(Instruction::SUB_RR as u8);
116 bytes.push(r1);
117 bytes.push(r2);
118 } else {
119 let imm: u8 = tokens[2].parse().unwrap();
120 bytes.push(Instruction::SUB_RI as u8);
121 bytes.push(r1);
122 bytes.push(imm);
123 }
124 }
125
126 "jmp" | "jz" | "jnz" => {
127 let opcode = match tokens[0].as_str() {
128 "jmp" => Instruction::JMP,
129 "jz" => Instruction::JZ,
130 "jnz" => Instruction::JNZ,
131 _ => unreachable!(),
132 };
133
134 let label = &tokens[1];
135 let addr = *symbols.get(label).expect("Uknown label");
136
137 bytes.push(opcode as u8);
138 bytes.push((addr & 0xFF) as u8); // low
139 bytes.push((addr >> 8) as u8); // high
140 }
141
142 "cmp" => {
143 let r1 = parse_reg(&tokens[1]);
144 if tokens[2].chars().all(|c| c.is_ascii_digit()) {
145 let imm: u8 = tokens[2].parse().unwrap();
146 bytes.push(Instruction::CMP_RI as u8);
147 bytes.push(r1);
148 bytes.push(imm);
149 } else {
150 let r2 = parse_reg(&tokens[2]);
151 bytes.push(Instruction::CMP_RR as u8);
152 bytes.push(r1);
153 bytes.push(r2);
154 }
155 }
156
157 "mul" => {
158 let r1 = parse_reg(&tokens[1]);
159 let r2 = parse_reg(&tokens[2]);
160
161 bytes.push(Instruction::MUL as u8);
162 bytes.push(r1);
163 bytes.push(r2);
164
165 }
166
167 "div" => {
168 let r1 = parse_reg(&tokens[1]);
169 let r2 = parse_reg(&tokens[2]);
170
171 bytes.push(Instruction::DIV as u8);
172 bytes.push(r1);
173 bytes.push(r2);
174 }
175
176 "call" => {
177 let addr = *symbols
178 .get(&tokens[1])
179 .expect("Unknown label");
180
181 bytes.push(Instruction::CALL as u8);
182 bytes.push((addr & 0xFF) as u8); // low
183 bytes.push((addr >> 8) as u8); // high
184 }
185
186 "ret" => {
187 bytes.push(Instruction::RET as u8);
188 }
189
190 "hlt" => {
191 bytes.push(Instruction::HLT as u8);
192 }
193
194 "sys" => {
195 let imm = tokens[1].parse().unwrap();
196 bytes.push(Instruction::SYS as u8);
197 bytes.push(imm);
198 }
199
200 _ => panic!("Line {}: unknown instruction", line_no + 1),
201 }
202 }
203
204 bytes
205}