From 4004c5ee6d26d86178dc4c114faa5a4169b9898e Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Sun, 27 Mar 2022 23:50:28 +0200 Subject: [PATCH] squashing bugs related to Int32 wrapping and sign extension --- riscemu/instructions/RV32I.py | 24 ++++++++++++------------ riscemu/registers.py | 2 +- riscemu/types/int32.py | 24 ++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/riscemu/instructions/RV32I.py b/riscemu/instructions/RV32I.py index 50d2076..9955743 100644 --- a/riscemu/instructions/RV32I.py +++ b/riscemu/instructions/RV32I.py @@ -24,11 +24,11 @@ class RV32I(InstructionSet): def instruction_lb(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 1))) + self.regs.set(rd, Int32.sign_extend(self.mmu.read(addr.unsigned_value, 1), 8)) def instruction_lh(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 2))) + self.regs.set(rd, Int32.sign_extend(self.mmu.read(addr.unsigned_value, 2), 16)) def instruction_lw(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) @@ -36,23 +36,23 @@ class RV32I(InstructionSet): def instruction_lbu(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.regs.set(rd, UInt32(self.mmu.read(addr.unsigned_value, 1))) + self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 1))) def instruction_lhu(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.regs.set(rd, UInt32(self.mmu.read(addr.unsigned_value, 2))) + self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 2))) def instruction_sb(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.mmu.write(addr.value, 1, self.regs.get(rd).to_bytes(1)) + self.mmu.write(addr.unsigned_value, 1, self.regs.get(rd).to_bytes(1)) def instruction_sh(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.mmu.write(addr.value, 2, self.regs.get(rd).to_bytes(2)) + self.mmu.write(addr.unsigned_value, 2, self.regs.get(rd).to_bytes(2)) def instruction_sw(self, ins: 'Instruction'): rd, addr = self.parse_mem_ins(ins) - self.mmu.write(addr.value, 4, self.regs.get(rd).to_bytes(4)) + self.mmu.write(addr.unsigned_value, 4, self.regs.get(rd).to_bytes(4)) def instruction_sll(self, ins: 'Instruction'): ASSERT_LEN(ins.args, 3) @@ -140,7 +140,7 @@ class RV32I(InstructionSet): def instruction_lui(self, ins: 'Instruction'): ASSERT_LEN(ins.args, 2) reg = ins.get_reg(0) - imm = UInt32(ins.get_imm(1)) << 12 + imm = UInt32(ins.get_imm(1) << 12) self.regs.set(reg, Int32(imm)) def instruction_auipc(self, ins: 'Instruction'): @@ -263,14 +263,14 @@ class RV32I(InstructionSet): ASSERT_LEN(ins.args, 2) reg = ins.get_reg(0) addr = ins.get_imm(1) - self.regs.set(reg, self.pc) + self.regs.set(reg, Int32(self.pc)) self.pc = addr def instruction_jalr(self, ins: 'Instruction'): ASSERT_LEN(ins.args, 2) reg = ins.get_reg(0) addr = ins.get_imm(1) - self.regs.set(reg, self.pc) + self.regs.set(reg, Int32(self.pc)) self.pc = addr def instruction_ret(self, ins: 'Instruction'): @@ -307,13 +307,13 @@ class RV32I(InstructionSet): ASSERT_LEN(ins.args, 2) reg = ins.get_reg(0) immediate = ins.get_imm(1) - self.regs.set(reg, immediate) + self.regs.set(reg, Int32(immediate)) def instruction_la(self, ins: 'Instruction'): ASSERT_LEN(ins.args, 2) reg = ins.get_reg(0) immediate = ins.get_imm(1) - self.regs.set(reg, immediate) + self.regs.set(reg, Int32(immediate)) def instruction_mv(self, ins: 'Instruction'): ASSERT_LEN(ins.args, 2) diff --git a/riscemu/registers.py b/riscemu/registers.py index caa7e36..cce48c4 100644 --- a/riscemu/registers.py +++ b/riscemu/registers.py @@ -109,7 +109,7 @@ class Registers: if mark_set: self.last_set = reg # check 32 bit signed bounds - self.vals[reg] = val + self.vals[reg] = val.unsigned() return True def get(self, reg, mark_read=True) -> 'Int32': diff --git a/riscemu/types/int32.py b/riscemu/types/int32.py index ab0f587..1d61a85 100644 --- a/riscemu/types/int32.py +++ b/riscemu/types/int32.py @@ -16,7 +16,8 @@ class Int32: def __init__(self, val: Union[int, c_int32, c_uint32, 'Int32', bytes, bytearray] = 0): if isinstance(val, (bytes, bytearray)): - self._val = self.__class__._type(int.from_bytes(val, 'little', signed=True)) + signed = len(val) == 4 and self._type == c_int32 + self._val = self.__class__._type(int.from_bytes(val, 'little', signed=signed)) elif isinstance(val, self.__class__._type): self._val = val elif isinstance(val, (c_uint32, c_int32, Int32)): @@ -187,7 +188,7 @@ class Int32: :param bytes: The length of the bytearray :return: A little-endian representation of the contained integer """ - return bytearray(self.unsigned_value.to_bytes(bytes, 'little')) + return bytearray(self.unsigned_value.to_bytes(4, 'little'))[0:bytes] def signed(self) -> 'Int32': """ @@ -225,6 +226,25 @@ class Int32: def __hex__(self): return hex(self.value) + @classmethod + def sign_extend(cls, data: Union[bytes, bytearray, int], bits: int): + """ + Create an instance of Int32 by sign extending :param:bits bits from :param:data + to 32 bits + + :param data: The source data + :param bits: The number of bits in the source data + :return: An instance of Int32, holding the sign-extended value + """ + if isinstance(data, (bytes, bytearray)): + data = int.from_bytes(data, 'little') + sign = data >> (bits - 1) + if sign > 1: + print("overflow in Int32.sext!") + if sign: + data = (data & (2 ** (bits - 1) - 1)) - 2**(bits-1) + return cls(data) + class UInt32(Int32): """