Krolyxon
Commit 4809d71

Branches

Tags

Refs →

bench514809d71

initial commit

Author
krolxon
Date
2026-01-04 18:39 +0530
Parents
None
Commit
4809d7159f373a358da4932571e2219b39b561ed

+228 −0

7 files changed

  1. A .gitignore +1 −0
  2. A Cargo.lock +7 −0
  3. A Cargo.toml +6 −0
  4. A src/cpu.rs +22 −0
  5. A src/instructions.rs +9 −0
  6. A src/main.rs +166 −0
  7. A src/memory.rs +17 −0
A .gitignore View
@@ -0,0 +1 @@
1+/target
A Cargo.lock View
@@ -0,0 +1,7 @@
1+# This file is automatically @generated by Cargo.
2+# It is not intended for manual editing.
3+version = 4
4+
5+[[package]]
6+name = "cpu-emulator"
7+version = "0.1.0"
A Cargo.toml View
@@ -0,0 +1,6 @@
1+[package]
2+name = "cpu-emulator"
3+version = "0.1.0"
4+edition = "2024"
5+
6+[dependencies]
A src/cpu.rs View
@@ -0,0 +1,22 @@
1+#[derive(Default)]
2+#[derive(Debug)]
3+pub struct CPU{
4+ pub a: u8,
5+ pub b: u8,
6+ pub c: u8,
7+ pub d: u8,
8+
9+ pub pc: u16,
10+ pub sp: u16,
11+
12+ pub zero: bool,
13+ pub carry: bool,
14+
15+ pub halted: bool,
16+}
17+
18+impl CPU {
19+ pub fn inc_cp(&mut self) {
20+ self.pc += 1;
21+ }
22+}
A src/instructions.rs View
@@ -0,0 +1,9 @@
1+#[repr(u8)]
2+pub enum Instruction {
3+ MOV = 0x01,
4+ ADD = 0x02,
5+ SUB = 0x03,
6+ JMP = 0x04,
7+ JZ = 0x05,
8+ HLT = 0xFF,
9+}
A src/main.rs View
@@ -0,0 +1,166 @@
1+mod cpu;
2+mod instructions;
3+mod memory;
4+
5+use cpu::CPU;
6+use memory::Memory;
7+use instructions::Instruction;
8+
9+fn main() {
10+ let mut cpu = CPU::default();
11+ let mut mem = Memory::new();
12+
13+
14+
15+ // a = 10
16+ mem.write(0x0000, Instruction::MOV as u8);
17+ mem.write(0x0001, 0);
18+ mem.write(0x0002, 5);
19+
20+ // b = 2
21+ mem.write(0x0003, Instruction::MOV as u8);
22+ mem.write(0x0004, 1);
23+ mem.write(0x0005, 10);
24+
25+ // a = a + b
26+ mem.write(0x0006, Instruction::SUB as u8);
27+ mem.write(0x0007, 0);
28+ mem.write(0x0008, 1);
29+
30+ // set b = 0
31+ mem.write(0x0009, Instruction::MOV as u8);
32+ mem.write(0x000a, 1);
33+ mem.write(0x000b, 0);
34+
35+ // halt
36+ mem.write(0x000c, Instruction::HLT as u8);
37+
38+ while !cpu.halted {
39+ let opcode = mem.read(cpu.pc);
40+ cpu.inc_cp();
41+
42+ match opcode {
43+ x if x == Instruction::MOV as u8 => {
44+ let reg = mem.read(cpu.pc); cpu.inc_cp();
45+ let val = mem.read(cpu.pc); cpu.inc_cp();
46+
47+ match reg {
48+ 0 => cpu.a = val,
49+ 1 => cpu.b = val,
50+ 2 => cpu.c = val,
51+ 3 => cpu.d = val,
52+ _ => {}
53+ }
54+ }
55+
56+ x if x == Instruction::ADD as u8 => {
57+ let dest = mem.read(cpu.pc); cpu.pc += 1;
58+ let src = mem.read(cpu.pc); cpu.pc += 1;
59+
60+ let (result, carry) = match (dest, src) {
61+ // What the fuck do these tuples mean?
62+ // so basically they are the numbers assigned to register
63+ // 0 => A, 1 => B ....
64+ // so when it is (0, 0), it basically says add the
65+ // value of register B into register A,
66+ // thats exactly whats replicated in the code below
67+ (0, 0) => cpu.a.overflowing_add(cpu.a),
68+ (0, 1) => cpu.a.overflowing_add(cpu.b),
69+ (0, 2) => cpu.a.overflowing_add(cpu.c),
70+ (0, 3) => cpu.a.overflowing_add(cpu.d),
71+
72+ (1, 0) => cpu.b.overflowing_add(cpu.a),
73+ (1, 1) => cpu.b.overflowing_add(cpu.b),
74+ (1, 2) => cpu.b.overflowing_add(cpu.c),
75+ (1, 3) => cpu.b.overflowing_add(cpu.d),
76+
77+ (2, 0) => cpu.c.overflowing_add(cpu.a),
78+ (2, 1) => cpu.c.overflowing_add(cpu.b),
79+ (2, 2) => cpu.c.overflowing_add(cpu.c),
80+ (2, 3) => cpu.c.overflowing_add(cpu.d),
81+
82+ (3, 0) => cpu.d.overflowing_add(cpu.a),
83+ (3, 1) => cpu.d.overflowing_add(cpu.b),
84+ (3, 2) => cpu.d.overflowing_add(cpu.c),
85+ (3, 3) => cpu.d.overflowing_add(cpu.d),
86+
87+ _ => (0, false),
88+ };
89+
90+
91+
92+ match dest {
93+ 0 => cpu.a = result,
94+ 1 => cpu.b = result,
95+ 2 => cpu.c = result,
96+ 3 => cpu.d = result,
97+ _ => {}
98+ }
99+
100+ cpu.zero = result == 0;
101+ cpu.carry = carry;
102+ }
103+ x if x == Instruction::SUB as u8 => {
104+ let dest = mem.read(cpu.pc); cpu.pc += 1;
105+ let src = mem.read(cpu.pc); cpu.pc += 1;
106+
107+ let (result, borrow) = match (dest, src) {
108+ // What the fuck do these tuples mean?
109+ // so basically they are the numbers assigned to register
110+ // 0 => A, 1 => B ....
111+ // so when it is (0, 0), it basically says add the
112+ // value of register B into register A,
113+ // thats exactly whats replicated in the code below
114+ (0, 0) => cpu.a.overflowing_sub(cpu.a),
115+ (0, 1) => cpu.a.overflowing_sub(cpu.b),
116+ (0, 2) => cpu.a.overflowing_sub(cpu.c),
117+ (0, 3) => cpu.a.overflowing_sub(cpu.d),
118+
119+ (1, 0) => cpu.b.overflowing_sub(cpu.a),
120+ (1, 1) => cpu.b.overflowing_sub(cpu.b),
121+ (1, 2) => cpu.b.overflowing_sub(cpu.c),
122+ (1, 3) => cpu.b.overflowing_sub(cpu.d),
123+
124+ (2, 0) => cpu.c.overflowing_sub(cpu.a),
125+ (2, 1) => cpu.c.overflowing_sub(cpu.b),
126+ (2, 2) => cpu.c.overflowing_sub(cpu.c),
127+ (2, 3) => cpu.c.overflowing_sub(cpu.d),
128+
129+ (3, 0) => cpu.d.overflowing_sub(cpu.a),
130+ (3, 1) => cpu.d.overflowing_sub(cpu.b),
131+ (3, 2) => cpu.d.overflowing_sub(cpu.c),
132+ (3, 3) => cpu.d.overflowing_sub(cpu.d),
133+
134+ _ => (0, false),
135+ };
136+
137+
138+
139+ match dest {
140+ 0 => cpu.a = result,
141+ 1 => cpu.b = result,
142+ 2 => cpu.c = result,
143+ 3 => cpu.d = result,
144+ _ => {}
145+ }
146+
147+ cpu.zero = result == 0;
148+ cpu.carry = borrow;
149+ }
150+
151+
152+ x if x == Instruction::JMP as u8 => {}
153+
154+ x if x == Instruction::JZ as u8 => {}
155+
156+ x if x == Instruction::HLT as u8 => {
157+ cpu.halted = true;
158+ }
159+
160+ _ => panic!("Unknown opcode {:02X}", opcode),
161+ }
162+ }
163+
164+ println!("{:#?}", cpu);
165+
166+}
A src/memory.rs View
@@ -0,0 +1,17 @@
1+pub struct Memory {
2+ pub data: [u8; 65536],
3+}
4+
5+impl Memory {
6+ pub fn new() -> Self {
7+ Self { data: [0; 65536] }
8+ }
9+
10+ pub fn read(&self, addr: u16) -> u8 {
11+ self.data[addr as usize]
12+ }
13+
14+ pub fn write(&mut self, addr: u16, value: u8) {
15+ self.data[addr as usize] = value;
16+ }
17+}