diff --git a/Cargo.lock b/Cargo.lock index 6c796fd..e92f4a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,12 @@ version = 3 [[package]] name = "toy_cpu_4bit" version = "0.1.0" +dependencies = [ + "ux", +] + +[[package]] +name = "ux" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" diff --git a/Cargo.toml b/Cargo.toml index 873e92b..962f61b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +ux = "0.1.3" diff --git a/DESIGN.md b/DESIGN.md index 3064f67..33979a3 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -57,6 +57,32 @@ significant bits. `IIII` — 4-bit immediate value. +### NEAR JUMP BACKWARD + +``` ++—+—+—+—+—+—+———+ +|1|0|0|0|1|C|O O| ++—+—+—+—+—+—+———+ +``` + +`C` — `0` to ignore carry flag and jump unconditionally, +`1` to only jump if carry flag is set. + +`OO` — jump offset. + +### NEAR JUMP FORWARD + +``` ++—+—+—+—+—+—————+ +|1|0|0|1|C|O O O| ++—+—+—+—+—+—————+ +``` + +`C` — `0` to ignore carry flag and jump unconditionally, +`1` to only jump if carry flag is set. + +`OOO` — jump offset. + ### FAR JUMP ``` diff --git a/MISTAKES.md b/MISTAKES.md new file mode 100644 index 0000000..85d3be9 --- /dev/null +++ b/MISTAKES.md @@ -0,0 +1,7 @@ +# Mistakes + +## Don't design opcodes in your head + +You WILL frick up and your binary encodings WILL overlap. +You definitely need some tool to design instruction opcodes +for anything more complicated than just 1-byte instructions. diff --git a/src/main.rs b/src/main.rs index a711439..ffba700 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,144 @@ // SPDX-License-Identifier: MIT // Copyright Murad Karammaev, Nikita Kuzmin -fn main() {} +use ux::{u2, u3, u4}; + +#[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, + } +} + +fn main() { + for i in 0..=255u8 { + println!("{:#08b} -> {:?}", i, decode(i)); + } +}