bench51deac6a4
add add_ri, sub_ri
+101 −29
5 files changed
- M README.md +3 −3
- M examples/addition.asm +2 −1
- M src/assembler.rs +29 −17
- M src/cpu.rs +60 −4
- M src/instructions.rs +7 −4
| @@ -4,15 +4,15 @@ | |||
| 4 | 4 | - Word Size | |
| 5 | 5 | - **Data Width:** 8 bits | |
| 6 | 6 | - **Address width:** 16 bits | |
| 7 | − | - **Address space:** 64 KB (0x0000- 0xFFFF) | |
| 7 | + | - **Address space:** 64 KB (0x0000-0xFFFF) | |
| 8 | 8 | ||
| 9 | 9 | ## Supported Instructions | |
| 10 | 10 | ||
| 11 | 11 | | Instruction | Syntax | | |
| 12 | 12 | | ----------- | ------------ | | |
| 13 | 13 | | MOV | mov reg, imm OR mov reg, reg | | |
| 14 | − | | ADD | add r1, r2 | | |
| 15 | − | | SUB | sub r1, r2 | | |
| 14 | + | | ADD | add reg, imm OR add r1, r2 | | |
| 15 | + | | SUB | sub reg, imm OR add r1, r2 | | |
| 16 | 16 | | JMP | jmp addr | | |
| 17 | 17 | | JZ | jz addr | | |
| 18 | 18 | | JNZ | jnz addr | | |
| @@ -3,6 +3,7 @@ mov a, 10 | |||
| 3 | 3 | ; move imm 5 to register B | |
| 4 | 4 | mov b, 5 | |
| 5 | 5 | ; Add a & B | |
| 6 | − | add a, b | |
| 6 | + | add a, 2 | |
| 7 | + | sub a, 2 | |
| 7 | 8 | ; Halt | |
| 8 | 9 | hlt | |
| @@ -2,12 +2,13 @@ use crate::instructions::Instruction; | |||
| 2 | 2 | use std::collections::HashMap; | |
| 3 | 3 | ||
| 4 | 4 | fn tokenize(line: &str) -> Vec<String> { | |
| 5 | − | line.split(|c| c == ' ' || c == ',' || c == '\t') | |
| 5 | + | line.split([' ', ',', '\t']) | |
| 6 | 6 | .filter(|s| !s.is_empty()) | |
| 7 | 7 | .map(|s| s.to_lowercase()) | |
| 8 | 8 | .collect() | |
| 9 | 9 | } | |
| 10 | 10 | ||
| 11 | + | // Returns corrensponding u8 value of the register | |
| 11 | 12 | fn parse_reg(s: &str) -> u8 { | |
| 12 | 13 | match s { | |
| 13 | 14 | "a" => 0, | |
| @@ -19,10 +20,7 @@ fn parse_reg(s: &str) -> u8 { | |||
| 19 | 20 | } | |
| 20 | 21 | ||
| 21 | 22 | fn is_reg(s: &str) -> bool { | |
| 22 | − | match s { | |
| 23 | − | "a" | "b" | "c" | "d" => true, | |
| 24 | − | _ => false, | |
| 25 | − | } | |
| 23 | + | matches!(s, "a" | "b" | "c" | "d") | |
| 26 | 24 | } | |
| 27 | 25 | ||
| 28 | 26 | fn instr_size(tokens: &[String]) -> u16 { | |
| @@ -76,16 +74,16 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 76 | 74 | match tokens[0].as_str() { | |
| 77 | 75 | "mov" => { | |
| 78 | 76 | // mov reg, imm | |
| 79 | − | let reg = parse_reg(&tokens[1]); | |
| 77 | + | let r1= parse_reg(&tokens[1]); | |
| 80 | 78 | if is_reg(&tokens[2]) { | |
| 81 | 79 | let r2 = parse_reg(&tokens[2]); | |
| 82 | 80 | bytes.push(Instruction::MOV_RR as u8); | |
| 83 | − | bytes.push(reg); | |
| 81 | + | bytes.push(r1); | |
| 84 | 82 | bytes.push(r2); | |
| 85 | 83 | } else { | |
| 86 | 84 | let imm: u8 = tokens[2].parse().unwrap(); | |
| 87 | 85 | bytes.push(Instruction::MOV_RI as u8); | |
| 88 | − | bytes.push(reg); | |
| 86 | + | bytes.push(r1); | |
| 89 | 87 | bytes.push(imm); | |
| 90 | 88 | } | |
| 91 | 89 | } | |
| @@ -93,21 +91,35 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 93 | 91 | "add" => { | |
| 94 | 92 | // add a, b | |
| 95 | 93 | let r1 = parse_reg(&tokens[1]); | |
| 96 | − | let r2 = parse_reg(&tokens[2]); | |
| 97 | 94 | ||
| 98 | − | bytes.push(Instruction::ADD as u8); | |
| 99 | − | bytes.push(r1); | |
| 100 | − | bytes.push(r2); | |
| 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 | + | ||
| 101 | 107 | } | |
| 102 | 108 | ||
| 103 | 109 | "sub" => { | |
| 104 | 110 | // sub a, b | |
| 105 | − | let r2 = parse_reg(&tokens[2]); | |
| 106 | 111 | let r1 = parse_reg(&tokens[1]); | |
| 107 | − | ||
| 108 | − | bytes.push(Instruction::SUB as u8); | |
| 109 | − | bytes.push(r1); | |
| 110 | − | bytes.push(r2); | |
| 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 | + | } | |
| 111 | 123 | } | |
| 112 | 124 | ||
| 113 | 125 | "jmp" | "jz" | "jnz" => { | |
| @@ -42,8 +42,10 @@ impl CPU { | |||
| 42 | 42 | match opcode { | |
| 43 | 43 | x if x == Instruction::MOV_RI as u8 => self.mov_ri(mem), | |
| 44 | 44 | x if x == Instruction::MOV_RR as u8 => self.mov_rr(mem), | |
| 45 | − | x if x == Instruction::ADD as u8 => self.add(mem), | |
| 46 | − | x if x == Instruction::SUB as u8 => self.sub(mem), | |
| 45 | + | x if x == Instruction::ADD_RR as u8 => self.add_rr(mem), | |
| 46 | + | x if x == Instruction::ADD_RI as u8 => self.add_ri(mem), | |
| 47 | + | x if x == Instruction::SUB_RR as u8 => self.sub_rr(mem), | |
| 48 | + | x if x == Instruction::SUB_RI as u8 => self.sub_ri(mem), | |
| 47 | 49 | x if x == Instruction::JMP as u8 => self.jmp(mem), | |
| 48 | 50 | x if x == Instruction::JZ as u8 => self.jz(mem), | |
| 49 | 51 | x if x == Instruction::JNZ as u8 => self.jnz(mem), | |
| @@ -92,7 +94,7 @@ impl CPU { | |||
| 92 | 94 | } | |
| 93 | 95 | ||
| 94 | 96 | ||
| 95 | − | pub fn add(&mut self, mem: &Memory) { | |
| 97 | + | pub fn add_rr(&mut self, mem: &Memory) { | |
| 96 | 98 | let dest = mem.read(self.pc); | |
| 97 | 99 | self.pc += 1; | |
| 98 | 100 | let src = mem.read(self.pc); | |
| @@ -133,8 +135,35 @@ impl CPU { | |||
| 133 | 135 | self.zero = result == 0; | |
| 134 | 136 | self.carry = carry; | |
| 135 | 137 | } | |
| 138 | + | pub fn add_ri(&mut self, mem: &Memory) { | |
| 136 | 139 | ||
| 137 | − | pub fn sub(&mut self, mem: &Memory) { | |
| 140 | + | let dest = mem.read(self.pc); | |
| 141 | + | self.pc += 1; | |
| 142 | + | let imm = mem.read(self.pc); | |
| 143 | + | self.pc += 1; | |
| 144 | + | ||
| 145 | + | let (result, carry) = match dest { | |
| 146 | + | 0 => self.a.overflowing_add(imm), | |
| 147 | + | 1 => self.b.overflowing_add(imm), | |
| 148 | + | 2 => self.c.overflowing_add(imm), | |
| 149 | + | 3 => self.d.overflowing_add(imm), | |
| 150 | + | _ => (0, false), | |
| 151 | + | }; | |
| 152 | + | ||
| 153 | + | match dest { | |
| 154 | + | 0 => self.a = result, | |
| 155 | + | 1 => self.b = result, | |
| 156 | + | 2 => self.c = result, | |
| 157 | + | 3 => self.d = result, | |
| 158 | + | _ => {} | |
| 159 | + | } | |
| 160 | + | ||
| 161 | + | self.zero = result == 0; | |
| 162 | + | self.carry = carry; | |
| 163 | + | } | |
| 164 | + | ||
| 165 | + | ||
| 166 | + | pub fn sub_rr(&mut self, mem: &Memory) { | |
| 138 | 167 | let dest = mem.read(self.pc); | |
| 139 | 168 | self.pc += 1; | |
| 140 | 169 | let src = mem.read(self.pc); | |
| @@ -176,6 +205,33 @@ impl CPU { | |||
| 176 | 205 | self.carry = borrow; | |
| 177 | 206 | } | |
| 178 | 207 | ||
| 208 | + | pub fn sub_ri(&mut self, mem: &Memory) { | |
| 209 | + | let dest = mem.read(self.pc); | |
| 210 | + | self.pc += 1; | |
| 211 | + | let imm = mem.read(self.pc); | |
| 212 | + | self.pc += 1; | |
| 213 | + | ||
| 214 | + | let (result, borrow) = match dest { | |
| 215 | + | 0 => self.a.overflowing_sub(imm), | |
| 216 | + | 1 => self.b.overflowing_sub(imm), | |
| 217 | + | 2 => self.c.overflowing_sub(imm), | |
| 218 | + | 3 => self.d.overflowing_sub(imm), | |
| 219 | + | _ => (0, false), | |
| 220 | + | }; | |
| 221 | + | ||
| 222 | + | match dest { | |
| 223 | + | 0 => self.a = result, | |
| 224 | + | 1 => self.b = result, | |
| 225 | + | 2 => self.c = result, | |
| 226 | + | 3 => self.d = result, | |
| 227 | + | _ => {} | |
| 228 | + | } | |
| 229 | + | ||
| 230 | + | self.zero = result == 0; | |
| 231 | + | self.carry = borrow; | |
| 232 | + | } | |
| 233 | + | ||
| 234 | + | ||
| 179 | 235 | pub fn jmp(&mut self, mem: &Memory) { | |
| 180 | 236 | let low = mem.read(self.pc) as u16; | |
| 181 | 237 | self.inc_pc(); | |
| @@ -1,9 +1,12 @@ | |||
| 1 | 1 | #[repr(u8)] | |
| 2 | + | #[allow(non_camel_case_types)] | |
| 2 | 3 | pub enum Instruction { | |
| 3 | 4 | MOV_RI = 0x01, | |
| 4 | 5 | MOV_RR = 0x08, | |
| 5 | − | ADD = 0x02, | |
| 6 | − | SUB = 0x03, | |
| 6 | + | ADD_RR = 0x02, | |
| 7 | + | ADD_RI = 0x0A, | |
| 8 | + | SUB_RR = 0x03, | |
| 9 | + | SUB_RI = 0x0B, | |
| 7 | 10 | JMP = 0x04, | |
| 8 | 11 | JZ = 0x05, | |
| 9 | 12 | JNZ = 0x06, | |
| @@ -16,8 +19,8 @@ impl Instruction { | |||
| 16 | 19 | pub fn opcode_name(op: u8) -> &'static str{ | |
| 17 | 20 | match op { | |
| 18 | 21 | 0x01 | 0x08 => "MOV", | |
| 19 | − | 0x02 => "ADD", | |
| 20 | − | 0x03 => "SUB", | |
| 22 | + | 0x02 | 0x0A => "ADD", | |
| 23 | + | 0x03 | 0x0B => "SUB", | |
| 21 | 24 | 0x04 => "JMP", | |
| 22 | 25 | 0x05 => "JZ", | |
| 23 | 26 | 0x06 => "JNZ", | |