aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkrolxon <krolyxon@tutanota.com>2026-01-04 18:39:25 +0530
committerkrolxon <krolyxon@tutanota.com>2026-01-04 18:39:25 +0530
commit4809d7159f373a358da4932571e2219b39b561ed (patch)
treee4251acc18ebe6419e3b55cccf3efbf0099ee6f5
initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml6
-rw-r--r--src/cpu.rs22
-rw-r--r--src/instructions.rs9
-rw-r--r--src/main.rs166
-rw-r--r--src/memory.rs17
7 files changed, 228 insertions, 0 deletions
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;
+ }
+}