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")