use crate::generate::insert_label; use crate::asm::*; use crate::generate::generate_binary; use std::collections::HashMap; use std::fmt; use serde::Serialize; #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] pub struct StateRegister { pub carry: bool, pub zero: bool, pub negative: bool } #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] pub struct OpcodeInfo { pub addr: u8, pub content: u8 } #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] pub struct State { pub step: usize, pub clk: bool, pub pc: u8, pub addr_bus: u8, pub data_bus: u8, pub ir: u8, // instruction register pub dr: u8, // data register pub akku: u8, pub sr: StateRegister, pub opcode_info: Option } impl fmt::Display for State { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "")?; writeln!(f, "{}", self.step)?; writeln!(f, "{}", self.clk as u8)?; writeln!(f, "{}", self.pc)?; writeln!(f, "{}", self.addr_bus)?; writeln!(f, "{}", self.data_bus)?; writeln!(f, "{}", self.ir)?; writeln!(f, "{}", self.dr)?; writeln!(f, "{}", self.akku)?; writeln!(f, "C: {}, Z: {}, N: {}", self.sr.carry as u8, self.sr.zero as u8, self.sr.negative as u8)?; if let Some(opcode_info) = self.opcode_info { writeln!(f, "addr: {}, val: {}", opcode_info.addr, opcode_info.content)?; } else { writeln!(f, "")?; } writeln!(f, "") } } 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; let mut pc: u8 = 0; let mut addr_bus: u8; let mut data_bus: u8; 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: Option = None; let mut next_akku = 0; let mut next_carry = false; let mut next_data_mem_addr: Option = None; let mut next_data_mem_val: Option = None; loop { if next_akku != akku { next_carry = (next_akku & (1<<4)) != 0; } if let Some(mut next_pc_value) = next_pc { next_pc_value = next_pc_value % 16; next_akku = next_akku % 16; pc = next_pc_value; next_pc = None; } akku = next_akku; if let Some(addr) = next_data_mem_addr { data_memory[addr] = next_data_mem_val.unwrap(); next_data_mem_addr = None; next_data_mem_val = None; } let instruction = if pc as usize > instructions.len() - 1 { Instruction::NoArgumentInstruction(NoArgumentInstruction::NOP, None) } else { 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); } }, JumpArgument::MemoryLocation(address) => { BinaryInstruction { opcode: 12, argument: address } } }, }; 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 } } }, Instruction::Jump(JumpArgument::MemoryLocation(location), _) => { Some(OpcodeInfo{ addr: location, content: data_memory[location as usize] }) } _ => None }; states.push(State{ step, clk, pc, addr_bus, data_bus, ir, dr, akku, sr, opcode_info }); clk = true; sr.carry = next_carry; sr.zero = akku == 0 || akku == (1<<4); sr.negative = (akku & (1<<3)) != 0; dr = binary_instruction.argument; ir = binary_instruction.opcode; addr_bus = dr; match instruction { Instruction::NoArgumentInstruction(instruction, _) => match instruction { NoArgumentInstruction::NOP => {} }, Instruction::ConstantArgumentInstruction(instruction, _) => match instruction { ConstantArgumentInstruction::BRC(arg) if sr.carry => next_pc = Some(pc + arg), ConstantArgumentInstruction::BRN(arg) if sr.negative => next_pc = Some(pc + arg), ConstantArgumentInstruction::BRZ(arg) if sr.zero => next_pc = Some(pc + arg), _ => {} }, Instruction::Jump(arg, _) => match arg { JumpArgument::Label(label) => { next_pc = Some(*labels.get(label).unwrap()); addr_bus = *labels.get(label).unwrap(); }, JumpArgument::Location(location) => { next_pc = Some(location); addr_bus = location; }, JumpArgument::MemoryLocation(location) => { next_pc = Some(data_memory[location as usize]); addr_bus = data_memory[location as usize]; } }, Instruction::MemoryLocationInstruction(arg, _) => match arg { MemoryLocationInstruction::STA(arg) => { next_data_mem_addr = Some(arg as usize); next_data_mem_val = Some(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] ^ 0b1111) + 1, Argument::Constant(val) => next_akku = akku + (val ^ 0b1111) + 1 } } } data_bus = data_memory[addr_bus as usize]; if let None = next_pc { next_pc = Some(pc + 1); } states.push(State{ step, clk, pc, addr_bus, data_bus, ir, dr, akku, sr, opcode_info }); step += 1; if step == max_steps { break; } } return states; }