diff --git a/.idea/misc.xml b/.idea/misc.xml
index 998148d..3d5f5f4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/.idea/riscemu.iml b/.idea/riscemu.iml
index 9f548db..b97886d 100644
--- a/.idea/riscemu.iml
+++ b/.idea/riscemu.iml
@@ -10,7 +10,7 @@
-
+
diff --git a/riscemu/MMU.py b/riscemu/MMU.py
index 677d183..d02ecbd 100644
--- a/riscemu/MMU.py
+++ b/riscemu/MMU.py
@@ -16,6 +16,7 @@ from .types import (
Program,
InstructionContext,
Int32,
+ Float32,
)
from .types.exceptions import InvalidAllocationException, MemoryAccessException
@@ -187,6 +188,9 @@ class MMU:
def read_int(self, addr: int) -> Int32:
return Int32(self.read(addr, 4))
+ def read_float(self, addr: int) -> Float32:
+ return Float32(self.read(addr, 4))
+
def translate_address(self, address: T_AbsoluteAddress) -> str:
sec = self.get_sec_containing(address)
if not sec:
diff --git a/riscemu/instructions/RV32F.py b/riscemu/instructions/RV32F.py
new file mode 100644
index 0000000..9eff582
--- /dev/null
+++ b/riscemu/instructions/RV32F.py
@@ -0,0 +1,609 @@
+"""
+RiscEmu (c) 2023 Anton Lydike
+
+SPDX-License-Identifier: MIT
+
+This file contains copious amounts of docstrings that were all taken
+from https://msyksphinz-self.github.io/riscv-isadoc/html/rvfd.html
+(all the docstrings on the instruction methods documenting the opcodes
+and their function)
+"""
+from typing import Tuple
+
+from .instruction_set import InstructionSet, Instruction
+from riscemu.types import INS_NOT_IMPLEMENTED, Float32, Int32, UInt32
+
+
+class RV32F(InstructionSet):
+ def instruction_fmadd_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |rs3 |00 |rs2 |rs1 |rm |rd |10000|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fmadd.s rd,rs1,rs2,rs3
+
+ :Description:
+ | Perform single-precision fused multiply addition.
+
+ :Implementation:
+ | f[rd] = f[rs1]×f[rs2]+f[rs3]
+
+ """
+ rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
+ self.regs.set_f(rd, rs1 * rs2 + rs3)
+
+ def instruction_fmsub_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |rs3 |00 |rs2 |rs1 |rm |rd |10001|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fmsub.s rd,rs1,rs2,rs3
+
+ :Description:
+ | Perform single-precision fused multiply addition.
+
+ :Implementation:
+ | f[rd] = f[rs1]×f[rs2]-f[rs3]
+ """
+ rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
+ self.regs.set_f(rd, rs1 * rs2 - rs3)
+
+ def instruction_fnmsub_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |rs3 |00 |rs2 |rs1 |rm |rd |10010|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fnmsub.s rd,rs1,rs2,rs3
+
+ :Description:
+ | Perform single-precision fused multiply addition.
+
+ :Implementation:
+ | f[rd] = -f[rs1]×f[rs2]+f[rs3]
+ """
+ rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
+ self.regs.set_f(rd, -rs1 * rs2 + rs3)
+
+ def instruction_fnmadd_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |rs3 |00 |rs2 |rs1 |rm |rd |10011|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fnmadd.s rd,rs1,rs2,rs3
+
+ :Description:
+ | Perform single-precision fused multiply addition.
+
+ :Implementation:
+ | f[rd] = -f[rs1]×f[rs2]-f[rs3]
+ """
+ rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
+ self.regs.set_f(rd, -rs1 * rs2 - rs3)
+
+ def instruction_fadd_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00000|00 |rs2 |rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fadd.s rd,rs1,rs2
+
+ :Description:
+ | Perform single-precision floating-point addition.
+
+ :Implementation:
+ | f[rd] = f[rs1] + f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ self.regs.get_f(rs1) + self.regs.get_f(rs2),
+ )
+
+ def instruction_fsub_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00001|00 |rs2 |rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fsub.s rd,rs1,rs2
+
+ :Description:
+ | Perform single-precision floating-point substraction.
+
+ :Implementation:
+ | f[rd] = f[rs1] - f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ self.regs.get_f(rs1) - self.regs.get_f(rs2),
+ )
+
+ def instruction_fmul_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00010|00 |rs2 |rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fmul.s rd,rs1,rs2
+
+ :Description:
+ | Perform single-precision floating-point multiplication.
+
+ :Implementation:
+ | f[rd] = f[rs1] × f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ self.regs.get_f(rs1) * self.regs.get_f(rs2),
+ )
+
+ def instruction_fdiv_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00011|00 |rs2 |rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fdiv.s rd,rs1,rs2
+
+ :Description:
+ | Perform single-precision floating-point division.
+
+ :Implementation:
+ | f[rd] = f[rs1] / f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ self.regs.get_f(rs1) / self.regs.get_f(rs2),
+ )
+
+ def instruction_fsqrt_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |01011|00 |00000|rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fsqrt.s rd,rs1
+
+ :Description:
+ | Perform single-precision square root.
+
+ :Implementation:
+ | f[rd] = sqrt(f[rs1])
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set_f(self.regs.get_f(rs) ** 0.5)
+
+ def instruction_fsgnj_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00100|00 |rs2 |rs1 |000 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fsgnj.s rd,rs1,rs2
+
+ :Description:
+ | Produce a result that takes all bits except the sign bit from rs1.
+ | The result’s sign bit is rs2’s sign bit.
+
+ :Implementation:
+ | f[rd] = {f[rs2][31], f[rs1][30:0]}
+ """
+ INS_NOT_IMPLEMENTED(ins)
+
+ def instruction_fsgnjn_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00100|00 |rs2 |rs1 |001 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+ :Format:
+ | fsgnjn.s rd,rs1,rs2
+
+ :Description:
+ | Produce a result that takes all bits except the sign bit from rs1.
+ | The result’s sign bit is opposite of rs2’s sign bit.
+
+ :Implementation:
+ | f[rd] = {~f[rs2][31], f[rs1][30:0]}
+ """
+ INS_NOT_IMPLEMENTED(ins)
+
+ def instruction_fsgnjx_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00100|00 |rs2 |rs1 |010 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fsgnjx.s rd,rs1,rs2
+
+ :Description:
+ | Produce a result that takes all bits except the sign bit from rs1.
+ | The result’s sign bit is XOR of sign bit of rs1 and rs2.
+
+ :Implementation:
+ | f[rd] = {f[rs1][31] ^ f[rs2][31], f[rs1][30:0]}
+ """
+ INS_NOT_IMPLEMENTED(ins)
+
+ def instruction_fmin_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00101|00 |rs2 |rs1 |000 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fmin.s rd,rs1,rs2
+
+ :Description:
+ | Write the smaller of single precision data in rs1 and rs2 to rd.
+
+ :Implementation:
+ | f[rd] = min(f[rs1], f[rs2])
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ Float32(
+ min(
+ self.regs.get_f(rs1).value,
+ self.regs.get_f(rs2).value,
+ )
+ ),
+ )
+
+ def instruction_fmax_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |00101|00 |rs2 |rs1 |001 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fmax.s rd,rs1,rs2
+
+ :Description:
+ | Write the larger of single precision data in rs1 and rs2 to rd.
+
+ :Implementation:
+ | f[rd] = max(f[rs1], f[rs2])
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set_f(
+ rd,
+ Float32(
+ max(
+ self.regs.get_f(rs1).value,
+ self.regs.get_f(rs2).value,
+ )
+ ),
+ )
+
+ def instruction_fcvt_w_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11000|00 |00000|rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fcvt.w.s rd,rs1
+
+ :Description:
+ | Convert a floating-point number in floating-point register rs1 to a signed 32-bit in integer register rd.
+
+ :Implementation:
+ | x[rd] = sext(s32_{f32}(f[rs1]))
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set(rd, Int32(self.regs.get_f(rs).value))
+
+ def instruction_fcvt_wu_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11000|00 |00001|rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fcvt.wu.s rd,rs1
+
+ :Description:
+ | Convert a floating-point number in floating-point register rs1 to a signed 32-bit in unsigned integer register rd.
+
+ :Implementation:
+ | x[rd] = sext(u32_{f32}(f[rs1]))
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set(rd, UInt32(self.regs.get_f(rs).value))
+
+ def instruction_fmv_x_w(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11100|00 |00000|rs1 |000 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fmv.x.w rd,rs1
+
+ :Description:
+ | Move the single-precision value in floating-point register rs1 represented in IEEE 754-2008 encoding to the lower 32 bits of integer register rd.
+
+ :Implementation:
+ | x[rd] = sext(f[rs1][31:0])
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set(rd, UInt32(self.regs.get_f(rs).bits))
+
+ def instruction_feq_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |10100|00 |rs2 |rs1 |010 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | feq.s rd,rs1,rs2
+
+ :Description:
+ | Performs a quiet equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
+ | Only signaling NaN inputs cause an Invalid Operation exception.
+ | The result is 0 if either operand is NaN.
+
+ :Implementation:
+ | x[rd] = f[rs1] == f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set(rd, Int32(rs1 == rs2))
+
+ def instruction_flt_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |10100|00 |rs2 |rs1 |001 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | flt.s rd,rs1,rs2
+
+ :Description:
+ | Performs a quiet less comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
+ | Only signaling NaN inputs cause an Invalid Operation exception.
+ | The result is 0 if either operand is NaN.
+
+ :Implementation:
+ | x[rd] = f[rs1] < f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set(rd, Int32(rs1 < rs2))
+
+ def instruction_fle_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |10100|00 |rs2 |rs1 |000 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fle.s rd,rs1,rs2
+
+ :Description:
+ | Performs a quiet less or equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
+ | Only signaling NaN inputs cause an Invalid Operation exception.
+ | The result is 0 if either operand is NaN.
+
+ :Implementation:
+ | x[rd] = f[rs1] <= f[rs2]
+ """
+ rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
+ self.regs.set(rd, Int32(rs1 <= rs2))
+
+ def instruction_fclass_s(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11100|00 |00000|rs1 |001 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fclass.s rd,rs1
+
+ :Description:
+ | Examines the value in floating-point register rs1 and writes to integer register rd a 10-bit mask that indicates the class of the floating-point number.
+ | The format of the mask is described in [classify table]_.
+ | The corresponding bit in rd will be set if the property is true and clear otherwise.
+ | All other bits in rd are cleared. Note that exactly one bit in rd will be set.
+
+ :Implementation:
+ | x[rd] = classifys(f[rs1])
+ """
+ INS_NOT_IMPLEMENTED(ins)
+
+ def instruction_fcvt_s_w(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11010|00 |00000|rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fcvt.s.w rd,rs1
+
+ :Description:
+ | Converts a 32-bit signed integer, in integer register rs1 into a floating-point number in floating-point register rd.
+
+ :Implementation:
+ | f[rd] = f32_{s32}(x[rs1])
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set_f(rd, Float32(self.regs.get(rs).signed().value))
+
+ def instruction_fcvt_s_wu(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11010|00 |00001|rs1 |rm |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | fcvt.s.wu rd,rs1
+
+ :Description:
+ | Converts a 32-bit unsigned integer, in integer register rs1 into a floating-point number in floating-point register rd.
+
+ :Implementation:
+ | f[rd] = f32_{u32}(x[rs1])
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set_f(rd, Float32(self.regs.get(rs).unsigned_value))
+
+ def instruction_fmv_w_x(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |11110|00 |00000|rs1 |000 |rd |10100|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+
+
+ :Format:
+ | fmv.w.x rd,rs1
+
+ :Description:
+ | Move the single-precision value encoded in IEEE 754-2008 standard encoding from the lower 32 bits of integer register rs1 to the floating-point register rd.
+
+ :Implementation:
+ | f[rd] = x[rs1][31:0]
+ """
+ rd, rs = self.parse_rd_rs(ins)
+ self.regs.set_f(rd, Float32.from_bytes(self.regs.get(rs).unsigned_value))
+
+ def instruction_flw(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+-----+-----+---+
+ |imm[11:0] |rs1 |010 |rd |00001|11 |
+ +-----+-----+-----+-----+-----+-----+-----+---+
+
+ :Format:
+ | flw rd,offset(rs1)
+
+ :Description:
+ | Load a single-precision floating-point value from memory into floating-point register rd.
+
+ :Implementation:
+ | f[rd] = M[x[rs1] + sext(offset)][31:0]
+ """
+ rd, addr = self.parse_mem_ins(ins)
+ self.regs.set_f(rd, self.mmu.read_float(addr))
+
+ def instruction_fsw(self, ins: Instruction):
+ """
+ +-----+-----+-----+-----+-----+--------+-----+---+
+ |31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+ +-----+-----+-----+-----+-----+--------+-----+---+
+ |imm[11:5] |rs2 |rs1 |010 |imm[4:0]|01001|11 |
+ +-----+-----+-----+-----+-----+--------+-----+---+
+
+ :Format:
+ | fsw rs2,offset(rs1)
+
+ :Description:
+ | Store a single-precision value from floating-point register rs2 to memory.
+
+ :Implementation:
+ | M[x[rs1] + sext(offset)] = f[rs2][31:0]
+ """
+ rs, addr = self.parse_mem_ins(ins)
+ val = self.regs.get_f(rs)
+ self.mmu.write(addr, 4, bytearray(val.bytes))
+
+ def parse_rd_rs(self, ins: Instruction) -> Tuple[str, str]:
+ assert len(ins.args) == 2
+ return ins.get_reg(0), ins.get_reg(1)
+
+ def parse_rd_rs_rs(self, ins: Instruction) -> Tuple[str, Float32, Float32]:
+ assert len(ins.args) == 3
+ return (
+ ins.get_reg(0),
+ self.regs.get_f(ins.get_reg(1)),
+ self.regs.get_f(ins.get_reg(2)),
+ )
+
+ def parse_rd_rs_rs_rs(
+ self, ins: Instruction
+ ) -> Tuple[str, Float32, Float32, Float32]:
+ assert len(ins.args) == 4
+ return (
+ ins.get_reg(0),
+ self.regs.get_f(ins.get_reg(1)),
+ self.regs.get_f(ins.get_reg(2)),
+ self.regs.get_f(ins.get_reg(3)),
+ )
diff --git a/riscemu/instructions/__init__.py b/riscemu/instructions/__init__.py
index 30e1f18..510ef2a 100644
--- a/riscemu/instructions/__init__.py
+++ b/riscemu/instructions/__init__.py
@@ -10,6 +10,7 @@ from .instruction_set import InstructionSet, Instruction
from .RV32M import RV32M
from .RV32I import RV32I
from .RV32A import RV32A
+from .RV32F import RV32F
from .RV_Debug import RV_Debug
-InstructionSetDict = {v.__name__: v for v in [RV32I, RV32M, RV32A, RV_Debug]}
+InstructionSetDict = {v.__name__: v for v in [RV32I, RV32M, RV32A, RV32F, RV_Debug]}
diff --git a/riscemu/parser.py b/riscemu/parser.py
index 18cf438..c72986a 100644
--- a/riscemu/parser.py
+++ b/riscemu/parser.py
@@ -139,5 +139,5 @@ class AssemblyFileLoader(ProgramLoader):
return 0.01
@classmethod
- def get_options(cls, argv: List[str]) -> tuple[List[str], T_ParserOpts]:
+ def get_options(cls, argv: List[str]) -> Tuple[List[str], T_ParserOpts]:
return argv, {}
diff --git a/riscemu/registers.py b/riscemu/registers.py
index 338ebde..d11a22a 100644
--- a/riscemu/registers.py
+++ b/riscemu/registers.py
@@ -1,15 +1,15 @@
"""
-RiscEmu (c) 2021-2022 Anton Lydike
+RiscEmu (c) 2023 Anton Lydike
SPDX-License-Identifier: MIT
"""
from collections import defaultdict
+from typing import Union
from .helpers import *
-if typing.TYPE_CHECKING:
- from .types import Int32
+from .types import Int32, Float32
class Registers:
@@ -51,6 +51,9 @@ class Registers:
"a5",
"a6",
"a7",
+ }
+
+ float_regs = {
"ft0",
"ft1",
"ft2",
@@ -82,11 +85,12 @@ class Registers:
}
def __init__(self, infinite_regs: bool = False):
- from .types import Int32
+ self.vals: dict[str, Int32] = defaultdict(UInt32)
+ self.float_vals: dict[str, Float32] = defaultdict(Float32)
- self.vals: defaultdict[str, Int32] = defaultdict(lambda: Int32(0))
self.last_set = None
self.last_read = None
+
self.infinite_regs = infinite_regs
def dump(self, full: bool = False):
@@ -165,8 +169,6 @@ class Registers:
:return: If the operation was successful
"""
- from .types import Int32
-
# remove after refactoring is complete
if not isinstance(val, Int32):
raise RuntimeError(
@@ -208,75 +210,17 @@ class Registers:
self.last_read = reg
return self.vals[reg]
- @staticmethod
- def all_registers():
- """
- Return a list of all valid registers
- :return: The list
- """
- return [
- "zero",
- "ra",
- "sp",
- "gp",
- "tp",
- "s0",
- "fp",
- "t0",
- "t1",
- "t2",
- "t3",
- "t4",
- "t5",
- "t6",
- "s1",
- "s2",
- "s3",
- "s4",
- "s5",
- "s6",
- "s7",
- "s8",
- "s9",
- "s10",
- "s11",
- "a0",
- "a1",
- "a2",
- "a3",
- "a4",
- "a5",
- "a6",
- "a7",
- "ft0",
- "ft1",
- "ft2",
- "ft3",
- "ft4",
- "ft5",
- "ft6",
- "ft7",
- "fs0",
- "fs1",
- "fs2",
- "fs3",
- "fs4",
- "fs5",
- "fs6",
- "fs7",
- "fs8",
- "fs9",
- "fs10",
- "fs11",
- "fa0",
- "fa1",
- "fa2",
- "fa3",
- "fa4",
- "fa5",
- "fa6",
- "fa7",
- ]
+ def get_f(self, reg: str, mark_read: bool = True) -> "Float32":
+ if not self.infinite_regs and reg not in self.float_regs:
+ raise RuntimeError("Invalid float register: {}".format(reg))
+ if mark_read:
+ self.last_read = reg
+ return self.float_vals[reg]
+
+ def set_f(self, reg: str, val: Union[float, "Float32"]):
+ if not self.infinite_regs and reg not in self.float_regs:
+ raise RuntimeError("Invalid float register: {}".format(reg))
+ self.float_vals[reg] = Float32(val)
@staticmethod
def named_registers():
@@ -287,8 +231,9 @@ class Registers:
return ["zero", "ra", "sp", "gp", "tp", "fp"]
def __repr__(self):
- return "".format(
+ return "".format(
+ "float" if self.supports_floats else "nofloat",
"{"
+ ", ".join(self._reg_repr("a{}".format(i), 2, "0x") for i in range(8))
- + "}"
+ + "}",
)
diff --git a/riscemu/types/__init__.py b/riscemu/types/__init__.py
index a509fd5..6188735 100644
--- a/riscemu/types/__init__.py
+++ b/riscemu/types/__init__.py
@@ -13,6 +13,7 @@ NUMBER_SYMBOL_PATTERN = re.compile(r"^\d+[fb]$")
# base classes
from .flags import MemoryFlags
from .int32 import UInt32, Int32
+from .float32 import Float32
from .instruction import Instruction
from .instruction_context import InstructionContext
from .memory_section import MemorySection
@@ -36,4 +37,5 @@ from .exceptions import (
InvalidAllocationException,
InvalidSyscallException,
UnimplementedInstruction,
+ INS_NOT_IMPLEMENTED,
)
diff --git a/riscemu/types/float32.py b/riscemu/types/float32.py
new file mode 100644
index 0000000..5ae2a77
--- /dev/null
+++ b/riscemu/types/float32.py
@@ -0,0 +1,203 @@
+import struct
+from ctypes import c_float
+from typing import Union, Any
+
+bytes_t = bytes
+
+
+class Float32:
+ __slots__ = ("_val",)
+
+ _val: c_float
+
+ @property
+ def value(self) -> float:
+ """
+ The value represented by this float
+ """
+ return self._val.value
+
+ @property
+ def bytes(self) -> bytes:
+ """
+ The values bit representation (as a bytes object)
+ """
+ return struct.pack(" int:
+ """
+ The values bit representation as an int (for easy bit manipulation)
+ """
+ return int.from_bytes(self.bytes, byteorder="little")
+
+ @classmethod
+ def from_bytes(cls, val: Union[int, bytes_t, bytearray]):
+ if isinstance(val, int):
+ val = int.to_bytes(byteorder="little")
+ return Float32(val)
+
+ def __init__(
+ self, val: Union[float, c_float, "Float32", bytes_t, bytearray, int] = 0
+ ):
+ if isinstance(val, (float, int)):
+ self._val = c_float(val)
+ elif isinstance(val, c_float):
+ self._val = c_float(val.value)
+ elif isinstance(val, (bytes, bytearray)):
+ self._val = c_float(struct.unpack(" bool:
+ if isinstance(other, (float, int)):
+ return self.value == other
+ elif isinstance(other, Float32):
+ return self.value == other.value
+ return False
+
+ def __neg__(self):
+ return self.__class__(-self.value)
+
+ def __abs__(self):
+ return self.__class__(abs(self.value))
+
+ def __bytes__(self) -> bytes_t:
+ return self.bytes
+
+ def __repr__(self):
+ return "{}({})".format(self.__class__.__name__, self.value)
+
+ def __str__(self):
+ return str(self.value)
+
+ def __format__(self, format_spec: str):
+ return self.value.__format__(format_spec)
+
+ def __hash__(self):
+ return hash(self.value)
+
+ def __gt__(self, other: Any):
+ if isinstance(other, Float32):
+ other = other.value
+ return self.value > other
+
+ def __lt__(self, other: Any):
+ if isinstance(other, Float32):
+ other = other.value
+ return self.value < other
+
+ def __le__(self, other: Any):
+ if isinstance(other, Float32):
+ other = other.value
+ return self.value <= other
+
+ def __ge__(self, other: Any):
+ if isinstance(other, Float32):
+ other = other.value
+ return self.value >= other
+
+ def __bool__(self):
+ return bool(self.value)
+
+ def __cmp__(self, other: Any):
+ if isinstance(other, Float32):
+ other = other.value
+ return self.value.__cmp__(other)
+
+ def __pow__(self, power, modulo=None):
+ if modulo is not None:
+ raise ValueError("Float32 pow with modulo unsupported")
+ return self.__class__(self.value**power)
+
+ # right handed binary operators
+
+ def __radd__(self, other: Any):
+ return self + other
+
+ def __rsub__(self, other: Any):
+ return self.__class__(other) - self
+
+ def __rmul__(self, other: Any):
+ return self * other
+
+ def __rtruediv__(self, other: Any):
+ return self.__class__(other) // self
+
+ def __rfloordiv__(self, other: Any):
+ return self.__class__(other) // self
+
+ def __rmod__(self, other: Any):
+ return self.__class__(other) % self
+
+ def __rand__(self, other: Any):
+ return self.__class__(other) & self
+
+ def __ror__(self, other: Any):
+ return self.__class__(other) | self
+
+ def __rxor__(self, other: Any):
+ return self.__class__(other) ^ self
+
+ # bytewise operators:
+
+ def __and__(self, other: Union["Float32", float, int]):
+ if isinstance(other, float):
+ other = Float32(other)
+ if isinstance(other, Float32):
+ other = other.bits
+ return self.from_bytes(self.bits & other)
+
+ def __or__(self, other: Union["Float32", float]):
+ if isinstance(other, float):
+ other = Float32(other)
+ if isinstance(other, Float32):
+ other = other.bits
+ return self.from_bytes(self.bits | other)
+
+ def __xor__(self, other: Union["Float32", float]):
+ if isinstance(other, float):
+ other = Float32(other)
+ if isinstance(other, Float32):
+ other = other.bits
+ return self.from_bytes(self.bits ^ other)
+
+ def __lshift__(self, other: int):
+ return self.from_bytes(self.bits << other)
+
+ def __rshift__(self, other: int):
+ return self.from_bytes(self.bits >> other)
diff --git a/riscemu/types/int32.py b/riscemu/types/int32.py
index 211a5dc..02d18fc 100644
--- a/riscemu/types/int32.py
+++ b/riscemu/types/int32.py
@@ -16,7 +16,7 @@ class Int32:
__slots__ = ("_val",)
def __init__(
- self, val: Union[int, c_int32, c_uint32, "Int32", bytes, bytearray] = 0
+ self, val: Union[int, c_int32, c_uint32, "Int32", bytes, bytearray, bool] = 0
):
if isinstance(val, (bytes, bytearray)):
signed = len(val) == 4 and self._type == c_int32
@@ -27,7 +27,7 @@ class Int32:
self._val = val
elif isinstance(val, (c_uint32, c_int32, Int32)):
self._val = self.__class__._type(val.value)
- elif isinstance(val, int):
+ elif isinstance(val, (int, bool)):
self._val = self.__class__._type(val)
else:
raise RuntimeError(
diff --git a/test/test_float32.py b/test/test_float32.py
new file mode 100644
index 0000000..7c29c5a
--- /dev/null
+++ b/test/test_float32.py
@@ -0,0 +1,21 @@
+import math
+
+from riscemu.types import Float32
+
+# pi encoded as a 32bit little endian float
+PI_BYTES_LE = b"\xdb\x0fI@"
+
+
+def test_float_serialization():
+ assert Float32(PI_BYTES_LE) == Float32(math.pi)
+ assert Float32(math.pi).bytes == PI_BYTES_LE
+
+
+def test_random_float_ops():
+ val = Float32(5)
+ assert val**2 == 25
+ assert val // 2 == 2
+ assert val * 3 == 15
+ assert val - 2 == 3
+ assert val * val == 25
+ assert Float32(9) ** 0.5 == 3
diff --git a/test/test_regs.py b/test/test_regs.py
new file mode 100644
index 0000000..be5bfea
--- /dev/null
+++ b/test/test_regs.py
@@ -0,0 +1,26 @@
+import pytest
+
+from riscemu.registers import Registers
+from riscemu.types import Float32
+
+
+def test_float_regs():
+ r = Registers()
+ # uninitialized register is zero
+ assert r.get_f("fs0") == 0
+ # get/set
+ val = Float32(3.14)
+ r.set_f("fs0", val)
+ assert r.get_f("fs0") == val
+
+
+def test_unlimited_regs_works():
+ r = Registers(infinite_regs=True)
+ r.get("infinite")
+ r.get_f("finfinite")
+
+
+def test_unknown_reg_fails():
+ r = Registers(infinite_regs=False)
+ with pytest.raises(RuntimeError, match="Invalid register: az1"):
+ r.get("az1")