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
View File
@@ -0,0 +1 @@
/target
+8
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
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
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
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>
Generated
+6
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>
Generated
+7
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
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
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
View File
@@ -0,0 +1,6 @@
mod registers;
mod instructions;
fn main() {
println!("Hello, world!");
}
+79
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;
}
}