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