lots more documentation cleanup
This commit is contained in:
parent
fdcb3a71be
commit
0574766a81
@ -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)
|
||||
)
|
||||
)
|
||||
|
@ -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_<name> -> id)
|
||||
:return:
|
||||
Generate global syscall symbols (such as SCALL_READ, SCALL_EXIT etc)
|
||||
|
||||
:return: dictionary of all syscall symbols (SCALL_<name> -> 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(
|
||||
|
@ -25,3 +25,6 @@ from .Syscall import SyscallInterface, Syscall
|
||||
from .CPU import CPU
|
||||
|
||||
from .Config import RunConfig
|
||||
|
||||
__author__ = "Anton Lydike <Anton@Lydike.com"
|
||||
__copyright__ = "Copyright 2021 Anton Lydike"
|
@ -8,14 +8,14 @@ from math import log10, ceil
|
||||
from .Exceptions import *
|
||||
|
||||
|
||||
def align_addr(addr: int, to_bytes: int = 8):
|
||||
def align_addr(addr: int, to_bytes: int = 8) -> 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)
|
||||
|
@ -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
|
||||
|
@ -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'):
|
||||
|
Loading…
Reference in New Issue
Block a user