commit 7fabdd6a1ec4309862ccd16c01991015cf884180
parent 57f237af2aabb16c63e6ff698551e38e5e72447c
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Thu, 16 Jun 2022 19:54:24 -0700
Basic execution stub
Diffstat:
9 files changed, 1228 insertions(+), 241 deletions(-)
diff --git a/README.org b/README.org
@@ -8,6 +8,7 @@ An emulated PC with a RISC II processor.
** Feature completeness
- [ ] RISC II emulation
- [ ] SDL window, I/O, sound
+- [ ] MMU
- [ ] Firmware (probably IEEE 1275) implementation
- [ ] Floating point co processor
- [ ] Multicore
diff --git a/src/cpu.rs b/src/cpu.rs
@@ -12,11 +12,13 @@
// 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 instruction::ShortSource;
+use memory::Memory;
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 register windows the RISCII supports.
+pub const NUM_REG_WINDOWS: 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).
@@ -30,18 +32,43 @@ 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;
+pub const NUM_WINDOW_REGISTERS: usize = NUM_REG_WINDOWS * NUM_ADDED_PER_WINDOW;
/// Number of "special" registers (cwp, swp, sp, etc.).
-pub const NUM_SPECIAL_REGISTERS: usize = 5;
+pub const NUM_SPECIAL_REGISTERS: usize = 3;
/// 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;
+pub const SIZEOF_REG_FILE: usize = TOTAL_NUM_REGISTERS * 4;
+/// The size of an instruction in bytes. Amount to increment the program counter registers by.
+pub const SIZEOF_INSTRUCTION: u32 = 4;
// Struct definitions.
/// A RISC II 32bit register.
type Register = u32;
+/// PSW. Contains internal state that is usually opaque to the system.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct ProcessorStatusWord {
+ /// Current window pointer (MOD 8).
+ cwp: u32,
+ /// Saved window pointer (MOD 8).
+ swp: u32,
+ /// If interrupts are enabled.
+ interrupt_enable_bit: bool,
+ /// 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,
+}
+
/// The CPU's register state.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct RegisterFile {
@@ -55,43 +82,21 @@ pub struct RegisterFile {
/// (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.
}
-/// Load/Store queries. Specifies the register and the value to set it to (if store).
-pub enum LoadStore {
- /// Global register.
- Global {
- /// Which global register. g0 is special and will always return 0,
- /// attempts to store to it are ignored. Values above 10 throw an error.
- which: u32,
- /// Value to set register to (ignored owhen loading).
- val: u32,
- },
- /// Window register.
- Window {
- /// Which window register. Values above 22 throw an error.
- which: u32,
- /// Value to set register to (ignored owhen loading).
- val: u32,
- },
-}
-
// Struct implementations.
impl RegisterFile {
/// Create a 0'd out register window.
pub fn new() -> Self {
Self {
- cwp: 0,
- swp: 0,
+ nxtpc: 0,
+ pc: 0,
+ lstpc: 0,
globals: [0; NUM_GLOBALS],
window_regs: [0; NUM_WINDOW_REGISTERS],
}
@@ -100,24 +105,20 @@ impl RegisterFile {
/// Create a register state from a buffer.
/// # Arguments
/// * `buf` - A byte buffer that is the size of the sum of of register::RegisterFile's
- /// members (in bytes) (see `SIZEOF_STATE`).
+ /// members (in bytes) (see `SIZEOF_REG_FILE`).
/// 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 {
+ pub fn from_buf(buf: [u8; SIZEOF_REG_FILE]) -> 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()),
+ lstpc: u32::from_be_bytes(buf[8..cur_offset].try_into().unwrap()),
globals: {
let mut result = [0u32; NUM_GLOBALS];
for i in 0..result.len() {
@@ -142,24 +143,29 @@ impl RegisterFile {
}
/// Convert self to a byte buffer of all of the register values.
- 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());
+ pub fn to_buf(&self) -> [u8; SIZEOF_REG_FILE] {
+ let mut result = [0u8; SIZEOF_REG_FILE];
+ // Offset of the special registers to the general purpose registers (bytes).
+ const SPECIAL_OFFSET: usize = NUM_SPECIAL_REGISTERS * 4;
+ result[..4].copy_from_slice(&self.nxtpc.to_be_bytes());
+ result[4..8].copy_from_slice(&self.pc.to_be_bytes());
+ result[8..SPECIAL_OFFSET].copy_from_slice(&self.lstpc.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[i * SPECIAL_OFFSET..i * SPECIAL_OFFSET + 4]
+ .copy_from_slice(&self.globals[i].to_be_bytes());
}
tmp
};
+ const GLOBAL_OFFSET: usize = NUM_SPECIAL_REGISTERS + NUM_GLOBALS * 4;
+ result[NUM_SPECIAL_REGISTERS..GLOBAL_OFFSET].copy_from_slice(&globals);
- 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[i * SPECIAL_OFFSET..i * SPECIAL_OFFSET + 4]
+ .copy_from_slice(&self.window_regs[i].to_be_bytes());
}
tmp
};
@@ -168,88 +174,91 @@ impl RegisterFile {
result
}
- /// Push the register window stack. Increment CWP by 1 and flush the bottom
- /// windows to memory if necessary and change SWP.
- 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;
- }
- }
-
- /// Pop the register window stack. Decrement CWP by 1 and pull the bottom
- /// windows from memory if necessary and change SWP.
- pub fn pop_reg_window(&mut self) {
- self.cwp -= 1;
- while self.swp >= self.cwp {
- // TODO load window_regs from memory.
- self.swp -= 1;
- }
+ /// Flush entire register window to memory.
+ /// # Arguments
+ /// * `mem` - Memory to flush to.
+ /// * `addr` - Memory address to flush to.
+ pub fn flush_to_mem(&self, mem: &mut Memory, addr: u32) {
+ mem.write_buf(addr, &self.to_buf());
}
- /// Load from a register (unsigned). Return the register's value
+ /// Get a register's value (unsigned). Return the register's value
/// on success and a string message on error.
+ /// Register mapping: [0-9] -> Globals
+ /// [10-15] -> Outs
+ /// [16-25] -> Locals
+ /// [31-26] -> Ins
+ /// Anything outside this [0-31] range is an invalid argument.
/// # Arguments
- /// * `ls` - Load/Store instruction. Will error if `which` is out of range.
- pub fn load_u(&self, ls: LoadStore) -> Result<u32, String> {
- type LS = LoadStore;
- Ok(match ls {
- 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
- ));
- }
- }
+ /// * `which` - Which register. [0-31] are the only valid values.
+ pub fn ru(&self, which: u32, psw: &ProcessorStatusWord) -> Result<u32, String> {
+ Ok(match which {
+ 0..=9 => self.globals[which],
+ 10..=31 => self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() + rd],
+ _ => return Err(format!("Register {} is out of range", which)),
})
}
- /// Load from a register (signed). Return the register's value
+ /// Get a register's value (signed). Return the register's value
/// on success and a string message on error.
+ /// Register mapping: [0-9] -> Globals
+ /// [10-15] -> Outs
+ /// [16-25] -> Locals
+ /// [31-26] -> Ins
+ /// Anything outside this [0-31] range is an invalid argument.
/// # Arguments
- /// * `ls` - Load/Store instruction. `val` is ignored.
- pub fn load_s(&self, ls: LoadStore) -> Result<i32, String> {
- self.load_u(ls)? as i32
+ /// * `which` - Which register. [0-31] are the only valid values.
+ pub fn rs(&self, which: u32, psw: &ProcessorStatusWord) -> Result<i32, String> {
+ Ok(self.ru(which, psw)? as i32)
}
- /// Store to a register. Return void on success and a string message on
- /// failure.
+ /// Set a register's value (unsigned). Return the register's value on
+ /// success and a string message on error.
+ /// Register mapping: [0-9] -> Globals
+ /// [10-15] -> Outs
+ /// [16-25] -> Locals
+ /// [31-26] -> Ins
+ /// Anything outside this [0-31] range is an invalid argument.
/// # Arguments
- /// * `ls` - Load/Store instruction. Will error if `which` is out of range.
- pub fn store(&mut self, ls: LoadStore) -> Result<(), String> {
- type LS = LoadStore;
- Ok(match ls {
- 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
- ));
- }
- }
+ /// * `which` - Which register. [0-31] are the only valid values.
+ pub fn rus(&mut self, which: u32, value: u32, psw: ProcessorStatusWord) -> Result<u32, String> {
+ match which {
+ 0..=9 => self.globals[which] = value,
+ 10..=31 => self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() + rd] = value,
+ _ => return Err(format!("Register {} is out of range", which)),
+ }
+ Ok(value)
+ }
+
+ pub fn get_last_pc(&self) -> u32 {
+ self.lstpc
+ }
+
+ pub fn get_pc(&self) -> u32 {
+ self.pc
+ }
+
+ pub fn get_next_pc(&self) -> u32 {
+ self.nxtpc
+ }
+
+ pub fn inc_pcs(&mut self) {
+ self.lstpc = self.pc;
+ self.pc = self.nxtpc;
+ self.nxtpc += SIZEOF_INSTRUCTION;
+ }
+
+ pub fn branch_to(&mut self, to: u32) {
+ self.lstpc = self.pc;
+ self.pc = nxtpc;
+ self.nxtpc = to;
+ }
+
+ pub fn get_ss_val(&self, ss: ShortSource) -> Result<u32, String> {
+ type SS = ShortSource;
+ Ok(match ss {
+ SS::Reg(r) => self.ru(r as u32)?,
+ SS::Imm13(u) => u,
})
}
}
@@ -261,11 +270,230 @@ impl fmt::Display for RegisterFile {
"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
+ self.nxtpc, self.pc, self.lstpc, self.globals, self.window_regs
)
}
}
+
+impl ProcessorStatusWord {
+ /// Create a 0'd out PSW.
+ pub fn new() -> Self {
+ Self {
+ cwp: 0,
+ swp: 0,
+ interrupt_enable_bit: false,
+ system_mode: false,
+ previous_system_mode: false,
+ cc_zero: false,
+ cc_neg: false,
+ cc_overflow: false,
+ cc_carry: false,
+ }
+ }
+
+ pub fn from_u32(v: u32) -> Self {
+ Self {
+ cwp: (v & (0x7 << 7)) >> 7,
+ swp: (v & (0x7 << 10)) >> 10,
+ interrupt_enabled_bit: v & (0x1 << 6) != 0,
+ previous_system_bit: v & (0x1 << 5) != 0,
+ system_bit: v & (0x1 << 4) != 0,
+ cc_zero: v & (0x1 << 3) != 0,
+ cc_neg: v & (0x1 << 2) != 0,
+ cc_overflow: v & (0x1 << 1) != 0,
+ cc_carry: v & 0x1 != 0,
+ }
+ }
+
+ pub fn init(
+ cwp: u32,
+ swp: u32,
+ interrupt_enable_bit: bool,
+ previous_system_bit: bool,
+ system_bit: bool,
+ cc_zero: bool,
+ cc_neg: bool,
+ cc_overflow: bool,
+ cc_carry: bool,
+ ) -> Self {
+ Self {
+ cwp: cwp % 8,
+ swp: swp % 8,
+ interrupt_enable_bit: interrupt_enable_bit,
+ previous_system_bit: previous_system_bit,
+ system_bit: system_bit,
+ cc_zero: cc_zero,
+ cc_neg: cc_neg,
+ cc_overflow: cc_overflow,
+ cc_carry: cc_carry,
+ }
+ }
+
+ /// Get the 13 bit PSW value. PSW is the state of the system's special
+ /// registers and CC's. After the 13th bit PSW is 0 padded.
+ /// Format of PSW:
+ /// [0]: Carry bit
+ /// [1]: Overflow bit
+ /// [2]: Negative bit
+ /// [3]: Zero bit
+ /// [4]: Previous system mode bit.
+ /// [5]: System mode bit.
+ /// [6]: Interrupt enable bit.
+ /// [7-9]: SWP register mod 8.
+ /// [10-12]: CWP register mod 8.
+ pub fn to_u32(&self) -> u32 {
+ (self.cc_carry as u32
+ | (self.cc_overflow as u32) << 1
+ | (self.cc_neg as u32) << 2
+ | (self.cc_zero as u32) << 3
+ | (self.previous_system_mode as u32) << 4
+ | (self.system_mode as u32) << 5
+ | (self.interrupt_enable_bit as u32) << 6
+ | (self.cwp) << 7
+ | (self.swp) << 10)
+ & 0x1fff
+ }
+
+ /// Push the register window stack. Set CWP to CWP-1 MOD 8. Push the top
+ /// window to memory and increment SWP if necessary.
+ pub fn push_reg_window(&mut self) {
+ self.cwp = (self.cwp - 1) % NUM_REG_WINDOWS;
+ if self.cwp == self.swp {
+ // TODO save windows to memory.
+ self.swp = (self.swp + 1) % NUM_REG_WINDOWS;
+ }
+ }
+
+ /// Pop the register window stack. Set CWP to CWP+1 MOD 8. Pull the bottom
+ /// window from memory and decrement SWP if necessary.
+ pub fn pop_reg_window(&mut self) {
+ self.cwp = (self.cwp + 1) % NUM_REG_WINDOWS;
+ if self.cwp == self.swp {
+ // TODO restore windows from memory.
+ self.swp = (self.swp - 1) % NUM_REG_WINDOWS;
+ }
+ }
+
+ pub fn set_cwp(&mut self, v: u32) {
+ self.cwp = v % NUM_REG_WINDOWS;
+ }
+
+ pub fn set_swp(&mut self, v: u32) {
+ self.swp = v % NUM_REG_WINDOWS;
+ }
+
+ pub fn get_cwp(&self) -> u32 {
+ self.cwp
+ }
+
+ pub fn get_swp(&self) -> u32 {
+ self.swp
+ }
+
+ pub fn get_cc_overflow(&self) -> bool {
+ self.cc_overflow
+ }
+
+ pub fn get_cc_carry(&self) -> bool {
+ self.cc_carry
+ }
+
+ pub fn get_cc_zero(&self) -> bool {
+ self.cc_zero
+ }
+
+ pub fn get_cc_neg(&self) -> bool {
+ self.cc_neg
+ }
+
+ pub fn set_cc_overflow(&mut self, value: bool) {
+ self.cc_overflow = value;
+ }
+
+ pub fn set_cc_carry(&mut self, value: bool) {
+ self.cc_carry = value;
+ }
+
+ pub fn set_cc_zero(&mut self, value: bool) {
+ self.cc_zero = value;
+ }
+
+ pub fn set_cc_neg(&mut self, value: bool) {
+ self.cc_neg = value;
+ }
+
+ pub fn set_system_bit(&mut self, v: bool) {
+ self.system_mode = v;
+ }
+
+ pub fn set_previous_system_bit(&mut self, v: bool) {
+ self.previous_system_mode = v;
+ }
+
+ pub fn set_interrupt_enabled(&mut self, v: bool) {
+ self.interrupt_enable_bit = v;
+ }
+
+ pub fn is_system_mode(&self) -> bool {
+ self.system_mode
+ }
+
+ pub fn is_previous_system_mode(&self) -> bool {
+ self.previous_system_mode
+ }
+
+ pub fn is_interrupt_enabled(&self) -> bool {
+ self.interrupt_enable_bit
+ }
+}
+
+impl fmt::Display for ProcessorStatusWord {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "Current window pointer: {}
+Saved window pointer: {}
+Interrupts Enabled: {}
+System mode: {}
+Previous system mode: {}
+CC Zero: {}
+CC Negative: {}
+CC Overflow: {}
+CC Carry: {}",
+ self.cwp,
+ self.swp,
+ 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.
+
+/// Create a descriptive string for the system's privilege state bits.
+/// # Arguments
+/// * `s` - Privilege state bit.
+fn privilege_string(s: bool) -> &str {
+ if s {
+ "Privileged"
+ } else {
+ "Unprivileged"
+ }
+}
+
+/// Stringify booleans with hardware terminology.
+/// # Arguments
+/// * `s` - Boolean.
+fn bool_hl_string(s: bool) -> &str {
+ if s {
+ "High"
+ } else {
+ "Low"
+ }
+}
diff --git a/src/decode.rs b/src/decode.rs
@@ -65,7 +65,7 @@ pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> {
0 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)),
1 => I::Calli(ShortInstruction::new(scc, dest, rs1, short_source)),
2 => I::GetPSW(ShortInstruction::new(scc, dest, rs1, short_source)),
- 3 => I::GetIPC(ShortInstruction::new(scc, dest, rs1, short_source)),
+ 3 => I::GetLPC(ShortInstruction::new(scc, dest, rs1, short_source)),
4 => I::PutPSW(ShortInstruction::new(scc, dest, rs1, short_source)),
5..=7 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)),
8 => I::Callx(ShortInstruction::new(scc, dest, rs1, short_source)),
diff --git a/src/decode_test.rs b/src/decode_test.rs
@@ -49,7 +49,7 @@ mod test {
fn decode_getipc() -> Result<(), DecodeError> {
assert_eq!(
decode(0x07293f69)?,
- I::GetIPC(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69)))
+ I::GetLPC(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69)))
);
Ok(())
}
@@ -437,7 +437,7 @@ mod test {
match *self {
I::Calli(o) => format!("Calli {}", o),
I::GetPSW(o) => format!("GetPSW {}", o),
- I::GetIPC(o) => format!("GetIPC {}", o),
+ I::GetLPC(o) => format!("GetLPC {}", o),
I::PutPSW(o) => format!("GetPSW {}", o),
I::Callx(o) => format!("Callx {}", o),
I::Callr(o) => format!("Callr {}", o),
diff --git a/src/execute.rs b/src/execute.rs
@@ -14,54 +14,653 @@
// 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 cpu::ProcessorStatusWord;
use instruction::*;
use system::System;
+use util::U32_MSB;
+
+// Public structs.
+
+pub struct ExecResult {
+ psw: ProcessorStatusWord,
+ regs: RegisterFile,
+ was_branch: bool,
+ psw_delayed: bool,
+}
// Public functions.
-pub fn execute(instruction: &Instruction, system: &System) -> Result<(), String> {
+// TODO timing and memory reads/writes. Need to emulate the pipeline and cpu clock.
+pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecResult, String> {
type I = Instruction;
+
+ let mut result = ExecResult::from_system(system);
+ let mut register_file = result.get_register_file();
+ let cur_pc = register_file.get_pc();
+
+ let mut memory = system.get_mem_ref();
+
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),
+ I::Calli {
+ scc: scc,
+ dest: dest,
+ rs1: _,
+ short_source: _,
+ } => {
+ if !system.is_system_mode {
+ // TODO error
+ }
+ system.call();
+ let lstpc = system.get_last_pc();
+ if scc {
+ system.set_cc_zero(lstpc == 0);
+ system.set_cc_neg(lstpc & U32_MSB != 0);
+ }
+ register_file.rus(dest, lstpc)?;
+ // TODO maybe handle interrupts.
+ }
+ I::GetPSW {
+ scc: scc,
+ dest: dest,
+ rs1: _,
+ short_source: ss,
+ } => {
+ let psw = system.get_psw12() & 0xffff7;
+ register_file.rus(dest, psw)?;
+ if scc {
+ let dest_val = register_file.ru(dest)?;
+ self.set_cc_neg(dest_val & U32_MSB != 1);
+ self.set_cc_zero(dest_val == 0);
+ self.set_cc_carry(false);
+ self.set_cc_overflow(false);
+ }
+ }
+ I::GetLPC {
+ scc: scc,
+ dest: dest,
+ rs1: _,
+ short_source: _,
+ } => {
+ if !system.is_system_mode {
+ // TODO error
+ }
+ let lstpc = system.get_last_pc();
+ register_file.rus(dest, lstpc)?;
+ if scc {
+ system.set_cc_zero(lstpc == 0);
+ system.set_cc_neg(lstpc & U32_MSB != 0);
+ }
+ }
+ I::PutPSW {
+ scc: scc,
+ dest: _,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if !system.is_system_mode {
+ // TODO error
+ }
+ if scc {
+ // TODO error
+ }
+
+ let val = register_file.get_ss_val(ss)?;
+ result.set_psw(register_file.ru(rs1)? + val);
+ }
+ I::Callx {
+ scc: _,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ // TODO test alignment (addr[0] == 1).
+ let rs_val = register_file.ru(rs1)?;
+ let addr = register_file.get_ss_val(ss)? + rs_val;
+ register_file.push_reg_window();
+ register_file.branch_to(addr);
+ result.rus(cur_pc)?;
+ }
+ I::Callr {
+ scc: _,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ // TODO test alignment (addr[0] == 1).
+ result.set_branch(true);
+ register_file.push_reg_window();
+ register_file.branch_to(cur_pc + imm19);
+ }
+ I::Jmpx {
+ scc: _,
+ dest: cond,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ // TODO test alignment (addr[0] == 1).
+ if exec_conditional(cond, result.get_psw()) {
+ result.set_branch(true);
+ let rs_val = register_file.ru(rs1)?;
+ let addr = register_file.get_ss_val(ss)? + rs_val;
+ register_file.branch_to(addr);
+ }
+ }
+ I::Jmpr {
+ scc: _,
+ dest: cond,
+ imm19: imm19,
+ } => {
+ if exec_conditional(cond, result.get_psw()) {
+ result.set_branch(true);
+ register_file.branch_to(cur_pc + imm19);
+ }
+ }
+ I::Ret {
+ scc: _,
+ dest: cond,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if exec_conditional(cond, result.get_psw()) {
+ result.set_branch(true);
+ let rs_val = register_file.ru(rs1)?;
+ register_file.branch_to(rs_val + (SIZEOF_INSTRUCTION * 2));
+ register_file.pop_reg_window();
+ }
+ }
+ I::Reti {
+ scc: _,
+ dest: cond,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if !system.is_system_mode {
+ // TODO error
+ }
+ if exec_conditional(cond, result.get_psw()) {
+ result.set_branch(true);
+ let rs_val = register_file.ru(rs1)?;
+ register_file.branch_to(rs_val + (SIZEOF_INSTRUCTION * 2));
+ register_file.pop_reg_window();
+ }
+ }
+ I::Sll {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val << s2_val)?;
+ if scc {
+ set_shift_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Srl {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val >> s2_val)?;
+ if scc {
+ set_shift_cc(scc, result.get_psw_ref(), d);
+ }
+ }
+ I::Sra {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val as i32 >> s2_val)?;
+ if scc {
+ set_shift_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Or {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val | s2_val)?;
+ if scc {
+ set_shift_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::And {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val & s2_val)?;
+ if scc {
+ set_shift_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Xor {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, s1_val ^ s2_val)?;
+ if scc {
+ set_shift_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Add {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let (res, o) = s1_val.overflowing_add(s2_val);
+ let d = register_file.rus(dest, res)?;
+ if scc {
+ let mut psw = result.get_psw_ref();
+ set_operator_cc(psw, d);
+ psw.set_cc_overflow(o);
+ psw.set_cc_carry(o);
+ }
+ }
+ I::Addc {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let mut psw = result.get_psw_ref();
+ let (r1, o1) = s1_val.overflowing_add(s2_val);
+ let (res, o2) = r1.overflowing_add(psw.get_cc_carry() as u32);
+ let o = o1 || o2;
+ let d = register_file.rus(dest, res)?;
+ if scc {
+ set_operator_cc(psw, d);
+ psw.set_cc_overflow(o);
+ psw.set_cc_carry(o);
+ }
+ }
+ I::Sub {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let (res, o) = s1_val.overflowing_sub(s2_val);
+ let d = register_file.rus(dest, res)?;
+ if scc {
+ let mut psw = result.get_psw_ref();
+ set_operator_cc(psw, d);
+ psw.set_cc_overflow(o);
+ psw.set_cc_carry(!o);
+ }
+ }
+ I::Subc {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let mut psw = result.get_psw_ref();
+ let (r1, o1) = s1_val.overflowing_sub(s2_val);
+ let (res, o2) = r1.overflowing_sub(!psw.get_cc_carry() as u32);
+ let o = o2 || o1;
+ let d = register_file.rus(dest, res);
+ if scc {
+ let mut psw = result.get_psw_ref();
+ set_operator_cc(psw, d);
+ psw.set_cc_overflow(o);
+ psw.set_cc_carry(!o);
+ }
+ }
+ I::Subi {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let (res, o) = s2_val.overflowing_sub(s1_val);
+ let d = register_file.rus(dest, res)?;
+ if scc {
+ let mut psw = result.get_psw_ref();
+ set_operator_cc(psw, d);
+ let v = d > s2_val;
+ psw.set_cc_overflow(v);
+ psw.set_cc_carry(!v);
+ }
+ }
+ I::Subci {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let s1_val = register_file.ru(rs1)?;
+ let s2_val = register_file.get_ss_val(ss)?;
+ let mut psw = result.get_psw_ref();
+ let (r1, o1) = s2_val.overflowing_sub(s1_val);
+ let (res, o2) = r1.overflowing_sub(!psw.get_cc_carry() as u32);
+ let o = o1 || o2;
+ let d = register_file.rus(dest, res)?;
+ if scc {
+ set_operator_cc(psw, d);
+ psw.set_cc_overflow(o);
+ psw.set_cc_carry(!o);
+ }
+ }
+ I::Ldhi {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ // TODO Test alignment
+ let cur_d = register_file.ru(dest)?;
+ let d = register_file.rus(dest, dest & ((imm19 << 13) & 0x1fff))?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldxw {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ // TODO Test alignment
+ let ss_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, memory.get_word(ss_val)?)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldrw {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ let addr = imm19 + regs.get_pc();
+ let d = register_file.rus(dest, memory.get_word(ss_val)?)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldxhs {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let ss_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, memory.get_hword(ss_val)? as i32 as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldrhs {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ let addr = imm19 + regs.get_pc();
+ let d = register_file.rus(dest, memory.get_hword(ss_val)? as i32 as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldxhu {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let ss_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, memory.get_hword(ss_val)? as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldrhu {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ let addr = imm19 + regs.get_pc();
+ let d = register_file.rus(dest, memory.get_hword(ss_val)? as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldxbs {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let ss_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, memory.get_byte(ss_val)? as i32 as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldrbs {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ let addr = imm19 + regs.get_pc();
+ let d = register_file.rus(dest, memory.get_byte(ss_val)? as i32 as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldxbu {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ let ss_val = register_file.get_ss_val(ss)?;
+ let d = register_file.rus(dest, memory.get_byte(ss_val)? as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Ldrbu {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ let addr = imm19 + regs.get_pc();
+ let d = register_file.rus(dest, memory.get_byte(ss_val)? as u32)?;
+ if scc {
+ set_load_cc(result.get_psw_ref(), d);
+ }
+ }
+ I::Stxw {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let ss_val = register_file.get_ss_val(ss)?;
+ let rs1_val = register_file.ru(rs1);
+ let dest_val = register_file.ru(dest);
+ memory.set_word(ss_val + rs1_val, dest_val);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ I::Strw {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let dest_val = register_file.ru(dest);
+ memory.set_word(register_file.get_pc() + imm19, dest_val);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ I::Stxh {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let ss_val = register_file.get_ss_val(ss)?;
+ let rs1_val = register_file.ru(rs1);
+ let dest_val = register_file.ru(dest);
+ memory.set_hword(ss_val + rs1_val, dest_val as u16);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ I::Strh {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let dest_val = register_file.ru(dest);
+ memory.set_hword(register_file.get_pc() + imm19, dest_val as u16);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ I::Stxb {
+ scc: scc,
+ dest: dest,
+ rs1: rs1,
+ short_source: ss,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let ss_val = register_file.get_ss_val(ss)?;
+ let rs1_val = register_file.ru(rs1);
+ let dest_val = register_file.ru(dest);
+ memory.set_byte(ss_val + rs1_val, dest_val as u8);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ I::Strb {
+ scc: scc,
+ dest: dest,
+ imm19: imm19,
+ } => {
+ if short_source == ShortSource::Reg(_) {
+ // warn
+ // return Err("Store instructions should be immediate only (not registers)");
+ }
+ let dest_val = register_file.ru(dest);
+ memory.set_byte(register_file.get_pc() + imm19, dest_val as u8);
+ if scc {
+ set_store_cc(result.get_psw_ref());
+ }
+ }
+ }
+
+ Ok(result)
+}
+
+// Struct impls.
+
+impl ExecResult {
+ pub fn from_system(system: &System) -> Self {
+ Self {
+ psw: system.get_psw(),
+ register_file: system.copy_register_file(),
+ was_branch: false,
+ psw_delayed: false,
+ }
+ }
+
+ pub fn set_psw(&mut self, psw: u32) {
+ self.psw.from_u32(psw);
+ }
+
+ pub fn get_psw_ref(&mut self) -> &mut ProcessorStatusWord {
+ &mut self.psw
}
- Ok(())
+ pub fn get_register_file(&mut self) -> &mut RegisterFile {
+ &mut self.regs
+ }
+
+ pub fn was_branch(&self) -> bool {
+ self.was_branch
+ }
+
+ pub fn set_branch(&mut self, v: bool) {
+ self.was_branch = v;
+ }
+}
+
+// Private functions.
+
+fn exec_conditional(what: Conditional, psw: ProcessorStatusWord) -> bool {
+ todo!()
+}
+
+fn set_operator_cc(psw: &mut ProcessorStatusWord, dest_val: u32) {
+ psw.set_cc_zero(register_file.ru(dest)? == 0);
+ psw.set_cc_neg(register_file.ru(dest)? & U32_MSB != 0);
+}
+
+fn set_shift_cc(psw: &mut ProcessorStatusWord, dest_val: u32) {
+ set_operator_cc(psw, dest_val);
+ psw.set_cc_overflow(false);
+ psw.set_cc_carry(false);
+}
+
+fn set_load_cc(psw: &mut ProcessorStatusWord, dest_val: u32) {
+ psw.set_cc_carry(false);
+ psw.set_cc_overflow(false);
+ psw.set_cc_zero(d == 0);
+ psw.set_cc_neg(d & U32_MSB != 0);
+}
+
+fn set_store_cc(psw: &mut ProcessorStatusWord) {
+ psw.set_cc_overflow(false);
+ psw.set_cc_carry(false);
}
diff --git a/src/instruction.rs b/src/instruction.rs
@@ -59,9 +59,7 @@ pub enum ShortSource {
/// Register name.
Reg(u8),
/// Unsigned 13 bit immediate, 0-padded to 32 bits.
- UImm13(u32),
- /// Signed 13 bit immediate, Sign-extended to 32 bits.
- SImm13(i32),
+ Imm13(u32),
}
/// Short instruction format data.
@@ -136,32 +134,62 @@ pub enum Instruction {
/// Call interrupt.
/// Notes:
/// - PRIVILEGED INSTRUCTION.
+ /// - Only meant for use by the interrupt handler.
/// - The `RS1` and `RS1` registers are read from the OLD window.
/// - The PC instruction saved is the `PC` at the `CALLI`.
/// - The `Rd` refers to the destination register in the NEW window.
/// - If the change to `CWP` makes it equal to `SWP`: stop execution,
- /// generate a trap, and go to address 0x80000020.
- /// CWP := CWP - 1 MOD 8, rd := LSTPC; CC's have same rules as getipc.
+ /// generate a trap, and go to address 0x80000020.
+ /// CWP := CWP - 1 MOD 8, rd := LSTPC;
+ /// Iff SCC == true, Z := [LSTPC == 0]; N := LSTPC<31>; V,C := garbage.
Calli(ShortInstruction),
- /// Get pointer to window. rd := (-1)<31:13> & PSW<12:0>;
+ /// Get information on the current CPU state and store it in the bottom 13
+ /// bits of rd. Set the top 19 bits to 1.
+ /// Format of PSW:
+ /// [0]: Carry bit
+ /// [1]: Overflow bit
+ /// [2]: Negative bit
+ /// [3]: Zero bit
+ /// [4]: Previous system mode bit.
+ /// [5]: System mode bit.
+ /// [6]: Interrupt enable bit.
+ /// [7-9]: SWP register mod 8.
+ /// [10-12]: CWP register mod 8.
+ /// Notes:
+ /// - Previous instruction must have its SCC bit off (for timing reasons?).
+ /// - shortsource must be a register and r0.
+ /// rd := (-1)<31:13> & PSW<12:0>;
+ /// Iff SCC == true, Z := [dest == 0]; N := LSTPC<31>; V,C := 0.
GetPSW(ShortInstruction),
/// Get the last Program Counter. rd := LSTPC.
/// Iff SCC == true, Z := [LSTPC == 0]; N := LSTPC<31>; V,C := garbage.
/// Notes:
/// - PRIVILEGED INSTRUCTION.
- GetIPC(ShortInstruction),
+ /// - Not transparent to interrupts.
+ /// - rs1 and short_source are discarded.
+ GetLPC(ShortInstruction),
/// Set PSW. PSW := [rs1 + ShortSource2]<12:0>;
+ /// Format of PSW.
+ /// [0]: Carry bit
+ /// [1]: Overflow bit
+ /// [2]: Negative bit
+ /// [3]: Zero bit
+ /// [4]: Previous system mode bit.
+ /// [5]: System mode bit.
+ /// [6]: Interrupt enable bit.
+ /// [7-9]: SWP register.
+ /// [10-12]: CWP registerr
/// Notes:
/// - PRIVILEGED INSTRUCTION.
- /// - SCC-bit MUST be false.
- /// - The next instruction CANNOT be `CALLX`, `CALLR`, CALLI`, `RET`, `RETI`,
- /// i.e. it cannot modify CWP. It also must not set the CC's.
+ /// - SCC-bit MUST be off.
+ /// - The next instruction CANNOT be `CALLX`, `CALLR`, `CALLI`, `RET`, `RETI`,
+ /// i.e. it cannot modify CWP/SWP. It also cannot modify the CC's.
/// - Rd is discarded.
/// - New PSW is not in effect until AFTER the next cycle following execution
/// of this instruction.
PutPSW(ShortInstruction),
/// Call procedure at `shortSource` + `rs1`.
- /// - The `RS1` and `RS1` registers are read from the OLD window.
+ /// - The `RS1` and `RS2` registers are read from the OLD window.
/// - The PC instruction saved is the `PC` at the `CALLI`.
/// - The `Rd` refers to the destination register in the NEW window.
/// - If the change to `CWP` makes it equal to `SWP`: stop execution,
@@ -278,7 +306,7 @@ impl ShortSource {
pub fn new(opcode: u32, signed: bool) -> Self {
// Short source immediate-mode bottom 13 bits <12-0> or rs1 <4-0>.
if opcode & 0x2000 != 0 {
- let mut tmp = Self::UImm13(opcode & 0x1fff);
+ let mut tmp = Self::Imm13(opcode & 0x1fff);
if signed {
tmp.uimm_to_simm()
} else {
@@ -293,15 +321,14 @@ impl ShortSource {
/// convert it a signed constant. Else, return `self`.
pub fn uimm_to_simm(&self) -> Self {
match *self {
- Self::UImm13(u) => {
+ Self::Imm13(u) => {
if u & 0x1000 != 0 {
// Sign-extend the 13 bit value to 32 bits.
- Self::SImm13(-(u as i32))
+ Self::Imm13((-(u as i32)) as u32)
} else {
- Self::SImm13(u as i32)
+ Self::Imm13(u)
}
}
- Self::SImm13(s) => *self,
Self::Reg(r) => *self,
}
}
@@ -311,8 +338,7 @@ impl fmt::Display for ShortSource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Reg(r) => write!(f, "Reg {}", r),
- Self::UImm13(u) => write!(f, "U{}", u),
- Self::SImm13(i) => write!(f, "S{}", i),
+ Self::Imm13(u) => write!(f, "U{}", u),
}
}
}
@@ -321,8 +347,7 @@ impl LowerHex for ShortSource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Reg(r) => write!(f, "Register {:x}", r),
- Self::UImm13(u) => write!(f, "(UImm) {:x}", u),
- Self::SImm13(i) => write!(f, "(SImm) {:x}", i),
+ Self::Imm13(u) => write!(f, "(UImm) {:x}", u),
}
}
}
diff --git a/src/memory.rs b/src/memory.rs
@@ -15,11 +15,11 @@
// Struct definitions.
-use util::File;
-
use config::Config;
+use util::{check_hword_alignment, check_word_alignment, File};
/// The real memory of the RISC II emulator.
+#[derive(Debug, Clone, Copy)]
pub struct Memory(Vec<u8>);
// Struct impls.
@@ -43,4 +43,97 @@ impl Memory {
file.write_vec(&self.0)?;
Ok(())
}
+
+ pub fn write_buf(&mut self, addr: u32, buf: &[u8]) {
+ self[addr..buf.len()].copy_from_slice(buf);
+ }
+
+ pub fn get_byte(&self, addr: u32) -> Result<u8, String> {
+ if addr >= self.len() {
+ Err(format!(
+ "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ Ok(self.0[addr])
+ }
+ }
+
+ pub fn get_hword(&self, addr: u32) -> Result<u16, String> {
+ check_hword_alignment(addr)?;
+ if addr >= self.len() {
+ Err(format!(
+ "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ Ok(u16::from_be_bytes(self.0[addr..addr + 1]))
+ }
+ }
+
+ pub fn get_word(&self, addr: u32) -> Result<u32, String> {
+ check_word_alignment(addr)?;
+ if addr >= self.len() {
+ Err(format!(
+ "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ Ok(self.0[addr])
+ }
+ }
+
+ pub fn set_word(&mut self, addr: u32, what: u32) -> Result<u32, String> {
+ check_word_alignment()?;
+ if addr >= self.len() - 4 {
+ Err(format!(
+ "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ let what_bytes = if cfg!(target_endian = "little") {
+ u32::from_ne_bytes(what.swap_bytes())
+ } else {
+ u32::from_ne_bytes(what)
+ };
+ self.0[addr..addr + 4].copy_from_slice(what_bytes);
+ Ok(what)
+ }
+ }
+
+ pub fn set_hword(&mut self, addr: u32, what: u16) -> Result<u32, String> {
+ check_word_alignment()?;
+ if addr >= self.len() - 2 {
+ Err(format!(
+ "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ let what_bytes = if cfg!(target_endian = "little") {
+ u16::from_ne_bytes(what.swap_bytes())
+ } else {
+ u16::from_ne_bytes(what)
+ };
+ self.0[addr..addr + 2].copy_from_slice(what_bytes);
+ Ok(what)
+ }
+ }
+
+ pub fn set_byte(&mut self, addr: u32, what: u8) -> Result<u32, String> {
+ if addr >= self.len() {
+ Err(format!(
+ "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})",
+ addr,
+ self.len()
+ ))
+ } else {
+ self.0[addr] = what;
+ Ok(what)
+ }
+ }
}
diff --git a/src/system.rs b/src/system.rs
@@ -15,28 +15,19 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use config::Config;
-use cpu::RegisterFile;
+use cpu::{ProcessorStatusWord, RegisterFile};
use memory::Memory;
use std::fmt;
/// RISC II emulated system.
+#[derive(Debug, Copy, Clone, PartialEq)]
pub struct System {
/// RISC II register file.
regs: RegisterFile,
+ /// Processor status.
+ psw: ProcessorStatusWord,
/// 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.
@@ -49,59 +40,88 @@ impl System {
pub fn new(config: &Config) -> Result<Self, String> {
Ok(Self {
regs: RegisterFile::new(),
+ psw: ProcessorStatusWord::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)
- )
+ /// Get the 13 bit PSW value. PSW is the state of the system's special
+ /// registers and CC's. After the 13th bit PSW is 0 padded.
+ /// Format of PSW:
+ /// [0]: Carry bit
+ /// [1]: Overflow bit
+ /// [2]: Negative bit
+ /// [3]: Zero bit
+ /// [4]: Previous system mode bit.
+ /// [5]: System mode bit.
+ /// [6]: Interrupt enable bit.
+ /// [7-9]: SWP register mod 8.
+ /// [10-12]: CWP register mod 8.
+ pub fn get_psw_as_u32(&self) -> u32 {
+ self.psw.to_u32()
}
-}
-// Private functions.
+ pub fn call(&mut self, addr: u32) {
+ self.regs.push_reg_window();
+ }
-/// Create a descriptive string for the system's privilege state bits.
-/// # Arguments
-/// * `s` - Privilege state bit.
-fn privilege_string(s: bool) -> &str {
- if s {
- "Privileged"
- } else {
- "Unprivileged"
+ pub fn ret(&mut self) {
+ self.regs.pop_reg_window();
+ }
+
+ pub fn get_register_file(&mut self) -> &mut RegisterFile {
+ &mut self.regs
+ }
+
+ pub fn copy_register_file(&self) -> RegisterFile {
+ self.regs
+ }
+
+ pub fn get_last_pc(&self) -> u32 {
+ self.regs.get_last_pc()
+ }
+
+ pub fn get_pc(&self) -> u32 {
+ self.regs.get_pc()
+ }
+
+ pub fn get_next_pc(&self) -> u32 {
+ self.regs.get_next_pc()
+ }
+
+ pub fn integrate_system_changes(&mut self, other: &System) {
+ self.regs = other.regs;
+ self.psw = other.psw;
+ }
+
+ pub fn get_psw(&self) -> ProcessorStatusWord {
+ self.psw
+ }
+
+ pub fn set_psw(&mut self, psw: u32) {
+ self.psw = ProcessorStatusWord::from_u32(psw);
+ }
+
+ pub fn copy_no_mem(&self) -> Self {
+ System {
+ regs: self.regs,
+ psw: self.psw,
+ mem: vec![0; 0],
+ }
+ }
+
+ pub fn get_mem_ref(&mut self) -> &mut Memory {
+ &mut self.memory
}
}
-/// Stringify booleans with hardware terminology.
-/// # Arguments
-/// * `s` - Boolean.
-fn bool_hl_string(s: bool) -> &str {
- if s {
- "High"
- } else {
- "Low"
+impl fmt::Display for System {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "CPU register state:\n{}
+Processor Status Word:\n{}",
+ self.regs, self.psw,
+ )
}
}
diff --git a/src/util.rs b/src/util.rs
@@ -21,6 +21,11 @@ use std::io::{Read, Write};
use std::path::Path;
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
+// Public constants.
+
+/// Most significant bit of a u32.
+pub const U32_MSB: u32 = 0x80000000;
+
// Public struct definitions.
/// File object. Wrapper around fs::File but caches more data.
@@ -97,6 +102,22 @@ pub fn get_home_nofail() -> String {
}
}
+pub fn check_hword_alignment(addr: u32) -> Result<(), String> {
+ if addr & 0x1 != 0 {
+ Err(format!("Bad half word alignment: 0x{:x}", addr))
+ } else {
+ Ok(())
+ }
+}
+
+pub fn check_word_alignment(addr: u32) -> Result<(), String> {
+ if addr & 0x3 != 0 {
+ Err(format!("Bad word alignment: 0x{:x}", addr))
+ } else {
+ Ok(())
+ }
+}
+
// Struct impls.
impl File {