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!")