diff --git a/src/core.rs b/src/core.rs index f46fd54..acc52f2 100644 --- a/src/core.rs +++ b/src/core.rs @@ -4,8 +4,6 @@ // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. -use std::fmt::format; - use crate::{ consts::{Addr, RegId, RegValue}, decode::Instruction, diff --git a/src/decode.rs b/src/decode.rs index 21a1e64..4258a6f 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -85,15 +85,10 @@ impl Instruction { imm_20 | imm_10_1 | imm_11 | imm_19_12 } - // The following are AFAIK only used for shift by immediate operations - + /// Technically part of immediate. Only used to determine shift type for immediate shifts afaik + /// 32bit ones use funct7 in this way #[inline] pub fn funct6(self) -> u8 { (self.0 >> 26 & 0x3f) as u8 } - - #[inline] - pub fn imm_shamt(self) -> usize { - (self.0 >> 20 & 0x3f) as usize - } } diff --git a/src/instructions.rs b/src/instructions.rs index c7af5ec..2942995 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -4,6 +4,9 @@ // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. +#[macro_use] +mod macros; + mod rvi; use crate::{ @@ -14,35 +17,60 @@ use crate::{ pub(crate) fn find_and_exec(instr: Instruction, core: &mut Core) -> Result<(), ExceptionType> { match instr.opcode_noncompressed() { - 0b01100 => match (instr.funct7(), instr.funct3()) { + 0b01100 => match (instr.funct3(), instr.funct7()) { // OP - (0b0000000, 0b000) => rvi::add(core, instr), - (0b0100000, 0b000) => rvi::sub(core, instr), - (0b0000000, 0b111) => rvi::and(core, instr), - (0b0000000, 0b110) => rvi::or(core, instr), + (0b000, 0b0000000) => rvi::add(core, instr), + (0b000, 0b0100000) => rvi::sub(core, instr), + (0b010, 0b0000000) => rvi::slt(core, instr), + (0b011, 0b0000000) => rvi::sltu(core, instr), + (0b001, 0b0000000) => rvi::sll(core, instr), + (0b101, 0b0000000) => rvi::srl(core, instr), + (0b101, 0b0100000) => rvi::sra(core, instr), + (0b111, 0b0000000) => rvi::and(core, instr), + (0b100, 0b0000000) => rvi::xor(core, instr), + (0b110, 0b0000000) => rvi::or(core, instr), + _ => Err(IllegalInstruction), + }, + 0b01110 => match (instr.funct3(), instr.funct7()) { + // OP_32 + (0b000, 0b0000000) => rvi::addw(core, instr), + (0b000, 0b0100000) => rvi::subw(core, instr), + (0b001, 0b0000000) => rvi::sllw(core, instr), + (0b101, 0b0000000) => rvi::srlw(core, instr), + (0b101, 0b0100000) => rvi::sraw(core, instr), _ => Err(IllegalInstruction), }, 0b00100 => match instr.funct3() { // OP_IMM 0b000 => rvi::addi(core, instr), - 0b001 => { - if instr.funct6() == 0 { - rvi::slli(core, instr) - } else { - Err(IllegalInstruction) - } - } - 0b101 => match instr.funct6() { - // immediate right-shift - 0b000000 => rvi::srli(core, instr), + 0b010 => rvi::slti(core, instr), + 0b011 => rvi::sltiu(core, instr), + 0b001 => match instr.funct6() { + 0 => rvi::slli(core, instr), _ => Err(IllegalInstruction), }, + 0b101 => match instr.funct6() { + 0b000000 => rvi::srli(core, instr), + 0b010000 => rvi::srai(core, instr), + _ => Err(IllegalInstruction), + }, + 0b100 => rvi::xori(core, instr), + 0b110 => rvi::ori(core, instr), 0b111 => rvi::andi(core, instr), _ => Err(IllegalInstruction), }, 0b00110 => match instr.funct3() { // OP_IMM_32 0b000 => rvi::addiw(core, instr), + 0b001 => match instr.funct7() { + 0 => rvi::slliw(core, instr), + _ => Err(IllegalInstruction), + }, + 0b101 => match instr.funct7() { + 0b0000000 => rvi::srliw(core, instr), + 0b0100000 => rvi::sraiw(core, instr), + _ => Err(IllegalInstruction), + }, _ => Err(IllegalInstruction), }, 0b01000 => match instr.funct3() { diff --git a/src/instructions/macros.rs b/src/instructions/macros.rs new file mode 100644 index 0000000..c88114c --- /dev/null +++ b/src/instructions/macros.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2025 taitep +// SPDX-License-Identifier: MIT +// +// This file is part of TRVE (https://gitea.taitep.se/taitep/trve) +// See LICENSE file in the project root for full license text. + +#[macro_export] +macro_rules! instr_branch { + ($name:ident, $cond:expr) => { + pub fn $name(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { + let a = core.reg_read(instr.rs1()); + let b = core.reg_read(instr.rs2()); + if $cond(a, b) { + core.pc = core.pc.wrapping_add(instr.imm_b()); + } else { + core.advance_pc(); + } + Ok(()) + } + }; +} + +#[macro_export] +macro_rules! instr_branch_signed { + ($name:ident, $cond:expr) => { + instr_branch!($name, |a, b| $cond((a as i64), (b as i64))); + }; +} + +#[macro_export] +macro_rules! instr_op_r { + ($name:ident, $op:expr) => { + pub fn $name(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { + let a = core.reg_read(instr.rs1()); + let b = core.reg_read(instr.rs2()); + let res = $op(a, b); + core.reg_write(instr.rd(), res); + core.advance_pc(); + Ok(()) + } + }; +} + +#[macro_export] +macro_rules! instr_op_i { + ($name:ident, $op:expr) => { + pub fn $name(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { + let a = core.reg_read(instr.rs1()); + let b = instr.imm_i(); + let res = $op(a, b); + core.reg_write(instr.rd(), res); + core.advance_pc(); + Ok(()) + } + }; +} + +#[macro_export] +macro_rules! instr_op { + ($name:ident, $name_imm:ident, $op:expr) => { + instr_op_r!($name, $op); + instr_op_i!($name_imm, $op); + }; +} diff --git a/src/instructions/rvi.rs b/src/instructions/rvi.rs index a348596..c35098f 100644 --- a/src/instructions/rvi.rs +++ b/src/instructions/rvi.rs @@ -4,83 +4,50 @@ // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. -use crate::{core::Core, decode::Instruction, exceptions::ExceptionType}; +use crate::{consts::RegValue, core::Core, decode::Instruction, exceptions::ExceptionType}; + +use std::ops::{BitAnd, BitOr, BitXor}; mod mem; pub use mem::*; -pub fn add(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write( - instr.rd(), - core.reg_read(instr.rs1()) - .wrapping_add(core.reg_read(instr.rs2())), - ); - core.advance_pc(); - Ok(()) -} +instr_op!(add, addi, RegValue::wrapping_add); +instr_op!( + addw, + addiw, + |a, b| RegValue::wrapping_add(a, b) as i32 as i64 as RegValue +); +instr_op_r!(sub, RegValue::wrapping_sub); +instr_op_r!(subw, |a, b| RegValue::wrapping_sub(a, b) as i32 as i64 + as RegValue); -pub fn sub(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write( - instr.rd(), - core.reg_read(instr.rs1()) - .wrapping_sub(core.reg_read(instr.rs2())), - ); - core.advance_pc(); - Ok(()) -} +instr_op!(and, andi, RegValue::bitand); +instr_op!(or, ori, RegValue::bitor); +instr_op!(xor, xori, RegValue::bitxor); -pub fn addi(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write( - instr.rd(), - core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()), - ); - core.advance_pc(); - Ok(()) -} +instr_op!(sll, slli, |x, shamt| x << (shamt & 0b111111)); +instr_op!( + sllw, + slliw, + |x, shamt| (x << (shamt & 0b11111)) as i32 as i64 as RegValue +); +instr_op!(srl, srli, |x, shamt| x >> (shamt & 0b111111)); +instr_op!( + srlw, + srliw, + |x, shamt| (x >> (shamt & 0b11111)) as i32 as i64 as RegValue +); +instr_op!(sra, srai, |x, shamt| (x as i64 >> (shamt & 0b111111)) + as RegValue); +instr_op!( + sraw, + sraiw, + |x, shamt| (x as i32 >> (shamt & 0b11111)) as i64 as RegValue +); -pub fn addiw(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - let res = core.reg_read(instr.rs1()).wrapping_add(instr.imm_i()) as i32; - core.reg_write(instr.rd(), res as i64 as u64); - core.advance_pc(); - Ok(()) -} - -pub fn and(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write( - instr.rd(), - core.reg_read(instr.rs1()) & core.reg_read(instr.rs2()), - ); - core.advance_pc(); - Ok(()) -} - -pub fn andi(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write(instr.rd(), core.reg_read(instr.rs1()) & instr.imm_i()); - core.advance_pc(); - Ok(()) -} - -pub fn or(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write( - instr.rd(), - core.reg_read(instr.rs1()) | core.reg_read(instr.rs2()), - ); - core.advance_pc(); - Ok(()) -} - -pub fn slli(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write(instr.rd(), core.reg_read(instr.rs1()) << instr.imm_shamt()); - core.advance_pc(); - Ok(()) -} - -pub fn srli(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - core.reg_write(instr.rd(), core.reg_read(instr.rs1()) >> instr.imm_shamt()); - core.advance_pc(); - Ok(()) -} +instr_op!(sltu, sltiu, |a, b| (a < b) as RegValue); +instr_op!(slt, slti, |a, b| ((a as i64) < (b as i64)) as RegValue); pub fn lui(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { core.reg_write(instr.rd(), instr.imm_u()); @@ -106,26 +73,6 @@ pub fn jalr(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { Ok(()) } -macro_rules! instr_branch { - ($name:ident, $cond:expr) => { - pub fn $name(core: &mut Core, instr: Instruction) -> Result<(), ExceptionType> { - let a = core.reg_read(instr.rs1()); - let b = core.reg_read(instr.rs2()); - if $cond(a, b) { - core.pc = core.pc.wrapping_add(instr.imm_b()); - } else { - core.advance_pc(); - } - Ok(()) - } - }; -} -macro_rules! instr_branch_signed { - ($name:ident, $cond:expr) => { - instr_branch!($name, |a, b| $cond((a as i64), (b as i64))); - }; -} - instr_branch!(beq, |a, b| a == b); instr_branch!(bne, |a, b| a != b); instr_branch!(bltu, |a, b| a < b);