Krolyxon
Commit 68d8540

Branches

Tags

Refs →

bench51srcassembler.rs

165 lines · 4.8 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" => 3,
29 "hlt" => 1,
30 _ => panic!("Unknown instruction {}", tokens[0]),
31 }
32}
33
34fn first_pass(source: &str) -> HashMap<String, u16> {
35 let mut symbols = HashMap::new();
36 let mut addr: u16 = 0;
37
38 for line in source.lines() {
39 let line = line.trim();
40
41 // Ignoring comments and empty lines
42 if line.is_empty() || line.starts_with(";") {
43 continue;
44 }
45
46 // Labels (ends with ":")
47 if line.ends_with(":") {
48 let label = line.trim_end_matches(":").to_string();
49 symbols.insert(label, addr);
50 continue;
51 }
52
53 let tokens = tokenize(line);
54 addr += instr_size(&tokens);
55 }
56
57 symbols
58}
59
60pub fn assembler(source: &str) -> Vec<u8> {
61 let symbols = first_pass(source);
62 let mut bytes = Vec::new();
63
64 for (line_no, line) in source.lines().enumerate() {
65 let line = line.trim();
66
67 // Comments in assembly start with ";"
68 if line.is_empty() || line.starts_with(';') || line.ends_with(":") {
69 continue;
70 }
71
72 let tokens = tokenize(line);
73
74 match tokens[0].as_str() {
75 "mov" => {
76 // mov reg, imm
77 let r1= parse_reg(&tokens[1]);
78 if is_reg(&tokens[2]) {
79 let r2 = parse_reg(&tokens[2]);
80 bytes.push(Instruction::MOV_RR as u8);
81 bytes.push(r1);
82 bytes.push(r2);
83 } else {
84 let imm: u8 = tokens[2].parse().unwrap();
85 bytes.push(Instruction::MOV_RI as u8);
86 bytes.push(r1);
87 bytes.push(imm);
88 }
89 }
90
91 "add" => {
92 // add a, b
93 let r1 = parse_reg(&tokens[1]);
94
95 if is_reg(&tokens[2]) {
96 let r2 = parse_reg(&tokens[2]);
97 bytes.push(Instruction::ADD_RR as u8);
98 bytes.push(r1);
99 bytes.push(r2);
100 } else {
101 let imm: u8 = tokens[2].parse().unwrap();
102 bytes.push(Instruction::ADD_RI as u8);
103 bytes.push(r1);
104 bytes.push(imm);
105 }
106
107 }
108
109 "sub" => {
110 // sub a, b
111 let r1 = parse_reg(&tokens[1]);
112 if is_reg(&tokens[2]) {
113 let r2 = parse_reg(&tokens[2]);
114 bytes.push(Instruction::SUB_RR as u8);
115 bytes.push(r1);
116 bytes.push(r2);
117 } else {
118 let imm: u8 = tokens[2].parse().unwrap();
119 bytes.push(Instruction::SUB_RI as u8);
120 bytes.push(r1);
121 bytes.push(imm);
122 }
123 }
124
125 "jmp" | "jz" | "jnz" => {
126 let opcode = match tokens[0].as_str() {
127 "jmp" => Instruction::JMP,
128 "jz" => Instruction::JZ,
129 "jnz" => Instruction::JNZ,
130 _ => unreachable!(),
131 };
132
133 let label = &tokens[1];
134 let addr = *symbols.get(label).expect("Uknown label");
135
136 bytes.push(opcode as u8);
137 bytes.push((addr & 0xFF) as u8); // low
138 bytes.push((addr >> 8) as u8); // high
139 }
140
141 "cmp" => {
142 let r1 = parse_reg(&tokens[1]);
143 if tokens[2].chars().all(|c| c.is_ascii_digit()) {
144 let imm: u8 = tokens[2].parse().unwrap();
145 bytes.push(Instruction::CMP_RI as u8);
146 bytes.push(r1);
147 bytes.push(imm);
148 } else {
149 let r2 = parse_reg(&tokens[2]);
150 bytes.push(Instruction::CMP_RR as u8);
151 bytes.push(r1);
152 bytes.push(r2);
153 }
154 }
155
156 "hlt" => {
157 bytes.push(Instruction::HLT as u8);
158 }
159
160 _ => panic!("Line {}: unknown instruction", line_no + 1),
161 }
162 }
163
164 bytes
165}