commit
a1ff7a1480
@ -0,0 +1 @@ |
|||||||
|
*.exe |
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,94 @@ |
|||||||
|
pub mod cardiac { |
||||||
|
|
||||||
|
pub struct Memory { |
||||||
|
pub memory: [[i8;2]; 32] |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ProcessCounter { |
||||||
|
pub pid: usize |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Accumulator { |
||||||
|
pub value: i8 |
||||||
|
} |
||||||
|
|
||||||
|
pub struct CPU { |
||||||
|
pub mem: Memory, |
||||||
|
pub acc: Accumulator, |
||||||
|
pub pc: ProcessCounter |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
impl CPU { |
||||||
|
|
||||||
|
fn decoder(&mut self, mem_loc: usize) { |
||||||
|
let opcode = self.mem.memory[mem_loc][0]; |
||||||
|
let address = self.mem.memory[mem_loc][1] as usize; |
||||||
|
self.pc.pid += 1; |
||||||
|
|
||||||
|
match opcode { |
||||||
|
1 => { self.acc.value = self.mem.memory[address][1] as i8}, |
||||||
|
2 => { self.acc.value += self.mem.memory[address][1] as i8}, |
||||||
|
3 => { |
||||||
|
if self.acc.value < 0 { |
||||||
|
self.pc.pid = address; |
||||||
|
} |
||||||
|
} |
||||||
|
5 => { println!("OUTPUT: {:?}", self.mem.memory[address][1]) } |
||||||
|
6 => { self.mem.memory[address][1] = self.acc.value } |
||||||
|
7 => { self.acc.value -= self.mem.memory[address][1] as i8} |
||||||
|
8 => { |
||||||
|
self.pc.pid = address; |
||||||
|
} |
||||||
|
9 => {
|
||||||
|
self.pc.pid = 0; |
||||||
|
println!("\n\n\n PROGRAM COMPLETE"); |
||||||
|
self.print_varibles(); |
||||||
|
panic!("Program Halted"); |
||||||
|
} |
||||||
|
0 => (), |
||||||
|
_ => println!("Instuction set not defined...") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn run(mut self) { |
||||||
|
let mut cycle_counter = 0; |
||||||
|
loop { |
||||||
|
println!("Current State: "); |
||||||
|
self.print_varibles(); |
||||||
|
println!("\nInstruction: {:?}", self.mem.memory[self.pc.pid]); |
||||||
|
self.decoder(self.pc.pid); |
||||||
|
println!("New State: "); |
||||||
|
self.print_varibles(); |
||||||
|
cycle_counter += 1; |
||||||
|
println!("Cycles: {:?}", cycle_counter); |
||||||
|
println!("\n\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn step(mut self, steps: u8){ |
||||||
|
for _i in 0..steps { |
||||||
|
println!("Current State: "); |
||||||
|
self.print_varibles(); |
||||||
|
println!("\nInstruction: {:?}", self.mem.memory[self.pc.pid]); |
||||||
|
self.decoder(self.pc.pid); |
||||||
|
println!("New State: "); |
||||||
|
self.print_varibles(); |
||||||
|
println!("\n\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn print_state(&self){ |
||||||
|
println!("Process Counter: {:?}", self.pc.pid); |
||||||
|
println!("Accumulator: {:?}", self.acc.value); |
||||||
|
println!("Memory: {:?}", self.mem.memory); |
||||||
|
} |
||||||
|
fn print_varibles(&self){ |
||||||
|
println!("Process Counter: {:?}", self.pc.pid); |
||||||
|
println!("Accumulator: {:?}", self.acc.value); |
||||||
|
println!("w: {:?} | x: {:?} | y: {:?} | z: {:?}", self.mem.memory[4],self.mem.memory[5],self.mem.memory[6],self.mem.memory[7]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,150 @@ |
|||||||
|
pub mod cardiac { // This is a module that will be imported in the main file
|
||||||
|
// Seperating this out was just to keep things neater
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Memory { |
||||||
|
// This structure represents the memory of the machine
|
||||||
|
// Its defined as an array of signed 8 bit values
|
||||||
|
pub memory: [[i8;2]; 32] |
||||||
|
} |
||||||
|
|
||||||
|
pub struct ProcessCounter { |
||||||
|
// This keeps track of where we are in the instruction set/memory
|
||||||
|
// usize is used to index the memory, luckily in this program address values
|
||||||
|
// are always positive or I would have had to deal with a conversion for negatives
|
||||||
|
pub pid: usize |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Accumulator { |
||||||
|
// This is the Accumulator, not much to explain
|
||||||
|
// It only holds an 8 bit signed value so -128 to 127
|
||||||
|
// Luckily this wasn't a problem for this example
|
||||||
|
pub value: i8 |
||||||
|
} |
||||||
|
|
||||||
|
pub struct CPU { |
||||||
|
// Here we define a structure to hold all of the pieces together
|
||||||
|
// This allows us to operate on self without having to make sure we specify
|
||||||
|
// the correct object
|
||||||
|
// It also helps with scope and reference issues
|
||||||
|
pub mem: Memory, |
||||||
|
pub acc: Accumulator, |
||||||
|
pub pc: ProcessCounter |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
impl CPU { |
||||||
|
// This is our implementation for CPU
|
||||||
|
// This defines all of the methods associated with the CPU struct
|
||||||
|
|
||||||
|
fn decoder(&mut self, mem_loc: usize) { |
||||||
|
// Decoder reads a memory location and decides what instructions to carry out
|
||||||
|
// &mut self allows us to change values in the CPU
|
||||||
|
// mem_loc is an index of a memory location
|
||||||
|
// in practive mem_loc will be prodivded by the process counter
|
||||||
|
|
||||||
|
let opcode = self.mem.memory[mem_loc][0]; |
||||||
|
// opcode is out operation code. This will be matched to a possible arm
|
||||||
|
// I didn't implement all the instruction as they were not needed and my time is limited
|
||||||
|
|
||||||
|
let address = self.mem.memory[mem_loc][1] as usize; |
||||||
|
// address is the index of the memory location mentioned in the current process counter location
|
||||||
|
|
||||||
|
self.pc.pid += 1; |
||||||
|
// Here we make sure that we will move onto the next instruction in the next cycle
|
||||||
|
// unless overridden by the instruction
|
||||||
|
|
||||||
|
match opcode { // Here we match the opcode to a value on the left of =>
|
||||||
|
// _ is a wildcard since out matching needs to exhaustive.
|
||||||
|
|
||||||
|
1 => { self.acc.value = self.mem.memory[address][1] as i8}, |
||||||
|
// 1: CLA this sets the Accumulator to the value of the address in the operend
|
||||||
|
2 => { self.acc.value += self.mem.memory[address][1] as i8}, |
||||||
|
// 2: ADD this adds the value of the address in the operend to the Accumulator
|
||||||
|
|
||||||
|
3 => { // 3: TAC this checks if the Accumulator is negative and if so it sets the process counter
|
||||||
|
// to the location of the propend
|
||||||
|
if self.acc.value < 0 { |
||||||
|
self.pc.pid = address; |
||||||
|
} |
||||||
|
} |
||||||
|
5 => { println!("OUTPUT: {:?}", self.mem.memory[address][1]) } |
||||||
|
// 5: OUT this prints the propend
|
||||||
|
|
||||||
|
6 => { self.mem.memory[address][1] = self.acc.value } |
||||||
|
// 6: STO this stores the value in the Accumulator to the propend
|
||||||
|
|
||||||
|
7 => { self.acc.value -= self.mem.memory[address][1] as i8} |
||||||
|
// 7: SUB this subtracts the value of the address in the operend to the Accumulator
|
||||||
|
|
||||||
|
8 => { // 8: JMP sets the process counter to the propend
|
||||||
|
self.pc.pid = address; |
||||||
|
} |
||||||
|
9 => { // 9: HRS resets the program counter to 0 and halts the program
|
||||||
|
// The halting is implemented as a panic, but that's fine
|
||||||
|
self.pc.pid = 0; |
||||||
|
println!("\n\n\n PROGRAM COMPLETE"); |
||||||
|
self.print_varibles(); |
||||||
|
panic!("Program Halted"); |
||||||
|
} |
||||||
|
0 => (), // This matches the 0 instruction which is passed over
|
||||||
|
|
||||||
|
_ => println!("Instuction set not defined...") // This is the catchall to make the match exhaustive
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn run(mut self) { |
||||||
|
// This runs the program in it's current state (with program counter wherever it currently is)
|
||||||
|
|
||||||
|
let mut cycle_counter = 0; |
||||||
|
// This keeps track of how many times we've run through the instructions
|
||||||
|
loop { // Loop is an infinate loop meaning this program will continue to run until HRS is called
|
||||||
|
|
||||||
|
println!("Current State: "); // This is just output printing
|
||||||
|
self.print_varibles(); // This calls a method that displays the current print_varibles
|
||||||
|
// It also shows the accum value and the process counter
|
||||||
|
println!("\nInstruction: {:?}", self.mem.memory[self.pc.pid]); |
||||||
|
// This prints the current instruction set based on the process counter
|
||||||
|
|
||||||
|
self.decoder(self.pc.pid); |
||||||
|
// This runs the decoder on the current memory address specified by the process counter
|
||||||
|
|
||||||
|
println!("New State: "); |
||||||
|
self.print_varibles(); |
||||||
|
cycle_counter += 1; // Records the completion of another cycle
|
||||||
|
println!("Cycles: {:?}", cycle_counter); |
||||||
|
println!("\n\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn step(mut self, steps: u8){ |
||||||
|
// This method was used in debugging.
|
||||||
|
// It allows us to step through a specified number of instructions
|
||||||
|
for _i in 0..steps { |
||||||
|
println!("Current State: "); |
||||||
|
self.print_varibles(); |
||||||
|
println!("\nInstruction: {:?}", self.mem.memory[self.pc.pid]); |
||||||
|
self.decoder(self.pc.pid); |
||||||
|
println!("New State: "); |
||||||
|
self.print_varibles(); |
||||||
|
println!("\n\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn print_state(&self){ |
||||||
|
// This prints out the entire memory of the machine as well as the accum and process counter
|
||||||
|
// This was mainly used in debugging
|
||||||
|
println!("Process Counter: {:?}", self.pc.pid); |
||||||
|
println!("Accumulator: {:?}", self.acc.value); |
||||||
|
println!("Memory: {:?}", self.mem.memory); |
||||||
|
} |
||||||
|
fn print_varibles(&self){ |
||||||
|
// This is less verbose than print_state, we print only varibles instead of the entire memory.
|
||||||
|
println!("Process Counter: {:?}", self.pc.pid); |
||||||
|
println!("Accumulator: {:?}", self.acc.value); |
||||||
|
println!("w: {:?} | x: {:?} | y: {:?} | z: {:?}", self.mem.memory[4],self.mem.memory[5],self.mem.memory[6],self.mem.memory[7]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
mod lib; |
||||||
|
use lib::cardiac; |
||||||
|
|
||||||
|
// See lib for in-depth documentation
|
||||||
|
|
||||||
|
fn main() { |
||||||
|
|
||||||
|
let program = cardiac::CPU { |
||||||
|
mem: cardiac::Memory { memory:
|
||||||
|
[ //op addr
|
||||||
|
[0,01], // 0
|
||||||
|
[0,00], // 1
|
||||||
|
[0,00], // 2
|
||||||
|
[0,00], // 3
|
||||||
|
[0,00], // 4 w DATA 0
|
||||||
|
[0,01], // 5 x DATA 1
|
||||||
|
[0,00], // 6 y DATA 0
|
||||||
|
[0,04], // 7 z DATA 4
|
||||||
|
[0,00], // 8 UNUSED MEMORY
|
||||||
|
[0,00], // 9 UNUSED MEMORY
|
||||||
|
[1,04], // 10 start CLA w
|
||||||
|
[2,05], // 11 ADD X
|
||||||
|
[2,06], // 12 ADD y
|
||||||
|
[2,06], // 13 ADD y
|
||||||
|
[6,04], // 14 STO w
|
||||||
|
[1,06], // 15 CLA y
|
||||||
|
[2,05], // 16 ADD x
|
||||||
|
[6,06], // 17 STO y
|
||||||
|
[1,07], // 18 CLA z
|
||||||
|
[7,00], // 19 SUB 00
|
||||||
|
[6,07], // 20 STO z
|
||||||
|
[5,04], // 21 OUT w
|
||||||
|
[3,24], // 22 TAX exit
|
||||||
|
[8,10], // 23 JUMP start
|
||||||
|
[9,00], // 24 HRS 00 exit
|
||||||
|
[0,00], // 25 UNUSED MEMORY
|
||||||
|
[0,00], // 26 UNUSED MEMORY
|
||||||
|
[0,00], // 27 UNUSED MEMORY
|
||||||
|
[0,00], // 28 UNUSED MEMORY
|
||||||
|
[0,00], // 29 UNUSED MEMORY
|
||||||
|
[0,00], // 30 UNUSED MEMORY
|
||||||
|
[0,00] // 31 UNUSED MEMORY
|
||||||
|
] |
||||||
|
}, |
||||||
|
acc: cardiac::Accumulator { value: 0 }, |
||||||
|
pc: cardiac::ProcessCounter { pid: 10} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
// 13 steps is 1 full cycle
|
||||||
|
//program.step(14);
|
||||||
|
program.run(); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
Loading…
Reference in new issue