2 // mmu.cpp: Memory management
5 // (C) 2013-2018 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 09/27/2013 Created this file
16 #include "firmware/firmware.h"
18 #include "mockingboard.h"
25 // Address Map enumeration
26 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
29 uint8_t ** addrPtrRead[0x10000];
30 uint8_t ** addrPtrWrite[0x10000];
31 uint16_t addrOffset[0x10000];
33 READFUNC(funcMapRead[0x10000]);
34 WRITEFUNC(funcMapWrite[0x10000]);
36 READFUNC(slotHandlerR[8]);
37 WRITEFUNC(slotHandlerW[8]);
39 READFUNC(slotHandler2KR[8]);
40 WRITEFUNC(slotHandler2KW[8]);
55 #define ADDRESS_MAP_END { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
57 // Dunno if I like this approach or not...
59 // AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
60 // AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
63 // Would need a pointer for 80STORE as well...
65 uint8_t * pageZeroMemory = &ram[0x0000]; // $0000 - $01FF
66 uint8_t * mainMemoryR = &ram[0x0200]; // $0200 - $BFFF (read)
67 uint8_t * mainMemoryW = &ram[0x0200]; // $0200 - $BFFF (write)
69 uint8_t * mainMemoryTextR = &ram[0x0400]; // $0400 - $07FF (read)
70 uint8_t * mainMemoryTextW = &ram[0x0400]; // $0400 - $07FF (write)
71 uint8_t * mainMemoryHGRR = &ram[0x2000]; // $2000 - $3FFF (read)
72 uint8_t * mainMemoryHGRW = &ram[0x2000]; // $2000 - $3FFF (write)
74 uint8_t * slotMemory = &rom[0xC100]; // $C100 - $C7FF
75 uint8_t * peripheralMemory= &rom[0xC800]; // $C800 - $CFFF
76 uint8_t * slot3Memory = &rom[0xC300]; // $C300 - $C3FF
77 uint8_t * slot4Memory = &rom[0xC400]; // $C400 - $C4FF
78 uint8_t * slot6Memory = &diskROM[0]; // $C600 - $C6FF
79 uint8_t * lcBankMemoryR = &ram[0xD000]; // $D000 - $DFFF (read)
80 uint8_t * lcBankMemoryW = &ram[0xD000]; // $D000 - $DFFF (write)
81 uint8_t * upperMemoryR = &ram[0xE000]; // $E000 - $FFFF (read)
82 uint8_t * upperMemoryW = &ram[0xE000]; // $E000 - $FFFF (write)
84 // Function prototypes
85 uint8_t ReadNOP(uint16_t);
86 void WriteNOP(uint16_t, uint8_t);
87 uint8_t ReadMemory(uint16_t);
88 void WriteMemory(uint16_t, uint8_t);
89 uint8_t SlotR(uint16_t address);
90 void SlotW(uint16_t address, uint8_t byte);
91 uint8_t Slot2KR(uint16_t address);
92 void Slot2KW(uint16_t address, uint8_t byte);
93 uint8_t ReadKeyboard(uint16_t);
94 void Switch80STORE(uint16_t, uint8_t);
95 void SwitchRAMRD(uint16_t, uint8_t);
96 void SwitchRAMWRT(uint16_t, uint8_t);
97 void SwitchSLOTCXROM(uint16_t, uint8_t);
98 void SwitchALTZP(uint16_t, uint8_t);
99 void SwitchSLOTC3ROM(uint16_t, uint8_t);
100 uint8_t SwitchINTC8ROMR(uint16_t);
101 void SwitchINTC8ROMW(uint16_t, uint8_t);
102 void Switch80COL(uint16_t, uint8_t);
103 void SwitchALTCHARSET(uint16_t, uint8_t);
104 uint8_t ReadKeyStrobe(uint16_t);
105 uint8_t ReadBANK2(uint16_t);
106 uint8_t ReadLCRAM(uint16_t);
107 uint8_t ReadRAMRD(uint16_t);
108 uint8_t ReadRAMWRT(uint16_t);
109 uint8_t ReadSLOTCXROM(uint16_t);
110 uint8_t ReadALTZP(uint16_t);
111 uint8_t ReadSLOTC3ROM(uint16_t);
112 uint8_t Read80STORE(uint16_t);
113 uint8_t ReadVBL(uint16_t);
114 uint8_t ReadTEXT(uint16_t);
115 uint8_t ReadMIXED(uint16_t);
116 uint8_t ReadPAGE2(uint16_t);
117 uint8_t ReadHIRES(uint16_t);
118 uint8_t ReadALTCHARSET(uint16_t);
119 uint8_t Read80COL(uint16_t);
120 void WriteKeyStrobe(uint16_t, uint8_t);
121 uint8_t ReadSpeaker(uint16_t);
122 void WriteSpeaker(uint16_t, uint8_t);
123 uint8_t SwitchLCR(uint16_t);
124 void SwitchLCW(uint16_t, uint8_t);
126 uint8_t SwitchTEXTR(uint16_t);
127 void SwitchTEXTW(uint16_t, uint8_t);
128 uint8_t SwitchMIXEDR(uint16_t);
129 void SwitchMIXEDW(uint16_t, uint8_t);
130 uint8_t SwitchPAGE2R(uint16_t);
131 void SwitchPAGE2W(uint16_t, uint8_t);
132 uint8_t SwitchHIRESR(uint16_t);
133 void SwitchHIRESW(uint16_t, uint8_t);
134 uint8_t SwitchDHIRESR(uint16_t);
135 void SwitchDHIRESW(uint16_t, uint8_t);
136 void SwitchIOUDIS(uint16_t, uint8_t);
137 uint8_t ReadCassetteIn(uint16_t);
138 uint8_t ReadButton0(uint16_t);
139 uint8_t ReadButton1(uint16_t);
140 uint8_t ReadPaddle0(uint16_t);
141 uint8_t ReadIOUDIS(uint16_t);
142 uint8_t ReadDHIRES(uint16_t);
144 // The main Apple //e memory map
145 AddressMap memoryMap[] = {
146 { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
147 { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
149 // These will overlay over the previously written memory accessors
150 { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
151 { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
153 { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
154 { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
155 { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
156 { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
157 { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
158 { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
159 { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
160 { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
161 { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
162 { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
163 { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
164 { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
165 { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
166 { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
167 { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
168 { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
169 { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
170 { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
171 { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
172 { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
173 { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
174 { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
175 { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
176 { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
177 // $C020 is "Cassette Out (RO)"
178 { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
179 // May have to put a "floating bus" read there... :-/
180 // Apparently, video RAM is put on 'non-responding address'. So will
181 // need to time those out.
182 // So... $C020-$C08F, when read, return video data.
183 // $C090-$C7FF do also, as long as the slot the range refers to is empty
184 // and last and least is $CFFF, which is the Expansion ROM disable.
185 { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
186 { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
187 { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
188 { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
189 { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
190 { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
191 // $C060 is Cassette IN. No idea what it reads with N/C
192 { 0xC060, 0xC060, AM_READ, 0, 0, ReadCassetteIn, 0 },
193 { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
194 { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
195 { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
196 { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
197 { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
198 { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
200 { 0xC100, 0xC7FF, AM_READ_WRITE, 0, 0, SlotR, SlotW },
201 { 0xC800, 0xCFFE, AM_READ_WRITE, 0, 0, Slot2KR, Slot2KW },
202 { 0xCFFF, 0xCFFF, AM_READ_WRITE, 0, 0, SwitchINTC8ROMR, SwitchINTC8ROMW },
204 { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
205 { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
209 Some stuff that may be useful:
211 N.B.: Page 5-22 of UTA2E has INTC8ROM ON/OFF backwards
212 INTC8ROM is turned OFF by R/W access to $CFFF
213 INTC8ROM is turned ON by $C3xx access and SLOTC3ROM' (off)
214 WRONG: (INTC8ROM on puts card's slot ROM/RAM(?) access in $C800-$CFFF)
216 OK, so it's slightly more complex than that. Basically, when there is an access to $CFFF, all peripheral cards must *stop* responding to I/O STROBE'. Only when a card gets an I/O SELECT' signal, can it respond to I/O STROBE'.
218 INTC8ROM inhibits I/O STROBE' and activates the MB ROM in $C800-$CFFF
219 INTC8ROM is 1 by access to $C3xx when SLOTC3ROM is 0
220 INTC8ROM is 0 by access to $CFFF
222 ICX = INTCXROM (aka SLOTCXROM), SC3 = SLOTC3ROM
224 ICX=0,SC3=0 ICX=0,SC3=1 ICX=1,SC3=0 ICX=1,SC3=1
225 $C100-$C2FF slot slot internal internal
226 $C300-$C3FF internal slot internal internal
227 $C400-$CFFF slot slot internal internal
229 Read from $C800-$CFFF causes I/O STROBE to go low (and INTCXROM and INTC8ROM are not set)
233 void SetupAddressMap(void)
235 for(uint32_t i=0; i<0x10000; i++)
237 funcMapRead[i] = ReadNOP;
238 funcMapWrite[i] = WriteNOP;
244 for(uint32_t i=0; i<8; i++)
246 slotHandlerR[i] = ReadNOP;
247 slotHandlerW[i] = WriteNOP;
248 slotHandler2KR[i] = ReadNOP;
249 slotHandler2KW[i] = WriteNOP;
254 while (memoryMap[i].type != AM_END_OF_LIST)
256 switch (memoryMap[i].type)
259 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
261 funcMapRead[j] = ReadMemory;
262 funcMapWrite[j] = WriteMemory;
263 addrPtrRead[j] = memoryMap[i].memory;
264 addrPtrWrite[j] = memoryMap[i].memory;
265 addrOffset[j] = j - memoryMap[i].start;
266 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
271 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
273 funcMapRead[j] = ReadMemory;
274 addrPtrRead[j] = memoryMap[i].memory;
275 addrOffset[j] = j - memoryMap[i].start;
280 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
282 funcMapRead[j] = ReadMemory;
283 funcMapWrite[j] = WriteMemory;
284 addrPtrRead[j] = memoryMap[i].memory;
285 addrPtrWrite[j] = memoryMap[i].altMemory;
286 addrOffset[j] = j - memoryMap[i].start;
291 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
292 funcMapRead[j] = memoryMap[i].read;
296 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
297 funcMapWrite[j] = memoryMap[i].write;
301 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
303 funcMapRead[j] = memoryMap[i].read;
304 funcMapWrite[j] = memoryMap[i].write;
313 // This should correctly set up the LC pointers, but it doesn't
314 // for some reason... :-/
315 // It's because we were storing pointers directly, instead of pointers
316 // to the pointer... It's complicated. :-)
321 // Reset the MMU state after a power down event
323 void ResetMMUPointers(void)
327 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
328 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
329 mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
330 mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
334 // Shouldn't mainMemoryTextR depend on ramrd??? (I think it should...)
335 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
336 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
337 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
338 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
341 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
342 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
343 // mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
344 // mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
346 // slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
347 // slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
348 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
351 WriteLog("RAMWRT = %s\n", (ramwrt ? "ON" : "off"));
352 WriteLog("RAMRD = %s\n", (ramrd ? "ON" : "off"));
353 WriteLog("SLOTCXROM = %s\n", (intCXROM ? "ON" : "off"));
354 WriteLog("SLOTC3ROM = %s\n", (slotC3ROM ? "ON" : "off"));
355 WriteLog("ALTZP = %s\n", (altzp ? "ON" : "off"));
360 // Set up slot access
362 void InstallSlotHandler(uint8_t slot, SlotData * slotData)
367 WriteLog("InstallSlotHandler: Caller attempted to put device into slot #%u...\n", slot);
371 // Set up I/O read & write functions
372 for(uint32_t i=0; i<16; i++)
375 funcMapRead[0xC080 + (slot * 16) + i] = slotData->ioR;
378 funcMapWrite[0xC080 + (slot * 16) + i] = slotData->ioW;
381 // Set up memory access read/write functions
383 slotHandlerR[slot] = slotData->pageR;
386 slotHandlerW[slot] = slotData->pageW;
388 if (slotData->extraR)
389 slotHandler2KR[slot] = slotData->extraR;
391 if (slotData->extraW)
392 slotHandler2KW[slot] = slotData->extraW;
394 Was thinking about how to make these things more self-contained, so that the management overhead would be less. IOW, you should be able to make an object (struct) that holds everything needed to interface the MMU with itself--InstallSlotHandler *almost* does this, but not quite. A consequence of this approach is that we would have to add generic slot I/O handlers into the mix, but that shouldn't be too horrible. So it could be something like so:
399 uint16_t type; // Probably an enum so we can figure out what 'object' is
403 WRITEFUNC(slotPageW);
408 So instead of a bunch of crappy shit that sucks in here, we would have a simple thing like:
412 to encapsulate slots. This also makes it easier to move them around and makes things less error prone.
421 // Built-in functions
423 uint8_t ReadNOP(uint16_t)
425 // This is for unconnected reads, and some software looks at addresses like
426 // these. In particular, Mr. Robot and His Robot Factory failed in that it
427 // was looking at the first byte of each slots 256 byte driver space and
428 // failing if it saw a zero there. Now I have no idea what happens in the
429 // real hardware, but I suspect it would return something that looks like
430 // ReadFloatingBus().
434 void WriteNOP(uint16_t, uint8_t)
438 uint8_t ReadMemory(uint16_t address)
440 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
441 // We are guaranteed a valid address here by the setup function, so there's
442 // no need to do any checking here.
443 return (*addrPtrRead[address])[addrOffset[address]];
446 void WriteMemory(uint16_t address, uint8_t byte)
448 // We can write protect memory this way, but it adds a branch to the mix.
449 // :-/ (this can be avoided by setting up another bank of memory which we
451 if ((*addrPtrWrite[address]) == 0)
454 (*addrPtrWrite[address])[addrOffset[address]] = byte;
458 // The main memory access functions used by V65C02
460 uint8_t AppleReadMem(uint16_t address)
463 if (address == 0xD4 || address == 0xAC20)
464 WriteLog("Reading $%X...\n", address);
467 uint8_t memRead = (*(funcMapRead[address]))(address);
468 static uint16_t lastAddr = 0;
469 static uint32_t lastCount = 0;
470 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
472 if (lastAddr == address)
477 WriteLog("%d times...\n", lastCount);
479 WriteLog("Reading $%02X from $%X ($%02X, $%02X)\n", memRead, address, diskROM[1], rom[0xC601]);
486 return (*(funcMapRead[address]))(address);
490 void AppleWriteMem(uint16_t address, uint8_t byte)
493 static uint16_t lastAddr = 0;
494 static uint32_t lastCount = 0;
495 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
497 if (lastAddr == address)
502 WriteLog("%d times...\n", lastCount);
504 WriteLog("Writing to $%X\n", address);
511 if (address == 0xD4 || address == 0xAC20)
512 WriteLog("Writing $%02X @ $%X...\n", byte, address);
515 //if (address >= 0x0827 && address <= 0x082A)
516 if (address == 0x000D)
517 WriteLog("Writing $%02X @ $%X (PC=$%04X)...\n", byte, address, mainCPU.pc);
519 (*(funcMapWrite[address]))(address, byte);
523 // Generic slot handlers. These are set up here so that we can catch INTCXROM,
524 // INTC8ROM & SLOTC3ROM here instead of having to catch them in each slot handler.
526 uint8_t SlotR(uint16_t address)
528 //WriteLog("SlotR: address=$%04X, intCXROM=%d, slotC3ROM=%d, intC8ROM=%d\n", address, intCXROM, slotC3ROM, intC8ROM);
532 uint8_t slot = (address & 0xF00) >> 8;
535 if ((slotC3ROM == 0) && (slot == 3))
541 return (*(slotHandlerR[slot]))(address & 0xFF);
544 void SlotW(uint16_t address, uint8_t byte)
549 uint8_t slot = (address & 0xF00) >> 8;
552 if ((slotC3ROM == 0) && (slot == 3))
558 (*(slotHandlerW[slot]))(address & 0xFF, byte);
562 // Slot handling for 2K address space at $C800-$CFFF
564 uint8_t Slot2KR(uint16_t address)
566 if (intCXROM || intC8ROM)
569 return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
572 void Slot2KW(uint16_t address, uint8_t byte)
574 if (intCXROM || intC8ROM)
577 (*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
581 // Actual emulated I/O functions follow
583 uint8_t ReadKeyboard(uint16_t /*addr*/)
585 return lastKeyPressed | ((uint8_t)keyDown << 7);
588 void Switch80STORE(uint16_t address, uint8_t)
590 store80Mode = (bool)(address & 0x01);
591 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
593 // It seems this affects more than just the text RAM, it also seems to affect the graphics RAM as well...
596 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
597 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
598 mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
599 mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
603 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
604 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
605 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
606 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
610 void SwitchRAMRD(uint16_t address, uint8_t)
612 ramrd = (bool)(address & 0x01);
613 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
614 // mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
619 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
620 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
623 void SwitchRAMWRT(uint16_t address, uint8_t)
625 ramwrt = (bool)(address & 0x01);
626 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
627 // mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
632 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
633 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
637 // Since any slots that aren't populated are set to read from the ROM anyway,
638 // we only concern ourselves with switching populated slots here. (Note that
639 // the MB slot is a split ROM / I/O device, and it's taken care of in the
642 // N.B.: SLOTCXROM is also INTCXROM
644 void SwitchSLOTCXROM(uint16_t address, uint8_t)
646 WriteLog("Setting SLOTCXROM to %s...\n", (address & 0x01 ? "ON" : "off"));
647 intCXROM = (bool)(address & 0x01);
649 // INTC8ROM trumps all (only in the $C800--$CFFF range... which we don't account for yet... :-/)
656 slot3Memory = &rom[0xC300];
657 slot6Memory = &rom[0xC600];
661 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
662 slot6Memory = &diskROM[0];
665 // slot3Memory = (intCXROM ? &rom[0xC300] : &rom[0]);
666 slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
671 void SwitchALTZP(uint16_t address, uint8_t)
673 altzp = (bool)(address & 0x01);
674 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
678 //extern bool dumpDis;
680 // The interpretation of this name is that if it's set then we access the ROM
681 // for the card actually sitting in SLOT 3 (if any)
683 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
686 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
687 slotC3ROM = (bool)(address & 0x01);
690 slot3Memory = &rom[0xC300];
692 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
694 // slotC3ROM = false;
695 // Seems the h/w forces this with an 80 column card in slot 3...
696 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
697 // slot3Memory = &rom[0xC300];
702 We need to see where this is being switched from; if we know that, we can switch in the appropriate ROM to $C800-$CFFF. N.B.: Will probably need a custom handler routine, as some cards (like the Apple Hi-Speed SCSI card) split the 2K range into a 1K RAM space and a 1K bank switch ROM space.
705 // This is a problem with split ROM / I/O regions. Because we can't do that
706 // cleanly, we have to have a read handler for this.
708 // N.B.: We could add AM_IOREAD_WRITE and AM_READ_IOWRITE to the memory handlers
709 // to take care of split ROM / I/O regions...
711 uint8_t SwitchINTC8ROMR(uint16_t)
713 WriteLog("Hitting INTC8ROM (read)...\n");
719 // This resets the INTC8ROM switch (RW)
721 void SwitchINTC8ROMW(uint16_t, uint8_t)
723 WriteLog("Hitting INTC8ROM (write)...\n");
727 void Switch80COL(uint16_t address, uint8_t)
729 col80Mode = (bool)(address & 0x01);
732 void SwitchALTCHARSET(uint16_t address, uint8_t)
734 alternateCharset = (bool)(address & 0x01);
735 WriteLog("Setting ALTCHARSET to %s...\n", (alternateCharset ? "ON" : "off"));
738 uint8_t ReadKeyStrobe(uint16_t)
740 // No character data is read from here, just the 'any key was pressed'
742 uint8_t byte = (uint8_t)keyDown << 7;
747 uint8_t ReadBANK2(uint16_t)
749 return (lcState < 0x04 ? 0x80 : 0x00);
752 uint8_t ReadLCRAM(uint16_t)
754 // If bits 0 & 1 are set, but not at the same time, then it's ROM
755 uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
756 return (lcROM ? 0x00 : 0x80);
759 uint8_t ReadRAMRD(uint16_t)
761 return (uint8_t)ramrd << 7;
764 uint8_t ReadRAMWRT(uint16_t)
766 return (uint8_t)ramwrt << 7;
769 uint8_t ReadSLOTCXROM(uint16_t)
771 return (uint8_t)intCXROM << 7;
774 uint8_t ReadALTZP(uint16_t)
776 return (uint8_t)altzp << 7;
779 uint8_t ReadSLOTC3ROM(uint16_t)
781 return (uint8_t)slotC3ROM << 7;
784 uint8_t Read80STORE(uint16_t)
786 return (uint8_t)store80Mode << 7;
789 uint8_t ReadVBL(uint16_t)
791 return (uint8_t)vbl << 7;
794 uint8_t ReadTEXT(uint16_t)
796 return (uint8_t)textMode << 7;
799 uint8_t ReadMIXED(uint16_t)
801 return (uint8_t)mixedMode << 7;
804 uint8_t ReadPAGE2(uint16_t)
806 return (uint8_t)displayPage2 << 7;
809 uint8_t ReadHIRES(uint16_t)
811 return (uint8_t)hiRes << 7;
814 uint8_t ReadALTCHARSET(uint16_t)
816 return (uint8_t)alternateCharset << 7;
819 uint8_t Read80COL(uint16_t)
821 return (uint8_t)col80Mode << 7;
824 void WriteKeyStrobe(uint16_t, uint8_t)
829 uint8_t ReadSpeaker(uint16_t)
833 // Seems this is needed for some things...
834 return ReadFloatingBus(0);
837 void WriteSpeaker(uint16_t, uint8_t)
842 uint8_t SwitchLCR(uint16_t address)
844 lcState = address & 0x0B;
847 // Seems this is needed for some things...
848 return ReadFloatingBus(0);
851 void SwitchLCW(uint16_t address, uint8_t)
853 lcState = address & 0x0B;
863 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
865 // [R ] Read RAM bank 2; no write
866 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
868 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
873 WriteLog("SwitchLC: Read ROM, write bank 2\n");
875 // [RR] Read ROM; write RAM bank 2
876 lcBankMemoryR = &rom[0xD000];
877 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
878 upperMemoryR = &rom[0xE000];
879 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
883 WriteLog("SwitchLC: Read ROM, no write\n");
885 // [R ] Read ROM; no write
886 lcBankMemoryR = &rom[0xD000];
888 upperMemoryR = &rom[0xE000];
893 WriteLog("SwitchLC: Read/write bank 2\n");
895 // [RR] Read RAM bank 2; write RAM bank 2
896 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
897 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
898 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
899 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
902 // [R ] Read RAM bank 1; no write
903 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
905 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
909 // [RR] Read ROM; write RAM bank 1
910 lcBankMemoryR = &rom[0xD000];
911 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
912 upperMemoryR = &rom[0xE000];
913 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
916 // [R ] Read ROM; no write
917 lcBankMemoryR = &rom[0xD000];
919 upperMemoryR = &rom[0xE000];
923 // [RR] Read RAM bank 1; write RAM bank 1
924 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
925 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
926 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
927 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
932 uint8_t SwitchTEXTR(uint16_t address)
934 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
935 textMode = (bool)(address & 0x01);
937 // Seems this is needed for some things...
938 return ReadFloatingBus(0);
941 void SwitchTEXTW(uint16_t address, uint8_t)
943 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
944 textMode = (bool)(address & 0x01);
947 uint8_t SwitchMIXEDR(uint16_t address)
949 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
950 mixedMode = (bool)(address & 0x01);
952 // Seems this is needed for some things...
953 return ReadFloatingBus(0);
956 void SwitchMIXEDW(uint16_t address, uint8_t)
958 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
959 mixedMode = (bool)(address & 0x01);
963 80STORE, PAGE2, and HIRES bank switch the primary display pages, $400--$7FF and $2000--$3FFF, between motherboard RAM and auxiliary card RAM. If 80STORE is set and HIRES is reset, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF range. If 80STORE is set and HIRES is set, then PAGE2 switches between motherboard RAM and auxiliary card RAM for reading and writing in the $400--$7FF and $2000--$3FFF ranges. PAGE2 set selects auxiliary card RAM, and PAGE2 reset selects motherboard RAM. If 80STORE is reset, then RAMRD and RAMWRT will bank switch the $400-$7FF and $2000--$3FFF ranges along with the rest of the $200--$BFFF range.
965 uint8_t SwitchPAGE2R(uint16_t address)
967 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
968 displayPage2 = (bool)(address & 0x01);
972 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
973 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
977 mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
978 mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
982 // Seems this is needed for some things...
983 return ReadFloatingBus(0);
986 void SwitchPAGE2W(uint16_t address, uint8_t)
988 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
989 displayPage2 = (bool)(address & 0x01);
993 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
994 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
998 mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
999 mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
1004 uint8_t SwitchHIRESR(uint16_t address)
1006 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1007 hiRes = (bool)(address & 0x01);
1009 // Seems this is needed for some things...
1010 return ReadFloatingBus(0);
1013 void SwitchHIRESW(uint16_t address, uint8_t)
1015 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1016 hiRes = (bool)(address & 0x01);
1019 uint8_t SwitchDHIRESR(uint16_t address)
1021 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1022 // Hmm, this breaks convention too, like SLOTCXROM
1024 dhires = !((bool)(address & 0x01));
1027 // Seems this is needed for some things...
1028 return ReadFloatingBus(0);
1031 void SwitchDHIRESW(uint16_t address, uint8_t)
1033 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1035 dhires = !((bool)(address & 0x01));
1038 void SwitchIOUDIS(uint16_t address, uint8_t)
1040 ioudis = !((bool)(address & 0x01));
1043 uint8_t ReadCassetteIn(uint16_t)
1045 // No idea what it's supposed to return if there's no cassette attached, so let's try this (Serpentine crashes if $FF is returned...)
1046 // Serpentine crashes even earlier if $00 is returned... Now what???
1047 // This seems to work for Serpentine, not sure what's supposed to be returned here... Maybe it's an RNG when N/C?
1051 uint8_t ReadButton0(uint16_t)
1053 return (uint8_t)openAppleDown << 7;
1056 uint8_t ReadButton1(uint16_t)
1058 return (uint8_t)closedAppleDown << 7;
1061 // The way the paddles work is that a strobe is written (or read) to $C070,
1062 // then software counts down the time that it takes for the paddle outputs
1063 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1065 // NB: This is really paddles 0-3, not just 0 :-P
1066 uint8_t ReadPaddle0(uint16_t)
1071 uint8_t ReadIOUDIS(uint16_t)
1073 return (uint8_t)ioudis << 7;
1076 uint8_t ReadDHIRES(uint16_t)
1078 return (uint8_t)dhires << 7;
1081 // Whenever a read is done to a MMIO location that is unconnected to anything,
1082 // it actually sees the RAM access done by the video generation hardware. Some
1083 // programs exploit this, so we emulate it here.
1085 // N.B.: frameCycles will be off by the true amount because this only
1086 // increments by the amount of a speaker cycle, not the cycle count when
1087 // the access happens... !!! FIX !!!
1088 uint8_t ReadFloatingBus(uint16_t)
1090 // Get the currently elapsed cycle count for this frame
1091 uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1093 // Make counters out of the cycle count. There are 65 cycles per line.
1094 uint32_t numLines = frameCycles / 65;
1095 uint32_t numHTicks = frameCycles - (numLines * 65);
1097 // Convert these to H/V counters
1098 uint32_t hcount = numHTicks - 1;
1100 // HC sees zero twice:
1101 if (hcount == 0xFFFFFFFF)
1104 uint32_t vcount = numLines + 0xFA;
1106 // Now do the address calculations
1107 uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1108 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1109 uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1111 // Add in particulars for the gfx mode we're in...
1112 if (textMode || (!textMode && !hiRes))
1113 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1114 | (!store80Mode && displayPage2 ? 0x800 : 0);
1116 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1117 | (!store80Mode && displayPage2 ? 0x4000 : 0)
1118 | ((vcount & 0x07) << 10);
1120 // The address so read is *always* in main RAM, never alt RAM
1121 return ram[address];