commit 338d24a611c0c8f6be6f04d18b03015b02d1132d
parent a75c3cb341f967507f918ba46dfc03e9eb0ec2e4
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Mon, 13 Jun 2022 00:59:25 -0700
Refactor register to cpu, register file struct, more local registers,
system object, and format printing
Diffstat:
A | src/cpu.rs | | | 262 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/execute.rs | | | 46 | +++++++++++++++++++++++++++++++++++++++++++++- |
M | src/main.rs | | | 27 | +++++---------------------- |
D | src/register.rs | | | 145 | ------------------------------------------------------------------------------- |
A | src/system.rs | | | 96 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 408 insertions(+), 168 deletions(-)
diff --git a/src/cpu.rs b/src/cpu.rs
@@ -0,0 +1,262 @@
+// RISC II register system.
+// (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/>.
+use std::convert::TryInto;
+use std::fmt;
+
+/// The number of register window_regs the RISCII supports.
+pub const NUM_WINDOW_REGS: usize = 8;
+/// The number of local registers per window.
+pub const NUM_LOCALS: usize = 10;
+/// The number of registers shared with the previous register window (input arguments).
+pub const NUM_SHARED_PREV: usize = 6;
+/// The number of registers shared with the next register window (output arguments).
+pub const NUM_SHARED_NEXT: usize = 6;
+/// The number of registers per window.
+pub const WINDOW_SIZE: usize = NUM_LOCALS + NUM_SHARED_PREV + NUM_SHARED_NEXT;
+/// Number of global registers.
+pub const NUM_GLOBALS: usize = 10;
+/// Number of registers that adding a window adds to the total amount of registers.
+pub const NUM_ADDED_PER_WINDOW: usize = NUM_LOCALS + NUM_SHARED_NEXT;
+/// Number of general purpose registers that exist in window_regs.
+pub const NUM_WINDOW_REGISTERS: usize = NUM_WINDOW_REGS * NUM_ADDED_PER_WINDOW;
+/// Number of "special" registers (cwp, swp, sp, etc.).
+pub const NUM_SPECIAL_REGISTERS: usize = 5;
+/// The total number of registers on the system.
+pub const TOTAL_NUM_REGISTERS: usize = NUM_SPECIAL_REGISTERS + NUM_GLOBALS + NUM_WINDOW_REGISTERS;
+/// The size of a register::RegisterFile object (in bytes).
+pub const SIZEOF_STATE: usize = TOTAL_NUM_REGISTERS * 4;
+// Struct definitions.
+
+/// A RISC II 32bit register.
+type Register = u32;
+
+/// The CPU's register state.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct RegisterFile {
+ /// Next program counter, holds the address of the instruction being
+ /// fetched for the next cycle.
+ nxtpc: Register,
+ /// Program counter, holds the address of current instruction being
+ /// executed.
+ pc: Register,
+ /// The lastpc, holds the address of the last executed instruction
+ /// (or last attempted to be executed). When running an interrupt `lstpc`
+ /// holds the address of the instruction that was aborted.
+ lstpc: Register,
+ /// Current window pointer, index of the currently active window.
+ cwp: Register,
+ /// Saved window pointer, the index of the youngest window saved in memory.
+ swp: Register,
+ /// Global registers.
+ globals: [Register; NUM_GLOBALS],
+ /// Register window stack.
+ window_regs: [Register; NUM_WINDOW_REGISTERS], // TODO test, there should be 138 regs.
+}
+
+///
+pub enum LoadStore {
+ NXTPC { val: u32 },
+ PC { val: u32 },
+ LSTPC { val: u32 },
+ CWP { val: u32 },
+ SWP { val: u32 },
+ Global { which: u32, val: u32 },
+ Window { which: u32, val: u32 },
+}
+
+// Struct implementations.
+
+impl RegisterFile {
+ /// Create a 0'd out register window.
+ pub fn new() -> Self {
+ Self {
+ cwp: 0,
+ swp: 0,
+ globals: [0; NUM_GLOBALS],
+ window_regs: [0; NUM_WINDOW_REGISTERS],
+ }
+ }
+
+ /// Create a register state from a buffer.
+ /// # Arguments
+ /// * `buffer` - A byte buffer that is the size of the sum of of register::RegisterFile's
+ /// members (in bytes) (see `SIZEOF_STATE`).
+ /// The registers should appear in the following order:
+ /// - NXTPC
+ /// - PC
+ /// - LSTPC
+ /// - CWP
+ /// - SWP
+ /// - Global registers
+ /// - Window registers
+ pub fn from_buf(buf: [u8; SIZEOF_STATE]) -> Self {
+ // Offset used for gloabls and window_regs.
+ let mut cur_offset = NUM_SPECIAL_REGISTERS * 4;
+ Self {
+ nxtpc: u32::from_be_bytes(buf[..4].try_into().unwrap()),
+ pc: u32::from_be_bytes(buf[4..8].try_into().unwrap()),
+ lstpc: u32::from_be_bytes(buf[8..12].try_into().unwrap()),
+ cwp: u32::from_be_bytes(buf[12..16].try_into().unwrap()),
+ swp: u32::from_be_bytes(buf[16..20].try_into().unwrap()),
+ globals: {
+ let mut result = [0u32; NUM_GLOBALS];
+ for i in 0..result.len() {
+ result[i] =
+ u32::from_be_bytes(buf[cur_offset..cur_offset + 4].try_into().unwrap());
+ cur_offset += 4;
+ }
+ // Ensure r0 is 0.
+ result[0] = 0;
+ result
+ },
+ window_regs: {
+ let mut result = [0u32; NUM_WINDOW_REGISTERS];
+ for i in 0..result.len() {
+ result[i] =
+ u32::from_be_bytes(buf[cur_offset..cur_offset + 4].try_into().unwrap());
+ cur_offset += 4;
+ }
+ result
+ },
+ }
+ }
+
+ pub fn to_buf(&self) -> [u8; SIZEOF_STATE] {
+ let mut result = [0u8; SIZEOF_STATE];
+ result[0..4].copy_from_slice(&self.cwp.to_be_bytes());
+ result[4..8].copy_from_slice(&self.swp.to_be_bytes());
+ let globals = {
+ let mut tmp = [0u8; NUM_GLOBALS * 4];
+ for i in 0..NUM_GLOBALS {
+ tmp[i * 4..i * 4 + 4].copy_from_slice(&self.globals[i].to_be_bytes());
+ }
+ tmp
+ };
+
+ const GLOBAL_OFFSET: usize = 8 + NUM_GLOBALS * 4;
+ result[8..GLOBAL_OFFSET].copy_from_slice(&globals);
+ let win_regs = {
+ let mut tmp = [0u8; NUM_WINDOW_REGISTERS * 4];
+ for i in 0..NUM_WINDOW_REGISTERS {
+ tmp[i * 4..i * 4 + 4].copy_from_slice(&self.window_regs[i].to_be_bytes());
+ }
+ tmp
+ };
+
+ result[GLOBAL_OFFSET..].copy_from_slice(&win_regs);
+ result
+ }
+
+ pub fn push_reg_window(&mut self) {
+ self.cwp += 1;
+ while self.cwp >= self.swp {
+ // TODO save the top window_regs into memory.
+ self.swp += 1;
+ }
+ }
+
+ pub fn pop_reg_window(&mut self) {
+ self.cwp -= 1;
+ while self.swp >= self.cwp {
+ // TODO load window_regs from memory.
+ self.swp -= 1;
+ }
+ }
+
+ pub fn load(&self, ls: LoadStore) -> Result<u32, String> {
+ type LS = LoadStore;
+ Ok(match ls {
+ LS::NXTPC { val: _ } => self.nxtpc,
+ LS::PC { val: _ } => self.pc,
+ LS::LSTPC { val: _ } => self.lstpc,
+ LS::CWP { val: _ } => self.cwp,
+ LS::SWP { val: _ } => self.swp,
+ LS::Global { which: rd, val: _ } => {
+ if rd <= NUM_GLOBALS {
+ self.globals[rd]
+ } else {
+ return Err(format!("RD for global registers out of range ({})", rd));
+ }
+ }
+ LS::Window { which: rd, val: _ } => {
+ let q = NUM_ADDED_PER_WINDOW * self.cwp + rd;
+ if q < NUM_WINDOW_REGISTERS {
+ self.window_regs[q]
+ } else {
+ return Err(format!(
+ "RD for window registers out of range ({}), window {} rd{}",
+ q, self.cwp, rd
+ ));
+ }
+ }
+ })
+ }
+
+ pub fn store(&mut self, ls: LoadStore) -> Result<(), String> {
+ type LS = LoadStore;
+ Ok(match ls {
+ LS::NXTPC { val: v } => {
+ self.nxtpc = v;
+ }
+ LS::PC { val: v } => {
+ self.pc = v;
+ }
+ LS::LSTPC { val: v } => {
+ self.lstpc = v;
+ }
+ LS::CWP { val: v } => {
+ self.cwp = v;
+ }
+ LS::SWP { val: v } => {
+ self.swp = v;
+ }
+ LS::Global { which: rd, val: v } => {
+ if rd <= NUM_GLOBALS && rd > 0 {
+ self.globals[rd] = val;
+ } else if rd == 0 {
+ } else {
+ return Err(format!("RD for global registers out of range ({})", rd));
+ }
+ }
+ LS::Window { which: rd, val: v } => {
+ let q = NUM_ADDED_PER_WINDOW * self.cwp + rd;
+ if q < NUM_WINDOW_REGISTERS {
+ self.window_regs[q] = v;
+ } else {
+ return Err(format!(
+ "RD for window registers out of range ({}), window {} rd{}",
+ q, self.cwp, rd
+ ));
+ }
+ }
+ })
+ }
+}
+
+impl fmt::Display for RegisterFile {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "Next PC: 0x{:x}
+PC: 0x{:x}
+Last PC: 0x{:x}
+Current window pointer: {}
+Saved window pointer: {}
+Globals: {:?}
+Window: {:?}",
+ self.nxtpc, self.pc, self.lstpc, self.cwp, self.swp, self.globals, self.window_regs
+ )
+ }
+}
diff --git a/src/execute.rs b/src/execute.rs
@@ -15,9 +15,53 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use instruction::*;
+use system::System;
// Public functions.
-pub fn execute(instruction: Instruction) -> Result<(), String> {
+pub fn execute(instruction: &Instruction, system: &System) -> Result<(), String> {
+ type I = Instruction;
+ match *instruction {
+ I::Calli(si) => {}
+ I::GetPSW(o) => format!("GetPSW {}", o),
+ I::GetIPC(o) => format!("GetIPC {}", o),
+ I::PutPSW(o) => format!("GetPSW {}", o),
+ I::Callx(o) => format!("Callx {}", o),
+ I::Callr(o) => format!("Callr {}", o),
+ I::Jmpx(o) => format!("Jmpx {}", o),
+ I::Jmpr(o) => format!("Jmpr {}", o),
+ I::Ret(o) => format!("Ret {}", o),
+ I::Reti(o) => format!("Reti {}", o),
+ I::Sll(o) => format!("Sll {}", o),
+ I::Srl(o) => format!("Srl {}", o),
+ I::Sra(o) => format!("Sra {}", o),
+ I::Or(o) => format!("Or {}", o),
+ I::And(o) => format!("And {}", o),
+ I::Xor(o) => format!("Xor {}", o),
+ I::Add(o) => format!("Add {}", o),
+ I::Addc(o) => format!("Addc {}", o),
+ I::Sub(o) => format!("Sub {}", o),
+ I::Subc(o) => format!("Subc {}", o),
+ I::Subi(o) => format!("Subi {}", o),
+ I::Subci(o) => format!("Subci {}", o),
+ I::Ldhi(o) => format!("Ldhi {}", o),
+ I::Ldxw(o) => format!("Ldxw {}", o),
+ I::Ldrw(o) => format!("Ldrw {}", o),
+ I::Ldxhs(o) => format!("Ldxhs {}", o),
+ I::Ldrhs(o) => format!("Ldrhs {}", o),
+ I::Ldxhu(o) => format!("Ldxhu {}", o),
+ I::Ldrhu(o) => format!("Ldrhu {}", o),
+ I::Ldxbs(o) => format!("Ldxbs {}", o),
+ I::Ldrbs(o) => format!("Ldrbs {}", o),
+ I::Ldxbu(o) => format!("Ldxbu {}", o),
+ I::Ldrbu(o) => format!("Ldxbu {}", o),
+ I::Stxw(o) => format!("Stxw {}", o),
+ I::Strw(o) => format!("Strw {}", o),
+ I::Stxh(o) => format!("Stxh {}", o),
+ I::Strh(o) => format!("Strh {}", o),
+ I::Stxb(o) => format!("Stxb {}", o),
+ I::Strb(o) => format!("Strb {}", o),
+ }
+
Ok(())
}
diff --git a/src/main.rs b/src/main.rs
@@ -32,23 +32,17 @@ use decode::decode_file;
use std::fs;
use config::Config;
-use memory::Memory;
-use register::State;
+use system::System;
// Struct/enum declarations.
-struct System {
- regs: register::State,
- mem: memory::Memory,
-}
-
fn get_program(path: &String) -> Result<Vec<u8>, String> {
println!("Opening binary file {}.", path);
- Ok(match fs::read(path) {
- Ok(raw_p) => raw_p.to_vec(),
- Err(raw_e) => return Err(raw_e.to_string()),
- })
+ match fs::read(path) {
+ Ok(raw_p) => Ok(raw_p.to_vec()),
+ Err(raw_e) => Err(raw_e.to_string()),
+ }
}
fn main() -> Result<(), String> {
@@ -64,14 +58,3 @@ fn main() -> Result<(), String> {
Ok(())
}
-
-// Impls.
-
-impl System {
- pub fn new(config: &Config) -> Result<Self, String> {
- Ok(Self {
- regs: State::new(),
- mem: Memory::new(config),
- })
- }
-}
diff --git a/src/register.rs b/src/register.rs
@@ -1,145 +0,0 @@
-// RISC II register system.
-// (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/>.
-use std::convert::TryInto;
-
-/// The number of register window_regs the RISCII supports.
-pub const NUM_WINDOW_REGS: usize = 8;
-/// The number of local registers per window.
-pub const NUM_LOCALS: usize = 10;
-/// The number of registers shared with the previous register window (input arguments).
-pub const NUM_SHARED_PREV: usize = 6;
-/// The number of registers shared with the next register window (output arguments).
-pub const NUM_SHARED_NEXT: usize = 6;
-/// The number of registers per window.
-pub const WINDOW_SIZE: usize = NUM_LOCALS + NUM_SHARED_PREV + NUM_SHARED_NEXT;
-/// Number of global registers.
-pub const NUM_GLOBALS: usize = 10;
-/// Number of general purpose registers that exist in window_regs.
-pub const NUM_WINDOW_REGISTERS: usize = NUM_WINDOW_REGS * (NUM_LOCALS + NUM_SHARED_NEXT);
-/// Number of "special" registers (cwp, swp, sp, etc.).
-pub const NUM_SPECIAL_REGISTERS: usize = 2;
-/// The total number of registers on the system.
-pub const TOTAL_NUM_REGISTERS: usize = NUM_SPECIAL_REGISTERS + NUM_GLOBALS + NUM_WINDOW_REGISTERS;
-/// The size of a register::State object (in bytes).
-pub const SIZEOF_STATE: usize = TOTAL_NUM_REGISTERS * 4;
-// Struct definitions.
-
-/// A RISC II 32bit register.
-type Register = u32;
-
-/// The CPU's register state.
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct State {
- /// Current window pointer, index of the currently active window.
- cwp: Register,
- /// Saved window pointer, the index of the youngest window saved in memory.
- swp: Register,
- /// Global registers.
- globals: [Register; NUM_GLOBALS],
- /// Register window stack.
- window_regs: [Register; NUM_WINDOW_REGISTERS],
-}
-
-// Struct implementations.
-
-impl State {
- /// Create a 0'd out register window.
- pub fn new() -> State {
- State {
- cwp: 0,
- swp: 0,
- globals: [0; NUM_GLOBALS],
- window_regs: [0; NUM_WINDOW_REGISTERS],
- }
- }
-
- /// Create a register state from a buffer.
- /// # Arguments
- /// * `buffer` - A byte buffer that is the size of the sum of of register::State's
- /// members (in bytes) (see `SIZEOF_STATE`).
- /// The registers should appear in the following order:
- /// - CWP
- /// - SWP
- /// - Global registers
- /// - Window registers
- pub fn from_buf(buf: [u8; SIZEOF_STATE]) -> Self {
- // Offset used for gloabls and window_regs.
- let mut cur_offset = NUM_SPECIAL_REGISTERS * 4;
- Self {
- cwp: u32::from_be_bytes(buf[..4].try_into().unwrap()),
- swp: u32::from_be_bytes(buf[4..8].try_into().unwrap()),
- globals: {
- let mut result = [0u32; NUM_GLOBALS];
- for i in 0..result.len() {
- result[i] =
- u32::from_be_bytes(buf[cur_offset..cur_offset + 4].try_into().unwrap());
- cur_offset += 4;
- }
- result
- },
- window_regs: {
- let mut result = [0u32; NUM_WINDOW_REGISTERS];
- for i in 0..result.len() {
- result[i] =
- u32::from_be_bytes(buf[cur_offset..cur_offset + 4].try_into().unwrap());
- cur_offset += 4;
- }
- result
- },
- }
- }
-
- pub fn to_buf(&self) -> [u8; SIZEOF_STATE] {
- let mut result = [0u8; SIZEOF_STATE];
- result[0..4].copy_from_slice(&self.cwp.to_be_bytes());
- result[4..8].copy_from_slice(&self.swp.to_be_bytes());
- let globals = {
- let mut tmp = [0u8; NUM_GLOBALS * 4];
- for i in 0..NUM_GLOBALS {
- tmp[i * 4..i * 4 + 4].copy_from_slice(&self.globals[i].to_be_bytes());
- }
- tmp
- };
-
- const GLOBAL_OFFSET: usize = 8 + NUM_GLOBALS * 4;
- result[8..GLOBAL_OFFSET].copy_from_slice(&globals);
- let win_regs = {
- let mut tmp = [0u8; NUM_WINDOW_REGISTERS * 4];
- for i in 0..NUM_WINDOW_REGISTERS {
- tmp[i * 4..i * 4 + 4].copy_from_slice(&self.window_regs[i].to_be_bytes());
- }
- tmp
- };
-
- result[GLOBAL_OFFSET..].copy_from_slice(&win_regs);
- result
- }
-
- pub fn push_reg_window(&mut self) {
- self.cwp += 1;
- while self.cwp >= self.swp {
- // TODO save the top window_regs into memory.
- self.swp += 1;
- }
- }
-
- pub fn pop_reg_window(&mut self) {
- self.cwp -= 1;
- while self.swp >= self.cwp {
- // TODO load window_regs from memory.
- self.swp -= 1;
- }
- }
-}
diff --git a/src/system.rs b/src/system.rs
@@ -0,0 +1,96 @@
+// RISC II emulated machine state.
+// See `decode.rs` for the first step, and `commit.rs` for the third step.
+// (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/>.
+
+use config::Config;
+use cpu::RegisterFile;
+use memory::Memory;
+use std::fmt;
+
+pub struct System {
+ /// RISC II register file.
+ regs: RegisterFile,
+ /// Memory state.
+ mem: Memory,
+ /// System bit, true if running in privileged state.
+ system_mode: bool,
+ /// The previous state of the `system_mode` bit the last time it was changed.
+ previous_system_mode: bool,
+ /// Condition codes zero (Z).
+ cc_zero: bool,
+ /// Condition code negative (N).
+ cc_neg: bool,
+ /// Condition code overflow (V).
+ cc_overflow: bool,
+ /// Condition code carry (C).
+ cc_carry: bool,
+}
+
+// Impls.
+
+impl System {
+ pub fn new(config: &Config) -> Result<Self, String> {
+ Ok(Self {
+ regs: RegisterFile::new(),
+ mem: Memory::new(config),
+ system_mode: true,
+ previous_system_mode: false,
+ cc_zero: false,
+ cc_neg: false,
+ cc_overflow: false,
+ cc_carry: false,
+ })
+ }
+}
+
+impl fmt::Display for System {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "CPU register state: \n{}
+Privilege level: {}
+Previous privilege level: {}
+CC Zero: {}
+CC Neg: {}
+CC Overflow: {}
+CC Carry: {}",
+ self.regs,
+ privilege_string(self.system_mode),
+ privilege_string(self.previous_system_mode),
+ bool_hl_string(self.cc_zero),
+ bool_hl_string(self.cc_neg),
+ bool_hl_string(self.cc_overflow),
+ bool_hl_string(self.cc_carry)
+ )
+ }
+}
+
+// Private functions.
+
+fn privilege_string(s: bool) -> String {
+ if s {
+ "Privileged".to_string()
+ } else {
+ "Unprivileged".to_string()
+ }
+}
+
+fn bool_hl_string(s: bool) -> String {
+ if s {
+ "High".to_string()
+ } else {
+ "Low".to_string()
+ }
+}