Exemple #1
0
class branch_code(Opcode):
    """
    All Branch operations will inherit from branch_code.
    there will be a conditional function to be overidden across child classes
    one must also redefine self.cond_code across child classes
    Additionally, a redefinition of command_matches(), from_str(), and disassemble_instruction() will suffice


    BCC:     Operation: If Condition True Then PC + dn → PC

    Syntax:  Bcc < label >

    Size = (Byte, Word, Long*)
    *(MC68020, MC68030, and MC68040 only)

    Description: If the specified condition is true, program execution continues at location (PC) + displacement. The program counter contains the address of the instruction word for
    the Bcc instruction plus two. The displacement is a twos-complement integer that
    represents the relative distance in bytes from the current program counter to the
    destination program counter. If the 8-bit displacement field in the instruction word is
    zero, a 16-bit displacement (the word immediately following the instruction) is used. If
    the 8-bit displacement field in the instruction word is all ones ($FF), the 32-bit
    displacement (long word immediately following the instruction) is used. Condition code
    cc specifies one of the following conditional tests (refer to Table 3-19 for more
                                                         information on these conditional tests):

    Condition Codes:    Not affected.

    Instruction Fields:
        Condition field—The binary code for one of the conditions listed in the table.
        8-Bit Displacement field—Twos complement integer specifying the number of bytes
            between the branch instruction and the next instruction to be executed if the
            condition is met.
        16-Bit Displacement field—Used for the displacement when the 8-bit displacement
            field contains $00.
        32-Bit Displacement field—Used for the displacement when the 8-bit displacement
            field contains $FF.

    NOTE    A branch to the immediately following instruction automatically
    uses the 16-bit displacement format because the 8-bit
    displacement field contains $00 (zero offset).
    """

    operand = None
    address = None
    cond_code = None
    offset = None
    size = None
    
    def __init__(self, params: list):
        assert len(params) == 2
        assert isinstance(params[0], int)
        assert isinstance(params[1], int)

        # Operand = target address
        self.operand = params[0]
        self.address = params[1]

        # make offset out of operand
        self.offset = int(self.operand) - int(self.address + 2)

        # get the size
        self.size = OpSize.BYTE if int(self.offset) < 128 else \
            (OpSize.WORD if int(self.offset) < 326778 else OpSize.LONG)

        # offset to bits
        self.offset = Bits(int=self.offset,
                           length=(self.size.get_number_of_bytes() * 8)).int

        # Note on Page 130 of the NXP 68K Manual
        # 0x00 8 bit offset defaults to word size
        if self.offset == 0:
            self.size = OpSize.WORD

    @abstractmethod
    def conditional(self, simulator: M68K):
        """
        This is a method stub to be overridden in child classes
        Checks the status register according to the specifics of 
        whatever Bcc variant this is.
        :param simulator: the M68K sim object that contains the CCR we are checking
        :return bool: returns true or false 
        """
        raise NotImplementedError("Branch Operation did not override Parent's conditional stub")
        
    def assemble(self) -> bytearray:
        """
        Assembles opcode into Hex
        :return: Hex for this operation
        """
        
        # yes you actually need to subclass this
        assert self.cond_code != None, 'No cond_code, assemble called on base clas of branch op?'

        # hold size
        extraSize = 2
        
        # clear for >8 bit offset
        lastbyte = '\x00'
        
        # if offset is 8 bits than it is inside the opword
        if self.size == OpSize.BYTE:
            lastbyte = self.offset.to_bytes(length=1, byteorder='big', signed=True)
            extraSize = 0

        # if offset is 32 bits than the last chunk of opword is FF
        if self.size == OpSize.LONG:
            lastbyte = '\xFF'
            extraSize = 4
            
        # first byte = 6+cond_code
        firstbyte =  ord('\x60')
        firstbyte |= ord(self.cond_code)
        
        opword = firstbyte << 8
        opword |= ord(lastbyte)

        # convert to bytearray
        opword = bytearray(opword.to_bytes(2, 'big'))

        # stop here if 8 bit offset
        if self.size == OpSize.BYTE:
            return opword

        # add offset
        opword.extend(self.offset.to_bytes(extraSize, "big"))

        return opword

    
    def execute(self, simulator: M68K):
        """
        Commands the Simulator according to this opcode
        :param simulator:
        :return: nothing
        """

        # check conditional
        if self.conditional(simulator):
            self.offset += 2
            simulator.increment_program_counter(self.offset)

    def __str__(self):
        """
        converts command to a string
        :return: string rep of command
        """

        return "{s} ${d}".format(
                s = COND_CODE_TO_OPCODE.get(self.cond_code),
                d = hex(self.operand or
                     (self.address + (self.size if self.size.get_number_of_bytes() > 1 else 0) + self.offset)))

    @classmethod
    @abstractmethod
    def command_matches(cls, command: str) -> bool:
        """
        Checks whether a command string is an instance of this command type
        :param command: The command string to check (e.g. 'MOVE.B', 'LEA', etc.)
        :return: Whether the string is an instance of this command type
        """
        raise NotImplementedError("Branch Operation did not override parent class command_matches stub!")

    @classmethod
    def get_word_length(cls, command: str, parameters: str) -> int:
        """
        Gets what the end length of this command will be in memory
        :param command: The text of the command itself (e.g. "LEA", "MOVE.B", etc.)
        :param parameters: The parameters after the command
        :return: The length of the bytes in memory in words
        """
        
        # BIG NOTE: I am explicitly ignoring size codes because there are no defined flippy boys
        # in the opword for Bcc ops. Yeezy68k might let you define a size code, but even it
        # will more or less ignore whatever you put (reduces unnessesary .l to .w)

        parameters = parameters.split(',')
        for i in parameters:
            i = i.strip()

        parameters[0] = parse_literal(parameters[0])
        parameters[1] = int(parameters[1])

        offset = abs(int(parameters[0]) - int(parameters[1] + 2))
        size = 1 if offset < 128 else (2 if offset < 326778 else 3)

        return size
    
    @classmethod
    def is_valid(cls, command: str, parameters: str) -> (bool, list):
        """
        Tests whether the given command is valid

        :param command: The command itself (e.g. 'MOVE.B', 'LEA', etc.)
        :param parameters: The parameters after the command (such as the source and destination of a move)
        :return: Whether the given command is valid and a list of issues/warnings encountered
        """
        
        passes = True
        errors = []
        parameters = parameters.split(',')
        
        # See the BIG NOTE from get_word_length()
        # check param length
        if len(parameters) > 1:
            passes = False
            errors.append("Too many operands for Branch Operation: " + ', '.join(parameters))
        
        # check offset dest validity (odd read)
        operand = parse_literal(parameters[0].strip())
        if operand % 2 == 1:
            passes = False
            errors.append("Reading word at {d} will cause crash".format(d=operand))
            
        return (passes, errors)
    
    @classmethod
    def disassemble_instruction(cls, data: bytes) -> Opcode:
        """
        Parses some raw data into an instance of the opcode class

        :param data: The data used to convert into an opcode instance
        :return: The constructed instance or none if there was an error and
            the amount of data in words that was used (e.g. extra for immediate
            data) or 0 for not a match
        """
        raise NotImplementedError("Branch Operation did not override parent class disassemble_instruction stub!")


    @classmethod
    def from_str(cls, command: str, parameters: str):
        """
        Parses a command from text

        :param command: The command itself (e.g. 'MOVE.B', 'LEA', etc.)
        :param parameters: The parameters after the command (such as the source and destination of a move)
        :param address: Address of this operation 
        :return: The parsed command
        """
        raise NotImplementedError("Branch Operation did not override parent class from_str stub!")