ii-vision/simulator/machine.py
kris 80402f25a5 - Allow HGR ROM entry point
- Don't trap unexpected entrypoint when crossing between regions via RTS
- Implement TICK handler
- Improve status printing in CPU loop
2019-02-27 22:46:53 +00:00

137 lines
3.8 KiB
Python

"""Abstract hardware machine with CPU and memory."""
import enum
from typing import Dict, Tuple, Callable, Optional
from py65 import memory as py65_memory
class AccessMode(enum.IntFlag):
READ = 0x1
WRITE = 0x2
RW = READ | WRITE
class Event(object):
def __init__(self, event_type, details):
self.event_type = event_type
self.details = details
def __str__(self) -> str:
return "Event(%s): %s" % (self.event_type, self.details)
def Log(region:str, message:str):
print("%s event: %s" % (region, message))
# TODO: why?
def _Event(region:str, message:str):
def _Event(_):
Log(region, message)
return _Event
class TrapException(Exception):
def __init__(self, address:int, msg:str):
self.address = address
self.msg = msg
def __str__(self) -> str:
return "$%04X: %s" % (self.address, self.msg)
class SoftSwitch:
def __init__(
self, name: str, clear_addr: int, set_addr: int,
status_addr: int, mode: AccessMode = AccessMode.WRITE,
callback=None):
self.name = name
self.clear_addr = clear_addr
self.set_addr = set_addr
self.status_addr = status_addr
# Whether switch is set/clear by READ/WRITE or both
self.mode = mode # type: AccessMode
self.state = False # type: bool
self.callback = callback # type: Callable[[bool], Optional[int]]
def set(self) -> Optional[int]:
self.state = True
Log(self.name, "Setting soft switch")
if self.callback:
return self.callback(True)
def clear(self) -> Optional[int]:
self.state = False
Log(self.name, "Clearing soft switch")
if self.callback:
return self.callback(False)
def get(self) -> int:
Log(self.name, "Reading soft switch (%s)" % (
"on" if self.state else "off"))
return 0x80 & self.state
@staticmethod
def unimplemented(_):
raise NotImplementedError
def register(self, io_map):
def _clear(mode, value):
return self.clear()
def _set(mode, value):
return self.set()
def _get(mode, value):
return self.get()
io_map[self.clear_addr] = (
self.mode, "%s OFF" % self.name, _clear)
io_map[self.set_addr] = (
self.mode, "%s ON" % self.name, _set)
io_map[self.status_addr] = (
AccessMode.READ, "%s READ" % self.name, _get)
class Machine:
def __init__(self):
self.memory_manager = None # type: memory.MemoryManager
self.memory = None # type: py65_memory.ObservableMemory
self.cpu = None
self.io_map = {} # type: Dict[int, Tuple[AccessMode, str, Callable]]
@staticmethod
def unimplemented_io_callback(mode, value):
raise NotImplementedError
def io_interceptor(self, address, value=None):
access_mode = (
AccessMode.READ if (value is None) else AccessMode.WRITE)
try:
(mode, name, callback) = self.io_map[address]
if access_mode & mode:
if access_mode & AccessMode.READ:
print("==== IO EVENT: READ %s" % name)
else:
print("==== IO EVENT: WRITE %s -> %02x" % (name, value))
if callback:
return callback(access_mode, value)
else:
return
else:
print("**** IO EVENT with unexpected mode: %s" % access_mode)
raise TrapException(address, access_mode)
except KeyError:
if value:
raise TrapException(
address, 'Wrote %02X ("%s")' % (value, chr(value)))
else:
raise TrapException(address, 'Read')