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:
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.