4 changed files with 445 additions and 478 deletions
@ -0,0 +1,51 @@ |
|||||
|
pub struct Alu { |
||||
|
op: u16, |
||||
|
a: u16, |
||||
|
b: u16, |
||||
|
c: u16, |
||||
|
} |
||||
|
|
||||
|
impl Alu { |
||||
|
pub fn new() -> Alu { |
||||
|
Alu { |
||||
|
op: 0, |
||||
|
a: 0, |
||||
|
b: 0, |
||||
|
c: 0, |
||||
|
} |
||||
|
} |
||||
|
pub fn set_input(&mut self, a: u16, b:u16) { |
||||
|
self.a = a % 32768; |
||||
|
self.b = b % 32768; |
||||
|
} |
||||
|
|
||||
|
pub fn set_op(&mut self, op: u16) { |
||||
|
self.op = op; |
||||
|
} |
||||
|
|
||||
|
pub fn get_res(&self) -> u16 { |
||||
|
self.c |
||||
|
} |
||||
|
pub fn execute(&mut self) { |
||||
|
match self.op { |
||||
|
0 => self.c = match self.a == self.b { //eq
|
||||
|
true => 1, |
||||
|
false => 0, |
||||
|
}, |
||||
|
1 => self.c = match self.a > self.b { //gt
|
||||
|
true => 1, |
||||
|
false => 0, |
||||
|
}, |
||||
|
2 => self.c = (self.a + self.b) % 32768, //add
|
||||
|
3 => self.c = ((self.a as u32 * self.b as u32) % 32768) as u16, //mult
|
||||
|
4 => self.c = self.a % self.b, //mod
|
||||
|
5 => self.c = self.a & self.b, //and
|
||||
|
6 => self.c = self.a | self.b, //or
|
||||
|
7 => self.c = (!self.a) % 32768, //not
|
||||
|
_ => self.c = self.a, //other
|
||||
|
} |
||||
|
if super::DEBUG { |
||||
|
println!("\tAlu{{op: {}, a: {}, b: {}, res: {}", self.op, self.a, self.b, self.c); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,370 @@ |
|||||
|
use std::fmt; |
||||
|
use std::fs::File; |
||||
|
use std::path::Path; |
||||
|
use std::io::prelude::*; |
||||
|
use super::*; |
||||
|
|
||||
|
pub struct Cpu { |
||||
|
stack: Vec<u16>, |
||||
|
mem: Vec<u16>, |
||||
|
reg: Vec<u16>, |
||||
|
pc: u16, |
||||
|
alu: Alu, |
||||
|
} |
||||
|
|
||||
|
impl fmt::Debug for Cpu { |
||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||
|
write!(f, "Cpu {{ pc = {}, ", self.pc)?; |
||||
|
write!(f, "reg = ")?; |
||||
|
for i in 0..7 { |
||||
|
write!(f, "[{}]: {}, ", i, self.reg[i])?; |
||||
|
} |
||||
|
write!(f, "stack = ")?; |
||||
|
for i in 0..self.stack.len() { |
||||
|
write!(f, "[{}]: {}, ", i, self.stack[i])?; |
||||
|
} |
||||
|
for i in 0..self.mem.len() { |
||||
|
write!(f, "[{}]: {}, ", i, self.mem[i])?; |
||||
|
} |
||||
|
write!(f, "}}")?; |
||||
|
Ok(()) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
impl Cpu { |
||||
|
pub fn new() -> Cpu { |
||||
|
Cpu { |
||||
|
stack: Vec::new(), |
||||
|
mem: vec![0; 32768], |
||||
|
reg: vec![0; 8], |
||||
|
pc: 0, |
||||
|
alu: Alu::new(), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pub fn read_program(&mut self, prog: Vec<u16>) { |
||||
|
self.pc = 0; |
||||
|
for i in prog { |
||||
|
self.mem[self.pc as usize] = i; |
||||
|
self.inc_pc(); |
||||
|
} |
||||
|
self.pc = 0; |
||||
|
} |
||||
|
|
||||
|
pub fn read_program_from_file(&mut self, filepath: &str) -> std::io::Result<()> { |
||||
|
self.reg = vec![0; 8]; |
||||
|
self.mem = vec![0; 32768]; |
||||
|
self.stack = Vec::new(); |
||||
|
let path = Path::new(filepath); |
||||
|
let mut file = File::open(path)?; |
||||
|
let mut content : Vec<u8> = Vec::new(); |
||||
|
file.read_to_end(&mut content)?; |
||||
|
self.pc = 0; |
||||
|
let mut counter = 0; |
||||
|
for i in content { |
||||
|
match counter % 2 { |
||||
|
0 => self.mem[counter / 2] = i as u16, |
||||
|
_ => self.mem[counter / 2] += (i as u16) << 8, |
||||
|
} |
||||
|
counter += 1; |
||||
|
} |
||||
|
let prog = self.mem.clone(); |
||||
|
Ok(()) |
||||
|
} |
||||
|
|
||||
|
pub fn execute(&mut self) -> Result<(), &'static str> { |
||||
|
let mut running : bool; |
||||
|
if self.mem.is_empty() { |
||||
|
return Err("No Program"); |
||||
|
} else { |
||||
|
running = true; |
||||
|
while running { |
||||
|
match self.readmem(self.pc).unwrap() { |
||||
|
0 => { //halt: stop execution and terminate the program
|
||||
|
if DEBUG { |
||||
|
println!("{}: halt", self.pc); |
||||
|
} |
||||
|
running = false; |
||||
|
}, |
||||
|
1 => { //set <a> <b>: set register <a> to the value of <b>
|
||||
|
if DEBUG { |
||||
|
println!("{}: set {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let reg = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let val = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.write(reg, val)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
2 => { //push <a>: push <a> onto the stack
|
||||
|
if DEBUG { |
||||
|
println!("{}: push {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let val = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.push(val); |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
3 => { //pop <a>: remove the top element from the stack and write it into <a>; empty stack = error
|
||||
|
if DEBUG { |
||||
|
println!("{}: pop {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
match self.pop() { |
||||
|
None => { |
||||
|
return Err("Pop: Empty Stack") |
||||
|
}, |
||||
|
Some(i) => { |
||||
|
let addr = self.readmem(self.pc).unwrap(); |
||||
|
self.write(addr, i)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
4|5 => { //eq <a> <b> <c>: set <a> to 1 if <b> is equal to <c>; set it to 0 otherwise
|
||||
|
if DEBUG { //gt <a> <b> <c>: set <a> to 1 if <b> is greater than <c>; set it to 0 otherwise
|
||||
|
println!("{}: comp_op {} {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize], self.mem[(self.pc + 3) as usize]); |
||||
|
} |
||||
|
let op:u16 = self.readmem(self.pc).unwrap() - 4; |
||||
|
self.inc_pc(); |
||||
|
let resaddr = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let a = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let b = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.alu.set_input(a, b); |
||||
|
self.alu.set_op(op); //alu eq|gt
|
||||
|
self.alu.execute(); |
||||
|
let res = self.alu.get_res(); |
||||
|
self.write(resaddr, res)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
6 => { //jmp <a>: jump to <a>
|
||||
|
if DEBUG { |
||||
|
println!("{}: jmp {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let target = self.readmem(self.pc).unwrap(); |
||||
|
self.jump(target)?; |
||||
|
}, |
||||
|
7 => { //jt <a> <b>: if <a> is nonzero, jump to <b>
|
||||
|
if DEBUG { |
||||
|
println!("{}: jt {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let a = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.alu.set_input(a, 0); |
||||
|
self.alu.set_op(0); //alu op: eq
|
||||
|
self.alu.execute(); |
||||
|
self.inc_pc(); |
||||
|
let target = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
match self.alu.get_res() { |
||||
|
0 => { |
||||
|
self.jump(target)?; |
||||
|
}, |
||||
|
_ => { |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
8 => { //jf <a> <b>: if <a> is zero, jump to <b>
|
||||
|
if DEBUG { |
||||
|
println!("{}: jf {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let a = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.alu.set_input(a, 0); |
||||
|
self.alu.set_op(0); //alu op: eq
|
||||
|
self.alu.execute(); |
||||
|
self.inc_pc(); |
||||
|
let target = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
match self.alu.get_res() { |
||||
|
0 => { |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
_ => { |
||||
|
self.jump(target)?; |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
9...13 => { //aluop <a> <b> <c>: compute result in alu with <a> = <b> op <c>
|
||||
|
if DEBUG { |
||||
|
println!("{}: aluop {} {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize], self.mem[(self.pc + 3) as usize]); |
||||
|
} |
||||
|
let op:u16 = self.readmem(self.pc).unwrap() - 7; |
||||
|
self.inc_pc(); |
||||
|
let resaddr = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let a = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let b = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.alu.set_input(a, b); |
||||
|
self.alu.set_op(op); //alu add|mult|mod|and|or
|
||||
|
self.alu.execute(); |
||||
|
let res = self.alu.get_res(); |
||||
|
self.write(resaddr, res)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
14 => { //not <a> <b>: stores 15-bit bitwise inverse of <b> in <a>
|
||||
|
if DEBUG { |
||||
|
println!("{}: not {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let resaddr = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let a = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.alu.set_input(a, 0); |
||||
|
self.alu.set_op(7); //alu op: not
|
||||
|
self.alu.execute(); |
||||
|
let res = self.alu.get_res(); |
||||
|
self.write(resaddr, res)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
15 => { //rmem <a> <b>: read memory at address <b> and write it to <a>
|
||||
|
if DEBUG { |
||||
|
println!("{}: rmem {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let resaddr = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let result = self.readmem(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.write(resaddr, result)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
16 => { //wmem <a> <b>: write the value from <b> into memory at address <a>
|
||||
|
if DEBUG { |
||||
|
println!("{}: wmem {} {}", self.pc, self.mem[(self.pc + 1) as usize], self.mem[(self.pc + 2) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let resaddr = self.readmem(self.pc).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let result = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.writemem(resaddr, result)?; |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
17 => { //call <a>: write the address of the next instruction to the stack and jump to <a>
|
||||
|
if DEBUG { |
||||
|
println!("{}: call {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let target = self.read(self.readmem(self.pc).unwrap()).unwrap(); |
||||
|
self.inc_pc(); |
||||
|
let pc = self.pc; |
||||
|
self.push(pc); |
||||
|
self.jump(target)?; |
||||
|
}, |
||||
|
18 => { //ret: remove the top element from the stack and jump to it; empty stack = halt
|
||||
|
if DEBUG { |
||||
|
println!("{}: ret", self.pc); |
||||
|
} |
||||
|
match self.pop() { |
||||
|
None => { |
||||
|
running = false; |
||||
|
}, |
||||
|
Some(target) => { |
||||
|
self.jump(target)?; |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
19 => { //out <a>: write the character represented by ascii code <a> to the terminal
|
||||
|
if DEBUG { |
||||
|
println!("{}: out {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
let c : char = self.read(self.readmem(self.pc).unwrap()).unwrap() as u8 as char; |
||||
|
print!("{}", c); |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
20 => { //in <a>: read a character from the terminal and write its ascii code to <a>; it can be assumed that once input starts, it will continue until a newline is encountered; this means that you can safely read whole lines from the keyboard and trust that they will be fully read
|
||||
|
if DEBUG { |
||||
|
println!("{}: in {}", self.pc, self.mem[(self.pc + 1) as usize]); |
||||
|
} |
||||
|
running = false; |
||||
|
}, |
||||
|
21 => { //noop: no operation
|
||||
|
if DEBUG { |
||||
|
println!("{}: noop", self.pc); |
||||
|
} |
||||
|
self.inc_pc(); |
||||
|
}, |
||||
|
_ => { //invalid
|
||||
|
if DEBUG { |
||||
|
println!("{}: {} (invalid)", self.pc, self.mem[self.pc as usize]); |
||||
|
} |
||||
|
return Err("Invalid Instruction"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return Ok(()); |
||||
|
} |
||||
|
|
||||
|
fn read(&self, val: u16) -> Option<u16> { |
||||
|
match val { |
||||
|
0...32767 => Some(val), |
||||
|
32768...32775 => Some(self.reg[(val - 32768) as usize]), |
||||
|
_ => None |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn readmem(&self, addr: u16) -> Option<u16> { |
||||
|
match self.read(addr) { |
||||
|
Some(address) => { |
||||
|
match address { |
||||
|
0...32767 => Some(self.mem[address as usize]), |
||||
|
_ => None |
||||
|
} |
||||
|
}, |
||||
|
None => None |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn pop(&mut self) -> Option<u16> { |
||||
|
self.stack.pop() |
||||
|
} |
||||
|
|
||||
|
fn write(&mut self, addr: u16, val: u16) -> Result<(), &'static str> { |
||||
|
match addr { |
||||
|
32768...32775 => { |
||||
|
self.reg[(addr - 32768) as usize] = val; |
||||
|
Ok(()) |
||||
|
} |
||||
|
_ => Err("Write: Invalid register") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn writemem(&mut self, addr: u16, val: u16) -> Result<(), &'static str> { |
||||
|
match self.read(addr) { |
||||
|
Some(address) => { |
||||
|
match address { |
||||
|
0...32767 => { |
||||
|
self.mem[address as usize] = val; |
||||
|
Ok(()) |
||||
|
}, |
||||
|
_ => Err("Write: Invalid address") |
||||
|
} |
||||
|
}, |
||||
|
None => Err("Write: Invalid address") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn push(&mut self, val: u16) { |
||||
|
self.stack.push(val); |
||||
|
} |
||||
|
|
||||
|
fn jump(&mut self, target: u16) -> Result<(), &'static str> { |
||||
|
match target { |
||||
|
0...32767 => { |
||||
|
self.pc = target; |
||||
|
Ok(()) |
||||
|
}, |
||||
|
_ => Err("Jump: Invalid address") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fn inc_pc(&mut self) { |
||||
|
self.pc += 1; |
||||
|
self.pc %= 32768; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
|
||||
|
use super::*; |
||||
|
|
||||
|
#[test] |
||||
|
fn nop_out_halt() { |
||||
|
let mut cpu = Cpu::new(); |
||||
|
let program: Vec<u16> = vec![21, 21, 19, 5, 0, 'a' as u16]; |
||||
|
cpu.write_program(program); |
||||
|
let mut result = cpu.execute(); |
||||
|
if result.is_ok() { |
||||
|
println!("Execution successful!"); |
||||
|
} else { |
||||
|
println!("Err: {}", result.err().unwrap()) |
||||
|
} |
||||
|
assert!(result.is_ok()); |
||||
|
} |
||||
Loading…
Reference in new issue