How to use the gilgamesh.snes.state.UnknownReason function in gilgamesh

To help you get started, we’ve selected a few gilgamesh examples, based on popular ways it is used in public projects.

Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.

github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
def call(self, i: Instruction, targets=None) -> bool:
        # Keep track of the state before the call.
        targets = self._calculate_targets(i, targets)
        saved_state, saved_state_change = copy(self.state), copy(self.state_change)
        possible_states = set()

        for _, target in targets:
            if target is None or self.rom.is_ram(target):
                # If we can't reliably derive the address of the subroutine
                # being called, we're left in an unknown state.
                return self._unknown_subroutine_state(
                    i, unknown_reason=UnknownReason.INDIRECT_JUMP
                )

            # Run a parallel instance of the CPU to execute
            # the subroutine that is being called.
            cpu = self.copy(new_subroutine=True)
            call_size = 2 if i.operation in (Op.JSR, Op.RTS) else 3
            cpu.stack.push(i, i.pc, call_size)
            cpu.stack_trace.append(self.subroutine_pc)
            cpu.subroutine_pc = target
            cpu.pc = target

            # Emulate the called subroutine.
            self.log.add_reference(i, target)
            self.log.add_subroutine(target, stack_trace=cpu.stack_trace)
            cpu.run()
github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
def _propagate_subroutine_state(
        self, call_pc: int, subroutine_pc: int
    ) -> Tuple[bool, UnknownReason]:
        known = True
        unknown_reason = UnknownReason.KNOWN

        # If the user defined a state assertion for the current instruction.
        if call_pc in self.log.instruction_assertions:
            return (known, unknown_reason)  # Execution can proceed.

        # If the subroutine can return in more than one distinct state, or its
        # state is unknown, we can't reliably propagate the state to the caller.
        subroutine = self.log.subroutines[subroutine_pc]
        return_states = subroutine.simplify_return_states(self.state)

        # Multiple known return states.
        if len([s for s in return_states if not s.unknown]) > 1:
            known = False
            unknown_reason = UnknownReason.MULTIPLE_RETURN_STATES
            self.log.add_subroutine_state(
                self.subroutine_pc, call_pc, StateChange(unknown_reason=unknown_reason)
github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
def execute(self, instruction: Instruction) -> bool:
        self.pc += instruction.size

        # See if we can learn something about the *required*
        # state of the CPU based on the current instruction.
        self._derive_state_inference(instruction)

        if instruction.is_return:
            return self.ret(instruction)
        elif instruction.is_interrupt:
            self._unknown_subroutine_state(
                instruction, unknown_reason=UnknownReason.SUSPECT_INSTRUCTION
            )
            return False
        elif instruction.is_jump:
            self.jump(instruction)
            return False
        elif instruction.is_call:
            return self.call(instruction)
        elif instruction.is_branch:
            self.branch(instruction)
        elif instruction.is_sep_rep:
            self.sep_rep(instruction)
        elif instruction.does_change_stack:
            self.change_stack(instruction)
        elif instruction.does_change_a:
            self.change_a(instruction)
        elif instruction.is_pop:
github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
known = True
        unknown_reason = UnknownReason.KNOWN

        # If the user defined a state assertion for the current instruction.
        if call_pc in self.log.instruction_assertions:
            return (known, unknown_reason)  # Execution can proceed.

        # If the subroutine can return in more than one distinct state, or its
        # state is unknown, we can't reliably propagate the state to the caller.
        subroutine = self.log.subroutines[subroutine_pc]
        return_states = subroutine.simplify_return_states(self.state)

        # Multiple known return states.
        if len([s for s in return_states if not s.unknown]) > 1:
            known = False
            unknown_reason = UnknownReason.MULTIPLE_RETURN_STATES
            self.log.add_subroutine_state(
                self.subroutine_pc, call_pc, StateChange(unknown_reason=unknown_reason)
            )
        # Unknown state with some reason.
        elif any(s.unknown for s in return_states):
            known = False
            unknown_state = [s for s in return_states if s.unknown][0]
            unknown_reason = unknown_state.unknown_reason
            self.log.add_subroutine_state(self.subroutine_pc, call_pc, unknown_state)

        # Unique return state, apply it.
        if known:
            assert len(return_states) == 1
            self._apply_state_change(return_states.pop())

        return (known, unknown_reason)
github AndreaOrru / gilgamesh / gilgamesh / subroutine.py View on Github external
def simplify_return_states(self, state: State) -> List[StateChange]:
        if len(self.state_changes) == 0:
            self.is_recursive = True
            return [StateChange(unknown_reason=UnknownReason.RECURSION)]

        # Simplify the state changes based on the caller state.
        changes = set()
        for change in self.state_changes.values():
            changes.add(change.simplify(state))
        return list(changes)
github AndreaOrru / gilgamesh / gilgamesh / snes / state.py View on Github external
def unknown(self):
        return self.unknown_reason != UnknownReason.KNOWN
github AndreaOrru / gilgamesh / gilgamesh / log.py View on Github external
def unified_state_suggestion():
            unified = i.subroutine.unified_state_change
            if unified is not None:
                return [("subroutine", unified)]
            elif i.subroutine.does_save_state_in_incipit:
                return [("subroutine", StateChange())]
            return []

        if not i.state_change_after.unknown:
            return []
        if i.has_asserted_state_change or i.asserted_subroutine_state_change:
            return []
        reason = i.state_change_after.unknown_reason

        if i.is_call and reason == UnknownReason.INDIRECT_JUMP:
            return [*jump_table_suggestion(), ("instruction", StateChange())]

        elif i.is_jump and reason == UnknownReason.INDIRECT_JUMP:
            if i.subroutine.does_save_state_in_incipit:
                return [*jump_table_suggestion(), ("subroutine", StateChange())]
            else:
                return [*jump_table_suggestion(), *unified_state_suggestion()]

        elif i.is_return and reason == UnknownReason.STACK_MANIPULATION:
            return unified_state_suggestion()

        elif i.is_return and reason == UnknownReason.INDIRECT_JUMP:
            if i.ret_indirect_type == RetIndirectType.CALL:
                return [*jump_table_suggestion(), ("instruction", StateChange())]
            elif i.ret_indirect_type == RetIndirectType.JUMP:
                return [*jump_table_suggestion(), *unified_state_suggestion()]
github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
def pop(self, i: Instruction) -> bool:
        if i.operation == Op.PLP:
            entry = self.stack.pop_one()
            if entry.instruction and entry.instruction.operation == Op.PHP:
                self.state, self.state_change = entry.data
            # We can't trust the disassembly if we don't know
            # which state the PLP instruction is restoring.
            else:
                return self._unknown_subroutine_state(
                    i,
                    unknown_reason=UnknownReason.STACK_MANIPULATION,
                    stack_manipulator=entry.instruction,
                )

        elif i.operation in (Op.PLX, Op.PLY):
            self.stack.pop(self.state.x_size)
        elif i.operation == Op.PLB:
            self.stack.pop_one()
        elif i.operation == Op.PLD:
            self.stack.pop(2)
        else:
            assert False
        return True
github AndreaOrru / gilgamesh / gilgamesh / app.py View on Github external
def change_sort(t: Tuple[int, StateChange]) -> int:
            return {
                UnknownReason.STACK_MANIPULATION: 1,
                UnknownReason.RECURSION: 2,
            }.get(t[1].unknown_reason, 0)
github AndreaOrru / gilgamesh / gilgamesh / snes / cpu.py View on Github external
def _unknown_subroutine_state(
        self,
        instruction: Instruction,
        unknown_reason: Optional[UnknownReason] = None,
        stack_manipulator: Optional[Instruction] = None,
    ) -> bool:
        # Check if the user defined a state assertion for the current instruction.
        if instruction.pc in self.log.instruction_assertions:
            return True  # Execution can proceed.

        # No custom assertion, we need to stop here.
        unknown_reason = unknown_reason or UnknownReason.UNKNOWN
        self.state_change = StateChange(unknown_reason=unknown_reason)
        self.log.add_subroutine_state(
            self.subroutine_pc, instruction.pc, copy(self.state_change)
        )

        # If the unknown state is due to stack manipulation:
        if unknown_reason == UnknownReason.STACK_MANIPULATION:
            # If we know which instruction performed the
            # manipulation, we flag it.
            if stack_manipulator:
                self.subroutine.has_stack_manipulation = True
                stack_manipulator.stack_manipulation = (
                    StackManipulation.CAUSES_UNKNOWN_STATE
                )

        return False