from typing import Union
from ...core.enum.ea_mode import EAMode
from ...core.enum.op_size import OpSize
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 ..models.assembly_parameter import AssemblyParameter
from ..models.memory_value import MemoryValue
from ..enum.register import Register
[docs]class Rts(Opcode):
"""
RTS: Return from Subroutine
Operation: (SP) -> PC; SP + 4 -> SP
Assembler Syntax: RTS
Attributes: Unsized
Description: Pulls the program counter value from the stack.
The previous program counter value is lost.
Condition Codes: Not affected
Instruction Format: 0100111001110101
"""
def __init__(self):
pass # Doesn't need to do anything else
[docs] def assemble(self) -> bytearray:
"""
Assembles this opcode into hex to be inserted into memory
:return: The hex version of this opcode
"""
# 0100111001110101
# ret_opcode is the binary value which represents the assembled instruction
ret_opcode = 0b0100111001110101
return bytearray(ret_opcode.to_bytes(OpSize.WORD.value, byteorder='big', signed=False))
[docs] def execute(self, simulator: M68K):
"""
Executes this command in a simulator
:param simulator: The simulator to execute the command on
:return: Nothing
"""
# (SP) -> PC
step_counter = 7 # Register A7 is SP
sp_val = AssemblyParameter(EAMode.ARI, step_counter).get_value(simulator, OpSize.LONG)
simulator.set_program_counter_value(sp_val.get_value_unsigned())
# get the value of src from the simulator
sp_val = simulator.get_register(Register.A7)
# The address is a long word address
new_sp_val = sp_val.get_value_unsigned() + OpSize.LONG.value
# SP + 4 -> SP
simulator.set_register(Register.A7, MemoryValue(OpSize.LONG, unsigned_int=new_sp_val))
def __str__(self):
# Makes this a bit easier to read in doctest output
return 'Rts command'
[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, 'RTS')
[docs] @classmethod
def get_word_length(cls, command: str, parameters: str) -> int:
"""
>>> Rts.get_word_length('RTS', '')
1
>>> Rts.get_word_length('RTS', ' ')
1
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
"""
# Ensure correct OPCODE
assert command.strip(' ') == 'RTS'
# OPCODE does not have any parameters
assert parameters is None or parameters.strip(' ') == ""
return 1
[docs] @classmethod
def is_valid(cls, command: str, parameters: str) -> (bool, list):
"""
Tests whether the given command is valid
>>> Rts.is_valid('RTS', '')[0]
True
>>> Rts.is_valid('RTS', '(A5)')[0]
False
>>> Rts.is_valid('RTS', '#5, D1')[0]
False
>>> Rts.is_valid('RTS.W', '(A1)')[0]
False
>>> Rts.is_valid('RT', '($1000).W')[0]
False
>>> Rts.is_valid('RTS.', '(A4)')[0]
False
>>> Rts.is_valid('RTS', '($7000).W')[0]
False
: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
"""
issues = []
try:
assert opcode_util.check_valid_command(command, 'RTS', can_take_size=False), 'RTS Command invalid'
assert not parameters.strip(), 'RTS takes no parameters'
return True, issues
except AssertionError as error:
issues.append((error.args[0], 'ERROR'))
return False, issues
[docs] @classmethod
def disassemble_instruction(cls, data: bytearray) -> Union[Opcode, None]:
"""
This has a non-RTS opcode
>>> Rts.disassemble_instruction(bytearray.fromhex('0280'))
RTS
>>> op = Rts.disassemble_instruction(bytearray.fromhex('4E75'))
>>> str(op)
'Rts command'
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 only one word'
first_word = int.from_bytes(data[0:2], 'big')
[opcode_bin] = split_bits(first_word, [16])
if opcode_bin != 0b0100111001110101:
return None
return cls()
[docs] @classmethod
def from_str(cls, command: str, parameters: str):
"""
Parses a RTS command from text.
>>> str(Rts.from_str('RTS', ''))
'Rts command'
: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
"""
# Ensure correct OPCODE
assert command.strip(' ') == 'RTS'
# OPCODE does not have any parameters
assert parameters is None or parameters.strip(' ') == ""
return cls()