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