ii-vision/simulator/apple2.py

209 lines
7.3 KiB
Python

import machine
import memory
import uthernet
from py65 import disassembler
from py65.devices import mpu65c02
class AppleII(machine.Machine):
def __init__(self, uthernet: uthernet.Uthernet):
memory_map = [
memory.MemoryRegion("Zero page", 0x0000, 0x00ff),
memory.MemoryRegion("Stack", 0x0100, 0x01ff),
memory.MemoryRegion(
"Text page 1", 0x0400, 0x7ff,
write_interceptor=self.TextPageWriteInterceptor),
memory.MemoryRegion("HiRes Page 1", 0x2000, 0x2fff),
memory.MemoryRegion("HiRes Page 2", 0x4000, 0x4fff, writable=False),
memory.MemoryRegion(
"IO page", 0xc000, 0xc0ff,
read_interceptor=self.io_interceptor,
write_interceptor=self.io_interceptor),
memory.MemoryRegion("Slot 1 ROM", 0xc100, 0xc1ff, writable=False),
memory.MemoryRegion("Slot 2 ROM", 0xc200, 0xc2ff, writable=False),
memory.MemoryRegion("Slot 3 ROM", 0xc300, 0xc3ff, writable=False),
memory.MemoryRegion("Slot 4 ROM", 0xc400, 0xc4ff, writable=False),
memory.MemoryRegion("Slot 5 ROM", 0xc500, 0xc5ff, writable=False),
memory.MemoryRegion("Slot 6 ROM", 0xc600, 0xc6ff, writable=False),
memory.MemoryRegion("Slot 7 ROM", 0xc700, 0xc7ff, writable=False),
memory.MemoryRegion(
"ROM", 0xd000, 0xffff,
entrypoints={
0xf3e2: machine._Event("ROM", "HGR"),
0xfca8: self._Wait,
0xfded: machine._Event("ROM", "COUT"),
0xfe89: machine._Event("ROM", "Select the keyboard (IN#0)")
},
writable=False
)
]
self.memory_manager = memory.MemoryManager(memory_map)
self.memory = self.memory_manager.memory
self.cpu = mpu65c02.MPU(memory=self.memory)
self.uthernet = uthernet # type: uthernet.Uthernet
self.disassembler = disassembler.Disassembler(self.cpu)
def _uther_wmode(mode, value):
if mode & machine.AccessMode.READ:
return self.uthernet.read_mode()
else:
return self.uthernet.write_mode(value)
def _uther_wadrh(mode, value):
old = self.uthernet.ptr
self.uthernet.ptr = (value << 8) | (self.uthernet.ptr & 0x7f)
machine.Log("WADRH", "%04x -> %04x" % (old, self.uthernet.ptr))
def _uther_wadrl(mode, value):
old = self.uthernet.ptr
self.uthernet.ptr = (self.uthernet.ptr & 0x7f00) | value
machine.Log("WADRL", "%04x -> %04x" % (old, self.uthernet.ptr))
def _uther_wdata(mode, value):
if mode & machine.AccessMode.READ:
return self.uthernet.read_data()
else:
return self.uthernet.write_data(value)
def _tick(mode, value):
machine.Log("Tick", self.cpu.processorCycles)
# Set up interceptors for accessing various interesting parts of the
# memory map
self.io_map = {
0xc030: (
machine.AccessMode.RW, "TICK", _tick),
0xc094: (
machine.AccessMode.RW, "WMODE", _uther_wmode),
0xc095: (
machine.AccessMode.WRITE, "WADRH", _uther_wadrh),
0xc096: (
machine.AccessMode.WRITE, "WADRL", _uther_wadrl),
0xc097: (
machine.AccessMode.RW, "WDATA", _uther_wdata),
}
self.soft_switches = {}
for ss in [
machine.SoftSwitch(
"80Store",
clear_addr=0xc000,
set_addr=0xc001,
status_addr=0xc018,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"RamRd",
clear_addr=0xc002,
set_addr=0xc003,
status_addr=0xc013,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"RamWrt",
clear_addr=0xc004,
set_addr=0xc005,
status_addr=0xc014,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"IntCxROM",
clear_addr=0xc006,
set_addr=0xc007,
status_addr=0xc015,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"AltZP",
clear_addr=0xc008,
set_addr=0xc009,
status_addr=0xc016,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"SlotC3ROM",
clear_addr=0xc00a,
set_addr=0xc00b,
status_addr=0xc017,
callback=machine.SoftSwitch.unimplemented
),
machine.SoftSwitch(
"80Col",
clear_addr=0xc00c,
set_addr=0xc00d,
status_addr=0xc01f
),
machine.SoftSwitch(
"AltCharSet",
clear_addr=0xc00e,
set_addr=0xc00f,
status_addr=0xc01e
),
machine.SoftSwitch(
"Text",
clear_addr=0xc050,
set_addr=0xc051,
status_addr=0xc01a,
mode=machine.AccessMode.RW
),
machine.SoftSwitch(
"Mixed",
clear_addr=0xc052, set_addr=0xc053,
status_addr=0xc01b,
mode=machine.AccessMode.RW
),
machine.SoftSwitch(
"Page2",
clear_addr=0xc054, set_addr=0xc055,
status_addr=0xc01c,
mode=machine.AccessMode.RW
),
machine.SoftSwitch(
"Hires",
clear_addr=0xc056, set_addr=0xc057,
status_addr=0xc01d,
mode=machine.AccessMode.RW
)
]:
self.soft_switches[ss.name] = ss
ss.register(self.io_map)
@staticmethod
def _Wait(_):
print("Waiting")
# TODO: convert addresses to screen coordinates
# See e.g. https://retrocomputing.stackexchange.com/questions/2534/what-are-the-screen-holes-in-apple-ii-graphics
@staticmethod
def TextPageWriteInterceptor(address, value):
print('Wrote "%s" to text page address $%04X' % (chr(value & 0x7f),
address))
def Run(self, pc, trace=False):
ctr = 0
self.cpu.pc = pc
old_pc = self.cpu.pc
while True:
self.memory_manager.MaybeInterceptExecution(self.cpu, old_pc)
old_pc = self.cpu.pc
if trace:
cpu = str(self.cpu).split("\n")
if ctr % 20 == 0:
print(cpu[0])
print(cpu[1], " $%04X: %-12s %d" % (
self.cpu.pc,
self.disassembler.instruction_at(self.cpu.pc)[1],
self.cpu.processorCycles
))
self.cpu.step()
if self.cpu.pc == old_pc:
break
ctr += 1