bench514166b0e
add cmp instruction, add reg, reg, and reg, imm variations
+125 −23
7 files changed
- M README.md +3 −2
- A examples/cmp.asm +4 −0
- A examples/mov.asm +5 −0
- D examples/subtraction.asm +0 −4
- M src/assembler.rs +38 −12
- M src/cpu.rs +69 −3
- M src/instructions.rs +6 −2
| @@ -10,12 +10,13 @@ | |||
| 10 | 10 | ||
| 11 | 11 | | Instruction | Syntax | | |
| 12 | 12 | | ----------- | ------------ | | |
| 13 | − | | MOV | mov reg, imm | | |
| 13 | + | | MOV | mov reg, imm OR mov reg, reg | | |
| 14 | 14 | | ADD | add r1, r2 | | |
| 15 | 15 | | SUB | sub r1, r2 | | |
| 16 | 16 | | JMP | jmp addr | | |
| 17 | 17 | | JZ | jz addr | | |
| 18 | 18 | | JNZ | jnz addr | | |
| 19 | + | | CMP | cmp reg, imm OR cmp reg, reg | | |
| 19 | 20 | | HLT (Halt) | hlt | | |
| 20 | 21 | ||
| 21 | 22 | ||
| @@ -46,7 +47,7 @@ cargo run -- --f <examples/filename.asc> | |||
| 46 | 47 | - [x] Lexer/Tokenizer | |
| 47 | 48 | - [x] Add label support (supporting JMP/JZ/JNZ) | |
| 48 | 49 | - [ ] Add instructions | |
| 49 | − | - [ ] CMP | |
| 50 | + | - [x] CMP | |
| 50 | 51 | - [ ] CALL | |
| 51 | 52 | - [ ] RET | |
| 52 | 53 | - [ ] Error handling | |
| @@ -0,0 +1,4 @@ | |||
| 1 | + | mov a, 10 | |
| 2 | + | mov b, 10 | |
| 3 | + | cmp a, b | |
| 4 | + | hlt | |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | mov a, 10 | |
| 2 | + | mov b, a | |
| 3 | + | cmp a, b | |
| 4 | + | cmp a, 10 | |
| 5 | + | hlt | |
| @@ -1,4 +0,0 @@ | |||
| 1 | − | mov a, 15 | |
| 2 | − | mov b, 15 | |
| 3 | − | sub a, b | |
| 4 | − | hlt | |
| @@ -18,16 +18,21 @@ fn parse_reg(s: &str) -> u8 { | |||
| 18 | 18 | } | |
| 19 | 19 | } | |
| 20 | 20 | ||
| 21 | − | fn instr_size(tokens: &[String]) -> u16 { | |
| 21 | + | fn is_reg(s: &str) -> bool { | |
| 22 | + | match s { | |
| 23 | + | "a" | "b" | "c" | "d" => true, | |
| 24 | + | _ => false, | |
| 25 | + | } | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | fn instr_size(tokens: &[String]) -> u16 { | |
| 22 | 29 | match tokens[0].as_str() { | |
| 23 | − | "mov" | "add" | "sub" | "jmp" | "jz" | "jnz" => 3, | |
| 30 | + | "mov" | "add" | "sub" | "jmp" | "jz" | "jnz" | "cmp" => 3, | |
| 24 | 31 | "hlt" => 1, | |
| 25 | − | _ => panic!("Unknown instruction {}", tokens[0]) | |
| 26 | − | ||
| 32 | + | _ => panic!("Unknown instruction {}", tokens[0]), | |
| 27 | 33 | } | |
| 28 | 34 | } | |
| 29 | 35 | ||
| 30 | − | ||
| 31 | 36 | fn first_pass(source: &str) -> HashMap<String, u16> { | |
| 32 | 37 | let mut symbols = HashMap::new(); | |
| 33 | 38 | let mut addr: u16 = 0; | |
| @@ -72,11 +77,17 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 72 | 77 | "mov" => { | |
| 73 | 78 | // mov reg, imm | |
| 74 | 79 | 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 | + | 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 | + | } | |
| 80 | 91 | } | |
| 81 | 92 | ||
| 82 | 93 | "add" => { | |
| @@ -91,8 +102,8 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 91 | 102 | ||
| 92 | 103 | "sub" => { | |
| 93 | 104 | // sub a, b | |
| 94 | − | let r1 = parse_reg(&tokens[1]); | |
| 95 | 105 | let r2 = parse_reg(&tokens[2]); | |
| 106 | + | let r1 = parse_reg(&tokens[1]); | |
| 96 | 107 | ||
| 97 | 108 | bytes.push(Instruction::SUB as u8); | |
| 98 | 109 | bytes.push(r1); | |
| @@ -104,7 +115,7 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 104 | 115 | "jmp" => Instruction::JMP, | |
| 105 | 116 | "jz" => Instruction::JZ, | |
| 106 | 117 | "jnz" => Instruction::JNZ, | |
| 107 | − | _ => unreachable!() | |
| 118 | + | _ => unreachable!(), | |
| 108 | 119 | }; | |
| 109 | 120 | ||
| 110 | 121 | let label = &tokens[1]; | |
| @@ -115,6 +126,21 @@ pub fn assembler(source: &str) -> Vec<u8> { | |||
| 115 | 126 | bytes.push((addr >> 8) as u8); // high | |
| 116 | 127 | } | |
| 117 | 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 | + | ||
| 118 | 144 | "hlt" => { | |
| 119 | 145 | bytes.push(Instruction::HLT as u8); | |
| 120 | 146 | } | |
| @@ -19,7 +19,6 @@ pub struct CPU { | |||
| 19 | 19 | } | |
| 20 | 20 | ||
| 21 | 21 | impl CPU { | |
| 22 | − | ||
| 23 | 22 | pub fn debug_instr(&self, mem: &Memory) { | |
| 24 | 23 | let opcode = mem.read(self.pc); | |
| 25 | 24 | ||
| @@ -41,12 +40,15 @@ impl CPU { | |||
| 41 | 40 | self.inc_pc(); | |
| 42 | 41 | ||
| 43 | 42 | match opcode { | |
| 44 | − | x if x == Instruction::MOV as u8 => self.mov(mem), | |
| 43 | + | x if x == Instruction::MOV_RI as u8 => self.mov_ri(mem), | |
| 44 | + | x if x == Instruction::MOV_RR as u8 => self.mov_rr(mem), | |
| 45 | 45 | x if x == Instruction::ADD as u8 => self.add(mem), | |
| 46 | 46 | x if x == Instruction::SUB as u8 => self.sub(mem), | |
| 47 | 47 | x if x == Instruction::JMP as u8 => self.jmp(mem), | |
| 48 | 48 | x if x == Instruction::JZ as u8 => self.jz(mem), | |
| 49 | 49 | x if x == Instruction::JNZ as u8 => self.jnz(mem), | |
| 50 | + | x if x == Instruction::CMP_RI as u8 => self.cmp_ri(mem), | |
| 51 | + | x if x == Instruction::CMP_RR as u8 => self.cmp_rr(mem), | |
| 50 | 52 | x if x == Instruction::HLT as u8 => self.halt(), | |
| 51 | 53 | _ => panic!("Unknown opcode {:02X}", opcode), | |
| 52 | 54 | } | |
| @@ -60,7 +62,7 @@ impl CPU { | |||
| 60 | 62 | self.halted = true; | |
| 61 | 63 | } | |
| 62 | 64 | ||
| 63 | − | pub fn mov(&mut self, mem: &Memory) { | |
| 65 | + | pub fn mov_ri(&mut self, mem: &Memory) { | |
| 64 | 66 | let reg = mem.read(self.pc); | |
| 65 | 67 | self.inc_pc(); | |
| 66 | 68 | let val = mem.read(self.pc); | |
| @@ -77,6 +79,19 @@ impl CPU { | |||
| 77 | 79 | self.zero = val == 0; | |
| 78 | 80 | } | |
| 79 | 81 | ||
| 82 | + | pub fn mov_rr(&mut self, mem: &Memory) { | |
| 83 | + | let dest = mem.read(self.pc); | |
| 84 | + | self.inc_pc(); | |
| 85 | + | let src= mem.read(self.pc); | |
| 86 | + | self.inc_pc(); | |
| 87 | + | ||
| 88 | + | let val = self.get_reg(src); | |
| 89 | + | self.set_reg(dest, val); | |
| 90 | + | ||
| 91 | + | self.zero = val == 0; | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | ||
| 80 | 95 | pub fn add(&mut self, mem: &Memory) { | |
| 81 | 96 | let dest = mem.read(self.pc); | |
| 82 | 97 | self.pc += 1; | |
| @@ -197,4 +212,55 @@ impl CPU { | |||
| 197 | 212 | self.pc = addrs; | |
| 198 | 213 | } | |
| 199 | 214 | } | |
| 215 | + | ||
| 216 | + | pub fn cmp_rr(&mut self, mem: &Memory) { | |
| 217 | + | let r1 = mem.read(self.pc); | |
| 218 | + | self.inc_pc(); | |
| 219 | + | let r2 = mem.read(self.pc); | |
| 220 | + | self.inc_pc(); | |
| 221 | + | ||
| 222 | + | let v1 = self.get_reg(r1); | |
| 223 | + | let v2 = self.get_reg(r2); | |
| 224 | + | ||
| 225 | + | let (result, borrow) = v1.overflowing_sub(v2); | |
| 226 | + | ||
| 227 | + | self.zero = result == 0; | |
| 228 | + | self.carry = borrow; | |
| 229 | + | } | |
| 230 | + | ||
| 231 | + | pub fn cmp_ri(&mut self, mem: &Memory) { | |
| 232 | + | let r1 = mem.read(self.pc); | |
| 233 | + | self.inc_pc(); | |
| 234 | + | let r2 = mem.read(self.pc); | |
| 235 | + | self.inc_pc(); | |
| 236 | + | ||
| 237 | + | let v1 = self.get_reg(r1); | |
| 238 | + | ||
| 239 | + | let (result, borrow) = v1.overflowing_sub(r2); | |
| 240 | + | ||
| 241 | + | self.zero = result == 0; | |
| 242 | + | self.carry = borrow; | |
| 243 | + | } | |
| 244 | + | ||
| 245 | + | ||
| 246 | + | fn get_reg(&self, r: u8) -> u8 { | |
| 247 | + | match r { | |
| 248 | + | 0 => self.a, | |
| 249 | + | 1 => self.b, | |
| 250 | + | 2 => self.c, | |
| 251 | + | 3 => self.d, | |
| 252 | + | _ => 0, | |
| 253 | + | } | |
| 254 | + | } | |
| 255 | + | ||
| 256 | + | fn set_reg(&mut self, dest: u8, val: u8) { | |
| 257 | + | match dest { | |
| 258 | + | 0 => self.a = val, | |
| 259 | + | 1 => self.b = val, | |
| 260 | + | 2 => self.c = val, | |
| 261 | + | 3 => self.d = val, | |
| 262 | + | _ => {}, | |
| 263 | + | } | |
| 264 | + | } | |
| 265 | + | ||
| 200 | 266 | } | |
| @@ -1,23 +1,27 @@ | |||
| 1 | 1 | #[repr(u8)] | |
| 2 | 2 | pub enum Instruction { | |
| 3 | − | MOV = 0x01, | |
| 3 | + | MOV_RI = 0x01, | |
| 4 | + | MOV_RR = 0x08, | |
| 4 | 5 | ADD = 0x02, | |
| 5 | 6 | SUB = 0x03, | |
| 6 | 7 | JMP = 0x04, | |
| 7 | 8 | JZ = 0x05, | |
| 8 | 9 | JNZ = 0x06, | |
| 10 | + | CMP_RI = 0x07, | |
| 11 | + | CMP_RR = 0x09, | |
| 9 | 12 | HLT = 0xFF, | |
| 10 | 13 | } | |
| 11 | 14 | ||
| 12 | 15 | impl Instruction { | |
| 13 | 16 | pub fn opcode_name(op: u8) -> &'static str{ | |
| 14 | 17 | match op { | |
| 15 | − | 0x01 => "MOV", | |
| 18 | + | 0x01 | 0x08 => "MOV", | |
| 16 | 19 | 0x02 => "ADD", | |
| 17 | 20 | 0x03 => "SUB", | |
| 18 | 21 | 0x04 => "JMP", | |
| 19 | 22 | 0x05 => "JZ", | |
| 20 | 23 | 0x06 => "JNZ", | |
| 24 | + | 0x07 | 0x09 => "CMP", | |
| 21 | 25 | 0xFF => "HLT", | |
| 22 | 26 | _ => "???", | |
| 23 | 27 | } | |