commit 4809d7159f373a358da4932571e2219b39b561ed Author: krolxon Date: Sun Jan 4 18:39:25 2026 +0530 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2417187 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cpu-emulator" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..411e763 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cpu-emulator" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..8b63433 --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,22 @@ +#[derive(Default)] +#[derive(Debug)] +pub struct CPU{ + pub a: u8, + pub b: u8, + pub c: u8, + pub d: u8, + + pub pc: u16, + pub sp: u16, + + pub zero: bool, + pub carry: bool, + + pub halted: bool, +} + +impl CPU { + pub fn inc_cp(&mut self) { + self.pc += 1; + } +} diff --git a/src/instructions.rs b/src/instructions.rs new file mode 100644 index 0000000..f5884dc --- /dev/null +++ b/src/instructions.rs @@ -0,0 +1,9 @@ +#[repr(u8)] +pub enum Instruction { + MOV = 0x01, + ADD = 0x02, + SUB = 0x03, + JMP = 0x04, + JZ = 0x05, + HLT = 0xFF, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..26db2f0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,166 @@ +mod cpu; +mod instructions; +mod memory; + +use cpu::CPU; +use memory::Memory; +use instructions::Instruction; + +fn main() { + let mut cpu = CPU::default(); + let mut mem = Memory::new(); + + + + // a = 10 + mem.write(0x0000, Instruction::MOV as u8); + mem.write(0x0001, 0); + mem.write(0x0002, 5); + + // b = 2 + mem.write(0x0003, Instruction::MOV as u8); + mem.write(0x0004, 1); + mem.write(0x0005, 10); + + // a = a + b + mem.write(0x0006, Instruction::SUB as u8); + mem.write(0x0007, 0); + mem.write(0x0008, 1); + + // set b = 0 + mem.write(0x0009, Instruction::MOV as u8); + mem.write(0x000a, 1); + mem.write(0x000b, 0); + + // halt + mem.write(0x000c, Instruction::HLT as u8); + + while !cpu.halted { + let opcode = mem.read(cpu.pc); + cpu.inc_cp(); + + match opcode { + x if x == Instruction::MOV as u8 => { + let reg = mem.read(cpu.pc); cpu.inc_cp(); + let val = mem.read(cpu.pc); cpu.inc_cp(); + + match reg { + 0 => cpu.a = val, + 1 => cpu.b = val, + 2 => cpu.c = val, + 3 => cpu.d = val, + _ => {} + } + } + + x if x == Instruction::ADD as u8 => { + let dest = mem.read(cpu.pc); cpu.pc += 1; + let src = mem.read(cpu.pc); cpu.pc += 1; + + let (result, carry) = match (dest, src) { + // What the fuck do these tuples mean? + // so basically they are the numbers assigned to register + // 0 => A, 1 => B .... + // so when it is (0, 0), it basically says add the + // value of register B into register A, + // thats exactly whats replicated in the code below + (0, 0) => cpu.a.overflowing_add(cpu.a), + (0, 1) => cpu.a.overflowing_add(cpu.b), + (0, 2) => cpu.a.overflowing_add(cpu.c), + (0, 3) => cpu.a.overflowing_add(cpu.d), + + (1, 0) => cpu.b.overflowing_add(cpu.a), + (1, 1) => cpu.b.overflowing_add(cpu.b), + (1, 2) => cpu.b.overflowing_add(cpu.c), + (1, 3) => cpu.b.overflowing_add(cpu.d), + + (2, 0) => cpu.c.overflowing_add(cpu.a), + (2, 1) => cpu.c.overflowing_add(cpu.b), + (2, 2) => cpu.c.overflowing_add(cpu.c), + (2, 3) => cpu.c.overflowing_add(cpu.d), + + (3, 0) => cpu.d.overflowing_add(cpu.a), + (3, 1) => cpu.d.overflowing_add(cpu.b), + (3, 2) => cpu.d.overflowing_add(cpu.c), + (3, 3) => cpu.d.overflowing_add(cpu.d), + + _ => (0, false), + }; + + + + match dest { + 0 => cpu.a = result, + 1 => cpu.b = result, + 2 => cpu.c = result, + 3 => cpu.d = result, + _ => {} + } + + cpu.zero = result == 0; + cpu.carry = carry; + } + x if x == Instruction::SUB as u8 => { + let dest = mem.read(cpu.pc); cpu.pc += 1; + let src = mem.read(cpu.pc); cpu.pc += 1; + + let (result, borrow) = match (dest, src) { + // What the fuck do these tuples mean? + // so basically they are the numbers assigned to register + // 0 => A, 1 => B .... + // so when it is (0, 0), it basically says add the + // value of register B into register A, + // thats exactly whats replicated in the code below + (0, 0) => cpu.a.overflowing_sub(cpu.a), + (0, 1) => cpu.a.overflowing_sub(cpu.b), + (0, 2) => cpu.a.overflowing_sub(cpu.c), + (0, 3) => cpu.a.overflowing_sub(cpu.d), + + (1, 0) => cpu.b.overflowing_sub(cpu.a), + (1, 1) => cpu.b.overflowing_sub(cpu.b), + (1, 2) => cpu.b.overflowing_sub(cpu.c), + (1, 3) => cpu.b.overflowing_sub(cpu.d), + + (2, 0) => cpu.c.overflowing_sub(cpu.a), + (2, 1) => cpu.c.overflowing_sub(cpu.b), + (2, 2) => cpu.c.overflowing_sub(cpu.c), + (2, 3) => cpu.c.overflowing_sub(cpu.d), + + (3, 0) => cpu.d.overflowing_sub(cpu.a), + (3, 1) => cpu.d.overflowing_sub(cpu.b), + (3, 2) => cpu.d.overflowing_sub(cpu.c), + (3, 3) => cpu.d.overflowing_sub(cpu.d), + + _ => (0, false), + }; + + + + match dest { + 0 => cpu.a = result, + 1 => cpu.b = result, + 2 => cpu.c = result, + 3 => cpu.d = result, + _ => {} + } + + cpu.zero = result == 0; + cpu.carry = borrow; + } + + + x if x == Instruction::JMP as u8 => {} + + x if x == Instruction::JZ as u8 => {} + + x if x == Instruction::HLT as u8 => { + cpu.halted = true; + } + + _ => panic!("Unknown opcode {:02X}", opcode), + } + } + + println!("{:#?}", cpu); + +} diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000..dbcebed --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,17 @@ +pub struct Memory { + pub data: [u8; 65536], +} + +impl Memory { + pub fn new() -> Self { + Self { data: [0; 65536] } + } + + pub fn read(&self, addr: u16) -> u8 { + self.data[addr as usize] + } + + pub fn write(&mut self, addr: u16, value: u8) { + self.data[addr as usize] = value; + } +}