Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create the main execution loop for the virtual machine. #6

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}