2019-02-27 22:26:35 +00:00
|
|
|
"""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")
|
2019-02-27 22:46:53 +00:00
|
|
|
if self.callback:
|
|
|
|
return self.callback(True)
|
2019-02-27 22:26:35 +00:00
|
|
|
|
|
|
|
def clear(self) -> Optional[int]:
|
|
|
|
self.state = False
|
|
|
|
Log(self.name, "Clearing soft switch")
|
2019-02-27 22:46:53 +00:00
|
|
|
if self.callback:
|
|
|
|
return self.callback(False)
|
2019-02-27 22:26:35 +00:00
|
|
|
|
|
|
|
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')
|