Source code for easier68k.core.opcodes.neg

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


[docs]class Neg(Opcode): """ NEG: Negate Operation: 0 - Destination -> Destination Assembler Syntax: NEG <ea> Attributes: Size = (Byte, Word, Long) Description: Subtracts the destination operand from zero and stores the result in the destination location. The size of the operation is specified as byte, word, or long. Condition Codes: X - Set the same as the carry bit. 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 - Cleared if the result is zero; set otherwise. Instruction Format: 01000100 Signature xx Size xxx EAMode xxx EARegister 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 alterable addressing modes can be used. Valid Modes - Dn, (An), (An)+, -(An), (xxx).W, (xxx).L """ # Allowed sizes for this opcode valid_sizes = [OpSize.BYTE, OpSize.WORD, OpSize.LONG] def __init__(self, params: list, size: OpSize = OpSize.WORD): assert len(params) == 1 assert isinstance(params[0], AssemblyParameter) # check ea param is valid assert params[0].mode != EAMode.ARD or params[0].mode != EAMode.IMM self.dest = params[0] assert size in Neg.valid_sizes self.size = size
[docs] def assemble(self) -> bytearray: """ Assembles this opcode into hex to be inserted into memory :return: The hex version of this opcode """ # 01000100 Signature xx Size xxx EAMode xxx EARegister # ret_opcode is the binary value which represents the assembled instruction ret_opcode = 0b01000100 << 8 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)) if 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 a simulator :param simulator: The simulator to execute the command on :return: Nothing """ # get the length val_length = self.size.get_number_of_bytes() # get the value of src from the simulator dest_val = self.dest.get_value(simulator, val_length) # increment the program counter by the length of the instruction (1 word) to_increment = OpSize.WORD.value # repeat for the dest if self.dest.mode in [EAMode.AbsoluteLongAddress]: to_increment += OpSize.LONG.value if self.dest.mode in [EAMode.AbsoluteWordAddress]: to_increment += OpSize.WORD.value # mask to apply to the source mask = 0xFF if self.size is OpSize.BYTE: mask = 0xFF if self.size is OpSize.WORD: mask = 0xFFFF if self.size is OpSize.LONG: mask = 0xFFFFFFFF # which bits of the total should not be modified inverted_mask = 0xFFFFFFFF ^ mask # preserve the upper bits of the operation if they aren't used preserve = dest_val.get_value_signed() & inverted_mask raw_total = 0 - dest_val.get_value_unsigned() total = (raw_total & mask) | preserve negative_bit = 0 if self.size is OpSize.BYTE: negative_bit = 0x80 elif self.size is OpSize.WORD: negative_bit = 0x8000 elif self.size is OpSize.LONG: negative_bit = 0x80000000 negative = total & negative_bit > 0 # Overflow occurs when a sign change occurs where it shouldn't occur. # For example: 0 - positive != positive. # other example: 0 - negative != negative # This doesn't make sense, so an overflow occurs overflow = False if total != 0: if dest_val.get_value_unsigned() & negative_bit == 0: # If it is positive if total & negative_bit == 0: # And total is positive overflow = True elif dest_val.get_value_unsigned() & negative_bit > 0: # If it is negative if total & negative_bit > 0: # If the total is negative overflow = True # Cleared if the result is 0. carry_bit = total != 0 # set me some heckin status simulator.set_ccr_reg(carry_bit, negative, (total == 0), overflow, carry_bit) # and set the value self.dest.set_value(simulator, MemoryValue(OpSize.LONG, unsigned_int=total)) # set the program counter value simulator.increment_program_counter(to_increment)
def __str__(self): # Makes this a bit easier to read in doctest output return 'Neg command: Size {}, dest {}'.format(self.size, self.dest)
[docs] @classmethod 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 """ return opcode_util.command_matches(command, 'SUB')
[docs] @classmethod def get_word_length(cls, command: str, parameters: str) -> int: """ >>> Neg.get_word_length('NEG.B', 'D3') 1 >>> Neg.get_word_length('NEG.W', '-(A5)') 1 >>> Neg.get_word_length('NEG.B', '($BBBB).W') 2 >>> Neg.get_word_length('NEG.W', '(A0)+') 1 >>> Neg.get_word_length('NEG.W', '($BBBB).W') 2 >>> Neg.get_word_length('NEG.L', '($BBBB).L') 3 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, as well as a list of warnings or errors encountered """ # Split the parameters into EA modes params = parameters.split(',') # src = parse_assembly_parameter(params[0].strip()) # Parse the source and make sure it parsed right dest = parse_assembly_parameter(params[0].strip()) length = 1 # Always 1 word not counting additions to end if dest.mode == EAMode.AWA: # Appends a word length += 1 if dest.mode == EAMode.ALA: # Appends a long, so 2 words length += 2 return length
[docs] @classmethod def is_valid(cls, command: str, parameters: str) -> (bool, list): """ Tests whether the given command is valid >>> Neg.is_valid('NEG.B', 'D1')[0] True >>> Neg.is_valid('NEG.W', '(A5)')[0] True >>> Neg.is_valid('NEG.B', '#5, D1')[0] False >>> Neg.is_valid('NEG.L', 'D2')[0] True >>> Neg.is_valid('NE.L', 'D1')[0] False >>> Neg.is_valid('NEG.', 'D1')[0] False >>> Neg.is_valid('NEG.W', '($7000).W')[0] True :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 """ return opcode_util.n_param_is_valid(command, parameters, "NEG", 1, param_invalid_modes=[[EAMode.ARD, EAMode.IMM]])[:2]
[docs] @classmethod def disassemble_instruction(cls, data: bytearray) -> Opcode: """ This has a non-subq opcode >>> Neg.disassemble_instruction(bytearray.fromhex('0280')) NEG.B D7 >>> op = Neg.disassemble_instruction(bytearray.fromhex('4407')) >>> str(op.dest) 'EA Mode: EAMode.DRD, Data: 7' NEG.W (A5) >>> op = Neg.disassemble_instruction(bytearray.fromhex('4455')) >>> str(op.dest) 'EA Mode: EAMode.ARI, Data: 5' NEG.L (A0)+ >>> op = Neg.disassemble_instruction(bytearray.fromhex('4498')) >>> str(op.dest) 'EA Mode: EAMode.ARIPI, Data: 0' NEG.W $4000 >>> op = Neg.disassemble_instruction(bytearray.fromhex('44784000')) >>> str(op.dest) 'EA Mode: EAMode.AWA, Data: 16384' 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 """ assert len(data) >= 2, 'Opcode size is at least one 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]) if opcode_bin != 0b01000100: return None # Determine size if size_bin == 0b00: size = OpSize.BYTE elif size_bin == 0b01: size = OpSize.WORD elif size_bin == 0b10: size = OpSize.LONG else: return None # populate destination data dest = parse_ea_from_binary(ea_mode_binary, ea_reg_bin, size, False, data[2:])[0] return cls([dest], size)
[docs] @classmethod def from_str(cls, command: str, parameters: str): """ Parses a NEG command from text. >>> str(Neg.from_str('NEG.B', 'D1')) 'Neg command: Size OpSize.BYTE, dest EA Mode: EAMode.DRD, Data: 1' >>> str(Neg.from_str('NEG.L', '(A0)')) 'Neg command: Size OpSize.LONG, dest EA Mode: EAMode.ARI, Data: 0' :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: The parsed command """ return opcode_util.n_param_from_str(command, parameters, Neg, 1, OpSize.WORD)