more work on syscalls
This commit is contained in:
parent
a69cc7d346
commit
a483db65c7
@ -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
|
||||
from . import MMU
|
||||
self.pc = 0
|
||||
self.exit = False
|
||||
self.exit_code = 0
|
||||
self.conf = conf
|
||||
|
||||
self.mmu = MMU(conf)
|
||||
self.regs = Registers()
|
||||
self.pc = 0
|
||||
self.exit = False
|
||||
self.conf = conf
|
||||
|
||||
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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
48
riscemu/Syscall.py
Normal file
48
riscemu/Syscall.py
Normal file
@ -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')
|
@ -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'
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user