Browse Source

implement all instructions with 100% test coverage

Also replace RESET with HALT.
pull/3/head
Murad 2 years ago
parent
commit
0bdf20e5a7
Signed by: foxpy GPG Key ID: 78BE32418B0C8450
  1. 4
      DESIGN.md
  2. 1610
      src/cpu.rs
  3. 235
      src/instruction.rs
  4. 144
      src/main.rs

4
DESIGN.md

@ -319,7 +319,7 @@ If `R1` value is greater than 8, `R0` is zeroed.
+—+—+—+—+—+—+—+—+
```
### RESET
### HALT
```
+—+—+—+—+—+—+—+—+
@ -327,7 +327,7 @@ If `R1` value is greater than 8, `R0` is zeroed.
+—+—+—+—+—+—+—+—+
```
Resets CPU registers and memory and zeroes `IP`.
Stops CPU execution.
### CLOAD

1610
src/cpu.rs

File diff suppressed because it is too large

235
src/instruction.rs

@ -0,0 +1,235 @@
// SPDX-License-Identifier: MIT
// Copyright Murad Karammaev, Nikita Kuzmin
use ux::{u2, u3, u4};
#[derive(Debug)]
pub enum Register {
R0,
R1,
}
#[derive(Debug)]
pub enum ShiftMode {
Logical,
Circular,
}
#[derive(Debug)]
pub enum Instruction {
Load {
reg: Register,
addr: u4,
},
Store {
reg: Register,
addr: u4,
},
LoadImmediate {
low: bool,
reg: Register,
val: u4,
},
NearJumpBackward {
use_carry: bool,
offset: u2,
},
NearJumpForward {
use_carry: bool,
offset: u3,
},
FarJump {
reg: Register,
use_carry: bool,
},
CmpEqual,
CmpGreater,
CmpLess,
Not,
BitShift {
right: bool,
reg: Register,
mode: ShiftMode,
len: u3,
},
Inc {
reg: Register,
},
Dec {
reg: Register,
},
Add,
Sub,
And,
Or,
Xor,
Xnor,
Complement {
reg: Register,
},
BitShiftVar {
right: bool,
mode: ShiftMode,
},
Copy {
reg_from: Register,
},
Setc {
carry: bool,
},
Nop,
Halt,
Cload {
reg: Register,
},
Zero {
reg: Register,
},
Div,
Mul,
Swap,
PortReady,
ReadPort,
WritePort,
}
pub fn decode(insn: u8) -> Instruction {
match insn {
0b00000000..=0b00111111 => {
let reg = if insn & 0b00010000 == 0 {
Register::R0
} else {
Register::R1
};
let addr = u4::new(insn & 0b00001111);
match insn & 0b00100000 {
0 => Instruction::Load { reg, addr },
_ => Instruction::Store { reg, addr },
}
}
0b01000000..=0b01111111 => Instruction::LoadImmediate {
low: insn & 0b00100000 == 0,
reg: if insn & 0b00010000 == 0 {
Register::R0
} else {
Register::R1
},
val: u4::new(insn & 0b00001111),
},
0b10001000..=0b10001111 => Instruction::NearJumpBackward {
use_carry: insn & 0b00000100 != 0,
offset: u2::new(insn & 0b00000011),
},
0b10010000..=0b10011111 => Instruction::NearJumpForward {
use_carry: insn & 0b00001000 != 0,
offset: u3::new(insn & 0b00000111),
},
0b10000000..=0b10000011 => Instruction::FarJump {
reg: if insn & 0b00000010 == 0 {
Register::R0
} else {
Register::R1
},
use_carry: insn & 0b00000001 != 0,
},
0b10000100 => Instruction::CmpEqual,
0b10000101 => Instruction::CmpGreater,
0b10000110 => Instruction::CmpLess,
0b10000111 => Instruction::Not,
0b10100000..=0b10111111 => Instruction::BitShift {
right: true,
reg: if insn & 0b00010000 == 0 {
Register::R0
} else {
Register::R1
},
mode: if insn & 0b00001000 == 0 {
ShiftMode::Logical
} else {
ShiftMode::Circular
},
len: u3::new(insn & 0b00000111),
},
0b11000000..=0b11011111 => Instruction::BitShift {
right: false,
reg: if insn & 0b00010000 == 0 {
Register::R0
} else {
Register::R1
},
mode: if insn & 0b00001000 == 0 {
ShiftMode::Logical
} else {
ShiftMode::Circular
},
len: u3::new(insn & 0b00000111),
},
0b11100000..=0b11100001 => Instruction::Inc {
reg: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11100010..=0b11100011 => Instruction::Dec {
reg: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11100100 => Instruction::Add,
0b11100101 => Instruction::Sub,
0b11100110 => Instruction::And,
0b11100111 => Instruction::Or,
0b11101000 => Instruction::Xor,
0b11101001 => Instruction::Xnor,
0b11101010..=0b11101011 => Instruction::Complement {
reg: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11101100..=0b11101111 => Instruction::BitShiftVar {
right: insn & 0b00000010 == 0,
mode: if insn & 0b00000001 == 0 {
ShiftMode::Logical
} else {
ShiftMode::Circular
},
},
0b11110000..=0b11110001 => Instruction::Copy {
reg_from: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11110010..=0b11110011 => Instruction::Setc {
carry: insn & 0b00000001 != 0,
},
0b11110100 => Instruction::Nop,
0b11110101 => Instruction::Halt,
0b11110110..=0b11110111 => Instruction::Cload {
reg: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11111000..=0b11111001 => Instruction::Zero {
reg: if insn & 0b00000001 == 0 {
Register::R0
} else {
Register::R1
},
},
0b11111010 => Instruction::Div,
0b11111011 => Instruction::Mul,
0b11111100 => Instruction::Swap,
0b11111101 => Instruction::PortReady,
0b11111110 => Instruction::ReadPort,
0b11111111 => Instruction::WritePort,
}
}

144
src/main.rs

@ -1,144 +1,16 @@
// SPDX-License-Identifier: MIT
// Copyright Murad Karammaev, Nikita Kuzmin
use ux::{u2, u3, u4};
mod cpu;
mod instruction;
#[derive(Debug)]
enum Register {
R0,
R1,
}
#[derive(Debug)]
enum ShiftMode {
Logical,
Circular,
}
#[derive(Debug)]
enum Instruction {
Load { reg: Register, addr: u4 },
Store { reg: Register, addr: u4 },
LoadImmediate { low: bool, reg: Register, val: u4 },
NearJumpBackward { use_carry: bool, offset: u2 },
NearJumpForward { use_carry: bool, offset: u3 },
FarJump { reg: Register, use_carry: bool },
CmpEqual,
CmpGreater,
CmpLess,
Not,
BitShift { right: bool, reg: Register, mode: ShiftMode, len: u3 },
Inc { reg: Register },
Dec { reg: Register },
Add,
Sub,
And,
Or,
Xor,
Xnor,
Complement { reg: Register },
BitShiftVar { right: bool, mode: ShiftMode },
Copy { reg_from: Register },
Setc { carry: bool },
Nop,
Reset,
Cload { reg: Register },
Zero { reg: Register },
Div,
Mul,
Swap,
PortReady,
ReadPort,
WritePort,
}
fn decode(insn: u8) -> Instruction {
match insn {
0b00000000..=0b00111111 => {
let reg = if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 };
let addr = u4::new(insn & 0b00001111);
match insn & 0b00100000 {
0 => Instruction::Load {reg, addr},
_ => Instruction::Store {reg, addr},
}
},
0b01000000..=0b01111111 => Instruction::LoadImmediate {
low: insn & 0b00100000 != 0,
reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 },
val: u4::new(insn & 0b00001111),
},
0b10001000..=0b10001111 => Instruction::NearJumpBackward {
use_carry: insn & 0b00000100 != 0,
offset: u2::new(insn & 0b00000011),
},
0b10010000..=0b10011111 => Instruction::NearJumpForward {
use_carry: insn & 0b00001000 != 0,
offset: u3::new(insn & 0b00000111),
},
0b10000000..=0b10000011 => Instruction::FarJump {
reg: if insn & 0b00000010 == 0 { Register::R0 } else { Register::R1 },
use_carry: insn & 0b00000001 != 0,
},
0b10000100 => Instruction::CmpEqual,
0b10000101 => Instruction::CmpGreater,
0b10000110 => Instruction::CmpLess,
0b10000111 => Instruction::Not,
0b10100000..=0b10111111 => Instruction::BitShift {
right: true,
reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 },
mode: if insn & 0b00001000 == 0 { ShiftMode::Logical } else { ShiftMode::Circular },
len: u3::new(insn & 0b00000111),
},
0b11000000..=0b11011111 => Instruction::BitShift {
right: false,
reg: if insn & 0b00010000 == 0 { Register::R0 } else { Register::R1 },
mode: if insn & 0b00001000 == 0 { ShiftMode::Logical } else { ShiftMode::Circular },
len: u3::new(insn & 0b00000111),
},
0b11100000..=0b11100001 => Instruction::Inc {
reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11100010..=0b11100011 => Instruction::Dec {
reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11100100 => Instruction::Add,
0b11100101 => Instruction::Sub,
0b11100110 => Instruction::And,
0b11100111 => Instruction::Or,
0b11101000 => Instruction::Xor,
0b11101001 => Instruction::Xnor,
0b11101010..=0b11101011 => Instruction::Complement {
reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11101100..=0b11101111 => Instruction::BitShiftVar {
right: insn & 0b00000010 != 0,
mode: if insn & 0b00000001 == 0 { ShiftMode::Logical } else { ShiftMode::Circular },
},
0b11110000..=0b11110001 => Instruction::Copy {
reg_from: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11110010..=0b11110011 => Instruction::Setc {
carry: insn & 0b00000001 != 0,
},
0b11110100 => Instruction::Nop,
0b11110101 => Instruction::Reset,
0b11110110..=0b11110111 => Instruction::Cload {
reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11111000..=0b11111001 => Instruction::Zero {
reg: if insn & 0b00000001 == 0 { Register::R0 } else { Register::R1 },
},
0b11111010 => Instruction::Div,
0b11111011 => Instruction::Mul,
0b11111100 => Instruction::Swap,
0b11111101 => Instruction::PortReady,
0b11111110 => Instruction::ReadPort,
0b11111111 => Instruction::WritePort,
}
}
use cpu::*;
fn main() {
for i in 0..=255u8 {
println!("{:#08b} -> {:?}", i, decode(i));
let mut cpu = CPU::new(&[]);
loop {
if cpu.step() {
break;
}
}
}

Loading…
Cancel
Save