mirror of
https://github.com/KrisKennaway/ii-vision.git
synced 2024-12-27 12:29:43 +00:00
80402f25a5
- Don't trap unexpected entrypoint when crossing between regions via RTS - Implement TICK handler - Improve status printing in CPU loop
209 lines
7.3 KiB
Python
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
|