diff --git a/riscemu/CPU.py b/riscemu/CPU.py index 7de1aa0..429341c 100644 --- a/riscemu/CPU.py +++ b/riscemu/CPU.py @@ -35,19 +35,19 @@ class CPU: self.instruction_sets: List['InstructionSet'] = list() self.instructions: Dict[str, Callable[[LoadedInstruction], None]] = dict() for set_class in instruction_sets: - ins_set = set_class() - self.instructions.update(ins_set.load(self)) + ins_set = set_class(self) + self.instructions.update(ins_set.load()) self.instruction_sets.append(ins_set) # provide global syscall symbols if option is set if conf.include_scall_symbols: self.mmu.global_symbols.update(self.syscall_int.get_syscall_symbols()) - def get_tokenizer(self, input): + def get_tokenizer(self, tokenizer_input): """ Returns a tokenizer that respects the language of the CPU """ - return RiscVTokenizer(input, self.all_instructions()) + return RiscVTokenizer(tokenizer_input, self.all_instructions()) def load(self, e: 'Executable'): """ @@ -93,23 +93,6 @@ class CPU: # this should never be reached, as unknown instructions are imparseable raise RuntimeError("Unknown instruction: {}".format(ins)) - def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: - """ - parses both rd, rs1, imm and rd, imm(rs1) arguments and returns (rd, imm+rs1) - (so a register and address tuple for memory instructions) - """ - if len(ins.args) == 3: - # handle rd, rs1, imm - rs1 = ins.get_reg(1) - imm = ins.get_imm(2) - else: - ASSERT_LEN(ins.args, 2) - ASSERT_IN("(", ins.args[1]) - imm, rs1 = ins.get_imm_reg(1) - # handle rd, imm(rs1) - rd = ins.get_reg(0) - return rd, self.regs.get(rs1) + imm - def all_instructions(self) -> List[str]: return list(self.instructions.keys()) diff --git a/riscemu/instructions/InstructionSet.py b/riscemu/instructions/InstructionSet.py index e52b453..5f7e6ea 100644 --- a/riscemu/instructions/InstructionSet.py +++ b/riscemu/instructions/InstructionSet.py @@ -8,16 +8,19 @@ class InstructionSet(ABC): """ Represents a collection of instructions """ - def __init__(self): - self.cpu: typing.Optional['CPU'] = None - self.mmu: typing.Optional['MMU'] = None - self.regs: typing.Optional['Registers'] = None - def load(self, cpu: 'CPU'): + def __init__(self, cpu: 'CPU'): self.cpu = cpu self.mmu = cpu.mmu self.regs = cpu.regs + def load(self) -> typing.Dict[str, typing.Callable[['LoadedInstruction'], None]]: + """ + This is called by the CPU once it instantiates this instruction set + + It returns a dictionary of all instructions in this instruction set, + pointing to the correct handler for it + """ return { name: ins for name, ins in self.get_instructions() } @@ -27,24 +30,46 @@ class InstructionSet(ABC): if member.startswith('instruction_'): yield member[12:].replace('_', '.'), getattr(self, member) - def parse_mem_ins(self, ins: 'LoadedInstruction'): - return self.cpu.parse_mem_ins(ins) + def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: + """ + parses both rd, rs, imm and rd, imm(rs) arguments and returns (rd, imm+rs1) + (so a register and address tuple for memory instructions) + """ + if len(ins.args) == 3: + # handle rd, rs1, imm + rs = self.get_reg_content(ins, 1) + imm = ins.get_imm(2) + else: + # handle rd, imm(rs1) + ASSERT_LEN(ins.args, 2) + ASSERT_IN("(", ins.args[1]) + imm, rs_name = ins.get_imm_reg(1) + rs = self.regs.get(rs_name) + rd = ins.get_reg(0) + return rd, rs + imm - def parse_rd_rs_rs(self, ins: 'LoadedInstruction'): + def parse_rd_rs_rs(self, ins: 'LoadedInstruction') -> Tuple[str, int, int]: + """ + Assumes the command is in rd, rs1, rs2 format + Returns the name of rd, and the values in rs1 and rs2 + """ ASSERT_LEN(ins.args, 3) return ins.get_reg(0), self.get_reg_content(ins, 1), self.get_reg_content(ins, 2) - def parse_rd_rs_imm(self, ins: 'LoadedInstruction'): + def parse_rd_rs_imm(self, ins: 'LoadedInstruction') -> Tuple[str, int, int]: + """ + Assumes the command is in rd, rs, imm format + Returns the name of rd, the value in rs and the immediate imm + """ ASSERT_LEN(ins.args, 3) return ins.get_reg(0), self.get_reg_content(ins, 1), ins.get_imm(2) - def get_reg_content(self, ins: 'LoadedInstruction', ind: int): + def get_reg_content(self, ins: 'LoadedInstruction', ind: int) -> int: """ get the register name from ins and then return the register contents """ return self.regs.get(ins.get_reg(ind)) - @property def pc(self): return self.cpu.pc @@ -57,4 +82,4 @@ class InstructionSet(ABC): return "InstructionSet[{}] with {} instructions".format( self.__class__.__name__, len(list(self.get_instructions())) - ) \ No newline at end of file + )