riscii

An emulator for the RISC II
Log | Files | Refs | LICENSE

commit ce727e90d9e7e2793d349ed38ec8ea105bcb47ec
parent 6c9a8b09d85d15e9abef421dbeeb198ba0ac075e
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Sun, 16 Oct 2022 20:39:21 -0700

Shifter and condition code checking

Diffstat:
Msrc/data_path.rs | 53+++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/instruction.rs | 31++++++++++++++++---------------
Asrc/shifter.rs | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/system.rs | 17++++++++++++++---
4 files changed, 139 insertions(+), 22 deletions(-)

diff --git a/src/data_path.rs b/src/data_path.rs @@ -294,7 +294,49 @@ impl DataPath { self.psw = ProcessorStatusWord::from_u16(psw); } - pub fn decode(&mut self) { + pub fn test_conditional(&self) -> bool { + let n = self.psw.get_cc_neg(); + let v = self.psw.get_cc_overflow(); + let z = self.psw.get_cc_zero(); + let c = self.psw.get_cc_carry(); + // TODO in the book some of these XOR's are +, not sure what the difference + // is between one bit + and XOR. + match self.rd2 & 0xf { + // Greater than. + 1 => !((n ^ v) ^ z), + // Less than or equal. + 2 => (n ^ v) ^ z, + // Greater than or equal to. + 3 => !(n ^ v), + // Less than, + 4 => n ^ v, + // Higher than + 5 => !(!c ^ z), + // Lower than or same. + 6 => !c ^ z, + // Lower than no carry. + 7 => !c, + // Higher than no sign. + 8 => c, + // Plus (test signed). + 9 => !n, + // Minus (test signed). + 10 => n, + // Not equal. + 11 => !z, + // Equal. + 12 => z, + // No overflow. + 13 => !v, + // Overflow. + 14 => v, + // Always. + 15 => true, + _ => false, + } + } + + pub fn decode(&mut self) -> InstructionCycle { let instruction = self.dimm; let memory = (instruction & (0b11 << 6) >> 6) == 1; let store = (instruction & (0b111 << 5) >> 5) == 0b11; @@ -307,22 +349,24 @@ impl DataPath { let mut dst_is_psw = false; let opcode = ((instruction & OPCODE_LOC) >> 25) as u8; + + let mut result = InstructionCycle::noop_cycle(); // TODO set ALU and shift operation. // Match opcode's prefix. match opcode >> 4 { 0 => match opcode & 0xf { 1 => { - // Calli. + // Calli TODO. } 2 => { // GetPSW - dst_is_psw = true; } 3 => { // GetLPC } 4 => { - // GetLPC + // PutPSW + dst_is_psw = true; } 8 => { // Callx @@ -368,6 +412,7 @@ impl DataPath { conditional, dst_is_psw, ); + result } pub fn current_instruction_is_memory(&self) -> bool { diff --git a/src/instruction.rs b/src/instruction.rs @@ -19,7 +19,7 @@ use clock::Phase; use data_path::{Control, DataPath}; use std::fmt; use std::fmt::LowerHex; -use std::ops::Fn; +use std::ops::Index; pub const SCC_LOC: u32 = 0x1000000; pub const DEST_LOC: u32 = 0x00F80000; @@ -33,22 +33,16 @@ pub const SHORT_IMM_SIGNEXT_BITS: u32 = 0xFFFFE000; // Public functions. -pub fn decode_opcode(instruction: u32) -> Control {} - // Enums and structs. -pub struct MicroOperation(fn(data_path: &mut DataPath) -> Self); +pub struct InstructionCycle([fn(dp: &mut DataPath); 4]); -pub fn noop(dp: &mut DataPath) -> MicroOperation { - MicroOperation::new(noop) -} +pub fn noop(dp: &mut DataPath) {} // Instructions change behavior of ALU, shifter, and for DIMM. // Also which register is loaded into the ALU (stores load Rd in bi). // Loads and stores suspend pipeline for 1 cycle. -//pub fn add_begin(dp: &mut DataPath) -> MicroOperation {} - /// Types of conditionals the RISC II supports. #[derive(PartialEq, Eq, Copy, Clone)] pub enum Conditional { @@ -592,14 +586,21 @@ impl fmt::Display for Conditional { } } -impl MicroOperation { - pub fn new(func: fn(data_path: &mut DataPath) -> Self) -> Self { - Self { 0: func } +impl InstructionCycle { + pub fn new(steps: [fn(dp: &mut DataPath); 4]) -> Self { + Self { 0: steps } } - // TODO temporary until implementing Fn becomes stable. - pub fn call(&self, data_path: &mut DataPath) -> Self { - self.0(data_path) + pub fn noop_cycle() -> Self { + Self { 0: [noop; 4] } + } +} + +impl Index<usize> for InstructionCycle { + type Output = fn(dp: &mut DataPath); + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] } } diff --git a/src/shifter.rs b/src/shifter.rs @@ -0,0 +1,60 @@ +// RISCII Shifter emulator. +// (C) Ryan Jeffrey <ryan@ryanmj.xyz>, 2022 +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at +// your option) any later version. + +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +// Public structs. +use std::fmt; + +/// Representation of the Shifter for RISCII. Implements left and right shifting. +#[derive(Clone, Copy)] +pub struct Shifter { + /// Input latch. + pub src: u32, + /// Amount to shift the input. + pub s_ham: u8, +} + +// Impls. + +impl Shifter { + /// Create a 0'd out shifter. + pub fn new() -> Self { + Self { src: 0, s_ham: 0 } + } + /// Left shift `src` by `s_ham` bits. + pub fn shift_left(&self) -> u32 { + self.src << (self.s_ham as u32) + } + + /// Right shift `src` by `s_ham` bits. + pub fn shift_right(&self) -> u32 { + self.src >> (self.s_ham as u32) + } +} + +impl fmt::Display for Shifter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}, {}", self.src, self.s_ham,) + } +} + +impl fmt::Debug for Shifter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Input register: {}, SHam latch: {}", + self.src, self.s_ham, + ) + } +} diff --git a/src/system.rs b/src/system.rs @@ -18,7 +18,7 @@ use clock::Clock; use config::Config; use cpu::OutputPins; use data_path::{Control, DataPath}; -use instruction::{noop, MicroOperation}; +use instruction::{noop, InstructionCycle}; use memory::Memory; use util::Result; @@ -32,7 +32,7 @@ pub struct System { /// External, four phase clock. clock: Clock, /// Next micro operation to perform for the currently executing instruction. - op: MicroOperation, + cycle_ops: InstructionCycle, /// Current CPU non-overlapping clock phase. phase: Phase, // TODO move below to an MMU emulator. @@ -50,7 +50,7 @@ impl System { data_path: dp, mem: Memory::new(config), clock: Clock::new(config), - op: nop, + cycle_ops: InstructionCycle::noop_cycle(), phase: Phase::One, pins_out: OutputPins::new(), pipeline_suspended: false, @@ -77,6 +77,8 @@ impl System { dp.shift_pipeline_latches(); // Registers are read and then sent to the input latches of the ALU. dp.route_regs_to_alu(); + // TODO determine when this callback should be run. + self.cycle_ops[0](dp); } Phase::Two } @@ -87,6 +89,8 @@ impl System { if !self.pipeline_suspended { // Route immediate to ALU. dp.route_imm_to_alu(); + // TODO determine when this callback should be run. + self.cycle_ops[1](dp); } // Route sources and immediate thru shifter. @@ -110,6 +114,11 @@ impl System { // Commit the result of the last instruction. dp.commit(); self.pipeline_suspended = true; + } else { + // Commit the result of the last instruction. + dp.commit(); + // TODO determine when this callback should be run. + self.cycle_ops[2](dp); } Phase::Four } @@ -119,6 +128,8 @@ impl System { self.pins_out.address = dp.get_out_address(); if !self.pipeline_suspended { + // TODO determine when this callback should be run. + self.cycle_ops[3](dp); dp.decode(); } // If the instruction was a load, shift the result if necessary.