diff --git a/riscemu/MMU.py b/riscemu/MMU.py index 5f77b4e..c50509c 100644 --- a/riscemu/MMU.py +++ b/riscemu/MMU.py @@ -17,32 +17,37 @@ class MMU: The MemoryManagementUnit (handles loading binaries, and reading/writing data) """ + max_size = 0xFFFFFFFF """ The maximum size of the memory in bytes """ - max_size = 0xFFFFFFFF + sections: List[LoadedMemorySection] """ A list of all loaded memory sections """ - sections: List[LoadedMemorySection] + binaries: List[LoadedExecutable] """ A list of all loaded executables """ - binaries: List[LoadedExecutable] + last_bin: Optional[LoadedExecutable] = None """ The last loaded executable (the next executable is inserted directly after this one) """ - last_bin: Optional[LoadedExecutable] = None + global_symbols: Dict[str, int] """ The global symbol table """ - global_symbols: Dict[str, int] def __init__(self, conf: RunConfig): + """ + Create a new MMU, respeccting the active RunConfiguration + + :param conf: The config to respect + """ self.sections = list() self.binaries = list() self.last_bin = None @@ -52,6 +57,7 @@ class MMU: def load_bin(self, bin: Executable) -> LoadedExecutable: """ Load an executable into memory + :param bin: the executable to load :return: A LoadedExecutable :raises OutOfMemoryException: When all memory is used @@ -88,6 +94,7 @@ class MMU: def get_sec_containing(self, addr: int) -> Optional[LoadedMemorySection]: """ Returns the section that contains the address addr + :param addr: the Address to look for :return: The LoadedMemorySection or None """ @@ -99,6 +106,7 @@ class MMU: def read_ins(self, addr: int) -> LoadedInstruction: """ Read a single instruction located at addr + :param addr: The location :return: The Instruction """ @@ -108,6 +116,7 @@ class MMU: def read(self, addr: int, size: int) -> bytearray: """ Read size bytes of memory at addr + :param addr: The addres at which to start reading :param size: The number of bytes to read :return: The bytearray at addr @@ -118,6 +127,7 @@ class MMU: def write(self, addr: int, size: int, data): """ Write bytes into memory + :param addr: The address at which to write :param size: The number of bytes to write :param data: The bytearray to write (only first size bytes are written) @@ -138,9 +148,10 @@ class MMU: return sec.dump(addr, *args, **kwargs) - def symbol(self, symb:str): + def symbol(self, symb: str): """ Look up the symbol symb in all local symbol tables (and the global one) + :param symb: The symbol name to look up """ print(FMT_MEM + "[MMU] Lookup for symbol {}:".format(symb) + FMT_NONE) @@ -153,4 +164,4 @@ class MMU: def __repr__(self): return "MMU(\n\t{}\n)".format( "\n\t".join(repr(x) for x in self.sections) - ) \ No newline at end of file + ) diff --git a/riscemu/Syscall.py b/riscemu/Syscall.py index 7fae1f7..48db82f 100644 --- a/riscemu/Syscall.py +++ b/riscemu/Syscall.py @@ -8,18 +8,15 @@ from dataclasses import dataclass from typing import Dict, IO import sys -from .Registers import Registers -from .Exceptions import InvalidSyscallException from .helpers import * +import riscemu + import typing if typing.TYPE_CHECKING: from . import CPU -""" -All available syscalls (mapped id->name) -""" SYSCALLS = { 63: 'read', 64: 'write', @@ -27,10 +24,8 @@ SYSCALLS = { 1024: 'open', 1025: 'close', } +"""All available syscalls (mapped id->name)""" -""" -All available file open modes -""" OPEN_MODES = { 0: 'rb', 1: 'wb', @@ -38,7 +33,7 @@ OPEN_MODES = { 3: 'x', 4: 'ab', } - +"""All available file open modes""" @dataclass(frozen=True) class Syscall: @@ -46,8 +41,9 @@ class Syscall: Represents a syscall """ id: int - registers: Registers - cpu: 'CPU' + """The syscall number (e.g. 64 - write)""" + cpu: 'riscemu.CPU' + """The CPU object that created the syscall""" @property def name(self): @@ -59,13 +55,14 @@ class Syscall: ) def ret(self, code): - self.registers.set('a0', code) + self.cpu.regs.set('a0', code) def get_syscall_symbols(): """ - Retuns a dictionary of all syscall symbols (SCALL_ -> id) - :return: + Generate global syscall symbols (such as SCALL_READ, SCALL_EXIT etc) + + :return: dictionary of all syscall symbols (SCALL_ -> id) """ return { ('SCALL_' + name.upper()): num for num, name in SYSCALLS.items() @@ -97,11 +94,11 @@ class SyscallInterface: read syscall (63): read from file no a0, into addr a1, at most a2 bytes on return a0 will be the number of read bytes or -1 if an error occured """ - fileno = scall.registers.get('a0') - addr = scall.registers.get('a1') - size = scall.registers.get('a2') + fileno = scall.cpu.regs.get('a0') + addr = scall.cpu.regs.get('a1') + size = scall.cpu.regs.get('a2') if fileno not in self.open_files: - scall.registers.set('a0', -1) + scall.cpu.regs.set('a0', -1) return chars = self.open_files[fileno].readline(size) @@ -119,9 +116,9 @@ class SyscallInterface: write syscall (64): write a2 bytes from addr a1 into fileno a0 on return a0 will hold the number of bytes written or -1 if an error occured """ - fileno = scall.registers.get('a0') - addr = scall.registers.get('a1') - size = scall.registers.get('a2') + fileno = scall.cpu.regs.get('a0') + addr = scall.cpu.regs.get('a1') + size = scall.cpu.regs.get('a2') if fileno not in self.open_files: return scall.ret(-1) @@ -152,9 +149,9 @@ class SyscallInterface: print(FMT_SYSCALL + '[Syscall] open: opening files not supported without scall-fs flag!' + FMT_NONE) return scall.ret(-1) - mode = scall.registers.get('a0') - addr = scall.registers.get('a1') - size = scall.registers.get('a2') + mode = scall.cpu.regs.get('a0') + addr = scall.cpu.regs.get('a1') + size = scall.cpu.regs.get('a2') mode_st = OPEN_MODES.get(mode, ) if mode_st == -1: @@ -181,7 +178,7 @@ class SyscallInterface: return -1 if an error was encountered, otherwise returns 0 """ - fileno = scall.registers.get('a0') + fileno = scall.cpu.regs.get('a0') if fileno not in self.open_files: print(FMT_SYSCALL + '[Syscall] close: unknown fileno {}!'.format(fileno) + FMT_NONE) return scall.ret(-1) @@ -196,7 +193,7 @@ class SyscallInterface: Exit syscall. Exits the system with status code a0 """ scall.cpu.exit = True - scall.cpu.exit_code = scall.registers.get('a0') + scall.cpu.exit_code = scall.cpu.regs.get('a0') def __repr__(self): return "{}(\n\tfiles={}\n)".format( diff --git a/riscemu/__init__.py b/riscemu/__init__.py index a7e9d5d..2d7d251 100644 --- a/riscemu/__init__.py +++ b/riscemu/__init__.py @@ -25,3 +25,6 @@ from .Syscall import SyscallInterface, Syscall from .CPU import CPU from .Config import RunConfig + +__author__ = "Anton Lydike int: """ align an address to `to_bytes` (meaning addr & to_bytes = 0) """ return addr + (-addr % to_bytes) -def parse_numeric_argument(arg: str): +def parse_numeric_argument(arg: str) -> int: """ parse hex or int strings """ @@ -27,7 +27,7 @@ def parse_numeric_argument(arg: str): raise ParseException('Invalid immediate argument \"{}\", maybe missing symbol?'.format(arg), (arg, ex)) -def int_to_bytes(val, bytes=4, unsigned=False): +def int_to_bytes(val, bytes=4, unsigned=False) -> bytearray: """ int -> byte (two's complement) """ @@ -38,7 +38,7 @@ def int_to_bytes(val, bytes=4, unsigned=False): ]) -def int_from_bytes(bytes, unsigned=False): +def int_from_bytes(bytes, unsigned=False) -> int: """ byte -> int (two's complement) """ @@ -53,19 +53,20 @@ def int_from_bytes(bytes, unsigned=False): return to_signed(num) -def to_unsigned(num: int, bytes=4): +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): +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)] @@ -83,6 +84,7 @@ def highlight_in_list(items, hi_ind): def format_bytes(byte_arr: bytearray, fmt: str, group: int = 1, highlight: int = -1): + """Format byte array as per fmt. Group into groups of size `group`, and highlight index `highlight`.""" chunks = create_chunks(byte_arr, group) if fmt == 'hex': return highlight_in_list(['0x{}'.format(ch.hex()) for ch in chunks], highlight) diff --git a/riscemu/instructions/InstructionSet.py b/riscemu/instructions/InstructionSet.py index b9fb7c8..2a620c2 100644 --- a/riscemu/instructions/InstructionSet.py +++ b/riscemu/instructions/InstructionSet.py @@ -24,6 +24,9 @@ class InstructionSet(ABC): """ def __init__(self, cpu: 'CPU'): + """Create a new instance of the Instruction set. This requires access to a CPU, and grabs vertain things + from it such as access to the MMU and registers. + """ self.name = self.__class__.__name__ self.cpu = cpu self.mmu = cpu.mmu diff --git a/riscemu/instructions/RV32I.py b/riscemu/instructions/RV32I.py index 8f2de40..fbb1186 100644 --- a/riscemu/instructions/RV32I.py +++ b/riscemu/instructions/RV32I.py @@ -293,7 +293,7 @@ class RV32I(InstructionSet): def instruction_scall(self, ins: 'LoadedInstruction'): ASSERT_LEN(ins.args, 0) - syscall = Syscall(self.regs.get('a7'), self.regs, self.cpu) + syscall = Syscall(self.regs.get('a7'), self.cpu) self.cpu.syscall_int.handle_syscall(syscall) def instruction_sbreak(self, ins: 'LoadedInstruction'):