Skip to content

Commit

Permalink
Create the main execution loop for the virtual machine.
Browse files Browse the repository at this point in the history
Create the main execution loop for the virtual machine.
  • Loading branch information
yezzfusl authored Aug 6, 2024
2 parents 45593b0 + 2a33b9f commit ea1fc23
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 114 deletions.
235 changes: 122 additions & 113 deletions src/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,72 @@
// src/cpu.rs

use std::collections::HashMap;
mod memory;
use memory::MemoryManagementUnit;
use crate::io::IOController;
use crate::memory::MemoryManagementUnit;

pub struct CPU {
registers: [u32; 8],
program_counter: usize,
mmu: MemoryManagementUnit,
io_controller: IOController,
instruction_set: HashMap<u8, fn(&mut CPU, u8, u8, u8)>,
flags: u8, // New: Flags register for comparison results
instruction_set: HashMap<u8, fn(&mut CPU, u8, u8)>,
flags: u8,
halted: bool,
}

impl CPU {
pub fn new(io_controller: IOController) -> Self {
pub fn new(io_controller: IOController, mmu: MemoryManagementUnit) -> Self {
let mut cpu = CPU {
registers: [0; 8],
program_counter: 0,
mmu: MemoryManagementUnit::new(),
mmu,
io_controller,
instruction_set: HashMap::new(),
flags: 0,
halted: false,
};
cpu.initialize_instruction_set();
cpu
}

fn initialize_instruction_set(&mut self) {
self.instruction_set.insert(0x00, CPU::add);
self.instruction_set.insert(0x01, CPU::sub);
self.instruction_set.insert(0x02, CPU::mul);
self.instruction_set.insert(0x03, CPU::div);
self.instruction_set.insert(0x04, CPU::load);
self.instruction_set.insert(0x05, CPU::store);
self.instruction_set.insert(0x06, CPU::input);
self.instruction_set.insert(0x07, CPU::output);
// New instructions
self.instruction_set.insert(0x08, CPU::and);
self.instruction_set.insert(0x09, CPU::or);
self.instruction_set.insert(0x0A, CPU::xor);
self.instruction_set.insert(0x0B, CPU::not);
self.instruction_set.insert(0x0C, CPU::shl);
self.instruction_set.insert(0x0D, CPU::shr);
self.instruction_set.insert(0x0E, CPU::cmp);
self.instruction_set.insert(0x0F, CPU::jmp);
self.instruction_set.insert(0x10, CPU::je);
self.instruction_set.insert(0x11, CPU::jne);
self.instruction_set.insert(0x12, CPU::jg);
self.instruction_set.insert(0x13, CPU::jl);
self.instruction_set.insert(0x40, CPU::add);
self.instruction_set.insert(0x41, CPU::sub);
self.instruction_set.insert(0x42, CPU::mul);
self.instruction_set.insert(0x43, CPU::div);
self.instruction_set.insert(0x44, CPU::load);
self.instruction_set.insert(0x45, CPU::store);
self.instruction_set.insert(0x46, CPU::input);
self.instruction_set.insert(0x47, CPU::output);
self.instruction_set.insert(0x48, CPU::and);
self.instruction_set.insert(0x49, CPU::or);
self.instruction_set.insert(0x4A, CPU::xor);
self.instruction_set.insert(0x4B, CPU::not);
self.instruction_set.insert(0x4C, CPU::shl);
self.instruction_set.insert(0x4D, CPU::shr);
self.instruction_set.insert(0x4E, CPU::cmp);
self.instruction_set.insert(0x4F, CPU::jmp);
self.instruction_set.insert(0x50, CPU::je);
self.instruction_set.insert(0x51, CPU::jne);
self.instruction_set.insert(0x52, CPU::jg);
self.instruction_set.insert(0x53, CPU::jl);
self.instruction_set.insert(0xFF, CPU::halt);
}

pub fn load_program(&mut self, program: &[u8]) {
for (i, &byte) in program.iter().enumerate() {
self.mmu.write_byte(i, byte);
}
}

pub fn run(&mut self) {
loop {
self.halted = false;
while !self.halted {
let opcode = self.fetch();
self.decode_and_execute(opcode);
}
println!("CPU halted. Final register state:");
self.print_registers();
}

fn fetch(&mut self) -> u8 {
Expand All @@ -66,49 +76,87 @@ impl CPU {
}

fn decode_and_execute(&mut self, opcode: u8) {
let (op, r1, r2, r3) = self.decode(opcode);
if let Some(instruction) = self.instruction_set.get(&op) {
instruction(self, r1, r2, r3);
let r1 = self.fetch();
let r2 = self.fetch();
if let Some(instruction) = self.instruction_set.get(&opcode) {
instruction(self, r1, r2);
} else {
panic!("Unknown opcode: {:02X}", opcode);
}
}

fn print_registers(&self) {
for (i, reg) in self.registers.iter().enumerate() {
println!("R{}: {:08X}", i, reg);
}
println!("Flags: {:08b}", self.flags);
}

// Instruction implementations

fn add(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] = self.registers[r1 as usize].wrapping_add(self.registers[r2 as usize]);
}

fn sub(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] = self.registers[r1 as usize].wrapping_sub(self.registers[r2 as usize]);
}

fn mul(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] = self.registers[r1 as usize].wrapping_mul(self.registers[r2 as usize]);
}

fn div(&mut self, r1: u8, r2: u8) {
if self.registers[r2 as usize] != 0 {
self.registers[r1 as usize] /= self.registers[r2 as usize];
} else {
panic!("Unknown opcode: {:02X}", op);
panic!("Division by zero");
}
}

fn decode(&self, opcode: u8) -> (u8, u8, u8, u8) {
let op = (opcode & 0xF0) >> 4;
let r1 = (opcode & 0x0C) >> 2;
let r2 = opcode & 0x03;
let r3 = 0; // For future use
(op, r1, r2, r3)
fn load(&mut self, r1: u8, r2: u8) {
let address = self.registers[r2 as usize] as usize;
self.registers[r1 as usize] = self.mmu.read_word(address);
}

// Existing arithmetic operations...
fn store(&mut self, r1: u8, r2: u8) {
let address = self.registers[r2 as usize] as usize;
self.mmu.write_word(address, self.registers[r1 as usize]);
}

fn and(&mut self, r1: u8, r2: u8, _r3: u8) {
fn input(&mut self, r1: u8, _r2: u8) {
self.registers[r1 as usize] = self.io_controller.input();
}

fn output(&mut self, r1: u8, _r2: u8) {
self.io_controller.output(self.registers[r1 as usize]);
}

fn and(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] &= self.registers[r2 as usize];
}

fn or(&mut self, r1: u8, r2: u8, _r3: u8) {
fn or(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] |= self.registers[r2 as usize];
}

fn xor(&mut self, r1: u8, r2: u8, _r3: u8) {
fn xor(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] ^= self.registers[r2 as usize];
}

fn not(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn not(&mut self, r1: u8, _r2: u8) {
self.registers[r1 as usize] = !self.registers[r1 as usize];
}

fn shl(&mut self, r1: u8, r2: u8, _r3: u8) {
fn shl(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] <<= self.registers[r2 as usize];
}

fn shr(&mut self, r1: u8, r2: u8, _r3: u8) {
fn shr(&mut self, r1: u8, r2: u8) {
self.registers[r1 as usize] >>= self.registers[r2 as usize];
}

fn cmp(&mut self, r1: u8, r2: u8, _r3: u8) {
fn cmp(&mut self, r1: u8, r2: u8) {
let (result, overflow) = self.registers[r1 as usize].overflowing_sub(self.registers[r2 as usize]);
self.flags = 0;
if result == 0 {
Expand All @@ -122,103 +170,64 @@ impl CPU {
}
}

fn jmp(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn jmp(&mut self, r1: u8, _r2: u8) {
self.program_counter = self.registers[r1 as usize] as usize;
}

fn je(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn je(&mut self, r1: u8, _r2: u8) {
if self.flags & 0b0001 != 0 {
self.program_counter = self.registers[r1 as usize] as usize;
}
}

fn jne(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn jne(&mut self, r1: u8, _r2: u8) {
if self.flags & 0b0001 == 0 {
self.program_counter = self.registers[r1 as usize] as usize;
}
}

fn jg(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn jg(&mut self, r1: u8, _r2: u8) {
if self.flags & 0b0011 == 0 {
self.program_counter = self.registers[r1 as usize] as usize;
}
}

fn jl(&mut self, r1: u8, _r2: u8, _r3: u8) {
fn jl(&mut self, r1: u8, _r2: u8) {
if self.flags & 0b0010 != 0 {
self.program_counter = self.registers[r1 as usize] as usize;
}
}

fn halt(&mut self, _r1: u8, _r2: u8) {
self.halted = true;
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::io::MockIOController;

// Existing tests...

#[test]
fn test_logical_operations() {
let io_controller = MockIOController::new();
let mut cpu = CPU::new(io_controller);

cpu.registers[0] = 0b1100;
cpu.registers[1] = 0b1010;

cpu.and(0, 1, 0);
assert_eq!(cpu.registers[0], 0b1000);

cpu.registers[0] = 0b1100;
cpu.or(0, 1, 0);
assert_eq!(cpu.registers[0], 0b1110);

cpu.registers[0] = 0b1100;
cpu.xor(0, 1, 0);
assert_eq!(cpu.registers[0], 0b0110);

cpu.registers[0] = 0b1100;
cpu.not(0, 0, 0);
assert_eq!(cpu.registers[0], 0xFFFFFFF3);
}

#[test]
fn test_shift_operations() {
let io_controller = MockIOController::new();
let mut cpu = CPU::new(io_controller);

cpu.registers[0] = 0b1100;
cpu.registers[1] = 2;

cpu.shl(0, 1, 0);
assert_eq!(cpu.registers[0], 0b110000);

cpu.registers[0] = 0b110000;
cpu.shr(0, 1, 0);
assert_eq!(cpu.registers[0], 0b1100);
}

#[test]
fn test_compare_and_jump() {
fn test_program_execution() {
let io_controller = MockIOController::new();
let mut cpu = CPU::new(io_controller);

cpu.registers[0] = 10;
cpu.registers[1] = 10;
cpu.registers[2] = 100; // Jump target

cpu.cmp(0, 1, 0);
assert_eq!(cpu.flags & 0b0001, 0b0001); // Zero flag should be set

cpu.je(2, 0, 0);
assert_eq!(cpu.program_counter, 100);

cpu.registers[1] = 11;
cpu.cmp(0, 1, 0);
assert_eq!(cpu.flags & 0b0010, 0b0010); // Negative flag should be set

cpu.program_counter = 0;
cpu.jl(2, 0, 0);
assert_eq!(cpu.program_counter, 100);
let mmu = MemoryManagementUnit::new();
let mut cpu = CPU::new(io_controller, mmu);

let program = vec![
0x46, 0x00, // INPUT R0
0x40, 0x10, // ADD R1, R0
0x47, 0x01, // OUTPUT R1
0xFF, 0x00, // HALT
];

cpu.load_program(&program);
cpu.io_controller.set_next_input(5);
cpu.run();

assert_eq!(cpu.registers[0], 5);
assert_eq!(cpu.registers[1], 5);
assert_eq!(cpu.io_controller.get_last_output(), 5);
assert!(cpu.halted);
}
}
19 changes: 18 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
// src/main.rs

mod cpu;
mod io;
mod memory;

use cpu::CPU;
use io::IOController;
use memory::MemoryManagementUnit;

fn main() {
println!("Virtual Machine Initializing...");
let io_controller = IOController::new();
let mut cpu = CPU::new(io_controller);
let mmu = MemoryManagementUnit::new();
let mut cpu = CPU::new(io_controller, mmu);

// Load a simple program into memory
let program = vec![
0x46, 0x00, // INPUT R0
0x47, 0x01, // OUTPUT R1
0x40, 0x10, // ADD R1, R0
0x47, 0x01, // OUTPUT R1
0x4E, 0x00, // CMP R0, R0
0x50, 0x00, // JE 0 (Loop back to start)
];

cpu.load_program(&program);
cpu.run();
}

0 comments on commit ea1fc23

Please # to comment.