diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f95f411 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "overture_cpu_emulator" +version = "0.1.0" +dependencies = [ + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..99ea614 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "overture_cpu_emulator" +version = "0.1.0" +edition = "2024" + +[dependencies] +bitflags = "2.10.0" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38cb0ac --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +run: + cargo run diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3c99419 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,310 @@ +use std::{thread, time, io, io::Read}; +use bitflags::bitflags; + +bitflags! { + struct Instruction: u8 { + const immediate = 0b00111111; + const left_args = 0b00111000; + const right_args = 0b00000111; + const mode = 0b11000000; + } +} + +enum Register { + R0, + R1, + R2, + R3, + R4, + R5, + IO, +} +impl From for Register { + fn from(value: u8) -> Self { + match value { + 0 => Self::R0, + 1 => Self::R1, + 2 => Self::R2, + 3 => Self::R3, + 4 => Self::R4, + 5 => Self::R5, + 6 => Self::IO, + _ => Self::R0, + } + } +} + +enum CalculateOperation { + OR, + NAND, + NOR, + AND, + ADD, + SUB, +} +impl From for CalculateOperation { + fn from(value: u8) -> Self { + match value { + 0 => Self::OR, + 1 => Self::NAND, + 2 => Self::NOR, + 3 => Self::AND, + 4 => Self::ADD, + 5 => Self::SUB, + _ => Self::OR, + } + } +} + +enum ConditionOperation { + NEVER, + EQZ, + LTZ, + LTEQZ, + ALWAYS, + NEQZ, + GTEQZ, + GTZ, +} +impl From for ConditionOperation { + fn from(value: u8) -> Self { + match value { + 0 => Self::NEVER, + 1 => Self::EQZ, + 2 => Self::LTZ, + 3 => Self::LTEQZ, + 4 => Self::ALWAYS, + 5 => Self::NEQZ, + 6 => Self::GTEQZ, + 7 => Self::GTZ, + _ => Self::NEVER, + } + } +} + +enum DecodedInstruction { + Immediate (u8), + Calculate (CalculateOperation), + Copy (Register, Register), + Condition (ConditionOperation), +} + +struct Overture<'a> { + rom: &'a [u8], + current_instruction: &'a u8, + program_counter: u8, + clock_rate_duration: time::Duration, + step_mode: bool, + + r0: u8, + r1: u8, + r2: u8, + r3: u8, + r4: u8, + r5: u8, + + io: u8, + input_enable: bool, + output_enable: bool, +} + +impl<'a> Overture<'a> { + pub fn new(rom: &'a [u8], clock_rate_hertz: u32, step_mode: bool) -> Self { + Overture { + rom: rom, + current_instruction: &rom[0], + program_counter: 0x00, + clock_rate_duration: Overture::from_hertz(clock_rate_hertz), + step_mode: step_mode, + + r0: 0x00, + r1: 0x00, + r2: 0x00, + r3: 0x00, + r4: 0x00, + r5: 0x00, + io: 0x00, + + input_enable: false, + output_enable: false, + } + } + + fn start(&mut self) { + let mut io_stdin = io::stdin(); + + loop { + self.program_counter = match self.program_counter.checked_add(1) { + Some(new) => new, + None => break + }; + self.current_instruction = match self.rom.get(usize::from(self.program_counter - 1)) { + Some(instruction) => instruction, + None => break + }; + self.input_enable = false; + self.output_enable = false; + + // Decode Instruction + let decoded_instruction: DecodedInstruction = match self.get_instruction_operand(Instruction::mode) { + 0b0000_0000 => DecodedInstruction::Immediate( + self.get_instruction_operand(Instruction::immediate) + ), + 0b0100_0000 => DecodedInstruction::Calculate( + self.get_instruction_operand(Instruction::right_args).into() + ), + 0b1000_0000 => DecodedInstruction::Copy( + (self.get_instruction_operand(Instruction::left_args) >> 3).into(), + self.get_instruction_operand(Instruction::right_args).into() + ), + 0b1100_0000 => DecodedInstruction::Condition( + self.get_instruction_operand(Instruction::right_args).into() + ), + _ => DecodedInstruction::Immediate( + self.get_instruction_operand(Instruction::immediate) + ), + }; + + // Execute Instruction + match decoded_instruction { + DecodedInstruction::Immediate(value) => { + self.r0 = value; + } + DecodedInstruction::Calculate(operation) => { + match operation { + CalculateOperation::OR => self.r3 = self.r1 | self.r2, + CalculateOperation::NAND => self.r3 = !(self.r1 & self.r2), + CalculateOperation::NOR => self.r3 = !(self.r1 | self.r2), + CalculateOperation::AND => self.r3 = self.r1 & self.r2, + CalculateOperation::ADD => self.r3 = self.r1 + self.r2, + CalculateOperation::SUB => self.r3 = self.r1 - self.r2, + } + } + DecodedInstruction::Copy(source, destination) => { + let multiplexed_source = match source { + Register::R0 => self.r0, + Register::R1 => self.r1, + Register::R2 => self.r2, + Register::R3 => self.r3, + Register::R4 => self.r4, + Register::R5 => self.r5, + Register::IO => { + self.input_enable = true; + + self.io + } + }; + + match destination { + Register::R0 => self.r0 = multiplexed_source, + Register::R1 => self.r1 = multiplexed_source, + Register::R2 => self.r2 = multiplexed_source, + Register::R3 => self.r3 = multiplexed_source, + Register::R4 => self.r4 = multiplexed_source, + Register::R5 => self.r5 = multiplexed_source, + Register::IO => self.io = { + self.output_enable = true; + + multiplexed_source + } + }; + } + DecodedInstruction::Condition(operation) => { + match operation { + ConditionOperation::NEVER => {} + ConditionOperation::EQZ => { + if self.r3 == 0 { + self.program_counter = self.r0 + } + } + ConditionOperation::LTZ => { + if self.r3 < 0 { + self.program_counter = self.r0 + } + } + ConditionOperation::LTEQZ => { + if self.r3 <= 0 { + self.program_counter = self.r0 + } + } + ConditionOperation::ALWAYS => { + self.program_counter = self.r0 + } + ConditionOperation::NEQZ => { + if self.r3 != 0 { + self.program_counter = self.r0 + } + } + ConditionOperation::GTEQZ => { + if self.r3 >= 0 { + self.program_counter = self.r0 + } + } + ConditionOperation::GTZ => { + if self.r3 > 0 { + self.program_counter = self.r0 + } + } + } + } + } + + self.print_diagnostics(); + + if self.step_mode { + match io_stdin.read(&mut [0]) { + Ok(_) => continue, + Err(_) => break + } + } else { + thread::sleep(self.clock_rate_duration); + } + } + } + + fn print_diagnostics(&self) { + println!(""); + println!("Program counter: {:x} ({})", self.program_counter, self.program_counter); + println!("Current instruction: {:x} ({})", self.current_instruction, self.current_instruction); + println!("r0: {:x} ({})", self.r0, self.r0); + println!("r1: {:x} ({})", self.r1, self.r1); + println!("r2: {:x} ({})", self.r2, self.r2); + println!("r3: {:x} ({})", self.r3, self.r3); + println!("r4: {:x} ({})", self.r4, self.r4); + println!("r5: {:x} ({})", self.r5, self.r5); + println!("io: {:x} ({})", self.io, self.io); + println!("input: {}", self.input_enable); + println!("output: {}", self.output_enable); + } + + fn from_hertz(hertz: u32) -> time::Duration { + let second = time::Duration::from_secs(1); + + match second.checked_div(hertz) { + Some(hertz) => hertz, + None => second + } + } + + fn get_instruction_operand(&self, operand_type: Instruction) -> u8 { + self.current_instruction & operand_type.bits() + } +} + +fn main() { + let rom: [u8; _] = [ + 0x3f, // immediate 63 + 0x81, // mov r0 r1 + 0x07, // immediate 7 + 0x82, // mov r0 r2 + 0x44, // add + 0x9e, // mov r3 out + ]; + let mut cpu = Overture::new( + &rom, + 4, + false + ); + + cpu.start(); +}