Maor round of bugfixes and incremental improvements

- fixed errors in TextIO and IOModule
 - moved to Int32 and UInt32 based arithmetic
 - added a lot of end-to-end and other tests
assembly-parser-rework
Anton Lydike 3 years ago
parent cd5795bb74
commit 71093fe72f

@ -4,6 +4,9 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/.mypy_cache" />
<excludeFolder url="file://$MODULE_DIR$/riscemu.egg-info" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

@ -17,7 +17,7 @@ from .colors import FMT_CPU, FMT_NONE
from .debug import launch_debug_session from .debug import launch_debug_session
from .exceptions import RiscemuBaseException, LaunchDebuggerException from .exceptions import RiscemuBaseException, LaunchDebuggerException
from .syscall import SyscallInterface, get_syscall_symbols from .syscall import SyscallInterface, get_syscall_symbols
from .types import CPU, ProgramLoader from .types import CPU, ProgramLoader, Int32
from .parser import AssemblyFileLoader from .parser import AssemblyFileLoader
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
@ -107,7 +107,7 @@ class UserModeCPU(CPU):
if not self.mmu.load_section(stack_sec, fixed_position=False): if not self.mmu.load_section(stack_sec, fixed_position=False):
return False return False
self.regs.set('sp', stack_sec.base + stack_sec.size) self.regs.set('sp', Int32(stack_sec.base + stack_sec.size))
return True return True
@classmethod @classmethod

@ -1,22 +1,22 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Optional
from riscemu.types import MemorySection, MemoryFlags, T_RelativeAddress
class IOModule(ABC):
addr: int
size: int
def __init__(self, addr: int, size: int): class IOModule(MemorySection, ABC):
self.addr = addr def __init__(self, name: str, flags: MemoryFlags, size: int, owner: str = 'system', base: int = 0):
self.size = size super(IOModule, self).__init__(name, flags, size, base, owner, None)
@abstractmethod def contains(self, addr, size: int = 0):
def read(self, addr: int, size: int): return self.base <= addr < self.base + self.size and \
pass self.base <= addr + size <= self.base + self.size
@abstractmethod def dump(self, start: T_RelativeAddress, end: Optional[T_RelativeAddress] = None, fmt: str = 'hex',
def write(self, addr: int, data: bytearray, size: int): bytes_per_row: int = 16, rows: int = 10, group: int = 4):
pass print(self)
def contains(self, addr, size: int = 0): def __repr__(self):
return self.addr <= addr < self.addr + self.size and \ return "{}[{}] at 0x{:0X} (size={}bytes, flags={})".format(
self.addr <= addr + size <= self.addr + self.size self.__class__.__name__, self.name, self.base, self.size, self.flags
)

@ -1,70 +1,28 @@
from .IOModule import IOModule from .IOModule import IOModule
from ..priv.Exceptions import InstructionAccessFault from ..priv.Exceptions import InstructionAccessFault
from ..helpers import int_from_bytes from ..types import T_RelativeAddress, Instruction, MemoryFlags, Int32
from threading import Thread
import time
def _window_loop(textIO: 'TextIO'):
try:
import PySimpleGUI as sg
logs = sg.Text(font="monospace")
col = sg.Column([[logs]], size=(640, 400), scrollable=True)
window = sg.Window("TextIO:{:x}".format(textIO.addr), [[col]])
lines = list()
window.finalize()
textIO.set_sg_window(window)
while True:
e, v = window.read()
if e == sg.WINDOW_CLOSED:
window.close()
textIO.set_sg_window(None)
break
if e == 'putlog':
lines.insert(0, v[0])
logs.update(value='\n'.join(lines) + '\n')
col.contents_changed()
except ImportError:
print("[TextIO] window disabled - please install PySimpleGui!")
textIO.set_sg_window(None)
class TextIO(IOModule): class TextIO(IOModule):
def __init__(self, addr: int, buflen: int = 128): def read_ins(self, offset: T_RelativeAddress) -> Instruction:
super(TextIO, self).__init__(addr, buflen + 4) raise InstructionAccessFault(self.base + offset)
def __init__(self, base: int, buflen: int = 128):
super(TextIO, self).__init__('TextIO', MemoryFlags(False, False), buflen + 4, base=base)
self.buff = bytearray(buflen) self.buff = bytearray(buflen)
self.current_line = "" self.current_line = ""
self.sg_window = None
self.start_buffer = list()
self.thread = Thread(target=_window_loop, args=(self,))
self.thread.start()
time.sleep(0.1)
def set_sg_window(self, window):
if self.sg_window is not None and window is not None:
raise Exception("cannot set window twice!")
self.sg_window = window
buff = self.start_buffer
self.start_buffer = None if window is None else list()
for line in buff:
self._present(line)
def read(self, addr: int, size: int) -> bytearray: def read(self, addr: int, size: int) -> bytearray:
raise InstructionAccessFault(addr) raise InstructionAccessFault(self.base + addr)
def write(self, addr: int, data: bytearray, size: int): def write(self, addr: int, size: int, data: bytearray):
if addr == self.addr: if addr == 0:
if size > 4: if size > 4:
raise InstructionAccessFault(addr) raise InstructionAccessFault(addr)
if int_from_bytes(data[0:4]) > 0: if Int32(data) != 0:
self._print() self._print()
return return
buff_start = addr - self.addr - 4 buff_start = addr - 4
self.buff[buff_start:buff_start + size] = data[0:size] self.buff[buff_start:buff_start + size] = data[0:size]
def _print(self): def _print(self):
@ -83,10 +41,4 @@ class TextIO(IOModule):
self.current_line += text self.current_line += text
def _present(self, text: str): def _present(self, text: str):
if self.sg_window is not None: print("[TextIO:{:x}] {}".format(self.base, text))
self.sg_window.write_event_value('putlog', text)
elif self.start_buffer is not None:
self.start_buffer.append(text)
else:
print("[TextIO:{:x}] {}".format(self.addr, text))

@ -4,13 +4,13 @@ RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
""" """
from typing import Dict, List, Optional from typing import Dict, List, Optional, Union
from .colors import * from .colors import *
from .exceptions import InvalidAllocationException, MemoryAccessException from .exceptions import InvalidAllocationException, MemoryAccessException
from .helpers import align_addr, int_from_bytes from .helpers import align_addr
from .types import Instruction, MemorySection, MemoryFlags, T_AbsoluteAddress, \ from .types import Instruction, MemorySection, MemoryFlags, T_AbsoluteAddress, \
Program, InstructionContext Program, InstructionContext, Int32
class MMU: class MMU:
@ -85,7 +85,7 @@ class MMU:
raise RuntimeError("No next instruction available!") raise RuntimeError("No next instruction available!")
return sec.read_ins(addr - sec.base) return sec.read_ins(addr - sec.base)
def read(self, addr: int, size: int) -> bytearray: def read(self, addr: Union[int, Int32], size: int) -> bytearray:
""" """
Read size bytes of memory at addr Read size bytes of memory at addr
@ -93,13 +93,16 @@ class MMU:
:param size: The number of bytes to read :param size: The number of bytes to read
:return: The bytearray at addr :return: The bytearray at addr
""" """
if isinstance(addr, Int32):
breakpoint()
addr = addr.unsigned_value
sec = self.get_sec_containing(addr) sec = self.get_sec_containing(addr)
if sec is None: if sec is None:
print(FMT_MEM + "[MMU] Trying to read data form invalid region at 0x{:x}! ".format(addr) + FMT_NONE) print(FMT_MEM + "[MMU] Trying to read data form invalid region at 0x{:x}! ".format(addr) + FMT_NONE)
raise MemoryAccessException("region is non-initialized!", addr, size, 'read') raise MemoryAccessException("region is non-initialized!", addr, size, 'read')
return sec.read(addr - sec.base, size) return sec.read(addr - sec.base, size)
def write(self, addr: int, size: int, data): def write(self, addr: int, size: int, data: bytearray):
""" """
Write bytes into memory Write bytes into memory
@ -137,32 +140,51 @@ class MMU:
print(FMT_MEM + "[MMU] Lookup for symbol {}:".format(symb) + FMT_NONE) print(FMT_MEM + "[MMU] Lookup for symbol {}:".format(symb) + FMT_NONE)
if symb in self.global_symbols: if symb in self.global_symbols:
print(" Found global symbol {}: 0x{:X}".format(symb, self.global_symbols[symb])) print(" Found global symbol {}: 0x{:X}".format(symb, self.global_symbols[symb]))
for section in self.sections: for bin in self.programs:
if symb in section.context.labels: if symb in bin.context.labels:
print(" Found local labels {}: 0x{:X} in {}".format(symb, section.context.labels[symb], section.name)) print(" Found local labels {}: 0x{:X} in {}".format(symb, bin.context.labels[symb], bin.name))
def read_int(self, addr: int) -> int: def read_int(self, addr: int) -> Int32:
return int_from_bytes(self.read(addr, 4)) return Int32(self.read(addr, 4))
def translate_address(self, address: T_AbsoluteAddress) -> str: def translate_address(self, address: T_AbsoluteAddress) -> str:
# FIXME: proper implementation using the debug info sec = self.get_sec_containing(address)
return str(address) if not sec:
return "unknown at 0x{:0x}".format(address)
bin = self.get_bin_containing(address)
secs = set(sec.name for sec in bin.sections) if bin else []
def key(x):
name, val = x
if name in secs or val > address:
return float('inf')
return address - val
name, val = min(sec.context.labels.items(), key=key, default=('.empty', None))
if val is None:
return "unknown at 0x{:0x}".format(address)
return str('{}:{} at {} (0x{:0x}) + 0x{:0x}'.format(
sec.owner, sec.name, name, val, address - val
))
def has_continous_free_region(self, start: int, end: int) -> bool: def has_continous_free_region(self, start: int, end: int) -> bool:
# if we have no sections we are all good # if we have no sections we are all good
if len(self.sections) == 0: if len(self.sections) == 0:
return True return True
# if the last section is located before the start we are also good # if the last section is located before the start we are also good
if start > self.sections[-1].base + self.sections[-1].size: if start >= self.sections[-1].base + self.sections[-1].size:
return True return True
for sec in self.sections: for sec in self.sections:
# skip all sections that end before the required start point # skip all sections that end before the required start point
if sec.base + sec.size < start: if sec.base + sec.size <= start:
continue continue
# we now have the first section that doesn't end **before** the start point # we now have the first section that doesn't end **before** the start point
# if this section starts after the specified end, we are good # if this section starts after the specified end, we are good
if sec.base > end: if sec.base >= end:
return True return True
# otherwise we can't continue # otherwise we can't continue
return False return False
@ -230,7 +252,8 @@ class MMU:
return self.sections[-1].base + self.sections[-1].size return self.sections[-1].base + self.sections[-1].size
def __repr__(self): def __repr__(self):
return "MMU(\n\t{}\n)".format( return "{}(\n\t{}\n)".format(
self.__class__.__name__,
"\n\t".join(repr(x) for x in self.programs) "\n\t".join(repr(x) for x in self.programs)
) )
@ -241,3 +264,16 @@ class MMU:
return sec.context return sec.context
return InstructionContext() return InstructionContext()
def report_addr(self, addr: T_AbsoluteAddress):
sec = self.get_sec_containing(addr)
if not sec:
print("addr is in no section!")
return
owner = [b for b in self.programs if b.name == sec.owner]
if owner:
print("owned by: {}".format(owner[0]))
print("{}: 0x{:0x} + 0x{:0x}".format(name, val, addr - val))

@ -25,4 +25,4 @@ from .parser import tokenize, parse_tokens, AssemblyFileLoader
__author__ = "Anton Lydike <Anton@Lydike.com>" __author__ = "Anton Lydike <Anton@Lydike.com>"
__copyright__ = "Copyright 2021 Anton Lydike" __copyright__ = "Copyright 2021 Anton Lydike"
__version__ = '1.0.0' __version__ = '2.0.0a1'

@ -1,13 +1,13 @@
from typing import Optional, Tuple, Union, List
from enum import Enum, auto from enum import Enum, auto
from typing import List
from typing import Optional, Tuple, Union from typing import Optional, Tuple, Union
from .helpers import parse_numeric_argument, align_addr, int_to_bytes, get_section_base_name from .base import BinaryDataMemorySection, InstructionMemorySection
from .types import Program, T_RelativeAddress, InstructionContext, Instruction
from .colors import FMT_PARSE, FMT_NONE from .colors import FMT_PARSE, FMT_NONE
from .exceptions import ParseException, ASSERT_LEN, ASSERT_NOT_NULL from .exceptions import ParseException, ASSERT_LEN
from .helpers import parse_numeric_argument, align_addr, get_section_base_name
from .tokenizer import Token from .tokenizer import Token
from .base import BinaryDataMemorySection, InstructionMemorySection from .types import Program, T_RelativeAddress, InstructionContext, Instruction, UInt32, Int32
INSTRUCTION_SECTION_NAMES = ('.text', '.init', '.fini') INSTRUCTION_SECTION_NAMES = ('.text', '.init', '.fini')
""" """
@ -96,7 +96,6 @@ class ParseContext:
if is_relative: if is_relative:
self.program.relative_labels.add(name) self.program.relative_labels.add(name)
def __repr__(self): def __repr__(self):
return "{}(\n\tsetion={},\n\tprogram={}\n)".format( return "{}(\n\tsetion={},\n\tprogram={}\n)".format(
self.__class__.__name__, self.section, self.program self.__class__.__name__, self.section, self.program
@ -176,7 +175,7 @@ class AssemblerDirectives:
if content is None: if content is None:
content = bytearray(size) content = bytearray(size)
if isinstance(content, int): if isinstance(content, int):
content = int_to_bytes(content, size, unsigned) content = bytearray(content.to_bytes(size, 'little', signed=not unsigned))
context.section.data += content context.section.data += content

@ -24,7 +24,7 @@ def format_ins(ins: int, name: str, fmt: str = 'int'):
return name return name
if opcode in (0x8, 0x0): if opcode in (0x8, 0x0):
r1, r2, imm = decoder(ins) r1, r2, imm = decoder(ins)
return f"{name:<7} {r1}, {imm}({r2})" return f"{name:<7} {RISCV_REGS[r1]}, {imm}({RISCV_REGS[r2]})"
elif decoder in (decode_i, decode_i_unsigned, decode_b, decode_i_shamt, decode_s): elif decoder in (decode_i, decode_i_unsigned, decode_b, decode_i_shamt, decode_s):
r1, r2, imm = decoder(ins) r1, r2, imm = decoder(ins)
r1, r2 = RISCV_REGS[r1], RISCV_REGS[r2] r1, r2 = RISCV_REGS[r1], RISCV_REGS[r2]

@ -5,9 +5,11 @@ SPDX-License-Identifier: MIT
""" """
from math import log10, ceil from math import log10, ceil
from .exceptions import *
from typing import Iterable, Iterator, TypeVar, Generic, List, Optional from typing import Iterable, Iterator, TypeVar, Generic, List, Optional
from .exceptions import *
import types
def align_addr(addr: int, to_bytes: int = 8) -> int: def align_addr(addr: int, to_bytes: int = 8) -> int:
""" """
@ -28,40 +30,6 @@ def parse_numeric_argument(arg: str) -> int:
raise ParseException('Invalid immediate argument \"{}\", maybe missing symbol?'.format(arg), (arg, ex)) raise ParseException('Invalid immediate argument \"{}\", maybe missing symbol?'.format(arg), (arg, ex))
def int_to_bytes(val, bytes=4, unsigned=False) -> bytearray:
"""
int -> byte (two's complement)
"""
if unsigned and val < 0:
raise NumberFormatException("unsigned negative number!")
return bytearray(to_unsigned(val, bytes).to_bytes(bytes, 'little'))
def int_from_bytes(bytes, unsigned=False) -> int:
"""
byte -> int (two's complement)
"""
num = int.from_bytes(bytes, 'little')
if unsigned:
return num
return to_signed(num, len(bytes))
def to_unsigned(num: int, bytes=4) -> int:
if num < 0:
return (2 ** (bytes * 8)) + num
return num
def to_signed(num: int, bytes=4) -> int:
if num >> (bytes * 8 - 1):
return num - 2 ** (8 * bytes)
return num
def create_chunks(my_list, chunk_size): def create_chunks(my_list, chunk_size):
"""Split a list like [a,b,c,d,e,f,g,h,i,j,k,l,m] into e.g. [[a,b,c,d],[e,f,g,h],[i,j,k,l],[m]]""" """Split a list like [a,b,c,d,e,f,g,h,i,j,k,l,m] into e.g. [[a,b,c,d],[e,f,g,h],[i,j,k,l],[m]]"""
return [my_list[i:i + chunk_size] for i in range(0, len(my_list), chunk_size)] return [my_list[i:i + chunk_size] for i in range(0, len(my_list), chunk_size)]
@ -87,10 +55,10 @@ def format_bytes(byte_arr: bytearray, fmt: str, group: int = 1, highlight: int =
return highlight_in_list(['0x{}'.format(ch.hex()) for ch in chunks], highlight) return highlight_in_list(['0x{}'.format(ch.hex()) for ch in chunks], highlight)
if fmt == 'int': if fmt == 'int':
spc = str(ceil(log10(2 ** (group * 8 - 1))) + 1) spc = str(ceil(log10(2 ** (group * 8 - 1))) + 1)
return highlight_in_list([('{:0' + spc + 'd}').format(int_from_bytes(ch)) for ch in chunks], highlight) return highlight_in_list([('{:0' + spc + 'd}').format(types.Int32(ch)) for ch in chunks], highlight)
if fmt == 'uint': if fmt == 'uint':
spc = str(ceil(log10(2 ** (group * 8)))) spc = str(ceil(log10(2 ** (group * 8))))
return highlight_in_list([('{:0' + spc + 'd}').format(int_from_bytes(ch, unsigned=True)) for ch in chunks], return highlight_in_list([('{:0' + spc + 'd}').format(types.UInt32(ch)) for ch in chunks],
highlight) highlight)
if fmt == 'ascii': if fmt == 'ascii':
return "".join(repr(chr(b))[1:-1] for b in byte_arr) return "".join(repr(chr(b))[1:-1] for b in byte_arr)

@ -1,6 +1,6 @@
from .instruction_set import InstructionSet, Instruction from .instruction_set import InstructionSet, Instruction
from ..exceptions import INS_NOT_IMPLEMENTED from ..exceptions import INS_NOT_IMPLEMENTED
from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed from ..types import Int32, UInt32
class RV32A(InstructionSet): class RV32A(InstructionSet):
@ -19,60 +19,60 @@ class RV32A(InstructionSet):
def instruction_amoswap_w(self, ins: 'Instruction'): def instruction_amoswap_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
if dest == 'zero': if dest == 'zero':
self.mmu.write(addr, int_to_bytes(addr, 4)) self.mmu.write(addr, val.to_bytes())
else: else:
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(val, 4)) self.mmu.write(addr, val.to_bytes())
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amoadd_w(self, ins: 'Instruction'): def instruction_amoadd_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(old + val, 4)) self.mmu.write(addr, (old + val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amoand_w(self, ins: 'Instruction'): def instruction_amoand_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(old & val, 4)) self.mmu.write(addr, (old & val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amoor_w(self, ins: 'Instruction'): def instruction_amoor_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(old | val, 4)) self.mmu.write(addr, (old | val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amoxor_w(self, ins: 'Instruction'): def instruction_amoxor_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(old ^ val, 4)) self.mmu.write(addr, (old ^ val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amomax_w(self, ins: 'Instruction'): def instruction_amomax_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(max(old, val), 4)) self.mmu.write(addr, max(old, val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amomaxu_w(self, ins: 'Instruction'): def instruction_amomaxu_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) val: UInt32
val = to_unsigned(val) dest, addr, val = self.parse_rd_rs_rs(ins, signed=False)
old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True) old = UInt32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(to_signed(max(old, val)), 4)) self.mmu.write(addr, max(old, val).to_bytes())
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amomin_w(self, ins: 'Instruction'): def instruction_amomin_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) dest, addr, val = self.parse_rd_rs_rs(ins)
old = int_from_bytes(self.mmu.read(addr, 4)) old = Int32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(min(old, val), 4)) self.mmu.write(addr, min(old, val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)
def instruction_amominu_w(self, ins: 'Instruction'): def instruction_amominu_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins) val: UInt32
val = to_unsigned(val) dest, addr, val = self.parse_rd_rs_rs(ins, signed=False)
old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True) old = UInt32(self.mmu.read(addr, 4))
self.mmu.write(addr, int_to_bytes(to_signed(min(old, val)), 4)) self.mmu.write(addr, min(old, val).to_bytes(4))
self.regs.set(dest, old) self.regs.set(dest, old)

@ -7,12 +7,11 @@ SPDX-License-Identifier: MIT
from .instruction_set import * from .instruction_set import *
from ..CPU import UserModeCPU from ..CPU import UserModeCPU
from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed
from ..colors import FMT_DEBUG, FMT_NONE from ..colors import FMT_DEBUG, FMT_NONE
from ..debug import launch_debug_session from ..debug import launch_debug_session
from ..exceptions import LaunchDebuggerException from ..exceptions import LaunchDebuggerException
from ..syscall import Syscall from ..syscall import Syscall
from ..types import Instruction from ..types import Instruction, Int32, UInt32
class RV32I(InstructionSet): class RV32I(InstructionSet):
@ -26,35 +25,35 @@ class RV32I(InstructionSet):
def instruction_lb(self, ins: 'Instruction'): def instruction_lb(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1))) self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 1)))
def instruction_lh(self, ins: 'Instruction'): def instruction_lh(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2))) self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 2)))
def instruction_lw(self, ins: 'Instruction'): def instruction_lw(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 4))) self.regs.set(rd, Int32(self.mmu.read(addr.unsigned_value, 4)))
def instruction_lbu(self, ins: 'Instruction'): def instruction_lbu(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1), unsigned=True)) self.regs.set(rd, UInt32(self.mmu.read(addr.unsigned_value, 1)))
def instruction_lhu(self, ins: 'Instruction'): def instruction_lhu(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2), unsigned=True)) self.regs.set(rd, UInt32(self.mmu.read(addr.unsigned_value, 2)))
def instruction_sb(self, ins: 'Instruction'): def instruction_sb(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 1, int_to_bytes(self.regs.get(rd), 1)) self.mmu.write(addr.value, 1, self.regs.get(rd).to_bytes(1))
def instruction_sh(self, ins: 'Instruction'): def instruction_sh(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 2, int_to_bytes(self.regs.get(rd), 2)) self.mmu.write(addr.value, 2, self.regs.get(rd).to_bytes(2))
def instruction_sw(self, ins: 'Instruction'): def instruction_sw(self, ins: 'Instruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 4, int_to_bytes(self.regs.get(rd), 4)) self.mmu.write(addr.value, 4, self.regs.get(rd).to_bytes(4))
def instruction_sll(self, ins: 'Instruction'): def instruction_sll(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 3) ASSERT_LEN(ins.args, 3)
@ -63,7 +62,7 @@ class RV32I(InstructionSet):
src2 = ins.get_reg(2) src2 = ins.get_reg(2)
self.regs.set( self.regs.set(
dst, dst,
to_signed(to_unsigned(self.regs.get(src1)) << (self.regs.get(src2) & 0b11111)) self.regs.get(src1) << (self.regs.get(src2) & 0b11111)
) )
def instruction_slli(self, ins: 'Instruction'): def instruction_slli(self, ins: 'Instruction'):
@ -73,7 +72,7 @@ class RV32I(InstructionSet):
imm = ins.get_imm(2) imm = ins.get_imm(2)
self.regs.set( self.regs.set(
dst, dst,
to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111)) self.regs.get(src1) << (imm & 0b11111)
) )
def instruction_srl(self, ins: 'Instruction'): def instruction_srl(self, ins: 'Instruction'):
@ -83,7 +82,7 @@ class RV32I(InstructionSet):
src2 = ins.get_reg(2) src2 = ins.get_reg(2)
self.regs.set( self.regs.set(
dst, dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (self.regs.get(src2) & 0b11111)) self.regs.get(src1).shift_right_logical(self.regs.get(src2) & 0b11111)
) )
def instruction_srli(self, ins: 'Instruction'): def instruction_srli(self, ins: 'Instruction'):
@ -93,7 +92,7 @@ class RV32I(InstructionSet):
imm = ins.get_imm(2) imm = ins.get_imm(2)
self.regs.set( self.regs.set(
dst, dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (imm & 0b11111)) self.regs.get(src1).shift_right_logical(imm & 0b11111)
) )
def instruction_sra(self, ins: 'Instruction'): def instruction_sra(self, ins: 'Instruction'):
@ -142,14 +141,14 @@ class RV32I(InstructionSet):
def instruction_lui(self, ins: 'Instruction'): def instruction_lui(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 2) ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0) reg = ins.get_reg(0)
imm = ins.get_imm(1) imm = UInt32(ins.get_imm(1)) << 12
self.regs.set(reg, imm << 12) self.regs.set(reg, Int32(imm))
def instruction_auipc(self, ins: 'Instruction'): def instruction_auipc(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 2) ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0) reg = ins.get_reg(0)
imm = to_unsigned(ins.get_imm(1)) imm = UInt32(ins.get_imm(1) << 12)
self.regs.set(reg, self.pc + (imm << 12)) self.regs.set(reg, imm.signed() + self.pc)
def instruction_xor(self, ins: 'Instruction'): def instruction_xor(self, ins: 'Instruction'):
rd, rs1, rs2 = self.parse_rd_rs_rs(ins) rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
@ -197,59 +196,59 @@ class RV32I(InstructionSet):
rd, rs1, rs2 = self.parse_rd_rs_rs(ins) rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set( self.regs.set(
rd, rd,
int(rs1 < rs2) Int32(int(rs1 < rs2))
) )
def instruction_slti(self, ins: 'Instruction'): def instruction_slti(self, ins: 'Instruction'):
rd, rs1, imm = self.parse_rd_rs_imm(ins) rd, rs1, imm = self.parse_rd_rs_imm(ins)
self.regs.set( self.regs.set(
rd, rd,
int(rs1 < imm) Int32(int(rs1 < imm))
) )
def instruction_sltu(self, ins: 'Instruction'): def instruction_sltu(self, ins: 'Instruction'):
dst, rs1, rs2 = self.parse_rd_rs_rs(ins, signed=False) dst, rs1, rs2 = self.parse_rd_rs_rs(ins, signed=False)
self.regs.set( self.regs.set(
dst, dst,
int(rs1 < rs2) Int32(int(rs1 < rs2))
) )
def instruction_sltiu(self, ins: 'Instruction'): def instruction_sltiu(self, ins: 'Instruction'):
dst, rs1, imm = self.parse_rd_rs_imm(ins, signed=False) dst, rs1, imm = self.parse_rd_rs_imm(ins, signed=False)
self.regs.set( self.regs.set(
dst, dst,
int(rs1 < imm) Int32(int(rs1 < imm))
) )
def instruction_beq(self, ins: 'Instruction'): def instruction_beq(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 == rs2: if rs1 == rs2:
self.pc = dst self.pc = dst.unsigned_value
def instruction_bne(self, ins: 'Instruction'): def instruction_bne(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 != rs2: if rs1 != rs2:
self.pc = dst self.pc = dst.unsigned_value
def instruction_blt(self, ins: 'Instruction'): def instruction_blt(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 < rs2: if rs1 < rs2:
self.pc = dst self.pc = dst.unsigned_value
def instruction_bge(self, ins: 'Instruction'): def instruction_bge(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 >= rs2: if rs1 >= rs2:
self.pc = dst self.pc = dst.unsigned_value
def instruction_bltu(self, ins: 'Instruction'): def instruction_bltu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False) rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 < rs2: if rs1 < rs2:
self.pc = dst self.pc = dst.unsigned_value
def instruction_bgeu(self, ins: 'Instruction'): def instruction_bgeu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False) rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 >= rs2: if rs1 >= rs2:
self.pc = dst self.pc = dst.unsigned_value
# technically deprecated # technically deprecated
def instruction_j(self, ins: 'Instruction'): def instruction_j(self, ins: 'Instruction'):
@ -277,7 +276,7 @@ class RV32I(InstructionSet):
def instruction_ret(self, ins: 'Instruction'): def instruction_ret(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 0) ASSERT_LEN(ins.args, 0)
self.pc = self.regs.get('ra') self.pc = self.regs.get('ra').value
def instruction_ecall(self, ins: 'Instruction'): def instruction_ecall(self, ins: 'Instruction'):
self.instruction_scall(ins) self.instruction_scall(ins)

@ -8,9 +8,8 @@ from typing import Tuple, Callable, Dict
from abc import ABC from abc import ABC
from ..CPU import CPU from ..CPU import CPU
from ..helpers import to_unsigned
from ..exceptions import ASSERT_LEN, ASSERT_IN from ..exceptions import ASSERT_LEN, ASSERT_IN
from ..types import Instruction from ..types import Instruction, Int32, UInt32
class InstructionSet(ABC): class InstructionSet(ABC):
@ -52,7 +51,7 @@ class InstructionSet(ABC):
if member.startswith('instruction_'): if member.startswith('instruction_'):
yield member[12:].replace('_', '.'), getattr(self, member) yield member[12:].replace('_', '.'), getattr(self, member)
def parse_mem_ins(self, ins: 'Instruction') -> Tuple[str, int]: def parse_mem_ins(self, ins: 'Instruction') -> Tuple[str, Int32]:
""" """
parses both rd, rs, imm and rd, imm(rs) argument format and returns (rd, imm+rs1) parses both rd, rs, imm and rd, imm(rs) argument format and returns (rd, imm+rs1)
(so a register and address tuple for memory instructions) (so a register and address tuple for memory instructions)
@ -70,7 +69,7 @@ class InstructionSet(ABC):
rd = ins.get_reg(0) rd = ins.get_reg(0)
return rd, rs + imm return rd, rs + imm
def parse_rd_rs_rs(self, ins: 'Instruction', signed=True) -> Tuple[str, int, int]: def parse_rd_rs_rs(self, ins: 'Instruction', signed=True) -> Tuple[str, Int32, Int32]:
""" """
Assumes the command is in <name> rd, rs1, rs2 format Assumes the command is in <name> rd, rs1, rs2 format
Returns the name of rd, and the values in rs1 and rs2 Returns the name of rd, and the values in rs1 and rs2
@ -82,10 +81,10 @@ class InstructionSet(ABC):
self.get_reg_content(ins, 2) self.get_reg_content(ins, 2)
else: else:
return ins.get_reg(0), \ return ins.get_reg(0), \
to_unsigned(self.get_reg_content(ins, 1)), \ UInt32(self.get_reg_content(ins, 1)), \
to_unsigned(self.get_reg_content(ins, 2)) UInt32(self.get_reg_content(ins, 2))
def parse_rd_rs_imm(self, ins: 'Instruction', signed=True) -> Tuple[str, int, int]: def parse_rd_rs_imm(self, ins: 'Instruction', signed=True) -> Tuple[str, Int32, Int32]:
""" """
Assumes the command is in <name> rd, rs, imm format Assumes the command is in <name> rd, rs, imm format
Returns the name of rd, the value in rs and the immediate imm Returns the name of rd, the value in rs and the immediate imm
@ -93,28 +92,28 @@ class InstructionSet(ABC):
ASSERT_LEN(ins.args, 3) ASSERT_LEN(ins.args, 3)
if signed: if signed:
return ins.get_reg(0), \ return ins.get_reg(0), \
self.get_reg_content(ins, 1), \ Int32(self.get_reg_content(ins, 1)), \
ins.get_imm(2) Int32(ins.get_imm(2))
else: else:
return ins.get_reg(0), \ return ins.get_reg(0), \
to_unsigned(self.get_reg_content(ins, 1)), \ UInt32(self.get_reg_content(ins, 1)), \
to_unsigned(ins.get_imm(2)) UInt32(ins.get_imm(2))
def parse_rs_rs_imm(self, ins: 'Instruction', signed=True) -> Tuple[int, int, int]: def parse_rs_rs_imm(self, ins: 'Instruction', signed=True) -> Tuple[Int32, Int32, Int32]:
""" """
Assumes the command is in <name> rs1, rs2, imm format Assumes the command is in <name> rs1, rs2, imm format
Returns the values in rs1, rs2 and the immediate imm Returns the values in rs1, rs2 and the immediate imm
""" """
if signed: if signed:
return self.get_reg_content(ins, 0), \ return Int32(self.get_reg_content(ins, 0)), \
self.get_reg_content(ins, 1), \ Int32(self.get_reg_content(ins, 1)), \
ins.get_imm(2) Int32(ins.get_imm(2))
else: else:
return to_unsigned(self.get_reg_content(ins, 0)), \ return UInt32(self.get_reg_content(ins, 0)), \
to_unsigned(self.get_reg_content(ins, 1)), \ UInt32(self.get_reg_content(ins, 1)), \
to_unsigned(ins.get_imm(2)) UInt32(ins.get_imm(2))
def get_reg_content(self, ins: 'Instruction', ind: int) -> int: def get_reg_content(self, ins: 'Instruction', ind: int) -> Int32:
""" """
get the register name from ins and then return the register contents get the register name from ins and then return the register contents
""" """

@ -2,49 +2,49 @@ from typing import Dict, Union, Callable, Optional
from collections import defaultdict from collections import defaultdict
from .privmodes import PrivModes from .privmodes import PrivModes
from .Exceptions import InstructionAccessFault from .Exceptions import InstructionAccessFault
from ..helpers import to_signed
from ..colors import FMT_CSR, FMT_NONE from ..colors import FMT_CSR, FMT_NONE
from .CSRConsts import CSR_NAME_TO_ADDR, MSTATUS_LEN_2, MSTATUS_OFFSETS from .CSRConsts import CSR_NAME_TO_ADDR, MSTATUS_LEN_2, MSTATUS_OFFSETS
from ..types import UInt32
class CSR: class CSR:
""" """
This holds all Control and Status Registers (CSR) This holds all Control and Status Registers (CSR)
""" """
regs: Dict[int, int] regs: Dict[int, UInt32]
""" """
All Control and Status Registers are stored here All Control and Status Registers are stored here
""" """
virtual_regs: Dict[int, Callable[[], int]] virtual_regs: Dict[int, Callable[[], UInt32]]
""" """
list of virtual CSR registers, with values computed on read list of virtual CSR registers, with values computed on read
""" """
listeners: Dict[int, Callable[[int, int], None]] listeners: Dict[int, Callable[[UInt32, UInt32], None]]
mstatus_cache: Dict[str, int] mstatus_cache: Dict[str, UInt32]
mstatus_cache_dirty = True mstatus_cache_dirty = True
def __init__(self): def __init__(self):
self.regs = defaultdict(lambda: 0) self.regs = defaultdict(lambda: UInt32(0))
self.listeners = defaultdict(lambda: (lambda x, y: None)) self.listeners = defaultdict(lambda: (lambda x, y: None))
self.virtual_regs = dict() self.virtual_regs = dict()
self.mstatus_cache = dict() self.mstatus_cache = dict()
# TODO: implement write masks (bitmasks which control writeable bits in registers # TODO: implement write masks (bitmasks which control writeable bits in registers
def set(self, addr: Union[str, int], val: int): def set(self, addr: Union[str, int], val: Union[int, UInt32]):
addr = self._name_to_addr(addr) addr = self._name_to_addr(addr)
if addr is None: if addr is None:
return return
val = to_signed(val) val = UInt32(val)
self.listeners[addr](self.regs[addr], val) self.listeners[addr](self.regs[addr], val)
if addr == 0x300: if addr == 0x300:
self.mstatus_cache_dirty = True self.mstatus_cache_dirty = True
self.regs[addr] = val self.regs[addr] = val
def get(self, addr: Union[str, int]) -> int: def get(self, addr: Union[str, int]) -> UInt32:
addr = self._name_to_addr(addr) addr = self._name_to_addr(addr)
if addr is None: if addr is None:
raise RuntimeError(f"Invalid CSR name: {addr}!") raise RuntimeError(f"Invalid CSR name: {addr}!")
@ -52,7 +52,7 @@ class CSR:
return self.virtual_regs[addr]() return self.virtual_regs[addr]()
return self.regs[addr] return self.regs[addr]
def set_listener(self, addr: Union[str, int], listener: Callable[[int, int], None]): def set_listener(self, addr: Union[str, int], listener: Callable[[UInt32, UInt32], None]):
addr = self._name_to_addr(addr) addr = self._name_to_addr(addr)
if addr is None: if addr is None:
print("unknown csr address name: {}".format(addr)) print("unknown csr address name: {}".format(addr))
@ -60,7 +60,7 @@ class CSR:
self.listeners[addr] = listener self.listeners[addr] = listener
# mstatus properties # mstatus properties
def set_mstatus(self, name: str, val: int): def set_mstatus(self, name: str, val: UInt32):
""" """
Set mstatus bits using this helper. mstatus is a 32 bit register, holding various machine status flags Set mstatus bits using this helper. mstatus is a 32 bit register, holding various machine status flags
Setting them by hand is super painful, so this helper allows you to set specific bits. Setting them by hand is super painful, so this helper allows you to set specific bits.
@ -79,7 +79,7 @@ class CSR:
new_val = erased | (val << off) new_val = erased | (val << off)
self.set('mstatus', new_val) self.set('mstatus', new_val)
def get_mstatus(self, name) -> int: def get_mstatus(self, name) -> UInt32:
if not self.mstatus_cache_dirty and name in self.mstatus_cache: if not self.mstatus_cache_dirty and name in self.mstatus_cache:
return self.mstatus_cache[name] return self.mstatus_cache[name]
@ -94,7 +94,7 @@ class CSR:
return val return val
def callback(self, addr: Union[str, int]): def callback(self, addr: Union[str, int]):
def inner(func: Callable[[int, int], None]): def inner(func: Callable[[UInt32, UInt32], None]):
self.set_listener(addr, func) self.set_listener(addr, func)
return func return func
@ -121,7 +121,7 @@ class CSR:
if addr is None: if addr is None:
print("unknown csr address name: {}".format(addr)) print("unknown csr address name: {}".format(addr))
def inner(func: Callable[[], int]): def inner(func: Callable[[], UInt32]):
self.virtual_regs[addr] = func self.virtual_regs[addr] = func
return func return func

@ -81,12 +81,14 @@ class ElfBinaryFileLoader(ProgramLoader):
) )
def _parse_symtab(self, symtab: 'SymbolTableSection'): def _parse_symtab(self, symtab: 'SymbolTableSection'):
from elftools.elf.enums import ENUM_ST_VISIBILITY
for sym in symtab.iter_symbols(): for sym in symtab.iter_symbols():
if not sym.name: if not sym.name:
continue continue
self.program.context.labels[sym.name] = sym.entry.st_value self.program.context.labels[sym.name] = sym.entry.st_value
# check if it has st_visibility bit set # check if it has st_visibility bit set
if sym.entry.st_shndx == 1: # STB_GLOBAL = 1 if sym.entry.st_info.bind == 'STB_GLOBAL':
self.program.global_labels.add(sym.name) self.program.global_labels.add(sym.name)
print(FMT_PARSE + "LOADED GLOBAL SYMBOL {}: {}".format(sym.name, sym.entry.st_value) + FMT_NONE) print(FMT_PARSE + "LOADED GLOBAL SYMBOL {}: {}".format(sym.name, sym.entry.st_value) + FMT_NONE)

@ -7,6 +7,7 @@ import typing
from .. import RiscemuBaseException from .. import RiscemuBaseException
from ..colors import FMT_PARSE, FMT_NONE from ..colors import FMT_PARSE, FMT_NONE
from ..types import UInt32
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .ElfLoader import ElfInstruction from .ElfLoader import ElfInstruction
@ -29,7 +30,7 @@ class CpuTrap(BaseException):
The isInterrupt bit in the mstatus register The isInterrupt bit in the mstatus register
""" """
mtval: int mtval: UInt32
""" """
contents of the mtval register contents of the mtval register
""" """
@ -47,7 +48,7 @@ class CpuTrap(BaseException):
def __init__(self, code: int, mtval, type: CpuTrapType, priv: PrivModes = PrivModes.MACHINE): def __init__(self, code: int, mtval, type: CpuTrapType, priv: PrivModes = PrivModes.MACHINE):
self.interrupt = 0 if type == CpuTrapType.EXCEPTION else 1 self.interrupt = 0 if type == CpuTrapType.EXCEPTION else 1
self.code = code self.code = code
self.mtval = mtval self.mtval = UInt32(mtval)
self.priv = priv self.priv = priv
self.type = type self.type = type

@ -26,7 +26,7 @@ class MemoryImageLoader(ProgramLoader):
return argv, {} return argv, {}
def parse(self) -> Iterable[Program]: def parse(self) -> Iterable[Program]:
if self.options.get('debug', False): if 'debug' not in self.options:
yield self.parse_no_debug() yield self.parse_no_debug()
return return
@ -43,11 +43,11 @@ class MemoryImageLoader(ProgramLoader):
if program.base is None: if program.base is None:
program.base = start program.base = start
in_code_sec = get_section_base_name(sec_name) in INSTRUCTION_SECTION_NAMES #in_code_sec = get_section_base_name(sec_name) in INSTRUCTION_SECTION_NAMES
program.add_section( program.add_section(
ElfMemorySection( ElfMemorySection(
data[start:start+size], sec_name, program.context, data[start:start+size], sec_name, program.context,
name, start, MemoryFlags(in_code_sec, in_code_sec) name, start, MemoryFlags(False, True)
) )
) )
@ -64,12 +64,12 @@ class MemoryImageLoader(ProgramLoader):
p = Program(self.filename) p = Program(self.filename)
p.add_section(ElfMemorySection( p.add_section(ElfMemorySection(
bytearray(data), 'memory image contents', p.context, p.name, 0, MemoryFlags(False, True) bytearray(data), '.text', p.context, p.name, 0, MemoryFlags(False, True)
)) ))
return p return p
@classmethod @classmethod
def instantiate(cls, source_path: str, options: T_ParserOpts) -> 'ProgramLoader': def instantiate(cls, source_path: str, options: T_ParserOpts) -> 'ProgramLoader':
if os.path.exists(source_path + '.dbg'): if os.path.isfile(source_path + '.dbg'):
return MemoryImageLoader(source_path, dict(**options, debug=source_path + '.dbg')) return MemoryImageLoader(source_path, dict(**options, debug=source_path + '.dbg'))
return MemoryImageLoader(source_path, options) return MemoryImageLoader(source_path, options)

@ -14,8 +14,9 @@ from .ImageLoader import MemoryImageLoader
from .PrivMMU import PrivMMU from .PrivMMU import PrivMMU
from .PrivRV32I import PrivRV32I from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes from .privmodes import PrivModes
from ..IO.TextIO import TextIO
from ..instructions import RV32A, RV32M from ..instructions import RV32A, RV32M
from ..types import Program from ..types import Program, UInt32
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from riscemu.instructions.instruction_set import InstructionSet from riscemu.instructions.instruction_set import InstructionSet
@ -55,12 +56,16 @@ class PrivCPU(CPU):
self.exit_code = 0 self.exit_code = 0
self._time_start = 0 self._time_start = 0
self._time_timecmp = 0 self._time_timecmp = UInt32(0)
self._time_interrupt_enabled = False self._time_interrupt_enabled = False
# performance counters # performance counters
self._perf_counters = list() self._perf_counters = list()
# add TextIO
io = TextIO(0xFF0000, 64)
self.mmu.load_section(io, True)
# init csr # init csr
self._init_csr() self._init_csr()
@ -105,11 +110,11 @@ class PrivCPU(CPU):
def _init_csr(self): def _init_csr(self):
# set up CSR # set up CSR
self.csr = CSR() self.csr = CSR()
self.csr.set('mhartid', 0) # core id self.csr.set('mhartid', UInt32(0)) # core id
# TODO: set correct value # TODO: set correct value
self.csr.set('mimpid', 0) # implementation id self.csr.set('mimpid', UInt32(0)) # implementation id
# set mxl to 1 (32 bit) and set bits for i and m isa # set mxl to 1 (32 bit) and set bits for i and m isa
self.csr.set('misa', (1 << 30) + (1 << 8) + (1 << 12)) # available ISA self.csr.set('misa', UInt32((1 << 30) + (1 << 8) + (1 << 12))) # available ISA
# CSR write callbacks: # CSR write callbacks:
@ -137,11 +142,11 @@ class PrivCPU(CPU):
@self.csr.virtual_register('time') @self.csr.virtual_register('time')
def get_time(): def get_time():
return (time.perf_counter_ns() // self.TIME_RESOLUTION_NS - self._time_start) & (2 ** 32 - 1) return UInt32(time.perf_counter_ns() // self.TIME_RESOLUTION_NS - self._time_start)
@self.csr.virtual_register('timeh') @self.csr.virtual_register('timeh')
def get_timeh(): def get_timeh():
return (time.perf_counter_ns() // self.TIME_RESOLUTION_NS - self._time_start) >> 32 return UInt32((time.perf_counter_ns() // self.TIME_RESOLUTION_NS - self._time_start) >> 32)
# add minstret and mcycle counters # add minstret and mcycle counters
@ -156,7 +161,7 @@ class PrivCPU(CPU):
self._timer_step() self._timer_step()
self._check_interrupt() self._check_interrupt()
ins = self.mmu.read_ins(self.pc) ins = self.mmu.read_ins(self.pc)
if verbose and self.mode == PrivModes.USER: if verbose and (self.mode == PrivModes.USER or self.conf.verbosity > 4):
print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins)) print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins))
self.run_instruction(ins) self.run_instruction(ins)
self.pc += self.INS_XLEN self.pc += self.INS_XLEN
@ -168,6 +173,7 @@ class PrivCPU(CPU):
self.mmu.translate_address(self.pc), self.mmu.translate_address(self.pc),
self.pc self.pc
) + FMT_NONE) ) + FMT_NONE)
breakpoint()
if self.conf.debug_on_exception: if self.conf.debug_on_exception:
raise LaunchDebuggerException() raise LaunchDebuggerException()
self.pc += self.INS_XLEN self.pc += self.INS_XLEN
@ -197,16 +203,16 @@ class PrivCPU(CPU):
self.csr.set_mstatus('mpie', self.csr.get_mstatus('mie')) self.csr.set_mstatus('mpie', self.csr.get_mstatus('mie'))
self.csr.set_mstatus('mpp', self.mode.value) self.csr.set_mstatus('mpp', self.mode.value)
self.csr.set_mstatus('mie', 0) self.csr.set_mstatus('mie', UInt32(0))
self.csr.set('mcause', trap.mcause) self.csr.set('mcause', trap.mcause)
self.csr.set('mepc', self.pc - self.INS_XLEN) self.csr.set('mepc', self.pc - self.INS_XLEN)
self.csr.set('mtval', trap.mtval) self.csr.set('mtval', trap.mtval)
self.mode = trap.priv self.mode = trap.priv
mtvec = self.csr.get('mtvec') mtvec = self.csr.get('mtvec')
if mtvec & 0b11 == 0: if mtvec & 0b11 == 0:
self.pc = mtvec self.pc = mtvec.value
if mtvec & 0b11 == 1: if mtvec & 0b11 == 1:
self.pc = (mtvec & 0b11111111111111111111111111111100) + (trap.code * 4) self.pc = ((mtvec & 0b11111111111111111111111111111100) + (trap.code * 4)).value
self.record_perf_profile() self.record_perf_profile()
if len(self._perf_counters) > 100: if len(self._perf_counters) > 100:
self.show_perf() self.show_perf()

@ -44,7 +44,6 @@ class PrivRV32I(RV32I):
old_val = self.cpu.csr.get(csr_addr) old_val = self.cpu.csr.get(csr_addr)
self.regs.set(rd, old_val) self.regs.set(rd, old_val)
def instruction_csrrc(self, ins: 'Instruction'): def instruction_csrrc(self, ins: 'Instruction'):
INS_NOT_IMPLEMENTED(ins) INS_NOT_IMPLEMENTED(ins)
@ -61,7 +60,6 @@ class PrivRV32I(RV32I):
self.cpu.csr.assert_can_write(self.cpu.mode, addr) self.cpu.csr.assert_can_write(self.cpu.mode, addr)
self.cpu.csr.set(addr, imm) self.cpu.csr.set(addr, imm)
def instruction_csrrci(self, ins: 'Instruction'): def instruction_csrrci(self, ins: 'Instruction'):
INS_NOT_IMPLEMENTED(ins) INS_NOT_IMPLEMENTED(ins)
@ -77,10 +75,10 @@ class PrivRV32I(RV32I):
self.cpu.mode = PrivModes(mpp) self.cpu.mode = PrivModes(mpp)
# restore pc # restore pc
mepc = self.cpu.csr.get('mepc') mepc = self.cpu.csr.get('mepc')
self.cpu.pc = mepc - self.cpu.INS_XLEN self.cpu.pc = (mepc - self.cpu.INS_XLEN).value
if self.cpu.conf.verbosity > 0: if self.cpu.conf.verbosity > 0:
sec = self.mmu.get_sec_containing(mepc) sec = self.mmu.get_sec_containing(mepc.value)
if sec is not None: if sec is not None:
print(FMT_CPU + "[CPU] returning to mode {} in {} (0x{:x})".format( print(FMT_CPU + "[CPU] returning to mode {} in {} (0x{:x})".format(
PrivModes(mpp).name, PrivModes(mpp).name,
@ -105,32 +103,32 @@ class PrivRV32I(RV32I):
def instruction_beq(self, ins: 'Instruction'): def instruction_beq(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 == rs2: if rs1 == rs2:
self.pc += dst - 4 self.pc += dst.value - 4
def instruction_bne(self, ins: 'Instruction'): def instruction_bne(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 != rs2: if rs1 != rs2:
self.pc += dst - 4 self.pc += dst.value - 4
def instruction_blt(self, ins: 'Instruction'): def instruction_blt(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 < rs2: if rs1 < rs2:
self.pc += dst - 4 self.pc += dst.value - 4
def instruction_bge(self, ins: 'Instruction'): def instruction_bge(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins) rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 >= rs2: if rs1 >= rs2:
self.pc += dst - 4 self.pc += dst.value - 4
def instruction_bltu(self, ins: 'Instruction'): def instruction_bltu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False) rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 < rs2: if rs1 < rs2:
self.pc += dst - 4 self.pc += dst.value - 4
def instruction_bgeu(self, ins: 'Instruction'): def instruction_bgeu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False) rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 >= rs2: if rs1 >= rs2:
self.pc += dst - 4 self.pc += dst.value - 4
# technically deprecated # technically deprecated
def instruction_j(self, ins: 'Instruction'): def instruction_j(self, ins: 'Instruction'):
@ -140,19 +138,24 @@ class PrivRV32I(RV32I):
ASSERT_LEN(ins.args, 2) ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0) reg = ins.get_reg(0)
addr = ins.get_imm(1) addr = ins.get_imm(1)
if reg == 'ra' and self.cpu.mode == PrivModes.USER and self.cpu.conf.verbosity > 1: if reg == 'ra' and (
print(FMT_CPU + 'Jumping to {} (0x{:x})'.format( (self.cpu.mode == PrivModes.USER and self.cpu.conf.verbosity > 1) or
(self.cpu.conf.verbosity > 3)
):
print(FMT_CPU + 'Jumping from 0x{:x} to {} (0x{:x})'.format(
self.pc,
self.mmu.translate_address(self.pc + addr), self.mmu.translate_address(self.pc + addr),
self.pc + addr self.pc + addr
) + FMT_NONE) ) + FMT_NONE)
self.regs.set(reg, self.pc) self.regs.dump_reg_a()
self.regs.set(reg, Int32(self.pc))
self.pc += addr - 4 self.pc += addr - 4
def instruction_jalr(self, ins: 'Instruction'): def instruction_jalr(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 3) ASSERT_LEN(ins.args, 3)
rd, rs, imm = self.parse_rd_rs_imm(ins) rd, rs, imm = self.parse_rd_rs_imm(ins)
self.regs.set(rd, self.pc) self.regs.set(rd, Int32(self.pc))
self.pc = rs + imm - 4 self.pc = rs.value + imm.value - 4
def instruction_sbreak(self, ins: 'Instruction'): def instruction_sbreak(self, ins: 'Instruction'):
raise LaunchDebuggerException() raise LaunchDebuggerException()

@ -34,4 +34,4 @@ if __name__ == '__main__':
for program in program_iter: for program in program_iter:
cpu.load_program(program) cpu.load_program(program)
cpu.launch() cpu.launch(verbose=args.verbose > 4)

@ -43,8 +43,8 @@ class ElfMemorySection(BinaryDataMemorySection):
def __init__(self, data: bytearray, name: str, context: InstructionContext, owner: str, base: int, def __init__(self, data: bytearray, name: str, context: InstructionContext, owner: str, base: int,
flags: MemoryFlags): flags: MemoryFlags):
super().__init__(data, name, context, owner, base=base, flags=flags) super().__init__(data, name, context, owner, base=base, flags=flags)
self.read_ins = lru_cache(maxsize=self.size // 4)(self.read_ins)
@lru_cache
def read_ins(self, offset): def read_ins(self, offset):
if not self.flags.executable: if not self.flags.executable:
print(FMT_PARSE + "Reading instruction from non-executable memory!" + FMT_NONE) print(FMT_PARSE + "Reading instruction from non-executable memory!" + FMT_NONE)
@ -65,7 +65,7 @@ class ElfMemorySection(BinaryDataMemorySection):
class MemoryImageDebugInfos: class MemoryImageDebugInfos:
VERSION = '1' VERSION = '1.0.0'
""" """
Schema version Schema version
""" """
@ -99,6 +99,8 @@ class MemoryImageDebugInfos:
self.sections = sections self.sections = sections
self.symbols = symbols self.symbols = symbols
self.globals = globals self.globals = globals
for name in globals:
globals[name] = set(globals[name])
self.base = base self.base = base
def serialize(self) -> str: def serialize(self) -> str:
@ -110,7 +112,13 @@ class MemoryImageDebugInfos:
return "<<unserializable {}>>".format(getattr(obj, '__qualname__', '{unknown}')) return "<<unserializable {}>>".format(getattr(obj, '__qualname__', '{unknown}'))
return json.dumps( return json.dumps(
dict(sections=self.sections, symbols=self.symbols, globals=self.globals, base=self.base), dict(
sections=self.sections,
symbols=self.symbols,
globals=self.globals,
base=self.base,
VERSION=self.VERSION
),
default=serialize default=serialize
) )
@ -124,7 +132,7 @@ class MemoryImageDebugInfos:
version: str = json_obj.pop('VERSION') version: str = json_obj.pop('VERSION')
# compare major version # compare major version
if version != cls.VERSION or version.split('.')[0] != cls.VERSION.split('.')[0]: if version != cls.VERSION and version.split('.')[0] != cls.VERSION.split('.')[0]:
raise RuntimeError( raise RuntimeError(
"Unknown MemoryImageDebugInfo version! This emulator expects version {}, debug info version {}".format( "Unknown MemoryImageDebugInfo version! This emulator expects version {}, debug info version {}".format(
cls.VERSION, version cls.VERSION, version

@ -8,6 +8,9 @@ from collections import defaultdict
from .helpers import * from .helpers import *
if typing.TYPE_CHECKING:
from .types import Int32
class Registers: class Registers:
""" """
@ -15,7 +18,8 @@ class Registers:
""" """
def __init__(self): def __init__(self):
self.vals = defaultdict(lambda: 0) from .types import Int32
self.vals = defaultdict(lambda: Int32(0))
self.last_set = None self.last_set = None
self.last_read = None self.last_read = None
@ -81,7 +85,7 @@ class Registers:
return FMT_GRAY + txt + FMT_NONE return FMT_GRAY + txt + FMT_NONE
return txt return txt
def set(self, reg, val, mark_set=True) -> bool: def set(self, reg, val: 'Int32', mark_set=True) -> bool:
""" """
Set a register content to val Set a register content to val
:param reg: The register to set :param reg: The register to set
@ -89,6 +93,12 @@ class Registers:
:param mark_set: If True, marks this register as "last accessed" (only used internally) :param mark_set: If True, marks this register as "last accessed" (only used internally)
:return: If the operation was successful :return: If the operation was successful
""" """
from .types import Int32
# remove after refactoring is complete
if not isinstance(val, Int32):
raise RuntimeError("Setting register to non-Int32 value! Please refactor your code!")
if reg == 'zero': if reg == 'zero':
return False return False
# if reg not in Registers.all_registers(): # if reg not in Registers.all_registers():
@ -99,10 +109,10 @@ class Registers:
if mark_set: if mark_set:
self.last_set = reg self.last_set = reg
# check 32 bit signed bounds # check 32 bit signed bounds
self.vals[reg] = bind_twos_complement(val) self.vals[reg] = val
return True return True
def get(self, reg, mark_read=True): def get(self, reg, mark_read=True) -> 'Int32':
""" """
Retuns the contents of register reg Retuns the contents of register reg
:param reg: The register name :param reg: The register name

@ -12,11 +12,12 @@ import re
import typing import typing
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import defaultdict from collections import defaultdict
from ctypes import c_uint32, c_int32
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Set, Union, Iterator, Callable, Type from typing import Dict, List, Optional, Tuple, Set, Union, Iterator, Callable, Type
from .config import RunConfig
from .colors import FMT_MEM, FMT_NONE, FMT_UNDERLINE, FMT_ORANGE, FMT_RED, FMT_BOLD from .colors import FMT_MEM, FMT_NONE, FMT_UNDERLINE, FMT_ORANGE, FMT_RED, FMT_BOLD
from .config import RunConfig
from .exceptions import ParseException from .exceptions import ParseException
from .helpers import format_bytes, get_section_base_name from .helpers import format_bytes, get_section_base_name
from .registers import Registers from .registers import Registers
@ -35,6 +36,206 @@ T_ParserOpts = Dict[str, any]
NUMBER_SYMBOL_PATTERN = re.compile(r'^\d+[fb]$') NUMBER_SYMBOL_PATTERN = re.compile(r'^\d+[fb]$')
class Int32:
_type = c_int32
__slots__ = ('_val',)
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))
elif isinstance(val, self.__class__._type):
self._val = val
elif isinstance(val, (c_uint32, c_int32, Int32)):
self._val = self.__class__._type(val.value)
elif isinstance(val, int):
self._val = self.__class__._type(val)
else:
raise RuntimeError(
"Unknonw {} input type: {} ({})".format(self.__class__.__name__, type(val), val)
)
def __add__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value + other)
def __sub__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value - other)
def __mul__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value * other)
def __truediv__(self, other):
return self // other
def __floordiv__(self, other):
if isinstance(other, Int32):
other = other.value
return self.__class__(self.value // other)
def __mod__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value % other)
def __and__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value & other)
def __or__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value | other)
def __xor__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self._val.value ^ other)
def __lshift__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self.value << other)
def __rshift__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.__class__(self.value >> other)
def __eq__(self, other: Union['Int32', int]):
if isinstance(other, Int32):
other = other.value
return self.value == other
def __neg__(self):
return self.__class__(-self._val.value)
def __abs__(self):
return self.__class__(abs(self.value))
def __bytes__(self):
return self.to_bytes(4)
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.value)
def __str__(self):
return str(self.value)
def __format__(self, format_spec):
return self.value.__format__(format_spec)
def __hash__(self):
return hash(self.value)
def __gt__(self, other):
if isinstance(other, Int32):
other = other.value
return self.value > other
def __lt__(self, other):
if isinstance(other, Int32):
other = other.value
return self.value < other
def __le__(self, other):
if isinstance(other, Int32):
other = other.value
return self.value <= other
def __ge__(self, other):
if isinstance(other, Int32):
other = other.value
return self.value >= other
def __bool__(self):
return bool(self.value)
def __cmp__(self, other):
if isinstance(other, Int32):
other = other.value
return self.value.__cmp__(other)
# right handed binary operators
def __radd__(self, other):
return self + other
def __rsub__(self, other):
return self.__class__(other) - self
def __rmul__(self, other):
return self * other
def __rtruediv__(self, other):
return self.__class__(other) // self
def __rfloordiv__(self, other):
return self.__class__(other) // self
def __rmod__(self, other):
return self.__class__(other) % self
def __rand__(self, other):
return self.__class__(other) & self
def __ror__(self, other):
return self.__class__(other) | self
def __rxor__(self, other):
return self.__class__(other) ^ self
@property
def value(self):
return self._val.value
def unsigned(self) -> 'UInt32':
return UInt32(self)
def to_bytes(self, bytes: int = 4) -> bytearray:
return bytearray(self.unsigned_value.to_bytes(bytes, 'little'))
def signed(self) -> 'Int32':
if self.__class__ == Int32:
return self
return Int32(self)
@property
def unsigned_value(self):
return c_uint32(self.value).value
def shift_right_logical(self, ammount: Union['Int32', int]):
if isinstance(ammount, Int32):
ammount = ammount.value
return self.__class__((self.value % 0x100000000) >> ammount)
def __int__(self):
return self.value
def __hex__(self):
return hex(self.value)
class UInt32(Int32):
_type = c_uint32
def unsigned(self) -> 'UInt32':
return self
@property
def unsigned_value(self):
return self._val.value
def shift_right_logical(self, ammount: Union['Int32', int]):
return self >> ammount
@dataclass(frozen=True) @dataclass(frozen=True)
class MemoryFlags: class MemoryFlags:
read_only: bool read_only: bool
@ -242,16 +443,17 @@ class Program:
# print a warning when a section is located before the programs base # print a warning when a section is located before the programs base
if self.base is not None: if self.base is not None:
if sec.base < self.base: if sec.base < self.base:
print(FMT_RED + FMT_BOLD + "WARNING: memory section {} in {} is placed before program base (0x{:x})".format( print(
sec, self.name, self.base FMT_RED + FMT_BOLD + "WARNING: memory section {} in {} is placed before program base (0x{:x})".format(
) + FMT_NONE) sec, self.name, self.base
) + FMT_NONE)
self.sections.append(sec) self.sections.append(sec)
# keep section list ordered # keep section list ordered
self.sections.sort(key=lambda section: section.base) self.sections.sort(key=lambda section: section.base)
def __repr__(self): def __repr__(self):
return "{}(name={},globals={},sections={},base={})".format( return "{}(name={},sections={},base={})".format(
self.__class__.__name__, self.name, self.global_labels, self.__class__.__name__, self.name, self.global_labels,
[s.name for s in self.sections], self.base [s.name for s in self.sections], self.base
) )
@ -273,6 +475,8 @@ class Program:
This will do a small sanity check to prevent programs loading twice, or at addresses they don't This will do a small sanity check to prevent programs loading twice, or at addresses they don't
expect to be loaded. expect to be loaded.
Then it will finalize all relative symbols defined in it to point to the correct addresses.
:param at_addr: the address where the program will be located :param at_addr: the address where the program will be located
""" """
if self.is_loaded: if self.is_loaded:
@ -449,4 +653,4 @@ class CPU(ABC):
@property @property
def programs(self): def programs(self):
return self.mmu.programs return self.mmu.programs

@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup( setuptools.setup(
name="riscemu", name="riscemu",
version=riscemu.__version__, version=riscemu.__version__,
author="Anton Lydike", author=riscemu.__author__,
author_email="pip@antonlydike.de", author_email="pip@antonlydike.de",
description="RISC-V userspace and privileged emulator", description="RISC-V userspace and privileged emulator",
long_description=long_description, long_description=long_description,

@ -1,2 +1,3 @@
from .test_tokenizer import * from .test_tokenizer import *
from .test_helpers import * from .test_helpers import *
from .test_integers import *

@ -4,29 +4,6 @@ from riscemu.helpers import *
class TestHelpers(TestCase): class TestHelpers(TestCase):
def test_int_to_bytes(self):
self.assertEqual(int_to_bytes(-1), bytearray([0xff] * 4), "-1")
self.assertEqual(int_to_bytes(1), bytearray([0, 0, 0, 1]), "1")
self.assertEqual(int_to_bytes(1231132), bytearray(b'\x00\x12\xc9\x1c'), "random number")
self.assertEqual(int_to_bytes(-1231132), bytearray(b'\xff\xed6\xe4'), "random negative number")
def test_int_from_bytes(self):
self.assertEqual(bytearray([0xff] * 4), int_to_bytes(-1), "-1")
self.assertEqual(bytearray([0, 0, 0, 1]), int_to_bytes(1), "1")
self.assertEqual(bytearray(b'\x00\x12\xc9\x1c'), int_to_bytes(1231132), "random number")
self.assertEqual(bytearray(b'\xff\xed6\xe4'), int_to_bytes(-1231132), "random negative number")
def test_to_unsigned(self):
self.assertEqual(to_unsigned(-1), 0xFFFFFFFF)
self.assertEqual(to_unsigned(-100), 0xffffff9c)
self.assertEqual(to_unsigned(1), 1)
self.assertEqual(to_unsigned(0xffffffff), 0xffffffff)
self.assertEqual(to_unsigned(0xffed36e4), 0xffed36e4)
def test_to_signed(self):
self.assertEqual(to_signed(0xFFFFFFFF), -1)
self.assertEqual(to_signed(0xffed36e4), -1231132)
self.assertEqual(to_signed(0x0FFFFFFF), 0x0FFFFFFF)
def test_bind_twos_complement(self): def test_bind_twos_complement(self):
minval = -(1 << 31) minval = -(1 << 31)

@ -0,0 +1,19 @@
from unittest import TestCase
from riscemu.types import Int32, UInt32
class TestTokenizer(TestCase):
def test_logical_right_shift(self):
a = Int32(100)
self.assertEqual(a.shift_right_logical(0), a)
self.assertEqual(a.shift_right_logical(10), 0)
self.assertEqual(a.shift_right_logical(1), 100>>1)
a = Int32(-100)
self.assertEqual(a.shift_right_logical(0), a)
self.assertEqual(a.shift_right_logical(1), 2147483598)
self.assertEqual(a.shift_right_logical(10), 4194303)
self.assertEqual(a.shift_right_logical(31), 1)
self.assertEqual(a.shift_right_logical(32), 0)

@ -1,6 +1,4 @@
from riscemu.colors import FMT_ERROR, FMT_NONE, FMT_BOLD, FMT_GREEN from riscemu.colors import FMT_ERROR, FMT_NONE, FMT_BOLD, FMT_GREEN
from riscemu.exceptions import ASSERT_LEN
from riscemu.helpers import int_from_bytes
from riscemu.instructions import InstructionSet from riscemu.instructions import InstructionSet
from riscemu.types import Instruction, CPU from riscemu.types import Instruction, CPU
from riscemu.decoder import RISCV_REGS from riscemu.decoder import RISCV_REGS

Loading…
Cancel
Save