Source code for easier68k.simulator.m68k

"""
Motorola 68k chip definition
"""

from .memory import Memory
from ..core.enum.register import Register, FULL_SIZE_REGISTERS, ALL_ADDRESS_REGISTERS
from ..core.enum.condition_status_code import ConditionStatusCode
from ..core.models.list_file import ListFile
import typing
import binascii
from ..core.models.memory_value import MemoryValue
from ..core.enum.op_size import OpSize

MAX_MEMORY_LOCATION = 16777216  # 2^24

[docs]class M68K: def __init__(self): """ Constructor """ self.memory = Memory() # has the simulation been halted using SIMHALT or .halt() self.halted = False # should the clock automatically cycle? self.clock_auto_cycle = True self._clock_cycles = 0 # todo add events for each clock cycle # this is necessary for implementing breakpoints # and watches for value changes # set up the registers to their default values self.registers = {} self.__init_registers() def __init_registers(self): """ Set the registers to their default values :return: """ # loop through all of the full size registers which are just 32 bits / 4 bytes long for register in FULL_SIZE_REGISTERS: self.registers[register] = MemoryValue(OpSize.LONG) # set up all of the odd registers (in this case, just the Condition Code Register) # which just uses 5 bits out of the lowermost byte (do we want to allocate it an entire word instead?) self.registers[Register.ConditionCodeRegister] = MemoryValue(OpSize.BYTE) # Easy68k initializes the step counter (A7) to 0x1000000 by default, so do the same self.set_register(Register.A7, MemoryValue(OpSize.LONG, unsigned_int=0x1000000))
[docs] def get_register(self, register: Register) -> MemoryValue: """ Gets the entire value of a register :param register: :return: """ return self.registers[register]
[docs] def set_register(self, register: Register, val: MemoryValue): """ Sets the value of a register using a 32-bit int :param register: :param val: :return: """ # if the register is the CCR, use that method to handle setting it # because of its different size if register == Register.ConditionCodeRegister: self._set_condition_code_register_value(val) return # if the register is an address register that is limited to fit in the bounds of memory if register in ALL_ADDRESS_REGISTERS: self.set_address_register_value(register, val) return # now for all other registers # ensure that the value is within bounds # actual negative numbers will need to be converted into 32-bit numbers assert 0 <= val.get_value_unsigned() <= 0xFFFFFFFF, 'The value for registers must fit into 4 bytes!' # set the value self.registers[register] = val
def _set_condition_code_register_value(self, val: MemoryValue): """ Sets the value for the condition code register :param val: :return: """ # ensure that the value is within bounds # since the CCR is just a single byte assert 0 <= val.get_value_unsigned() <= 0xFF, 'The value for the CCR must fit in a single byte!' # now set the value self.registers[Register.ConditionCodeRegister] = val
[docs] def get_program_counter_value(self) -> int: """ Gets the 32-bit unsigned integer value for the program counter value :return: """ mv = self.get_register(Register.ProgramCounter) ret = mv.get_value_unsigned() return ret
[docs] def set_address_register_value(self, reg: Register, new_value: MemoryValue): """ Sets the value of an address register, so the PC or A0-A7 :param reg: :param new_value: :return: """ # no longer assert that the address register value is a pointer to memory # since address register direct modes don't consider the amount of memory assert reg in ALL_ADDRESS_REGISTERS, 'The register given is not an address register!' # now set the value of the register self.registers[reg].set_value_unsigned_int(new_value.get_value_unsigned())
[docs] def set_program_counter_value(self, new_value: int): """ Sets the value of the program counter Must be a non negative integer that is less than the maximum location size :param new_value: :return: """ self.set_address_register_value(Register.ProgramCounter, MemoryValue(OpSize.LONG, unsigned_int=new_value))
[docs] def increment_program_counter(self, inc: int): """ Increments the program counter by the given value :param inc: :return: """ self.set_program_counter_value( self.get_program_counter_value() + inc)
[docs] def get_condition_status_code(self, code: ConditionStatusCode) -> bool: """ Gets the status of a code from the Condition Code Register :param code: :return: """ ccr = self.get_register(Register.CCR).get_value_unsigned() # ccr is only 1 byte, bit mask away the bit being looked for return (ccr & code) > 0
[docs] def set_condition_status_code(self, code: ConditionStatusCode, value: bool): """ Sets the status of a code from the Condition Code Register to value :param code: :return: """ ccr = self.get_register(Register.CCR) v = ccr.get_value_unsigned() if value: v |= code else: v &= ~code self._set_condition_code_register_value(MemoryValue(OpSize.BYTE, unsigned_int=v))
[docs] def run(self): """ Starts the automatic execution :return: """ if not self.halted: if not self.clock_auto_cycle: # run a single instruction self.step_instruction() else: while self.clock_auto_cycle: self.step_instruction()
[docs] def halt(self): """ Halts the auto simulation execution :return: """ self.clock_auto_cycle = False self.halted = True
[docs] def step_instruction(self): """ Increments the clock until the program counter increments :return: """ if not self.halted: # must be here or we get circular dependency issues from ..core.util.find_module import find_opcode_cls, valid_opcodes for op_str in valid_opcodes: op_class = find_opcode_cls(op_str) # We don't know this opcode, there's no module for it if op_class is None: print('Opcode {} is not known: skipping and continuing'.format(op_str)) assert False continue # 10 comes from 2 bytes for the op and max 2 longs which are each 4 bytes # note: this currently has the edge case that it will fail unintelligibly # if encountered at the end of memory pc_val = self.get_program_counter_value() op = op_class.disassemble_instruction(self.memory.memory[pc_val:pc_val+10]) if op is not None: op.execute(self) # done exeucting after doing an operation return
[docs] def reload_execution(self): """ restarts execution of the program up to the current program counter location :return: """ # get the current PC current_pc = self.get_program_counter_value()
# reset the PC value # todo, need to store the starting location # set the starting PC value # run until hits that PC value
[docs] def get_cycles(self): """ Returns how many clock cycles have been performed :return: """ return self._clock_cycles
[docs] def clear_cycles(self): """ Resets the count of clock cycles :return: """ self._clock_cycles = 0
[docs] def load_list_file(self, list_file: ListFile): """ Load List File load the contents of a list file into memory using the locations specified inside of the list file :param list_file: :return: """ self.memory.load_list_file(list_file) self.set_program_counter_value(int(list_file.starting_execution_address))
[docs] def load_memory(self, file : typing.BinaryIO): """ saves the raw memory into the designated file NOTE: file must be opened as binary or this won't work """ self.memory.load_memory(file)
[docs] def save_memory(self, file : typing.BinaryIO): """ Loads the raw memory from the designated file This includes programs NOTE: file must be opened as binary or this won't work """ self.memory.save_memory(file)
[docs] def set_ccr_reg(self, extend, negative, zero, overflow, carry): """ Accepts Boolean values for X,N,Z,V, and C, respectively and sets the CCR accordingly. Passing None in for any argument will cause it to ignore that bit. Returns nothing. :param extend: :param negative: :param zero: :param overflow: :param carry: :return: """ if extend is not None: extend = bool(extend) self.set_condition_status_code(ConditionStatusCode.X, extend) if negative is not None: negative = bool(negative) self.set_condition_status_code(ConditionStatusCode.N, negative) if zero is not None: zero = bool(zero) self.set_condition_status_code(ConditionStatusCode.Z, zero) if overflow is not None: overflow = bool(overflow) self.set_condition_status_code(ConditionStatusCode.V, overflow) if carry is not None: carry = bool(carry) self.set_condition_status_code(ConditionStatusCode.C, carry)