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:
308
src/instructions.rs
Normal file
308
src/instructions.rs
Normal 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
6
src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
mod registers;
|
||||
mod instructions;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
79
src/registers.rs
Normal file
79
src/registers.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user