Source code for easier68k.core.opcodes.cmp

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 Cmp(Opcode): """ CMP: Compare Operation: Destination - Source -> cc Assembler Syntax: CMP <ea>, Dn Attributes: Size = (Byte, Word, Long) Description: Subtracts the source operand from the destination data register and sets the condition codes according to the result; the data register is not changed. The size of the operation can be byte, word, or long. 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: 1 0 1 1 Dest Register xxx OpMode xxx EA mode xxx Register xxx Instruction Fields: Register field - Specifies the destination data register. Opmode field [Operation: Dn - <ea>] 000 - Byte 001 - Word 010 - Long Effective Address field - Specifies the source operand. All addressing modes can be used. Valid Modes - All (Word and Long size only for An) NOTE: CMPA is used when the destination is an address register. CMPI is used when the source is immediate data. CMPM is used for memory-to-memory compares. Most assemblers automatically make the distinction. """ # 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 (params[0]) can be any EA mode # ensure that the destination is Dn assert params[1].mode == EAMode.DataRegisterDirect self.src = params[0] self.dest = params[1] assert size in Cmp.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 """ # 1 0 1 1 Dest Register xxx OpMode xxx EA mode xxx Register xxx ret_opcode = 0b1011 << 12 # add the dest register in it's place ret_opcode |= self.dest.data << 9 # add the OpMode bytes if self.size == OpSize.BYTE: ret_opcode |= 0b000 << 6 elif self.size == OpSize.WORD: ret_opcode |= 0b001 << 6 elif self.size == OpSize.LONG: ret_opcode |= 0b010 << 6 # add the ea bits for the src # with mode first ret_opcode |= ea_mode_bin.parse_from_ea_mode_modefirst(self.src) # convert the int to bytes, then to a mutable bytearray return bytearray(ret_opcode.to_bytes(2, byteorder='big', signed=False))
[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 destination must be a data register. 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 max_val = 0 # Maximum value allowed for the given memory size if self.size is OpSize.BYTE: max_val = 0xFF elif self.size is OpSize.WORD: max_val = 0xFFFF elif self.size is OpSize.LONG: max_val = 0xFFFFFFFF # Overflow occurs if the result cannot be represented by the memory size # Get the signed value test_val = comp_mv.get_value_signed() # If it is negative, get the twos complement if test_val < 0: test_val = (abs(test_val) ^ 0xFFFFFFFF) + 1 # Check to see if it can hold the value overflow = test_val > max_val # ignore the carry bit # set freakin ccr 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 if self.src.mode is EAMode.AbsoluteLongAddress: to_increment += OpSize.LONG.value if self.src.mode is EAMode.AbsoluteWordAddress: to_increment += OpSize.WORD.value # increment PC simulator.increment_program_counter(to_increment)
def __str__(self): return 'CMP 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 CMP. Not CMPA, CMPI, CMPM. While CMP 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 'CMP.W', 'CMP' :return: Whether the string is an instance of CMP """ return opcode_util.command_matches(command, 'CMP')
[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 >>> Cmp.get_word_length('CMP', 'D0, D1') 1 >>> Cmp.get_word_length('CMP.W', 'D0, D1') 1 >>> Cmp.get_word_length('CMP.L', 'D0, D1') 1 >>> Cmp.get_word_length('CMP.L', '($AAAA).L, D7') 3 :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()) # minimum length is always 1 length = 1 if src.mode == EAMode.IMM: # immediate data is 2 words long if size == OpSize.LONG: length += 2 else: # bytes and words are 1 word long length += 1 if src.mode == EAMode.AWA: # appends a word length += 1 if src.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 >>> Cmp.is_valid('CMP', 'D0, D1')[0] True >>> Cmp.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, "CMP", 2)
[docs] @classmethod def disassemble_instruction(cls, data: bytes) -> Opcode: """ 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, register_bin, opmode_bin, ea_mode_bin, ea_reg_bin] = split_bits(first_word, [4, 3, 3, 3, 3]) # ensure that this is the correct opcode if opcode_bin != 0b1011: return None src = None dest = None size = None words_used = 1 if opmode_bin == 0b000: size = OpSize.BYTE elif opcode_bin == 0b001: size = OpSize.WORD elif opcode_bin == 0b010: size = OpSize.LONG else: return None src = parse_ea_from_binary(ea_mode_bin, ea_reg_bin, size, True, data[words_used * 2:])[0] dest = AssemblyParameter(EAMode.DRD, register_bin) # make a new reference of this type return cls([src, dest], size)
[docs] @classmethod def from_str(self, command: str, parameters: str): """ Parses a CMP from text. :param command: The command itself 'CMP.L' 'CMP', etc. :param parameters: The parameters after the command :return: The parsed command """ return opcode_util.n_param_from_str(command, parameters, Cmp, 2, OpSize.WORD)