diff --git a/riscemu/CPU.py b/riscemu/CPU.py index 794b275..bb385bc 100644 --- a/riscemu/CPU.py +++ b/riscemu/CPU.py @@ -5,6 +5,7 @@ from .Exceptions import * from .helpers import * from .Config import RunConfig from .Registers import Registers +from .Syscall import SyscallInterface, Syscall import typing if typing.TYPE_CHECKING: @@ -14,14 +15,14 @@ if typing.TYPE_CHECKING: class CPU: def __init__(self, conf: RunConfig): - from . import MMU, Executable, LoadedExecutable, LoadedInstruction - - self.mmu = MMU(conf) - self.regs = Registers() + from . import MMU self.pc = 0 self.exit = False + self.exit_code = 0 self.conf = conf + self.mmu = MMU(conf) + self.regs = Registers() self.syscall_int = SyscallInterface() def load(self, e: 'Executable'): @@ -215,6 +216,7 @@ class CPU: INS_NOT_IMPLEMENTED(ins) def instruction_scall(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 0) syscall = Syscall(self.regs.get('a7'), self.regs) self.syscall_int.handle_syscall(syscall) @@ -235,14 +237,3 @@ class CPU: if method.startswith('instruction_'): yield method[12:] - -@dataclass(frozen=True) -class Syscall: - id: int - registers: Registers - - -class SyscallInterface: - def handle_syscall(self, scall: Syscall): - print("syscall {} received!".format(scall.id)) - scall.registers.dump_reg_a() diff --git a/riscemu/Config.py b/riscemu/Config.py index 4c8a94e..6cd34e4 100644 --- a/riscemu/Config.py +++ b/riscemu/Config.py @@ -7,3 +7,7 @@ class RunConfig: color: bool = True preffered_stack_size: Optional[int] = None debug_instruction: bool = True + # allowed syscalls + scall_input: bool = True + scall_fs: bool = False + diff --git a/riscemu/Exceptions.py b/riscemu/Exceptions.py index c57b376..d1f400f 100644 --- a/riscemu/Exceptions.py +++ b/riscemu/Exceptions.py @@ -99,11 +99,23 @@ class InvalidRegisterException(RiscemuBaseException): ) +class InvalidSyscallException(RiscemuBaseException): + def __init__(self, scall): + self.scall = scall + + def message(self): + return "{}(Invalid syscall: {})".format( + self.__class__.__name__, + self.scall + ) + + + def INS_NOT_IMPLEMENTED(ins): raise UnimplementedInstruction(ins) -class NumberFormatException(RiscemuBaseException): +class NumberFormatException(RiscemuBaseException): def __init__(self, msg): super().__init__() self.msg = msg diff --git a/riscemu/Syscall.py b/riscemu/Syscall.py new file mode 100644 index 0000000..fe0909a --- /dev/null +++ b/riscemu/Syscall.py @@ -0,0 +1,48 @@ +from dataclasses import dataclass +from .Registers import Registers +from .Exceptions import InvalidSyscallException + +import typing + +if typing.TYPE_CHECKING: + from . import CPU + +SYSCALLS = { + 63: 'read', + 64: 'write', + 93: 'exit' +} + + +@dataclass(frozen=True) +class Syscall: + id: int + registers: Registers + cpu: 'CPU' + + @property + def name(self): + return SYSCALLS.get(self.id, "unknown") + + def __repr__(self): + return "Syscall(id={}, name={})".format( + self.id, self.name + ) + + +class SyscallInterface: + def handle_syscall(self, scall: Syscall): + if getattr(self, scall.name): + getattr(self, scall.name)(scall) + else: + raise InvalidSyscallException(scall) + + def read(self, scall: Syscall): + pass + + def write(self, scall: Syscall): + pass + + def exit(self, scall: Syscall): + scall.cpu.exit = True + scall.cpu.exit_code = scall.registers.get('a0') diff --git a/riscemu/helpers.py b/riscemu/helpers.py index caf2561..4ae0dce 100644 --- a/riscemu/helpers.py +++ b/riscemu/helpers.py @@ -41,8 +41,13 @@ def int_from_bytes(bytes, unsigned=False): return num +# Colors + FMT_ORANGE = '\033[33m' FMT_GRAY = '\033[37m' +FMT_CYAN = '\033[36m' +FMT_GREEN = '\033[32m' FMT_BOLD = '\033[1m' +FMT_MAGENTA = '\033[35m' FMT_NONE = '\033[0m' FMT_UNDERLINE = '\033[4m' \ No newline at end of file diff --git a/riscemu/main.py b/riscemu/main.py index e69de29..ffbb477 100644 --- a/riscemu/main.py +++ b/riscemu/main.py @@ -0,0 +1,52 @@ +if __name__ == '__main__': + from . import * + from .helpers import * + import argparse + + parser = argparse.ArgumentParser(description='RISC-V Userspace parser and emulator') + parser.add_argument('file', metavar='file.asm', type=str, help='The assembly file to interpret and run') + + # RunConfig parameters + parser.add_argument('color', type=bool, help='Colored output', default=True) + parser.add_argument('default_stack_size', type=int, help='Default stack size of loaded programs', default=None, + metavar='default-stack-size') + parser.add_argument('debug_instruction', type=bool, default=True, metavar='debug-instruction', + help='Adds the dbg instruction, which launches an interactive debuggin session, smilar to ' + 'a breakpoint.') + + parser.add_argument('print_tokens', metavar='print-tokens', type=bool, help='Print tokens after tokenization', + default=False) + + args = parser.parse_args() + + cfg = RunConfig( + color=args.color, + preffered_stack_size=args.default_stack_size, + debug_instruction=args.debug_instruction + ) + + if cfg.color: + FMT_PRINT = FMT_BOLD + FMT_MAGENTA + else: + FMT_NONE = "" + FMT_PRINT = "" + + with open(args.file, 'r') as f: + asm = f.read() + + tk = RiscVTokenizer(RiscVInput(asm)) + tk.tokenize() + + if args.print_tokens: + print(FMT_PRINT + "Tokens:" + FMT_NONE) + for token in tk.tokens: + print(token) + + executable = ExecutableParser(tk).parse().get_execuable() + + print(FMT_PRINT + "Executable:" + FMT_NONE, executable) + + cpu = CPU(cfg) + le = cpu.load(executable) + cpu.run_loaded(le) +