commit 698dca6504826f2ade27436f7e14b3db965b8daa
parent 5b4146fe2962b736541411ad06cf71b0a71e2616
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date: Sat, 8 Oct 2022 11:40:03 -0700
Encoding instructions test, move encoding to struct methods.
Diffstat:
7 files changed, 510 insertions(+), 81 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -13,5 +13,6 @@ overflow-checks = false
toml = "0.5.9"
serde = "1.0.137"
serde_derive = "1.0.137"
+assert_hex = "0.2.2"
[dependencies.sdl2]
version = "0.35"
\ No newline at end of file
diff --git a/src/decode.rs b/src/decode.rs
@@ -75,61 +75,6 @@ impl fmt::Debug for DecodeError {
}
}
-pub fn encode(inst: Instruction) -> u32 {
- type I = Instruction;
- match inst {
- I::Calli(s) => s.encode(0b0000001),
- I::GetPSW(s) => s.encode(0b0000010),
- I::PutPSW(s) => s.encode(0b0000100),
- I::GetLPC(s) => s.encode(0b0000011),
- I::Callx(s) => s.encode(0b0000100),
- I::Sll(s) => s.encode(0b0010001),
- I::Srl(s) => s.encode(0b0010011),
- I::Sra(s) => s.encode(0b0010010),
- I::Or(s) => s.encode(0b0010110),
- I::And(s) => s.encode(0b0010101),
- I::Xor(s) => s.encode(0b0010111),
- I::Add(s) => s.encode(0b0011000),
- I::Addc(s) => s.encode(0b0011001),
- I::Sub(s) => s.encode(0b0011100),
- I::Subc(s) => s.encode(0b0011101),
- I::Subi(s) => s.encode(0b0011110),
- I::Subci(s) => s.encode(0b0011111),
- I::Ldxw(s) => s.encode(0b0100110),
- I::Ldxhu(s) => s.encode(0b0101000),
- I::Ldxhs(s) => s.encode(0b0101010),
- I::Ldxbu(s) => s.encode(0b0101100),
- I::Ldxbs(s) => s.encode(0b0101110),
- I::Stxw(s) => s.encode(0b0110110),
- I::Stxh(s) => s.encode(0b0111010),
- I::Stxb(s) => s.encode(0b0111011),
- I::Jmpx(s) => s.encode(0b0001100),
- I::Ret(s) => s.encode(0b0001110),
- I::Reti(s) => s.encode(0b0001111),
-
- I::Jmpr(l) => l.encode(0b0001101),
- I::Callr(l) => l.encode(0b0001001),
- I::Ldhi(l) => l.encode(0b0010100),
- I::Ldrw(l) => l.encode(0b0100111),
- I::Ldrhu(l) => l.encode(0b0101001),
- I::Ldrhs(l) => l.encode(0b0101011),
- I::Ldrbu(l) => l.encode(0b0101101),
- I::Ldrbs(l) => l.encode(0b0101111),
- I::Strw(l) => l.encode(0b0110111),
- I::Strh(l) => l.encode(0b0111011),
- I::Strb(l) => l.encode(0b0111111),
- }
-}
-
-pub fn noop() -> u32 {
- encode(Instruction::And(ShortInstruction::new(
- false,
- 0,
- 0,
- ShortSource::Imm13(0),
- )))
-}
-
pub fn decode(opcode: u32) -> Result<Instruction> {
type I = Instruction;
// SCC flag (<24>).
@@ -141,7 +86,7 @@ pub fn decode(opcode: u32) -> Result<Instruction> {
// Immediate-mode bottom 19 bits <18-0>.
let imm19 = opcode & IMM19_LOC;
// Short source immediate-mode bottom 13 bits <12-0> or rs1 <4-0>.
- let short_source = if opcode & 0x2000 != 0 {
+ let short_source = if opcode & SHORT_SOURCE_TYPE_LOC != 0 {
ShortSource::Imm13(opcode & 0x1fff)
} else {
ShortSource::Reg((opcode & 0x1f) as u8)
diff --git a/src/encode_test.rs b/src/encode_test.rs
@@ -0,0 +1,426 @@
+// Test code for the RISC II encoder.
+// (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/>.
+
+#[cfg(test)]
+#[path = "encode.rs"]
+mod test {
+ extern crate assert_hex;
+
+ use super::super::*;
+ use assert_hex::*;
+ use decode::*;
+ use instruction::*;
+ use std::fmt;
+ use util::Result;
+
+ type I = Instruction;
+ type SS = ShortSource;
+ type SI = ShortInstruction;
+
+ #[test]
+ fn encode_and_noop_imm() -> Result<()> {
+ assert_eq_hex!(
+ I::And(SI::new(false, 0, 0, SS::Imm13(0))).encode(),
+ 0x2a002000
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_and_noop_register() -> Result<()> {
+ assert_eq_hex!(
+ I::And(SI::new(false, 0, 0, SS::Reg(0))).encode(),
+ 0x2a000000
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_calli() -> Result<()> {
+ assert_eq_hex!(
+ I::Calli(SI::new(true, 5, 7, SS::Imm13(4111))).encode(),
+ 0x0329f00f
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_getpsw() -> Result<()> {
+ assert_eq_hex!(
+ 0x05293fff,
+ I::GetPSW(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1fff))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_getipc() -> Result<()> {
+ assert_eq_hex!(
+ 0x07293f69,
+ I::GetLPC(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_putpsw() -> Result<()> {
+ assert_eq_hex!(
+ 0x09293f69,
+ I::PutPSW(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ // (unpriveleged) Call/jump/ret instructions.
+
+ #[test]
+ fn encode_callx() -> Result<()> {
+ assert_eq_hex!(
+ 0x11293f69,
+ I::Callx(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_callr() -> Result<()> {
+ assert_eq_hex!(
+ 0x132b3420,
+ I::Callr(LongInstruction::new(true, 5, 0x33420)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_jmpx() -> Result<()> {
+ assert_eq_hex!(
+ 0x19293f69,
+ I::Jmpx(ShortConditional::new(
+ true,
+ Conditional::Hi,
+ 4,
+ SS::Imm13(0x1f69)
+ ))
+ .encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_jmpr() -> Result<()> {
+ assert_eq_hex!(
+ 0x1bfb3420,
+ I::Jmpr(LongConditional::new(true, Conditional::Alw, 0x33420)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ret() -> Result<()> {
+ assert_eq_hex!(
+ 0x1d293f69,
+ I::Ret(ShortConditional::new(
+ true,
+ Conditional::Hi,
+ 4,
+ SS::Imm13(0x1f69)
+ ))
+ .encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_reti() -> Result<()> {
+ assert_eq_hex!(
+ 0x1f293f69,
+ I::Reti(ShortConditional::new(
+ true,
+ Conditional::Hi,
+ 4,
+ SS::Imm13(0x1f69)
+ ))
+ .encode()
+ );
+ Ok(())
+ }
+
+ // Arithmetic and logic instructions (except ldhi).
+
+ #[test]
+ fn encode_sll() -> Result<()> {
+ assert_eq_hex!(
+ 0x23293f69,
+ I::Sll(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_sra() -> Result<()> {
+ assert_eq_hex!(
+ 0x25293f69,
+ I::Sra(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_srl() -> Result<()> {
+ assert_eq_hex!(
+ 0x27293f69,
+ I::Srl(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldhi() -> Result<()> {
+ assert_eq_hex!(
+ 0x292b3f69,
+ I::Ldhi(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_and() -> Result<()> {
+ assert_eq_hex!(
+ 0x2b293f69,
+ I::And(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_or() -> Result<()> {
+ assert_eq_hex!(
+ 0x2d293f69,
+ I::Or(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_xor() -> Result<()> {
+ assert_eq_hex!(
+ 0x2f293f69,
+ I::Xor(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_add() -> Result<()> {
+ assert_eq_hex!(
+ 0x31293f69,
+ I::Add(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_addc() -> Result<()> {
+ assert_eq_hex!(
+ 0x33293f69,
+ I::Addc(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_sub() -> Result<()> {
+ assert_eq_hex!(
+ 0x39293f69,
+ I::Sub(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_subc() -> Result<()> {
+ assert_eq_hex!(
+ 0x3b293f69,
+ I::Subc(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_subi() -> Result<()> {
+ assert_eq_hex!(
+ 0x3d293f69,
+ I::Subi(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_subci() -> Result<()> {
+ assert_eq_hex!(
+ 0x3f293f69,
+ I::Subci(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ // Load instructions.
+
+ #[test]
+ fn encode_ldxw() -> Result<()> {
+ assert_eq_hex!(
+ 0x4d293f69,
+ I::Ldxw(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldrw() -> Result<()> {
+ assert_eq_hex!(
+ 0x4f2b3f69,
+ I::Ldrw(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldxhu() -> Result<()> {
+ assert_eq_hex!(
+ 0x51293f69,
+ I::Ldxhu(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldrhu() -> Result<()> {
+ assert_eq_hex!(
+ 0x532b3f69,
+ I::Ldrhu(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldxhs() -> Result<()> {
+ assert_eq_hex!(
+ 0x55293f69,
+ I::Ldxhs(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldrhs() -> Result<()> {
+ assert_eq_hex!(
+ 0x572b3f69,
+ I::Ldrhs(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldxbu() -> Result<()> {
+ assert_eq_hex!(
+ 0x59293f69,
+ I::Ldxbu(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldrbu() -> Result<()> {
+ assert_eq_hex!(
+ 0x5b2b3f69,
+ I::Ldrbu(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldxbs() -> Result<()> {
+ assert_eq_hex!(
+ 0x5d293f69,
+ I::Ldxbs(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_ldrbs() -> Result<()> {
+ assert_eq_hex!(
+ 0x5f2b3f69,
+ I::Ldrbs(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ // Store instructions.
+
+ #[test]
+ fn encode_stxw() -> Result<()> {
+ assert_eq_hex!(
+ 0x6d293f69,
+ I::Stxw(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_strw() -> Result<()> {
+ assert_eq_hex!(
+ 0x6f2b3f69,
+ I::Strw(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_stxh() -> Result<()> {
+ assert_eq_hex!(
+ 0x75293f69,
+ I::Stxh(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_strh() -> Result<()> {
+ assert_eq_hex!(
+ 0x772b3f69,
+ I::Strh(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_stxb() -> Result<()> {
+ assert_eq_hex!(
+ 0x7d293f69,
+ I::Stxb(ShortInstruction::new(true, 5, 4, SS::Imm13(0x1f69))).encode()
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn encode_strb() -> Result<()> {
+ assert_eq_hex!(
+ 0x7f2b3f69,
+ I::Strb(LongInstruction::new(true, 5, 0x33f69)).encode()
+ );
+ Ok(())
+ }
+}
diff --git a/src/instruction.rs b/src/instruction.rs
@@ -21,6 +21,13 @@ pub const SCC_LOC: u32 = 0x1000000;
pub const DEST_LOC: u32 = 0x00F80000;
pub const RS1_LOC: u32 = 0x7c000;
pub const IMM19_LOC: u32 = 0x7FFFF;
+pub const SHORT_SOURCE_TYPE_LOC: u32 = 0x2000;
+
+// Public functions.
+
+pub fn noop() -> u32 {
+ Instruction::And(ShortInstruction::new(false, 0, 0, ShortSource::Imm13(0))).encode()
+}
/// Types of conditionals the RISC II supports.
#[derive(PartialEq, Eq, Copy, Clone)]
@@ -302,6 +309,54 @@ pub enum Instruction {
// Impls.
+impl Instruction {
+ pub fn encode(&self) -> u32 {
+ type I = Instruction;
+ match *self {
+ I::Calli(s) => s.encode(0b0000001),
+ I::GetPSW(s) => s.encode(0b0000010),
+ I::PutPSW(s) => s.encode(0b0000100),
+ I::GetLPC(s) => s.encode(0b0000011),
+ I::Callx(s) => s.encode(0b0001000),
+ I::Sll(s) => s.encode(0b0010001),
+ I::Srl(s) => s.encode(0b0010011),
+ I::Sra(s) => s.encode(0b0010010),
+ I::Or(s) => s.encode(0b0010110),
+ I::And(s) => s.encode(0b0010101),
+ I::Xor(s) => s.encode(0b0010111),
+ I::Add(s) => s.encode(0b0011000),
+ I::Addc(s) => s.encode(0b0011001),
+ I::Sub(s) => s.encode(0b0011100),
+ I::Subc(s) => s.encode(0b0011101),
+ I::Subi(s) => s.encode(0b0011110),
+ I::Subci(s) => s.encode(0b0011111),
+ I::Ldxw(s) => s.encode(0b0100110),
+ I::Ldxhu(s) => s.encode(0b0101000),
+ I::Ldxhs(s) => s.encode(0b0101010),
+ I::Ldxbu(s) => s.encode(0b0101100),
+ I::Ldxbs(s) => s.encode(0b0101110),
+ I::Stxw(s) => s.encode(0b0110110),
+ I::Stxh(s) => s.encode(0b0111010),
+ I::Stxb(s) => s.encode(0b0111110),
+ I::Jmpx(s) => s.encode(0b0001100),
+ I::Ret(s) => s.encode(0b0001110),
+ I::Reti(s) => s.encode(0b0001111),
+
+ I::Jmpr(l) => l.encode(0b0001101),
+ I::Callr(l) => l.encode(0b0001001),
+ I::Ldhi(l) => l.encode(0b0010100),
+ I::Ldrw(l) => l.encode(0b0100111),
+ I::Ldrhu(l) => l.encode(0b0101001),
+ I::Ldrhs(l) => l.encode(0b0101011),
+ I::Ldrbu(l) => l.encode(0b0101101),
+ I::Ldrbs(l) => l.encode(0b0101111),
+ I::Strw(l) => l.encode(0b0110111),
+ I::Strh(l) => l.encode(0b0111011),
+ I::Strb(l) => l.encode(0b0111111),
+ }
+ }
+}
+
impl ShortSource {
/// Create a new short source.
/// # Arguments
@@ -363,7 +418,7 @@ impl LongInstruction {
let dest = (self.dest as u32) << 19;
let imm19 = self.imm19;
- ((opcode as u32) << 25) & scc & dest & imm19
+ ((opcode as u32) << 25) | scc | dest | imm19
}
/// Create a new long instruction.
/// # Arguments
@@ -394,7 +449,8 @@ impl LongConditional {
let scc = if self.scc { SCC_LOC } else { 0 };
let dest = (get_opdata_from_cond(self.dest) as u32) << 19;
let imm19 = self.imm19;
- ((opcode as u32) << 25) & scc & dest & imm19
+ println!("Lol 0x{:x}, 0x{:x}", dest, imm19);
+ ((opcode as u32) << 25) | scc | dest | imm19
}
/// Create a new long conditional instruction.
/// # Arguments
@@ -425,12 +481,11 @@ impl ShortInstruction {
let scc = if self.scc { SCC_LOC } else { 0 };
let dest = (self.dest as u32) << 19;
let rs1 = (self.rs1 as u32) << 14;
- let ss = match self.short_source {
- ShortSource::Reg(r) => r as u32,
- ShortSource::Imm13(i) => i,
+ let (ss, ss_bit) = match self.short_source {
+ ShortSource::Reg(r) => (r as u32, 0),
+ ShortSource::Imm13(i) => (i, SHORT_SOURCE_TYPE_LOC),
};
-
- ((opcode as u32) << 25) & scc & dest & rs1 & ss
+ ((opcode as u32) << 25) | scc | dest | rs1 | ss_bit | ss
}
/// Create a new long conditional instruction.
/// # Arguments
@@ -463,12 +518,11 @@ impl ShortConditional {
let scc = if self.scc { SCC_LOC } else { 0 };
let dest = (get_opdata_from_cond(self.dest) as u32) << 19;
let rs1 = (self.rs1 as u32) << 14;
- let ss = match self.short_source {
- ShortSource::Reg(r) => r as u32,
- ShortSource::Imm13(i) => i,
+ let (ss, ss_bit) = match self.short_source {
+ ShortSource::Reg(r) => (r as u32, 0),
+ ShortSource::Imm13(i) => (i, SHORT_SOURCE_TYPE_LOC),
};
-
- ((opcode as u32) << 25) & scc & dest & rs1
+ ((opcode as u32) << 25) | scc | dest | rs1 | ss_bit | ss
}
/// Create a new long conditional instruction.
/// # Arguments
@@ -520,7 +574,7 @@ impl fmt::Display for Conditional {
fn get_opdata_from_cond(cond: Conditional) -> u8 {
type C = Conditional;
- (match cond {
+ match cond {
C::Gt => 1,
C::Le => 2,
C::Ge => 3,
@@ -536,5 +590,5 @@ fn get_opdata_from_cond(cond: Conditional) -> u8 {
C::Nv => 13,
C::V => 14,
C::Alw => 15,
- })
+ }
}
diff --git a/src/main.rs b/src/main.rs
@@ -13,21 +13,26 @@
// 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/>.
+#[macro_use]
+extern crate assert_hex;
extern crate core;
extern crate sdl2;
#[cfg(test)]
mod decode_test;
#[cfg(test)]
+mod encode_test;
+#[cfg(test)]
mod main_test;
-mod config;
-mod cpu;
-mod decode;
-mod instruction;
-mod memory;
-mod sdl;
-mod system;
-mod util;
+// Modules declared as pub to shut up rust-analyzer about dead code.
+pub mod config;
+pub mod cpu;
+pub mod decode;
+pub mod instruction;
+pub mod memory;
+pub mod sdl;
+pub mod system;
+pub mod util;
use decode::decode_file;
use std::fs;
diff --git a/src/sdl.rs b/src/sdl.rs
@@ -30,7 +30,6 @@ use sdl2::VideoSubsystem;
use util::Result;
// Struct definitions.
-
/// SDL context structs.
pub struct Context {
/// SDL context.
@@ -42,7 +41,6 @@ pub struct Context {
/// Event queue.
event_pump: EventPump,
}
-
// Struct impls.
impl Context {
diff --git a/src/system.rs b/src/system.rs
@@ -16,7 +16,7 @@
use config::Config;
use cpu::{ProcessorStatusWord, RegisterFile};
-use decode::noop;
+use instruction::noop;
use memory::Memory;
use std::fmt;
use util::Result;