added lots more documentation and copyright notices

float_support
Anton Lydike 4 years ago
parent 2a68f16e99
commit 6e6ce90e9a

@ -62,7 +62,7 @@ class CPU:
# provide global syscall symbols if option is set # provide global syscall symbols if option is set
if conf.include_scall_symbols: if conf.include_scall_symbols:
self.mmu.global_symbols.update(self.syscall_int.get_syscall_symbols()) self.mmu.global_symbols.update(get_syscall_symbols())
def get_tokenizer(self, tokenizer_input): def get_tokenizer(self, tokenizer_input):
""" """

@ -1,3 +1,9 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional

@ -1,3 +1,9 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from abc import abstractmethod from abc import abstractmethod
from .colors import * from .colors import *

@ -1,3 +1,13 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
This file holds Executable and LoadedExecutable classes as well as loading and some linking code.
FIXME: refactor this code into muliple files
"""
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Union, Optional from typing import Dict, List, Tuple, Union, Optional
from .Exceptions import * from .Exceptions import *

@ -1,3 +1,11 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
This file holds the parser that parses the tokenizer output.
"""
from .helpers import parse_numeric_argument, int_to_bytes from .helpers import parse_numeric_argument, int_to_bytes
from .Executable import Executable, InstructionMemorySection, MemorySection, MemoryFlags from .Executable import Executable, InstructionMemorySection, MemorySection, MemoryFlags
from .Exceptions import * from .Exceptions import *
@ -8,6 +16,9 @@ from typing import Dict, Tuple, List, Optional
class ExecutableParser: class ExecutableParser:
"""
Parses output form the RiscVTokenizer
"""
tokenizer: 'RiscVTokenizer' tokenizer: 'RiscVTokenizer'
def __init__(self, tokenizer: 'RiscVTokenizer'): def __init__(self, tokenizer: 'RiscVTokenizer'):
@ -20,7 +31,12 @@ class ExecutableParser:
self.stack_pref: Optional[int] = None self.stack_pref: Optional[int] = None
self.globals: List[str] = list() self.globals: List[str] = list()
def parse(self): def parse(self) -> Executable:
"""
parse tokenizer output into an executable
:return: the parsed executable
:raise ParseException: Raises a ParseException when invalid input is read
"""
for token in self.tokenizer.tokens: for token in self.tokenizer.tokens:
if isinstance(token, RiscVInstructionToken): if isinstance(token, RiscVInstructionToken):
self.parse_instruction(token) self.parse_instruction(token)
@ -28,9 +44,9 @@ class ExecutableParser:
self.handle_symbol(token) self.handle_symbol(token)
elif isinstance(token, RiscVPseudoOpToken): elif isinstance(token, RiscVPseudoOpToken):
self.handle_pseudo_op(token) self.handle_pseudo_op(token)
return self.get_execuable() return self._get_execuable()
def get_execuable(self): def _get_execuable(self) -> Executable:
start_ptr = ('text', 0) start_ptr = ('text', 0)
if '_start' in self.symbols: if '_start' in self.symbols:
start_ptr = self.symbols['_start'] start_ptr = self.symbols['_start']
@ -38,24 +54,36 @@ class ExecutableParser:
start_ptr = self.symbols['main'] start_ptr = self.symbols['main']
return Executable(start_ptr, self.sections, self.symbols, self.stack_pref, self.globals, self.tokenizer.name) return Executable(start_ptr, self.sections, self.symbols, self.stack_pref, self.globals, self.tokenizer.name)
def parse_instruction(self, ins: 'RiscVInstructionToken'): def parse_instruction(self, ins: 'RiscVInstructionToken') -> None:
"""
parses an Instruction token
:param ins: the instruction token
"""
if self.active_section is None: if self.active_section is None:
self.op_text() self.op_text()
self.implicit_sections = True self.implicit_sections = True
ASSERT_EQ(self.active_section, 'text') ASSERT_EQ(self.active_section, 'text')
sec = self.curr_sec() sec = self._curr_sec()
if isinstance(sec, InstructionMemorySection): if isinstance(sec, InstructionMemorySection):
sec.add_insn(ins) sec.add_insn(ins)
else: else:
raise ParseException("SHOULD NOT BE REACHED") raise ParseException("SHOULD NOT BE REACHED")
def handle_symbol(self, token: 'RiscVSymbolToken'): def handle_symbol(self, token: 'RiscVSymbolToken'):
"""
Handle a symbol token (such as 'main:')
:param token: the symbol token
"""
ASSERT_NOT_IN(token.name, self.symbols) ASSERT_NOT_IN(token.name, self.symbols)
sec_pos = self.curr_sec().size sec_pos = self._curr_sec().size
self.symbols[token.name] = (self.active_section, sec_pos) self.symbols[token.name] = (self.active_section, sec_pos)
def handle_pseudo_op(self, op: 'RiscVPseudoOpToken'): def handle_pseudo_op(self, op: 'RiscVPseudoOpToken'):
"""
Handle a pseudo op token (such as '.word 0xffaabbcc')
:param op: the peseudo-op token
"""
name = 'op_' + op.name name = 'op_' + op.name
if hasattr(self, name): if hasattr(self, name):
getattr(self, name)(op) getattr(self, name)(op)
@ -64,68 +92,112 @@ class ExecutableParser:
## Pseudo op implementations: ## Pseudo op implementations:
def op_section(self, op: 'RiscVPseudoOpToken'): def op_section(self, op: 'RiscVPseudoOpToken'):
"""
handles a .section token
:param op: The token
"""
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
name = op.args[0][1:] name = op.args[0][1:]
ASSERT_IN(name, ('data', 'rodata', 'text')) ASSERT_IN(name, ('data', 'rodata', 'text'))
getattr(self, 'op_' + name)(op) getattr(self, 'op_' + name)(op)
def op_text(self, op: 'RiscVPseudoOpToken' = None): def op_text(self, op: 'RiscVPseudoOpToken' = None):
self.set_sec('text', MemoryFlags(read_only=True, executable=True), cls=InstructionMemorySection) """
handles a .text token
:param op: The token
"""
self._set_sec('text', MemoryFlags(read_only=True, executable=True), cls=InstructionMemorySection)
def op_data(self, op: 'RiscVPseudoOpToken' = None): def op_data(self, op: 'RiscVPseudoOpToken' = None):
self.set_sec('data', MemoryFlags(read_only=False, executable=False)) """
handles a .data token
:param op: The token
"""
self._set_sec('data', MemoryFlags(read_only=False, executable=False))
def op_rodata(self, op: 'RiscVPseudoOpToken' = None): def op_rodata(self, op: 'RiscVPseudoOpToken' = None):
self.set_sec('rodata', MemoryFlags(read_only=True, executable=False)) """
handles a .rodata token
:param op: The token
"""
self._set_sec('rodata', MemoryFlags(read_only=True, executable=False))
def op_space(self, op: 'RiscVPseudoOpToken'): def op_space(self, op: 'RiscVPseudoOpToken'):
"""
handles a .space token. Inserts empty space into the current (data or rodata) section
:param op: The token
"""
ASSERT_IN(self.active_section, ('data', 'rodata')) ASSERT_IN(self.active_section, ('data', 'rodata'))
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
size = parse_numeric_argument(op.args[0]) size = parse_numeric_argument(op.args[0])
self.curr_sec().add(bytearray(size)) self._curr_sec().add(bytearray(size))
def op_ascii(self, op: 'RiscVPseudoOpToken'): def op_ascii(self, op: 'RiscVPseudoOpToken'):
"""
handles a .ascii token. Inserts ascii encoded text into the currrent data section
:param op: The token
"""
ASSERT_IN(self.active_section, ('data', 'rodata')) ASSERT_IN(self.active_section, ('data', 'rodata'))
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
str = op.args[0][1:-1].encode('ascii').decode('unicode_escape') str = op.args[0][1:-1].encode('ascii').decode('unicode_escape')
self.curr_sec().add(bytearray(str, 'ascii')) self._curr_sec().add(bytearray(str, 'ascii'))
def op_asciiz(self, op: 'RiscVPseudoOpToken'): def op_asciiz(self, op: 'RiscVPseudoOpToken'):
"""
handles a .ascii token. Inserts nullterminated ascii encoded text into the currrent data section
:param op: The token
"""
ASSERT_IN(self.active_section, ('data', 'rodata')) ASSERT_IN(self.active_section, ('data', 'rodata'))
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
str = op.args[0][1:-1].encode('ascii').decode('unicode_escape') str = op.args[0][1:-1].encode('ascii').decode('unicode_escape')
self.curr_sec().add(bytearray(str + '\0', 'ascii')) self._curr_sec().add(bytearray(str + '\0', 'ascii'))
def op_stack(self, op: 'RiscVPseudoOpToken'): def op_stack(self, op: 'RiscVPseudoOpToken'):
"""
handles a .stack token. Sets the stack size preferences
:param op: The token
"""
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
size = parse_numeric_argument(op.args[0]) size = parse_numeric_argument(op.args[0])
self.stack_pref = size self.stack_pref = size
def op_global(self, op: 'RiscVPseudoOpToken'): def op_global(self, op: 'RiscVPseudoOpToken'):
"""
handles a .global token. Marks the token as global
:param op: The token
"""
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
name = op.args[0] name = op.args[0]
self.globals.append(name) self.globals.append(name)
def op_set(self, op: 'RiscVPseudoOpToken'): def op_set(self, op: 'RiscVPseudoOpToken'):
"""
handles a .set name, val token. Sets the symbol name to val
:param op: The token
"""
ASSERT_LEN(op.args, 2) ASSERT_LEN(op.args, 2)
name = op.args[0] name = op.args[0]
val = parse_numeric_argument(op.args[1]) val = parse_numeric_argument(op.args[1])
self.symbols[name] = ('_static_', val) self.symbols[name] = ('_static_', val)
def op_align(self, op: 'RiscVPseudoOpToken'): def op_align(self, op: 'RiscVPseudoOpToken'):
"""
handles an align token. Currently a nop (just not implemented fully yet, as linker handles most alignement tasks)
:param op: The token
"""
pass pass
def op_word(self, op: 'RiscVPseudoOpToken'): def op_word(self, op: 'RiscVPseudoOpToken'):
ASSERT_LEN(op.args, 1) ASSERT_LEN(op.args, 1)
val = parse_numeric_argument(op.args[0]) val = parse_numeric_argument(op.args[0])
self.curr_sec().add(int_to_bytes(val, 4)) self._curr_sec().add(int_to_bytes(val, 4))
## Section handler code ## Section handler code
def set_sec(self, name: str, flags: MemoryFlags, cls=MemorySection): def _set_sec(self, name: str, flags: MemoryFlags, cls=MemorySection):
if name not in self.sections: if name not in self.sections:
self.sections[name] = cls(name, flags) self.sections[name] = cls(name, flags)
self.active_section = name self.active_section = name
def curr_sec(self): def _curr_sec(self):
return self.sections[self.active_section] return self.sections[self.active_section]

@ -1,5 +1,11 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from .Config import RunConfig from .Config import RunConfig
from .Executable import Executable, LoadedExecutable, LoadedMemorySection from .Executable import Executable, LoadedExecutable, LoadedMemorySection, LoadedInstruction
from .helpers import align_addr from .helpers import align_addr
from .Exceptions import OutOfMemoryException from .Exceptions import OutOfMemoryException
from .colors import * from .colors import *
@ -7,13 +13,33 @@ from typing import Dict, List, Tuple, Optional
class MMU: class MMU:
"""
The MemoryManagementUnit (handles loading binaries, and reading/writing data)
"""
"""
The maximum size of the memory in bytes
"""
max_size = 0xFFFFFFFF max_size = 0xFFFFFFFF
# make each block accessible by it's base addr
"""
A list of all loaded memory sections
"""
sections: List[LoadedMemorySection] sections: List[LoadedMemorySection]
"""
A list of all loaded executables
"""
binaries: List[LoadedExecutable] binaries: List[LoadedExecutable]
"""
The last loaded executable (the next executable is inserted directly after this one)
"""
last_bin: Optional[LoadedExecutable] = None last_bin: Optional[LoadedExecutable] = None
"""
The global symbol table
"""
global_symbols: Dict[str, int] global_symbols: Dict[str, int]
def __init__(self, conf: RunConfig): def __init__(self, conf: RunConfig):
@ -23,7 +49,13 @@ class MMU:
self.conf = conf self.conf = conf
self.global_symbols = dict() self.global_symbols = dict()
def load_bin(self, bin: Executable): 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
"""
if self.last_bin is None: if self.last_bin is None:
addr = 0x100 # start at 0x100 instead of 0x00 addr = 0x100 # start at 0x100 instead of 0x00
else: else:
@ -54,31 +86,63 @@ class MMU:
return loaded_bin return loaded_bin
def get_sec_containing(self, addr: int) -> Optional[LoadedMemorySection]: 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
"""
for sec in self.sections: for sec in self.sections:
if sec.base <= addr < sec.base + sec.size: if sec.base <= addr < sec.base + sec.size:
return sec return sec
return None return None
def read_ins(self, addr: int): def read_ins(self, addr: int) -> LoadedInstruction:
"""
Read a single instruction located at addr
:param addr: The location
:return: The Instruction
"""
sec = self.get_sec_containing(addr) sec = self.get_sec_containing(addr)
return sec.read_instruction(addr - sec.base) return sec.read_instruction(addr - sec.base)
def read(self, addr: int, size: int): 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
"""
sec = self.get_sec_containing(addr) sec = self.get_sec_containing(addr)
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):
"""
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)
"""
sec = self.get_sec_containing(addr) sec = self.get_sec_containing(addr)
return sec.write(addr - sec.base, size, data) return sec.write(addr - sec.base, size, data)
# debugging interactions:
def dump(self, addr, *args, **kwargs): def dump(self, addr, *args, **kwargs):
"""
Dumpy the memory contents
:param addr: The address at which to dump
:param args: args for the dump function of the loaded memory section
:param kwargs: kwargs for the dump function of the loaded memory section
"""
sec = self.get_sec_containing(addr) sec = self.get_sec_containing(addr)
if sec is None: if sec is None:
return return
sec.dump(addr, *args, **kwargs) 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) 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]))

@ -1,17 +1,35 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from .Config import RunConfig from .Config import RunConfig
from .helpers import * from .helpers import *
from collections import defaultdict from collections import defaultdict
from .Exceptions import InvalidRegisterException from .Exceptions import InvalidRegisterException
class Registers: class Registers:
"""
Represents a bunch of registers
"""
def __init__(self, conf: RunConfig): def __init__(self, conf: RunConfig):
"""
Initialize the register configuration, respecting the RunConfig conf
:param conf: The RunConfig
"""
self.vals = defaultdict(lambda: 0) self.vals = defaultdict(lambda: 0)
self.last_set = None self.last_set = None
self.last_read = None self.last_read = None
self.conf = conf self.conf = conf
def dump(self, full=False): def dump(self, full=False):
named_regs = [self.reg_repr(reg) for reg in Registers.named_registers()] """
Dump all registers to stdout
:param full: If True, floating point registers are dumped too
"""
named_regs = [self._reg_repr(reg) for reg in Registers.named_registers()]
lines = [[] for i in range(12)] lines = [[] for i in range(12)]
if not full: if not full:
@ -31,7 +49,7 @@ class Registers:
lines[i].append(" " * 15) lines[i].append(" " * 15)
else: else:
reg = '{}{}'.format(name, i) reg = '{}{}'.format(name, i)
lines[i].append(self.reg_repr(reg)) lines[i].append(self._reg_repr(reg))
print("Registers[{},{}](".format( print("Registers[{},{}](".format(
FMT_ORANGE + FMT_UNDERLINE + 'read' + FMT_NONE, FMT_ORANGE + FMT_UNDERLINE + 'read' + FMT_NONE,
@ -49,9 +67,12 @@ class Registers:
print(")") print(")")
def dump_reg_a(self): def dump_reg_a(self):
print("Registers[a]:" + " ".join(self.reg_repr('a{}'.format(i)) for i in range(8))) """
Dump the a registers
"""
print("Registers[a]:" + " ".join(self._reg_repr('a{}'.format(i)) for i in range(8)))
def reg_repr(self, reg): def _reg_repr(self, reg):
txt = '{:4}=0x{:08X}'.format(reg, self.get(reg, False)) txt = '{:4}=0x{:08X}'.format(reg, self.get(reg, False))
if reg == 'fp': if reg == 'fp':
reg = 's0' reg = 's0'
@ -65,7 +86,14 @@ 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): def set(self, reg, val, mark_set=True) -> bool:
"""
Set a register content to val
:param reg: The register to set
:param val: The new value
:param mark_set: If True, marks this register as "last accessed" (only used internally)
:return: If the operation was successful
"""
if reg == 'zero': if reg == 'zero':
print("[Registers.set] trying to set read-only register: {}".format(reg)) print("[Registers.set] trying to set read-only register: {}".format(reg))
return False return False
@ -77,8 +105,15 @@ class Registers:
if mark_set: if mark_set:
self.last_set = reg self.last_set = reg
self.vals[reg] = val self.vals[reg] = val
return True
def get(self, reg, mark_read=True): def get(self, reg, mark_read=True):
"""
Retuns the contents of register reg
:param reg: The register name
:param mark_read: If the register should be markes as "last read" (only used internally)
:return: The contents of register reg
"""
if reg not in Registers.all_registers(): if reg not in Registers.all_registers():
raise InvalidRegisterException(reg) raise InvalidRegisterException(reg)
if reg == 'fp': if reg == 'fp':
@ -89,6 +124,10 @@ class Registers:
@staticmethod @staticmethod
def all_registers(): def all_registers():
"""
Return a list of all valid registers
:return: The list
"""
return ['zero', 'ra', 'sp', 'gp', 'tp', 's0', 'fp', return ['zero', 'ra', 'sp', 'gp', 'tp', 's0', 'fp',
't0', 't1', 't2', 't3', 't4', 't5', 't6', 't0', 't1', 't2', 't3', 't4', 't5', 't6',
's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
@ -99,4 +138,8 @@ class Registers:
@staticmethod @staticmethod
def named_registers(): def named_registers():
"""
Return all named registers
:return: The list
"""
return ['zero', 'ra', 'sp', 'gp', 'tp', 'fp'] return ['zero', 'ra', 'sp', 'gp', 'tp', 'fp']

@ -1,3 +1,9 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, IO from typing import Dict, IO
import sys import sys
@ -11,6 +17,9 @@ import typing
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from . import CPU from . import CPU
"""
All available syscalls (mapped id->name)
"""
SYSCALLS = { SYSCALLS = {
63: 'read', 63: 'read',
64: 'write', 64: 'write',
@ -19,6 +28,9 @@ SYSCALLS = {
1025: 'close', 1025: 'close',
} }
"""
All available file open modes
"""
OPEN_MODES = { OPEN_MODES = {
0: 'rb', 0: 'rb',
1: 'wb', 1: 'wb',
@ -30,6 +42,9 @@ OPEN_MODES = {
@dataclass(frozen=True) @dataclass(frozen=True)
class Syscall: class Syscall:
"""
Represents a syscall
"""
id: int id: int
registers: Registers registers: Registers
cpu: 'CPU' cpu: 'CPU'
@ -46,7 +61,21 @@ class Syscall:
def ret(self, code): def ret(self, code):
self.registers.set('a0', code) self.registers.set('a0', code)
def get_syscall_symbols():
"""
Retuns a dictionary of all syscall symbols (SCALL_<name> -> id)
:return:
"""
return {
('SCALL_' + name.upper()): num for num, name in SYSCALLS.items()
}
class SyscallInterface: class SyscallInterface:
"""
Handles syscalls
"""
open_files: Dict[int, IO] open_files: Dict[int, IO]
next_open_handle: int next_open_handle: int
@ -163,14 +192,12 @@ class SyscallInterface:
return scall.ret(0) return scall.ret(0)
def exit(self, scall: Syscall): def exit(self, scall: Syscall):
"""
Exit syscall. Exits the system with status code a0
"""
scall.cpu.exit = True scall.cpu.exit = True
scall.cpu.exit_code = scall.registers.get('a0') scall.cpu.exit_code = scall.registers.get('a0')
def get_syscall_symbols(self):
return {
('SCALL_' + name.upper()): num for num, name in SYSCALLS.items()
}
def __repr__(self): def __repr__(self):
return "{}(\n\tfiles={}\n)".format( return "{}(\n\tfiles={}\n)".format(
self.__class__.__name__, self.__class__.__name__,

@ -1,3 +1,9 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
import re import re
from enum import IntEnum from enum import IntEnum
from typing import List from typing import List
@ -75,6 +81,9 @@ def split_accepting_quotes(string, at=REG_ARG_SPLIT, quotes=('"', "'")):
class RiscVInput: class RiscVInput:
"""
Represents an Assembly file
"""
def __init__(self, content: str, name: str): def __init__(self, content: str, name: str):
self.content = content self.content = content
self.pos = 0 self.pos = 0
@ -240,10 +249,13 @@ class RiscVPseudoOpToken(RiscVToken):
class RiscVTokenizer: class RiscVTokenizer:
def __init__(self, input: RiscVInput, instructions: List[str]): """
self.input = input A tokenizer for the RISC-V syntax of a given CPU
"""
def __init__(self, input_assembly: RiscVInput, instructions: List[str]):
self.input = input_assembly
self.tokens: List[RiscVToken] = [] self.tokens: List[RiscVToken] = []
self.name = input.name self.name = input_assembly.name
self.instructions = instructions self.instructions = instructions
def tokenize(self): def tokenize(self):

@ -1,3 +1,13 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
This package aims at providing an all-round usable RISC-V emulator and debugger
It contains everything needed to run assembly files, so you don't need any custom compilers or toolchains
"""
from .Exceptions import RiscemuBaseException, LaunchDebuggerException, InvalidSyscallException, LinkerException, \ from .Exceptions import RiscemuBaseException, LaunchDebuggerException, InvalidSyscallException, LinkerException, \
ParseException, NumberFormatException, InvalidRegisterException, MemoryAccessException, OutOfMemoryException ParseException, NumberFormatException, InvalidRegisterException, MemoryAccessException, OutOfMemoryException

@ -1,3 +1,9 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
# Colors # Colors
FMT_RED = '\033[31m' FMT_RED = '\033[31m'

@ -1,3 +1,10 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
import typing import typing
from .Registers import Registers from .Registers import Registers
from .colors import FMT_DEBUG, FMT_NONE from .colors import FMT_DEBUG, FMT_NONE

@ -1,6 +1,10 @@
from math import log10, ceil, log """
from .Exceptions import NumberFormatException RiscEmu (c) 2021 Anton Lydike
from .colors import *
SPDX-License-Identifier: BSD-2-Clause
"""
from math import log10, ceil
from .Exceptions import * from .Exceptions import *

Loading…
Cancel
Save