diff --git a/.idea/riscemu.iml b/.idea/riscemu.iml
index 8ed6672..71b6faa 100644
--- a/.idea/riscemu.iml
+++ b/.idea/riscemu.iml
@@ -4,6 +4,9 @@
+
+
+
diff --git a/riscemu/CPU.py b/riscemu/CPU.py
index 3b9235d..2fcedd0 100644
--- a/riscemu/CPU.py
+++ b/riscemu/CPU.py
@@ -17,7 +17,7 @@ from .colors import FMT_CPU, FMT_NONE
from .debug import launch_debug_session
from .exceptions import RiscemuBaseException, LaunchDebuggerException
from .syscall import SyscallInterface, get_syscall_symbols
-from .types import CPU, ProgramLoader
+from .types import CPU, ProgramLoader, Int32
from .parser import AssemblyFileLoader
if typing.TYPE_CHECKING:
@@ -107,7 +107,7 @@ class UserModeCPU(CPU):
if not self.mmu.load_section(stack_sec, fixed_position=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
@classmethod
diff --git a/riscemu/IO/IOModule.py b/riscemu/IO/IOModule.py
index 21d6a97..521ae93 100644
--- a/riscemu/IO/IOModule.py
+++ b/riscemu/IO/IOModule.py
@@ -1,22 +1,22 @@
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):
- self.addr = addr
- self.size = size
+class IOModule(MemorySection, ABC):
+ def __init__(self, name: str, flags: MemoryFlags, size: int, owner: str = 'system', base: int = 0):
+ super(IOModule, self).__init__(name, flags, size, base, owner, None)
- @abstractmethod
- def read(self, addr: int, size: int):
- pass
+ def contains(self, addr, size: int = 0):
+ return self.base <= addr < self.base + self.size and \
+ self.base <= addr + size <= self.base + self.size
- @abstractmethod
- def write(self, addr: int, data: bytearray, size: int):
- pass
+ def dump(self, start: T_RelativeAddress, end: Optional[T_RelativeAddress] = None, fmt: str = 'hex',
+ bytes_per_row: int = 16, rows: int = 10, group: int = 4):
+ print(self)
- def contains(self, addr, size: int = 0):
- return self.addr <= addr < self.addr + self.size and \
- self.addr <= addr + size <= self.addr + self.size
+ def __repr__(self):
+ return "{}[{}] at 0x{:0X} (size={}bytes, flags={})".format(
+ self.__class__.__name__, self.name, self.base, self.size, self.flags
+ )
\ No newline at end of file
diff --git a/riscemu/IO/TextIO.py b/riscemu/IO/TextIO.py
index 1a615e0..a57e1ab 100644
--- a/riscemu/IO/TextIO.py
+++ b/riscemu/IO/TextIO.py
@@ -1,70 +1,28 @@
from .IOModule import IOModule
from ..priv.Exceptions import InstructionAccessFault
-from ..helpers import int_from_bytes
-from threading import Thread
-import time
+from ..types import T_RelativeAddress, Instruction, MemoryFlags, Int32
-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):
- def __init__(self, addr: int, buflen: int = 128):
- super(TextIO, self).__init__(addr, buflen + 4)
+ def read_ins(self, offset: T_RelativeAddress) -> Instruction:
+ 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.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:
- raise InstructionAccessFault(addr)
+ raise InstructionAccessFault(self.base + addr)
- def write(self, addr: int, data: bytearray, size: int):
- if addr == self.addr:
+ def write(self, addr: int, size: int, data: bytearray):
+ if addr == 0:
if size > 4:
raise InstructionAccessFault(addr)
- if int_from_bytes(data[0:4]) > 0:
+ if Int32(data) != 0:
self._print()
return
- buff_start = addr - self.addr - 4
+ buff_start = addr - 4
self.buff[buff_start:buff_start + size] = data[0:size]
def _print(self):
@@ -83,10 +41,4 @@ class TextIO(IOModule):
self.current_line += text
def _present(self, text: str):
- if self.sg_window is not None:
- 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))
-
+ print("[TextIO:{:x}] {}".format(self.base, text))
diff --git a/riscemu/MMU.py b/riscemu/MMU.py
index eeb75d7..fdbf822 100644
--- a/riscemu/MMU.py
+++ b/riscemu/MMU.py
@@ -4,13 +4,13 @@ RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT
"""
-from typing import Dict, List, Optional
+from typing import Dict, List, Optional, Union
from .colors import *
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, \
- Program, InstructionContext
+ Program, InstructionContext, Int32
class MMU:
@@ -85,7 +85,7 @@ class MMU:
raise RuntimeError("No next instruction available!")
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
@@ -93,13 +93,16 @@ class MMU:
:param size: The number of bytes to read
:return: The bytearray at addr
"""
+ if isinstance(addr, Int32):
+ breakpoint()
+ addr = addr.unsigned_value
sec = self.get_sec_containing(addr)
if sec is 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')
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
@@ -137,32 +140,51 @@ class MMU:
print(FMT_MEM + "[MMU] Lookup for symbol {}:".format(symb) + FMT_NONE)
if symb in self.global_symbols:
print(" Found global symbol {}: 0x{:X}".format(symb, self.global_symbols[symb]))
- for section in self.sections:
- if symb in section.context.labels:
- print(" Found local labels {}: 0x{:X} in {}".format(symb, section.context.labels[symb], section.name))
+ for bin in self.programs:
+ if symb in bin.context.labels:
+ print(" Found local labels {}: 0x{:X} in {}".format(symb, bin.context.labels[symb], bin.name))
- def read_int(self, addr: int) -> int:
- return int_from_bytes(self.read(addr, 4))
+ def read_int(self, addr: int) -> Int32:
+ return Int32(self.read(addr, 4))
def translate_address(self, address: T_AbsoluteAddress) -> str:
- # FIXME: proper implementation using the debug info
- return str(address)
+ sec = self.get_sec_containing(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:
# if we have no sections we are all good
if len(self.sections) == 0:
return True
# 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
for sec in self.sections:
# skip all sections that end before the required start point
- if sec.base + sec.size < start:
+ if sec.base + sec.size <= start:
continue
# 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 sec.base > end:
+ if sec.base >= end:
return True
# otherwise we can't continue
return False
@@ -230,7 +252,8 @@ class MMU:
return self.sections[-1].base + self.sections[-1].size
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)
)
@@ -241,3 +264,16 @@ class MMU:
return sec.context
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))
+
diff --git a/riscemu/__init__.py b/riscemu/__init__.py
index 22d1f8b..6c39581 100644
--- a/riscemu/__init__.py
+++ b/riscemu/__init__.py
@@ -25,4 +25,4 @@ from .parser import tokenize, parse_tokens, AssemblyFileLoader
__author__ = "Anton Lydike "
__copyright__ = "Copyright 2021 Anton Lydike"
-__version__ = '1.0.0'
\ No newline at end of file
+__version__ = '2.0.0a1'
diff --git a/riscemu/assembler.py b/riscemu/assembler.py
index 768d504..8e0fca0 100644
--- a/riscemu/assembler.py
+++ b/riscemu/assembler.py
@@ -1,13 +1,13 @@
-from typing import Optional, Tuple, Union, List
from enum import Enum, auto
+from typing import List
from typing import Optional, Tuple, Union
-from .helpers import parse_numeric_argument, align_addr, int_to_bytes, get_section_base_name
-from .types import Program, T_RelativeAddress, InstructionContext, Instruction
+from .base import BinaryDataMemorySection, InstructionMemorySection
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 .base import BinaryDataMemorySection, InstructionMemorySection
+from .types import Program, T_RelativeAddress, InstructionContext, Instruction, UInt32, Int32
INSTRUCTION_SECTION_NAMES = ('.text', '.init', '.fini')
"""
@@ -96,7 +96,6 @@ class ParseContext:
if is_relative:
self.program.relative_labels.add(name)
-
def __repr__(self):
return "{}(\n\tsetion={},\n\tprogram={}\n)".format(
self.__class__.__name__, self.section, self.program
@@ -176,7 +175,7 @@ class AssemblerDirectives:
if content is None:
content = bytearray(size)
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
diff --git a/riscemu/decoder/formatter.py b/riscemu/decoder/formatter.py
index c1c7955..0d7d304 100644
--- a/riscemu/decoder/formatter.py
+++ b/riscemu/decoder/formatter.py
@@ -24,7 +24,7 @@ def format_ins(ins: int, name: str, fmt: str = 'int'):
return name
if opcode in (0x8, 0x0):
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):
r1, r2, imm = decoder(ins)
r1, r2 = RISCV_REGS[r1], RISCV_REGS[r2]
diff --git a/riscemu/helpers.py b/riscemu/helpers.py
index 3048cb1..82774d1 100644
--- a/riscemu/helpers.py
+++ b/riscemu/helpers.py
@@ -5,9 +5,11 @@ SPDX-License-Identifier: MIT
"""
from math import log10, ceil
-from .exceptions import *
from typing import Iterable, Iterator, TypeVar, Generic, List, Optional
+from .exceptions import *
+import types
+
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))
-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):
"""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)]
@@ -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)
if fmt == 'int':
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':
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)
if fmt == 'ascii':
return "".join(repr(chr(b))[1:-1] for b in byte_arr)
diff --git a/riscemu/instructions/RV32A.py b/riscemu/instructions/RV32A.py
index 44c3f32..c7f7c15 100644
--- a/riscemu/instructions/RV32A.py
+++ b/riscemu/instructions/RV32A.py
@@ -1,6 +1,6 @@
from .instruction_set import InstructionSet, Instruction
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):
@@ -19,60 +19,60 @@ class RV32A(InstructionSet):
def instruction_amoswap_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
if dest == 'zero':
- self.mmu.write(addr, int_to_bytes(addr, 4))
+ self.mmu.write(addr, val.to_bytes())
else:
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(val, 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, val.to_bytes())
self.regs.set(dest, old)
def instruction_amoadd_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(old + val, 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, (old + val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amoand_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(old & val, 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, (old & val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amoor_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(old | val, 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, (old | val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amoxor_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(old ^ val, 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, (old ^ val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amomax_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(max(old, val), 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, max(old, val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amomaxu_w(self, ins: 'Instruction'):
- dest, addr, val = self.parse_rd_rs_rs(ins)
- val = to_unsigned(val)
- old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True)
+ val: UInt32
+ dest, addr, val = self.parse_rd_rs_rs(ins, signed=False)
+ 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)
def instruction_amomin_w(self, ins: 'Instruction'):
dest, addr, val = self.parse_rd_rs_rs(ins)
- old = int_from_bytes(self.mmu.read(addr, 4))
- self.mmu.write(addr, int_to_bytes(min(old, val), 4))
+ old = Int32(self.mmu.read(addr, 4))
+ self.mmu.write(addr, min(old, val).to_bytes(4))
self.regs.set(dest, old)
def instruction_amominu_w(self, ins: 'Instruction'):
- dest, addr, val = self.parse_rd_rs_rs(ins)
- val = to_unsigned(val)
- old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True)
+ val: UInt32
+ dest, addr, val = self.parse_rd_rs_rs(ins, signed=False)
+ 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)
diff --git a/riscemu/instructions/RV32I.py b/riscemu/instructions/RV32I.py
index 291ccbe..26d0bd9 100644
--- a/riscemu/instructions/RV32I.py
+++ b/riscemu/instructions/RV32I.py
@@ -7,12 +7,11 @@ SPDX-License-Identifier: MIT
from .instruction_set import *
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 ..debug import launch_debug_session
from ..exceptions import LaunchDebuggerException
from ..syscall import Syscall
-from ..types import Instruction
+from ..types import Instruction, Int32, UInt32
class RV32I(InstructionSet):
@@ -26,35 +25,35 @@ class RV32I(InstructionSet):
def instruction_lb(self, ins: 'Instruction'):
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'):
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'):
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'):
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'):
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'):
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'):
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'):
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'):
ASSERT_LEN(ins.args, 3)
@@ -63,7 +62,7 @@ class RV32I(InstructionSet):
src2 = ins.get_reg(2)
self.regs.set(
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'):
@@ -73,7 +72,7 @@ class RV32I(InstructionSet):
imm = ins.get_imm(2)
self.regs.set(
dst,
- to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111))
+ self.regs.get(src1) << (imm & 0b11111)
)
def instruction_srl(self, ins: 'Instruction'):
@@ -83,7 +82,7 @@ class RV32I(InstructionSet):
src2 = ins.get_reg(2)
self.regs.set(
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'):
@@ -93,7 +92,7 @@ class RV32I(InstructionSet):
imm = ins.get_imm(2)
self.regs.set(
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'):
@@ -142,14 +141,14 @@ class RV32I(InstructionSet):
def instruction_lui(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
- imm = ins.get_imm(1)
- self.regs.set(reg, imm << 12)
+ imm = UInt32(ins.get_imm(1)) << 12
+ self.regs.set(reg, Int32(imm))
def instruction_auipc(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
- imm = to_unsigned(ins.get_imm(1))
- self.regs.set(reg, self.pc + (imm << 12))
+ imm = UInt32(ins.get_imm(1) << 12)
+ self.regs.set(reg, imm.signed() + self.pc)
def instruction_xor(self, ins: 'Instruction'):
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)
self.regs.set(
rd,
- int(rs1 < rs2)
+ Int32(int(rs1 < rs2))
)
def instruction_slti(self, ins: 'Instruction'):
rd, rs1, imm = self.parse_rd_rs_imm(ins)
self.regs.set(
rd,
- int(rs1 < imm)
+ Int32(int(rs1 < imm))
)
def instruction_sltu(self, ins: 'Instruction'):
dst, rs1, rs2 = self.parse_rd_rs_rs(ins, signed=False)
self.regs.set(
dst,
- int(rs1 < rs2)
+ Int32(int(rs1 < rs2))
)
def instruction_sltiu(self, ins: 'Instruction'):
dst, rs1, imm = self.parse_rd_rs_imm(ins, signed=False)
self.regs.set(
dst,
- int(rs1 < imm)
+ Int32(int(rs1 < imm))
)
def instruction_beq(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 == rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
def instruction_bne(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 != rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
def instruction_blt(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 < rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
def instruction_bge(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 >= rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
def instruction_bltu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 < rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
def instruction_bgeu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 >= rs2:
- self.pc = dst
+ self.pc = dst.unsigned_value
# technically deprecated
def instruction_j(self, ins: 'Instruction'):
@@ -277,7 +276,7 @@ class RV32I(InstructionSet):
def instruction_ret(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 0)
- self.pc = self.regs.get('ra')
+ self.pc = self.regs.get('ra').value
def instruction_ecall(self, ins: 'Instruction'):
self.instruction_scall(ins)
diff --git a/riscemu/instructions/instruction_set.py b/riscemu/instructions/instruction_set.py
index 8b277c6..e0d3f06 100644
--- a/riscemu/instructions/instruction_set.py
+++ b/riscemu/instructions/instruction_set.py
@@ -8,9 +8,8 @@ from typing import Tuple, Callable, Dict
from abc import ABC
from ..CPU import CPU
-from ..helpers import to_unsigned
from ..exceptions import ASSERT_LEN, ASSERT_IN
-from ..types import Instruction
+from ..types import Instruction, Int32, UInt32
class InstructionSet(ABC):
@@ -52,7 +51,7 @@ class InstructionSet(ABC):
if member.startswith('instruction_'):
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)
(so a register and address tuple for memory instructions)
@@ -70,7 +69,7 @@ class InstructionSet(ABC):
rd = ins.get_reg(0)
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 rd, rs1, rs2 format
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)
else:
return ins.get_reg(0), \
- to_unsigned(self.get_reg_content(ins, 1)), \
- to_unsigned(self.get_reg_content(ins, 2))
+ UInt32(self.get_reg_content(ins, 1)), \
+ 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 rd, rs, imm format
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)
if signed:
return ins.get_reg(0), \
- self.get_reg_content(ins, 1), \
- ins.get_imm(2)
+ Int32(self.get_reg_content(ins, 1)), \
+ Int32(ins.get_imm(2))
else:
return ins.get_reg(0), \
- to_unsigned(self.get_reg_content(ins, 1)), \
- to_unsigned(ins.get_imm(2))
+ UInt32(self.get_reg_content(ins, 1)), \
+ 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 rs1, rs2, imm format
Returns the values in rs1, rs2 and the immediate imm
"""
if signed:
- return self.get_reg_content(ins, 0), \
- self.get_reg_content(ins, 1), \
- ins.get_imm(2)
+ return Int32(self.get_reg_content(ins, 0)), \
+ Int32(self.get_reg_content(ins, 1)), \
+ Int32(ins.get_imm(2))
else:
- return to_unsigned(self.get_reg_content(ins, 0)), \
- to_unsigned(self.get_reg_content(ins, 1)), \
- to_unsigned(ins.get_imm(2))
+ return UInt32(self.get_reg_content(ins, 0)), \
+ UInt32(self.get_reg_content(ins, 1)), \
+ 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
"""
diff --git a/riscemu/priv/CSR.py b/riscemu/priv/CSR.py
index 4a2cc7b..fbd83c6 100644
--- a/riscemu/priv/CSR.py
+++ b/riscemu/priv/CSR.py
@@ -2,49 +2,49 @@ from typing import Dict, Union, Callable, Optional
from collections import defaultdict
from .privmodes import PrivModes
from .Exceptions import InstructionAccessFault
-from ..helpers import to_signed
from ..colors import FMT_CSR, FMT_NONE
from .CSRConsts import CSR_NAME_TO_ADDR, MSTATUS_LEN_2, MSTATUS_OFFSETS
+from ..types import UInt32
class 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
"""
- virtual_regs: Dict[int, Callable[[], int]]
+ virtual_regs: Dict[int, Callable[[], UInt32]]
"""
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
def __init__(self):
- self.regs = defaultdict(lambda: 0)
+ self.regs = defaultdict(lambda: UInt32(0))
self.listeners = defaultdict(lambda: (lambda x, y: None))
self.virtual_regs = dict()
self.mstatus_cache = dict()
# 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)
if addr is None:
return
- val = to_signed(val)
+ val = UInt32(val)
self.listeners[addr](self.regs[addr], val)
if addr == 0x300:
self.mstatus_cache_dirty = True
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)
if addr is None:
raise RuntimeError(f"Invalid CSR name: {addr}!")
@@ -52,7 +52,7 @@ class CSR:
return self.virtual_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)
if addr is None:
print("unknown csr address name: {}".format(addr))
@@ -60,7 +60,7 @@ class CSR:
self.listeners[addr] = listener
# 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
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)
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:
return self.mstatus_cache[name]
@@ -94,7 +94,7 @@ class CSR:
return val
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)
return func
@@ -121,7 +121,7 @@ class CSR:
if addr is None:
print("unknown csr address name: {}".format(addr))
- def inner(func: Callable[[], int]):
+ def inner(func: Callable[[], UInt32]):
self.virtual_regs[addr] = func
return func
diff --git a/riscemu/priv/ElfLoader.py b/riscemu/priv/ElfLoader.py
index 48fab49..f8538c6 100644
--- a/riscemu/priv/ElfLoader.py
+++ b/riscemu/priv/ElfLoader.py
@@ -81,12 +81,14 @@ class ElfBinaryFileLoader(ProgramLoader):
)
def _parse_symtab(self, symtab: 'SymbolTableSection'):
+ from elftools.elf.enums import ENUM_ST_VISIBILITY
+
for sym in symtab.iter_symbols():
if not sym.name:
continue
self.program.context.labels[sym.name] = sym.entry.st_value
# 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)
print(FMT_PARSE + "LOADED GLOBAL SYMBOL {}: {}".format(sym.name, sym.entry.st_value) + FMT_NONE)
diff --git a/riscemu/priv/Exceptions.py b/riscemu/priv/Exceptions.py
index 01e863f..53214df 100644
--- a/riscemu/priv/Exceptions.py
+++ b/riscemu/priv/Exceptions.py
@@ -7,6 +7,7 @@ import typing
from .. import RiscemuBaseException
from ..colors import FMT_PARSE, FMT_NONE
+from ..types import UInt32
if typing.TYPE_CHECKING:
from .ElfLoader import ElfInstruction
@@ -29,7 +30,7 @@ class CpuTrap(BaseException):
The isInterrupt bit in the mstatus register
"""
- mtval: int
+ mtval: UInt32
"""
contents of the mtval register
"""
@@ -47,7 +48,7 @@ class CpuTrap(BaseException):
def __init__(self, code: int, mtval, type: CpuTrapType, priv: PrivModes = PrivModes.MACHINE):
self.interrupt = 0 if type == CpuTrapType.EXCEPTION else 1
self.code = code
- self.mtval = mtval
+ self.mtval = UInt32(mtval)
self.priv = priv
self.type = type
diff --git a/riscemu/priv/ImageLoader.py b/riscemu/priv/ImageLoader.py
index 9ef86e6..11f8fe7 100644
--- a/riscemu/priv/ImageLoader.py
+++ b/riscemu/priv/ImageLoader.py
@@ -26,7 +26,7 @@ class MemoryImageLoader(ProgramLoader):
return argv, {}
def parse(self) -> Iterable[Program]:
- if self.options.get('debug', False):
+ if 'debug' not in self.options:
yield self.parse_no_debug()
return
@@ -43,11 +43,11 @@ class MemoryImageLoader(ProgramLoader):
if program.base is None:
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(
ElfMemorySection(
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.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
@classmethod
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, options)
diff --git a/riscemu/priv/PrivCPU.py b/riscemu/priv/PrivCPU.py
index 483300e..c74d766 100644
--- a/riscemu/priv/PrivCPU.py
+++ b/riscemu/priv/PrivCPU.py
@@ -14,8 +14,9 @@ from .ImageLoader import MemoryImageLoader
from .PrivMMU import PrivMMU
from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes
+from ..IO.TextIO import TextIO
from ..instructions import RV32A, RV32M
-from ..types import Program
+from ..types import Program, UInt32
if typing.TYPE_CHECKING:
from riscemu.instructions.instruction_set import InstructionSet
@@ -55,12 +56,16 @@ class PrivCPU(CPU):
self.exit_code = 0
self._time_start = 0
- self._time_timecmp = 0
+ self._time_timecmp = UInt32(0)
self._time_interrupt_enabled = False
# performance counters
self._perf_counters = list()
+ # add TextIO
+ io = TextIO(0xFF0000, 64)
+ self.mmu.load_section(io, True)
+
# init csr
self._init_csr()
@@ -105,11 +110,11 @@ class PrivCPU(CPU):
def _init_csr(self):
# set up CSR
self.csr = CSR()
- self.csr.set('mhartid', 0) # core id
+ self.csr.set('mhartid', UInt32(0)) # core id
# 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
- 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:
@@ -137,11 +142,11 @@ class PrivCPU(CPU):
@self.csr.virtual_register('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')
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
@@ -156,7 +161,7 @@ class PrivCPU(CPU):
self._timer_step()
self._check_interrupt()
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))
self.run_instruction(ins)
self.pc += self.INS_XLEN
@@ -168,6 +173,7 @@ class PrivCPU(CPU):
self.mmu.translate_address(self.pc),
self.pc
) + FMT_NONE)
+ breakpoint()
if self.conf.debug_on_exception:
raise LaunchDebuggerException()
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('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('mepc', self.pc - self.INS_XLEN)
self.csr.set('mtval', trap.mtval)
self.mode = trap.priv
mtvec = self.csr.get('mtvec')
if mtvec & 0b11 == 0:
- self.pc = mtvec
+ self.pc = mtvec.value
if mtvec & 0b11 == 1:
- self.pc = (mtvec & 0b11111111111111111111111111111100) + (trap.code * 4)
+ self.pc = ((mtvec & 0b11111111111111111111111111111100) + (trap.code * 4)).value
self.record_perf_profile()
if len(self._perf_counters) > 100:
self.show_perf()
diff --git a/riscemu/priv/PrivRV32I.py b/riscemu/priv/PrivRV32I.py
index 81f446a..409f2ef 100644
--- a/riscemu/priv/PrivRV32I.py
+++ b/riscemu/priv/PrivRV32I.py
@@ -44,7 +44,6 @@ class PrivRV32I(RV32I):
old_val = self.cpu.csr.get(csr_addr)
self.regs.set(rd, old_val)
-
def instruction_csrrc(self, ins: 'Instruction'):
INS_NOT_IMPLEMENTED(ins)
@@ -61,7 +60,6 @@ class PrivRV32I(RV32I):
self.cpu.csr.assert_can_write(self.cpu.mode, addr)
self.cpu.csr.set(addr, imm)
-
def instruction_csrrci(self, ins: 'Instruction'):
INS_NOT_IMPLEMENTED(ins)
@@ -77,10 +75,10 @@ class PrivRV32I(RV32I):
self.cpu.mode = PrivModes(mpp)
# restore pc
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:
- sec = self.mmu.get_sec_containing(mepc)
+ sec = self.mmu.get_sec_containing(mepc.value)
if sec is not None:
print(FMT_CPU + "[CPU] returning to mode {} in {} (0x{:x})".format(
PrivModes(mpp).name,
@@ -105,32 +103,32 @@ class PrivRV32I(RV32I):
def instruction_beq(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 == rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
def instruction_bne(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 != rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
def instruction_blt(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 < rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
def instruction_bge(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins)
if rs1 >= rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
def instruction_bltu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 < rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
def instruction_bgeu(self, ins: 'Instruction'):
rs1, rs2, dst = self.parse_rs_rs_imm(ins, signed=False)
if rs1 >= rs2:
- self.pc += dst - 4
+ self.pc += dst.value - 4
# technically deprecated
def instruction_j(self, ins: 'Instruction'):
@@ -140,19 +138,24 @@ class PrivRV32I(RV32I):
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
addr = ins.get_imm(1)
- if reg == 'ra' and self.cpu.mode == PrivModes.USER and self.cpu.conf.verbosity > 1:
- print(FMT_CPU + 'Jumping to {} (0x{:x})'.format(
+ if reg == 'ra' and (
+ (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.pc + addr
) + FMT_NONE)
- self.regs.set(reg, self.pc)
+ self.regs.dump_reg_a()
+ self.regs.set(reg, Int32(self.pc))
self.pc += addr - 4
def instruction_jalr(self, ins: 'Instruction'):
ASSERT_LEN(ins.args, 3)
rd, rs, imm = self.parse_rd_rs_imm(ins)
- self.regs.set(rd, self.pc)
- self.pc = rs + imm - 4
+ self.regs.set(rd, Int32(self.pc))
+ self.pc = rs.value + imm.value - 4
def instruction_sbreak(self, ins: 'Instruction'):
raise LaunchDebuggerException()
diff --git a/riscemu/priv/__main__.py b/riscemu/priv/__main__.py
index bbdd1fb..6e74029 100644
--- a/riscemu/priv/__main__.py
+++ b/riscemu/priv/__main__.py
@@ -34,4 +34,4 @@ if __name__ == '__main__':
for program in program_iter:
cpu.load_program(program)
- cpu.launch()
+ cpu.launch(verbose=args.verbose > 4)
diff --git a/riscemu/priv/types.py b/riscemu/priv/types.py
index 585f580..f42d030 100644
--- a/riscemu/priv/types.py
+++ b/riscemu/priv/types.py
@@ -43,8 +43,8 @@ class ElfMemorySection(BinaryDataMemorySection):
def __init__(self, data: bytearray, name: str, context: InstructionContext, owner: str, base: int,
flags: MemoryFlags):
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):
if not self.flags.executable:
print(FMT_PARSE + "Reading instruction from non-executable memory!" + FMT_NONE)
@@ -65,7 +65,7 @@ class ElfMemorySection(BinaryDataMemorySection):
class MemoryImageDebugInfos:
- VERSION = '1'
+ VERSION = '1.0.0'
"""
Schema version
"""
@@ -99,6 +99,8 @@ class MemoryImageDebugInfos:
self.sections = sections
self.symbols = symbols
self.globals = globals
+ for name in globals:
+ globals[name] = set(globals[name])
self.base = base
def serialize(self) -> str:
@@ -110,7 +112,13 @@ class MemoryImageDebugInfos:
return "<>".format(getattr(obj, '__qualname__', '{unknown}'))
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
)
@@ -124,7 +132,7 @@ class MemoryImageDebugInfos:
version: str = json_obj.pop('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(
"Unknown MemoryImageDebugInfo version! This emulator expects version {}, debug info version {}".format(
cls.VERSION, version
diff --git a/riscemu/registers.py b/riscemu/registers.py
index aa45915..caa7e36 100644
--- a/riscemu/registers.py
+++ b/riscemu/registers.py
@@ -8,6 +8,9 @@ from collections import defaultdict
from .helpers import *
+if typing.TYPE_CHECKING:
+ from .types import Int32
+
class Registers:
"""
@@ -15,7 +18,8 @@ class Registers:
"""
def __init__(self):
- self.vals = defaultdict(lambda: 0)
+ from .types import Int32
+ self.vals = defaultdict(lambda: Int32(0))
self.last_set = None
self.last_read = None
@@ -81,7 +85,7 @@ class Registers:
return FMT_GRAY + txt + FMT_NONE
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
: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)
: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':
return False
# if reg not in Registers.all_registers():
@@ -99,10 +109,10 @@ class Registers:
if mark_set:
self.last_set = reg
# check 32 bit signed bounds
- self.vals[reg] = bind_twos_complement(val)
+ self.vals[reg] = val
return True
- def get(self, reg, mark_read=True):
+ def get(self, reg, mark_read=True) -> 'Int32':
"""
Retuns the contents of register reg
:param reg: The register name
diff --git a/riscemu/types.py b/riscemu/types.py
index 0746d3d..4a4692f 100644
--- a/riscemu/types.py
+++ b/riscemu/types.py
@@ -12,11 +12,12 @@ import re
import typing
from abc import ABC, abstractmethod
from collections import defaultdict
+from ctypes import c_uint32, c_int32
from dataclasses import dataclass
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 .config import RunConfig
from .exceptions import ParseException
from .helpers import format_bytes, get_section_base_name
from .registers import Registers
@@ -35,6 +36,206 @@ T_ParserOpts = Dict[str, any]
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)
class MemoryFlags:
read_only: bool
@@ -242,16 +443,17 @@ class Program:
# print a warning when a section is located before the programs base
if self.base is not None:
if sec.base < self.base:
- print(FMT_RED + FMT_BOLD + "WARNING: memory section {} in {} is placed before program base (0x{:x})".format(
- sec, self.name, self.base
- ) + FMT_NONE)
+ print(
+ FMT_RED + FMT_BOLD + "WARNING: memory section {} in {} is placed before program base (0x{:x})".format(
+ sec, self.name, self.base
+ ) + FMT_NONE)
self.sections.append(sec)
# keep section list ordered
self.sections.sort(key=lambda section: section.base)
def __repr__(self):
- return "{}(name={},globals={},sections={},base={})".format(
+ return "{}(name={},sections={},base={})".format(
self.__class__.__name__, self.name, self.global_labels,
[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
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
"""
if self.is_loaded:
@@ -449,4 +653,4 @@ class CPU(ABC):
@property
def programs(self):
- return self.mmu.programs
\ No newline at end of file
+ return self.mmu.programs
diff --git a/setup.py b/setup.py
index 238e88f..edaae98 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setuptools.setup(
name="riscemu",
version=riscemu.__version__,
- author="Anton Lydike",
+ author=riscemu.__author__,
author_email="pip@antonlydike.de",
description="RISC-V userspace and privileged emulator",
long_description=long_description,
diff --git a/test/__init__.py b/test/__init__.py
index 8030002..1f2c0dd 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -1,2 +1,3 @@
from .test_tokenizer import *
-from .test_helpers import *
\ No newline at end of file
+from .test_helpers import *
+from .test_integers import *
\ No newline at end of file
diff --git a/test/test_helpers.py b/test/test_helpers.py
index 60d93b0..f37517d 100644
--- a/test/test_helpers.py
+++ b/test/test_helpers.py
@@ -4,29 +4,6 @@ from riscemu.helpers import *
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):
minval = -(1 << 31)
diff --git a/test/test_integers.py b/test/test_integers.py
new file mode 100644
index 0000000..bb11141
--- /dev/null
+++ b/test/test_integers.py
@@ -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)
diff --git a/test/test_isa.py b/test/test_isa.py
index 80a7a13..cc69052 100644
--- a/test/test_isa.py
+++ b/test/test_isa.py
@@ -1,6 +1,4 @@
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.types import Instruction, CPU
from riscemu.decoder import RISCV_REGS