From 57b4c0e32a7de6fdeb779626d8fcde03da6b6290 Mon Sep 17 00:00:00 2001 From: Marco Thomas Date: Sat, 28 Nov 2020 22:31:43 +0100 Subject: [PATCH] Add State Generator --- src/generate.rs | 4 +- src/main.rs | 45 +++++++---- src/simulate.rs | 209 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 src/simulate.rs diff --git a/src/generate.rs b/src/generate.rs index 2e54049..cf74cd0 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fmt; pub struct Program { - data_memory: [u8; 16], + pub data_memory: [u8; 16], program_memory: [u8; 16], } @@ -82,7 +82,7 @@ pub fn generate_binary(instructions: Vec) -> Program { } } -fn insert_label<'a>(hashmap: &mut HashMap<&'a str, u8>, label: &Option>) { +pub fn insert_label<'a>(hashmap: &mut HashMap<&'a str, u8>, label: &Option>) { if let Some(label) = label { hashmap.insert(label.name, label.location); } diff --git a/src/main.rs b/src/main.rs index 79e7de6..86bd97a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,26 +9,41 @@ use pest::Parser; mod asm; mod generate; mod parse; +mod simulate; use generate::generate_binary; use parse::{parse_asm, AsmParser}; fn main() { - let file_name = env::args().nth(1); + let sub_cmd = env::args().nth(1); + let file_name = env::args().nth(2); + let steps = env::args().nth(3); - let file_content = match file_name { - Some(file_name) => { - fs::read_to_string(file_name).expect("Could not read the provided asm file") - } - None => { - println!("No input file was provided"); - return; - } - }; + if let Some(sub_cmd) = sub_cmd { + let file_content = match file_name { + Some(file_name) => { + fs::read_to_string(file_name).expect("Could not read the provided asm file") + } + None => { + println!("No input file was provided"); + return; + } + }; + + let instructions = parse_asm( + AsmParser::parse(parse::Rule::program, &file_content).unwrap_or_else(|e| panic!("{}", e)), + ); + + if sub_cmd == "generate" { + let binary = generate_binary(instructions); + println!("{}", binary); + } + else if sub_cmd == "simulate" { + if let Some(steps) = steps { + let states = simulate::simulate(instructions, steps.parse::().unwrap()); + println!("{:#?}", states); + } + } + } - let instructions = parse_asm( - AsmParser::parse(parse::Rule::program, &file_content).unwrap_or_else(|e| panic!("{}", e)), - ); - let binary = generate_binary(instructions); - println!("{}", binary); } diff --git a/src/simulate.rs b/src/simulate.rs new file mode 100644 index 0000000..979dad7 --- /dev/null +++ b/src/simulate.rs @@ -0,0 +1,209 @@ +use crate::generate::insert_label; +use crate::asm::*; +use crate::generate::generate_binary; + +use std::collections::HashMap; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct StateRegister { + carry: bool, + zero: bool, + negative: bool +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OpcodeInfo { + addr: u8, + content: u8 +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct State { + step: usize, + clk: bool, + pc: u8, + addr_bus: u8, + data_bus: u8, + ir: u8, // instruction register + dr: u8, // data register + akku: u8, + sr: StateRegister, + opcode_info: Option +} + +pub fn simulate<'a>(instructions: Vec>, max_steps: usize) -> Vec { + let mut data_memory = generate_binary(instructions.clone()).data_memory; + + let mut labels: HashMap<&str, u8> = HashMap::new(); + + let mut states: Vec = Vec::new(); + let mut step: usize = 0; + let mut clk: bool = false; + let mut pc: u8 = 0; + let mut addr_bus: u8 = 0; + let mut data_bus: u8 = 0; + let mut ir : u8 = 0; + let mut dr : u8 = 0; + let mut akku : u8 = 0; + let mut sr: StateRegister = StateRegister { + carry: false, + zero: false, + negative: false + }; + + for instruction in instructions.iter() { + match instruction { + Instruction::NoArgumentInstruction(_, label) + | Instruction::MemoryLocationInstruction(_, label) + | Instruction::ConstantArgumentInstruction(_, label) + | Instruction::ArgumentInstruction(_, label) + | Instruction::Jump(_, label) => { + insert_label(&mut labels, label); + } + } + } + + let mut next_pc = 0; + let mut next_akku = 0; + let mut next_data_mem_addr = 0; + let mut next_data_mem_val = 0; + let mut first_iter = true; + loop { + pc = next_pc; + akku = next_akku; + if !first_iter { + data_memory[next_data_mem_addr] = next_data_mem_val; + } + let instruction = instructions[pc as usize]; + + let binary_instruction: BinaryInstruction = match instruction { + Instruction::NoArgumentInstruction(instruction, _) => instruction.into(), + Instruction::MemoryLocationInstruction(instruction, _) => instruction.into(), + Instruction::ConstantArgumentInstruction(instruction, _) => instruction.into(), + Instruction::ArgumentInstruction(instruction, _) => instruction.into(), + Instruction::Jump(argument, _) => match argument { + JumpArgument::Location(arg) => BinaryInstruction { + opcode: 8, + argument: arg, + }, + JumpArgument::Label(arg) => { + if let Some(address) = labels.get(arg) { + BinaryInstruction { + opcode: 8, + argument: *address, + } + } else { + panic!("Tried to JMP to label: {}, which does not exist", arg); + } + } + }, + }; + + clk = false; + addr_bus = pc; + data_bus = data_memory[pc as usize]; + let opcode_info = match instruction { + Instruction::MemoryLocationInstruction(instruction, _) => { + match instruction { + MemoryLocationInstruction::STA(location) => Some(OpcodeInfo{addr: location, content: data_memory[location as usize]}), + } + }, + Instruction::ArgumentInstruction(instruction, _) => { + match instruction { + ArgumentInstruction::ADD(argument) | ArgumentInstruction::SUB(argument) | ArgumentInstruction::LDA(argument) => match argument { + Argument::MemoryLocation(location) => Some(OpcodeInfo{addr: location, content: data_memory[location as usize]}), + _ => None + } + } + } + _ => None + }; + states.push(State{ + step, + clk, + pc, + addr_bus, + data_bus, + ir, + dr, + akku, + sr, + opcode_info + }); + + clk = true; + dr = binary_instruction.argument; + ir = binary_instruction.opcode; + + match instruction { + Instruction::NoArgumentInstruction(instruction, _) => match instruction { + NoArgumentInstruction::NOP => {} + }, + Instruction::ConstantArgumentInstruction(instruction, _) => match instruction { + ConstantArgumentInstruction::BRC(arg) if sr.carry => next_pc = pc + arg, + ConstantArgumentInstruction::BRN(arg) if sr.negative => next_pc = pc + arg, + ConstantArgumentInstruction::BRZ(arg) if sr.zero => next_pc = pc + arg, + _ => {} + }, + Instruction::Jump(arg, _) => match arg { + JumpArgument::Label(label) => { + next_pc = *labels.get(label).unwrap(); + addr_bus = next_pc; + }, + JumpArgument::Location(location) => { + next_pc = location; + addr_bus = next_pc; + }, + }, + Instruction::MemoryLocationInstruction(arg, _) => match arg { + MemoryLocationInstruction::STA(arg) => { + next_data_mem_addr = arg as usize; + next_data_mem_val = akku; + } + }, + Instruction::ArgumentInstruction(instruction, _) => match instruction { + ArgumentInstruction::LDA(arg) => match arg { + Argument::MemoryLocation(location) => next_akku = data_memory[location as usize], + Argument::Constant(val) => next_akku = val + }, + ArgumentInstruction::ADD(arg) => match arg { + Argument::MemoryLocation(location) => next_akku = akku + data_memory[location as usize], + Argument::Constant(val) => next_akku = akku + val + }, + ArgumentInstruction::SUB(arg) => match arg { + Argument::MemoryLocation(location) => next_akku = akku - data_memory[location as usize], + Argument::Constant(val) => next_akku = akku - val + } + } + } + + if next_pc == pc { + next_pc = pc + 1; + } + + states.push(State{ + step, + clk, + pc, + addr_bus, + data_bus, + ir, + dr, + akku, + sr, + opcode_info + }); + + sr.carry = (akku & (1<<4)) != 0; + sr.zero = akku == 0 || akku == (1<<4); + sr.negative = (akku & (1<<3)) != 0; + + step += 1; + first_iter = false; + if step == max_steps { + break; + } + } + + return states; +}