"""
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)