Source code for easier68k.core.opcodes.cmpi

from ...core.enum.ea_mode import EAMode
from ...core.enum.op_size import OpSize
from ...core.enum import ea_mode_bin
from ...core.enum.ea_mode_bin import parse_ea_from_binary
from ...simulator.m68k import M68K
from ...core.opcodes.opcode import Opcode
from ...core.util.split_bits import split_bits
from ...core.util import opcode_util
from ..util.parsing import parse_assembly_parameter
from ..models.assembly_parameter import AssemblyParameter
from ..enum.condition_status_code import ConditionStatusCode
from ..models.memory_value import MemoryValue, mask_value_for_length


[docs]class Cmpi(Opcode): """ CMPI: Compare Immediate Operation: Destination - Immediate Data -> cc Assembler Syntax: CMPI #<data>, <ea> Attributes: Size = (Byte, Word, Long) Description: Subtracts the immediate data from the destination operand and sets the condition codes according to the result; the destination location is not changed. The size of the operation may be specified as byte, word, or long. The size of the immediate data matches the operation size. Condition Codes: X - Not affected N - Set if the result is negative; cleared otherwise. Z - Set if the result is zero; cleared otherwise. V - Set if an overflow occurs; cleared otherwise. C - Set if a borrow occurs; cleared otherwise. Instruction Format: 0100111010 Signature xxx EAMode xxx EARegister | 16-BIT WORD DATA | 8-BIT BYTE DATA | | 32-BIT LONG DATA | Instruction Fields: Size field - Specifies the size of the operation. 00 - Byte operation 01 - Word operation 10 - Long operation Effective Address field - Specifies the address of the next instruction. Only data addressing modes can be used. Valid Modes - Dn, (An), (An)+, -(An), (xxx).W, (xxx).L Immediate field - Data immediately following the instruction. If size = 00, the data is the low-order byte of the immediate word If size = 01, the data is the entire immediate word. If size = 10, the data is the next two immediate words. """ # the allowed sizes for this opcode valid_sizes = [OpSize.BYTE, OpSize.WORD, OpSize.LONG] def __init__(self, params: list, size: OpSize = OpSize.WORD): # ensure that the parameters are valid assert len(params) == 2 assert isinstance(params[0], AssemblyParameter) assert isinstance(params[1], AssemblyParameter) # source can only be immediate data assert params[0].mode == EAMode.Immediate # destination can be any EA mode # except for An and immediate assert params[1].mode != EAMode.AddressRegisterDirect and params[1].mode != EAMode.Immediate self.src = params[0] self.dest = params[1] assert size in Cmpi.valid_sizes self.size = size
[docs] def assemble(self) -> bytearray: """ Assembles this opcode into a bytearray to be inserted into memory :return: The bytearray which represents this assembled opcode """ # 00001100 signature xx size xxx EAMode xxx EARegister ret_opcode = 0b00001100 << 8 # add the size if self.size == OpSize.BYTE: ret_opcode |= 0b00 << 6 elif self.size == OpSize.WORD: ret_opcode |= 0b01 << 6 elif self.size == OpSize.LONG: ret_opcode |= 0b10 << 6 ret_opcode |= ea_mode_bin.parse_from_ea_mode_modefirst(self.dest) << 0 ret_bytes = bytearray(ret_opcode.to_bytes(2, byteorder='big', signed=False)) # extend to include source ret_bytes.extend(opcode_util.ea_to_binary_post_op(self.src, self.size).get_value_bytearray()) # extend to include destination (if needed) if self.dest.mode == EAMode.IMM or self.dest.mode == EAMode.AWA or self.dest.mode == EAMode.ALA: ret_bytes.extend(opcode_util.ea_to_binary_post_op(self.dest, self.size).get_value_bytearray()) return ret_bytes
[docs] def execute(self, simulator: M68K): """ Executes this command in the simulator Subtracts the source operand from the destination operand and set the condition codes accordingly. The source must be an immediate number. The destination is not modified by this instruction. :param simulator: the simulator that this opcode is being run on :return: """ # get the src and dest values src_val = self.src.get_value(simulator, self.size.get_number_of_bytes()) dest_val = self.dest.get_value(simulator, self.size.get_number_of_bytes()) comparison = dest_val.get_value_signed() - src_val.get_value_signed() raw_total = dest_val.get_value_unsigned() - src_val.get_value_unsigned() comp_mv = dest_val - src_val # mask out only the bits we need/want comp_mv = MemoryValue(self.size, unsigned_int=mask_value_for_length(self.size, comp_mv.get_value_unsigned())) negative = False if self.size is OpSize.BYTE: negative = comparison & 0x80 > 0 elif self.size is OpSize.WORD: negative = comparison & 0x8000 > 0 elif self.size is OpSize.LONG: negative = comparison & 0x80000000 > 0 # Overflow occurs when a sign change occurs where it shouldn't occur. # For example: positive - negative != negative. # This doesn't make sense, so an overflow occurs overflow = False if src_val.get_negative() is False: if dest_val.get_negative() is True: if raw_total > 0 and raw_total & 0x80000000 > 0: overflow = True # set register whomst have status simulator.set_ccr_reg(None, negative, (comparison == 0), overflow, (raw_total < 0)) # set the number of bytes to increment equal to the length of the # instruction (1 word) to_increment = OpSize.WORD.value # Increment by size to_increment += OpSize.LONG.value if self.size == OpSize.LONG else OpSize.WORD.value # any additional increments by destination value if self.dest.mode is EAMode.AbsoluteLongAddress: to_increment += OpSize.LONG.value if self.dest.mode is EAMode.AbsoluteWordAddress: to_increment += OpSize.WORD.value # increment PC simulator.increment_program_counter(to_increment)
def __str__(self): return 'CMPI Size {}, Src {}, Dest {}'.format(self.size, self.src, self.dest)
[docs] @classmethod def command_matches(cls, command: str) -> bool: """ Checks whether a command string is an instance of this command type This will only allow for CMPI. Not CMPA, CMP, CMPM. While CMPI is not checking for the types that would make it actually one of these different types, those instructions must be implemented separately. :param command: The command string to check 'CMPI.W', 'CMPI' :return: Whether the string is an instance of CMPI """ return opcode_util.command_matches(command, 'CMPI')
[docs] @classmethod def get_word_length(cls, command: str, parameters: str) -> int: """ Gets the length of this command in memory, including the length of the single opcode and the length of any immediate parameter values >>> Cmpi.get_word_length('CMPI', '#123, D3') 2 >>> Cmpi.get_word_length('CMPI.L', '#12345678, D1') 3 >>> Cmpi.get_word_length('CMPI.W', '#$FFFF, (A2)+') 2 >>> Cmpi.get_word_length('CMPI.L', '#12345678, (A7)') 3 >>> Cmpi.get_word_length('CMPI.W', '#$FFFF, ($AAAA).W') 3 >>> Cmpi.get_word_length('CMPI.L', '#12345678, ($AAAA).L') 5 :param command: :param parameters: :return: """ # split the command to get the size, if specified parts = command.split('.') if len(parts) == 1: size = OpSize.WORD else: size = OpSize.parse(parts[1]) params = parameters.split(',') # parse the src and dest parameters src = parse_assembly_parameter(params[0].strip()) dest = parse_assembly_parameter(params[1].strip()) # length is at least either 2 or 3 depending on size length = 3 if size == OpSize.LONG else 2 if dest.mode == EAMode.AWA: # appends a word length += 1 if dest.mode == EAMode.ALA: # appends a long length += 2 return length
[docs] @classmethod def is_valid(cls, command: str, parameters: str) -> (bool, list): """ Tests whether the given command is valid >>> Cmpi.is_valid('CMPI', '#123, D1')[0] True >>> Cmpi.is_valid('CMP.', '#123, D7')[0] False :param command: :param parameters: :return: """ # don't bother with param invalid modes return opcode_util.n_param_is_valid( command, parameters, "CMPI", 2)
[docs] @classmethod def disassemble_instruction(cls, data: bytes) -> Opcode: """ CMPI.W #123,D7 >>> op = Cmpi.disassemble_instruction(bytearray.fromhex('0C47007B')) >>> str(op.src) 'EA Mode: EAMode.IMM, Data: 123' >>> str(op.dest) 'EA Mode: EAMode.DRD, Data: 7' Disassembles the instruction into an instance of the CMP class :param data: :return: """ assert len(data) >= 2, 'Opcode size must be at least 1 word' first_word = int.from_bytes(data[0:2], 'big') [opcode_bin, size_bin, ea_mode_binary, ea_reg_bin] = split_bits(first_word, [8, 2, 3, 3]) # ensure that this is the correct opcode if opcode_bin != 0b00001100: return None src = None dest = None size = None words_used = 1 if size_bin == 0b00: size = OpSize.BYTE elif size_bin == 0b01: size = OpSize.WORD elif size_bin == 0b10: size = OpSize.LONG else: return None src_size = 4 if size == OpSize.LONG else 2 src_value = int.from_bytes(data[2:2+src_size], 'big') src = AssemblyParameter(EAMode.Immediate, src_value) dest = parse_ea_from_binary(ea_mode_binary, ea_reg_bin, size, True, data[words_used * 2:])[0] # make a new reference of this type return cls([src, dest], size)
def __str__(self): return 'CMPI Size {}, Src {}, Dest {}'.format(self.size, self.src, self.dest)
[docs] @classmethod def from_str(self, command: str, parameters: str): """ Parses a CMPI from text. >>> str(Cmpi.from_str('CMPI.B', '#1337, D1')) 'CMPI Size OpSize.BYTE, Src EA Mode: EAMode.IMM, Data: 1337, Dest EA Mode: EAMode.DRD, Data: 1' :param command: The command itself 'CMPI' 'CMPI.B', etc. :param parameters: The parameters after the command :return: The parsed command """ return opcode_util.n_param_from_str(command, parameters, Cmpi, 2, OpSize.WORD)