From e6113316bfa033ef6d6b1515e1e25900cd9943c9 Mon Sep 17 00:00:00 2001 From: Ajurna Date: Sat, 26 Apr 2025 08:40:42 +0100 Subject: [PATCH] Implement basic CPU architecture with instruction set This commit sets up a foundational CPU simulation with registers, a memory bus, and an initial instruction set implementation in Rust. It includes operations like ADD, SUB, AND, and bit manipulations, as well as basic project configurations through Cargo and IDE settings. --- .gitignore | 1 + .idea/.gitignore | 8 + .idea/dictionaries/project.xml | 11 ++ .idea/modules.xml | 8 + .idea/untitled.iml | 11 ++ .idea/vcs.xml | 6 + Cargo.lock | 7 + Cargo.toml | 8 + src/instructions.rs | 308 +++++++++++++++++++++++++++++++++ src/main.rs | 6 + src/registers.rs | 79 +++++++++ 11 files changed, 453 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/dictionaries/project.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/untitled.iml create mode 100644 .idea/vcs.xml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/instructions.rs create mode 100644 src/main.rs create mode 100644 src/registers.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..f5998d2 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,11 @@ + + + + adchl + adcn + addhl + rrca + rrla + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..aeb7613 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/untitled.iml b/.idea/untitled.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/untitled.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ccfeb2c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "untitled" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..62ea391 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "untitled" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/instructions.rs b/src/instructions.rs new file mode 100644 index 0000000..afa81c8 --- /dev/null +++ b/src/instructions.rs @@ -0,0 +1,308 @@ +use crate::registers::Registers; + +struct CPU { + registers: Registers, + pc: u16, + bus: MemoryBus, + sp: u16, +} + +struct MemoryBus { + memory: [u8; 0xFFFF] +} +impl MemoryBus { + fn read_byte(&self, address: u16) -> u8 { + self.memory[address as usize] + } +} + +enum Target { + U8Register(TargetRegister), + U16Register(TargetU16Register), + Address(u16), +} + + +enum Instruction { + ADC(Target), + ADD(Target), + ADDHL(TargetU16Register), + AND(Target), + + SUB(TargetRegister), + SBC(TargetRegister), + + OR(TargetRegister), + XOR(TargetRegister), + CP(TargetRegister), + INC(TargetRegister), + DEC(TargetRegister), + CCF, + SCF, + RRA, + RLA, + RRCA, + CPL, + BIT(u8, TargetRegister), +} +enum TargetRegister { A, B, C, D, E, H, L, } +enum TargetU16Register {AF, BC, DE, HL, SP, PC} +impl CPU { + fn get_u8_reg_value(&mut self, target: TargetRegister) -> u8 { + match target { + TargetRegister::A => { self.registers.a } + TargetRegister::B => { self.registers.b } + TargetRegister::C => { self.registers.c } + TargetRegister::D => { self.registers.d } + TargetRegister::E => { self.registers.e } + TargetRegister::H => { self.registers.h } + TargetRegister::L => { self.registers.l } + } + } + fn get_u16_reg_value(&mut self, target: TargetU16Register) -> u16 { + match target { + TargetU16Register::AF => {self.registers.get_af()} + TargetU16Register::BC => {self.registers.get_bc()} + TargetU16Register::DE => {self.registers.get_de()} + TargetU16Register::HL => {self.registers.get_hl()} + TargetU16Register::SP => {self.sp} + TargetU16Register::PC => {self.pc} + } + } + + fn get_target_value(&mut self, target: Target) -> u8 { + match target { + Target::U8Register(target_register) => { self.get_u8_reg_value(target_register) }, + Target::U16Register(target_register) => { self.bus.read_byte(self.get_u16_reg_value(target_register)) }, + Target::Address(address) => { self.bus.read_byte(address) }, + } + } + + /// + fn execute(&mut self, instruction: Instruction) { + match instruction { + Instruction::ADC(target) => { + let value = self.get_target_value(target); + let (mut new_value, did_overflow) = self.registers.a.overflowing_add(value); + new_value += did_overflow as u8; + self.registers.a = new_value; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = false; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + } + Instruction::ADD(target) => { + let value = self.get_target_value(target); + let (new_value, did_overflow) = self.registers.a.overflowing_add(value); + self.registers.a = new_value; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = false; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + } + Instruction::ADDHL(target) => { + let value = self.get_u16_reg_value(target); + let (new_value, did_overflow) = self.registers.get_hl().overflowing_add(value); + self.registers.set_hl(new_value); + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = false; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (new_value & 0xFF) + (value & 0xFF) > 0xFF; + } + + Instruction::AND(target) => { + let value = self.get_target_value(target); + self.registers.a = value & self.registers.a; + self.registers.f.zero = self.registers.a == 0; + self.registers.f.subtract = false; + self.registers.f.carry = false; + self.registers.f.half_carry = true; + } + + + Instruction::SUB(target) => { + let value = self.get_u8_reg_value(target); + let (new_value, did_overflow) = self.registers.a.overflowing_sub(value); + self.registers.a = new_value; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = true; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + } + Instruction::SBC(target) => { + let value = self.get_u8_reg_value(target); + let (mut new_value, did_overflow) = self.registers.a.overflowing_sub(value); + new_value -= did_overflow as u8; + self.registers.a = new_value; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = true; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + } + + Instruction::OR(target) => { + let value = self.get_u8_reg_value(target); + self.registers.a = value | self.registers.a; + self.registers.f.zero = self.registers.a == 0; + self.registers.f.subtract = false; + self.registers.f.carry = false; + self.registers.f.half_carry = false; + } + Instruction::XOR(target) => { + let value = self.get_u8_reg_value(target); + self.registers.a = value ^ self.registers.a; + self.registers.f.zero = self.registers.a == 0; + self.registers.f.subtract = false; + self.registers.f.carry = false; + self.registers.f.half_carry = false; + } + Instruction::CP(target) => { + let value = self.get_u8_reg_value(target); + let (new_value, did_overflow) = self.registers.a.overflowing_sub(value); + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = true; + self.registers.f.carry = did_overflow; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + } + Instruction::INC(target) => { + let (value, new_value) = match target { + TargetRegister::A => { + let value = self.registers.a; + let (new_value, _) = value.overflowing_add(1); + self.registers.a = new_value; + (value, new_value) + }, + TargetRegister::B => { + let value = self.registers.b; + let (new_value, _) = value.overflowing_add(1); + self.registers.b = new_value; + (value, new_value) + }, + TargetRegister::C => { + let value = self.registers.c; + let (new_value, _) = value.overflowing_add(1); + self.registers.c = new_value; + (value, new_value) + }, + TargetRegister::D => { + let value = self.registers.d; + let (new_value, _) = value.overflowing_add(1); + self.registers.d = new_value; + (value, new_value) + }, + TargetRegister::E => { + let value = self.registers.e; + let (new_value, _) = value.overflowing_add(1); + self.registers.e = new_value; + (value, new_value) + }, + TargetRegister::H => { + let value = self.registers.h; + let (new_value, _) = value.overflowing_add(1); + self.registers.h = new_value; + (value, new_value) + }, + TargetRegister::L => { + let value = self.registers.l; + let (new_value, _) = value.overflowing_add(1); + self.registers.l = new_value; + (value, new_value) + } + }; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = false; + self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; + } + Instruction::DEC(target) => { + let (value, new_value) = match target { + TargetRegister::A => { + let value = self.registers.a; + let (new_value, _) = value.overflowing_sub(1); + self.registers.a = new_value; + (value, new_value) + }, + TargetRegister::B => { + let value = self.registers.b; + let (new_value, _) = value.overflowing_sub(1); + self.registers.b = new_value; + (value, new_value) + }, + TargetRegister::C => { + let value = self.registers.c; + let (new_value, _) = value.overflowing_sub(1); + self.registers.c = new_value; + (value, new_value) + }, + TargetRegister::D => { + let value = self.registers.d; + let (new_value, _) = value.overflowing_sub(1); + self.registers.d = new_value; + (value, new_value) + }, + TargetRegister::E => { + let value = self.registers.e; + let (new_value, _) = value.overflowing_sub(1); + self.registers.e = new_value; + (value, new_value) + }, + TargetRegister::H => { + let value = self.registers.h; + let (new_value, _) = value.overflowing_sub(1); + self.registers.h = new_value; + (value, new_value) + }, + TargetRegister::L => { + let value = self.registers.l; + let (new_value, _) = value.overflowing_sub(1); + self.registers.l = new_value; + (value, new_value) + } + }; + self.registers.f.zero = new_value == 0; + self.registers.f.subtract = true; + self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; + } + Instruction::CCF => { + self.registers.f.carry = !self.registers.f.carry; + } + Instruction::SCF => { + self.registers.f.carry = true; + } + Instruction::RRA => { + let old_carry = self.registers.f.carry; + self.registers.f.carry = self.registers.a & 0x1 == 0x1; + self.registers.a = (self.registers.a >> 1) | (if old_carry { 0x80 } else { 0 }); + self.registers.f.zero = false; + self.registers.f.subtract = false; + self.registers.f.half_carry = false; + } + Instruction::RLA => { + let old_carry = self.registers.f.carry; + self.registers.f.carry = self.registers.a & 0x80 == 0x80; + self.registers.a = (self.registers.a << 1) | (if old_carry { 0x01 } else { 0 }); + self.registers.f.zero = false; + self.registers.f.subtract = false; + self.registers.f.half_carry = false; + } + Instruction::RRCA => { + self.registers.f.carry = self.registers.a & 0x1 == 0x1; + self.registers.a = self.registers.a >> 1; + self.registers.f.zero = false; + self.registers.f.subtract = false; + self.registers.f.half_carry = false; + } + Instruction::CPL => { + self.registers.a = !self.registers.a; + self.registers.f.subtract = true; + self.registers.f.half_carry = true; + } + Instruction::BIT(bit, target) => { + let value = self.get_u8_reg_value(target); + self.registers.f.zero = value >> bit & 0x1 == 0; + self.registers.f.subtract = false; + self.registers.f.half_carry = true; + } + + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b2ee0a1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +mod registers; +mod instructions; + +fn main() { + println!("Hello, world!"); +} diff --git a/src/registers.rs b/src/registers.rs new file mode 100644 index 0000000..479a3c9 --- /dev/null +++ b/src/registers.rs @@ -0,0 +1,79 @@ + +#[derive(Clone, Copy)] +pub(crate) struct FlagsRegister { + pub(crate) zero: bool, + pub(crate) subtract: bool, + pub(crate) half_carry: bool, + pub(crate) carry: bool +} + +const ZERO_FLAG_BYTE_POSITION: u8 = 7; +const SUBTRACT_FLAG_BYTE_POSITION: u8 = 6; +const HALF_CARRY_FLAG_BYTE_POSITION: u8 = 5; +const CARRY_FLAG_BYTE_POSITION: u8 = 4; + +impl std::convert::From for u8 { + fn from(flag: FlagsRegister) -> u8 { + (if flag.zero { 1 } else { 0 }) << ZERO_FLAG_BYTE_POSITION | + (if flag.subtract { 1 } else { 0 }) << SUBTRACT_FLAG_BYTE_POSITION | + (if flag.half_carry { 1 } else { 0 }) << HALF_CARRY_FLAG_BYTE_POSITION | + (if flag.carry { 1 } else { 0 }) << CARRY_FLAG_BYTE_POSITION + } +} + +impl std::convert::From for FlagsRegister { + fn from(byte: u8) -> Self { + let zero = ((byte >> ZERO_FLAG_BYTE_POSITION) & 0b1) != 0; + let subtract = ((byte >> SUBTRACT_FLAG_BYTE_POSITION) & 0b1) != 0; + let half_carry = ((byte >> HALF_CARRY_FLAG_BYTE_POSITION) & 0b1) != 0; + let carry = ((byte >> CARRY_FLAG_BYTE_POSITION) & 0b1) != 0; + + FlagsRegister { + zero, + subtract, + half_carry, + carry + } + } +} +pub(crate) struct Registers { + pub(crate) a: u8, + pub(crate) b: u8, + pub(crate) c: u8, + pub(crate) d: u8, + pub(crate) e: u8, + pub(crate) f: FlagsRegister, + pub(crate) h: u8, + pub(crate) l: u8, +} + +impl Registers { + pub(crate) fn get_af(&self) -> u16 { + (self.a as u16) << 8 | u8::from(self.f) as u16 + } + pub(crate) fn set_af(&mut self, value: u16) { + self.a = ((value & 0xFF00) >> 8) as u8; + self.f = FlagsRegister::from((value & 0xFF) as u8); + } + pub(crate) fn get_bc(&self) -> u16 { + (self.b as u16) << 8 | self.c as u16 + } + pub(crate) fn set_bc(&mut self, value: u16) { + self.b = ((value & 0xFF00) >> 8) as u8; + self.c = (value & 0xFF) as u8; + } + pub(crate) fn get_de(&self) -> u16 { + (self.d as u16) << 8 | self.e as u16 + } + pub(crate) fn set_de(&mut self, value: u16) { + self.d = ((value & 0xFF00) >> 8) as u8; + self.e = (value & 0xFF) as u8; + } + pub(crate) fn get_hl(&self) -> u16 { + (self.h as u16) << 8 | self.l as u16 + } + pub(crate) fn set_hl(&mut self, value: u16) { + self.h = ((value & 0xFF00) >> 8) as u8; + self.l = (value & 0xFF) as u8; + } +} \ No newline at end of file