riscii

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

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:
MCargo.toml | 1+
Msrc/decode.rs | 57+--------------------------------------------------------
Asrc/encode_test.rs | 426+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/instruction.rs | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/main.rs | 21+++++++++++++--------
Msrc/sdl.rs | 2--
Msrc/system.rs | 2+-
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;