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.
This commit is contained in:
2025-04-26 08:40:42 +01:00
commit e6113316bf
11 changed files with 453 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

8
.idea/.gitignore generated vendored Normal file
View File

@@ -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

11
.idea/dictionaries/project.xml generated Normal file
View File

@@ -0,0 +1,11 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>adchl</w>
<w>adcn</w>
<w>addhl</w>
<w>rrca</w>
<w>rrla</w>
</words>
</dictionary>
</component>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/untitled.iml" filepath="$PROJECT_DIR$/.idea/untitled.iml" />
</modules>
</component>
</project>

11
.idea/untitled.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

7
Cargo.lock generated Normal file
View File

@@ -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"

8
Cargo.toml Normal file
View File

@@ -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]

308
src/instructions.rs Normal file
View File

@@ -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;
}
}
}
}

6
src/main.rs Normal file
View File

@@ -0,0 +1,6 @@
mod registers;
mod instructions;
fn main() {
println!("Hello, world!");
}

79
src/registers.rs Normal file
View File

@@ -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<FlagsRegister> 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<u8> 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;
}
}