Source code for easier68k.core.opcodes.jsr

from ...core.enum.ea_mode import EAMode
from ...core.enum.op_size import OpSize
from ...core.enum import ea_mode_bin
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 ..models.memory_value import MemoryValue
from ..enum.register import Register
from typing import Union


[docs]class Jsr(Opcode): """ JSR: Jump to Subroutine Operation: SP - 4 -> SP; PC -> (SP); Destination Address -> PC Assembler Syntax: JSR <ea> Attributes: Unsized Description: Pushes the long-word address of the instruction immediately following the JSR instruction onto the system stack. Program execution then continues at the address specified in the instruction. Condition Codes: Not affected Instruction Format: 0100111010 Signature xxx EAMode xxx EARegister Instruction Field: Effective Address field - Specifies the address of the next instruction. Only control addressing modes can be used as listed in the following tables. Valid Modes - (An), (xxx).W, (xxx).L """ def __init__(self, params: list): assert len(params) == 1 assert isinstance(params[0], AssemblyParameter) # check ea param is valid assert params[0].mode == EAMode.ARI or params[0].mode == EAMode.AWA or params[0].mode == EAMode.ALA self.dest = params[0]
[docs] def assemble(self) -> bytearray: """ Assembles this opcode into hex to be inserted into memory :return: The hex version of this opcode """ # 0100111010 Signature xxx EAMode xxx EARegister # ret_opcode is the binary value which represents the assembled instruction ret_opcode = 0b0100111010 << 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: size = OpSize.WORD if self.dest.mode == EAMode.AWA else OpSize.LONG ret_bytes.extend(opcode_util.ea_to_binary_post_op(self.dest, 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: None """ # 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 elif self.dest.mode in [EAMode.AbsoluteWordAddress]: to_increment += OpSize.WORD.value # set the program counter value simulator.increment_program_counter(to_increment) # get the value of src from the simulator dest_val = self.dest.get_value(simulator, OpSize.LONG) sp_val = simulator.get_register(Register.A7) new_sp_val = sp_val.get_value_unsigned() - 4 # SP – 4 -> SP simulator.set_register(Register.A7, MemoryValue(OpSize.LONG, unsigned_int=new_sp_val)) pc_val = simulator.get_program_counter_value() # PC -> (SP) AssemblyParameter(EAMode.ALA, new_sp_val).set_value(simulator, MemoryValue(OpSize.LONG, unsigned_int=pc_val)) # Destination Address -> PC simulator.set_register(Register.PC, dest_val)
def __str__(self): # Makes this a bit easier to read in doctest output return 'Jsr command: dest {}'.format(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, 'JSR')
[docs] @classmethod def get_word_length(cls, command: str, parameters: str) -> int: """ >>> Jsr.get_word_length('JSR', '(A5)') 1 >>> Jsr.get_word_length('JSR', '-(A7)') 1 >>> Jsr.get_word_length('JSR', '($BBBB).W') 2 >>> Jsr.get_word_length('JSR', '($1000).W') 2 >>> Jsr.get_word_length('JSR', '($FFFF).L') 3 >>> Jsr.get_word_length('JSR', '($8000).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 """ dest = parse_assembly_parameter(parameters.strip()) # Parse the destination 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 >>> Jsr.is_valid('JSR', '(A0)')[0] True >>> Jsr.is_valid('JSR', '(A5)')[0] True >>> Jsr.is_valid('JSR', '#5, D1')[0] False >>> Jsr.is_valid('JSR.W', '(A1)')[0] True >>> Jsr.is_valid('JS', '($1000).W')[0] False >>> Jsr.is_valid('JSR.', '(A4)')[0] False >>> Jsr.is_valid('JSR', '($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, "JSR", 1, param_invalid_modes=[[EAMode.DRD, EAMode.ARD, EAMode.ARIPD, EAMode.ARIPI, EAMode.IMM]])[:2]
[docs] @classmethod def disassemble_instruction(cls, data: bytearray) -> Union[Opcode, None]: """ This has a non-JSR opcode >>> Jsr.disassemble_instruction(bytearray.fromhex('0280')) JSR (A0) >>> op = Jsr.disassemble_instruction(bytearray.fromhex('4E90')) >>> str(op.dest) 'EA Mode: EAMode.ARI, Data: 0' JSR (A5) >>> op = Jsr.disassemble_instruction(bytearray.fromhex('4E95')) >>> str(op.dest) 'EA Mode: EAMode.ARI, Data: 5' JSR $4000 >>> op = Jsr.disassemble_instruction(bytearray.fromhex('4EB84000')) >>> str(op.dest) 'EA Mode: EAMode.AWA, Data: 16384' JSR $FFFF7000 >>> op = Jsr.disassemble_instruction(bytearray.fromhex('4EB87000')) >>> str(op.dest) 'EA Mode: EAMode.AWA, Data: 28672' 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, ea_mode_binary, ea_reg_bin] = split_bits(first_word, [10, 3, 3]) if opcode_bin != 0b0100111010: return None if ea_mode_binary == 0b111: mode = EAMode.AWA if ea_reg_bin == 0 else EAMode.ALA dest = AssemblyParameter(mode, int.from_bytes(data[2:], byteorder='big', signed=False)) elif ea_mode_binary == 0b010: dest = AssemblyParameter(EAMode.ARI, ea_reg_bin) else: return None return cls([dest])
[docs] @classmethod def from_str(cls, command: str, parameters: str): """ Parses a JSR command from text. >>> str(Jsr.from_str('JSR', '(A0)')) 'Jsr command: dest EA Mode: EAMode.ARI, Data: 0' >>> str(Jsr.from_str('JSR', '($4000).W')) 'Jsr command: dest EA Mode: EAMode.AWA, Data: 16384' :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, Jsr, 1, None)