Krolyxon
Commit 4166b0e

Branches

Tags

Refs →

bench51srcassembler.rs

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