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:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+8
@@ -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
|
||||||
Generated
+11
@@ -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>
|
||||||
Generated
+8
@@ -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>
|
||||||
Generated
+11
@@ -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
@@ -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
@@ -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"
|
||||||
@@ -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]
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
mod registers;
|
||||||
|
mod instructions;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user