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
18 #include "mockingboard.h"
26 // Address Map enumeration
27 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
30 uint8_t ** addrPtrRead[0x10000];
31 uint8_t ** addrPtrWrite[0x10000];
32 uint16_t addrOffset[0x10000];
34 READFUNC(funcMapRead[0x10000]);
35 WRITEFUNC(funcMapWrite[0x10000]);
37 READFUNC(slotHandlerR[8]);
38 WRITEFUNC(slotHandlerW[8]);
40 READFUNC(slotHandler2KR[8]);
41 WRITEFUNC(slotHandler2KW[8]);
56 #define ADDRESS_MAP_END { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
58 // Dunno if I like this approach or not...
60 // AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
61 // AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
64 // Would need a pointer for 80STORE as well...
66 uint8_t * pageZeroMemory = &ram[0x0000]; // $0000 - $01FF
67 uint8_t * mainMemoryR = &ram[0x0200]; // $0200 - $BFFF (read)
68 uint8_t * mainMemoryW = &ram[0x0200]; // $0200 - $BFFF (write)
70 uint8_t * mainMemoryTextR = &ram[0x0400]; // $0400 - $07FF (read)
71 uint8_t * mainMemoryTextW = &ram[0x0400]; // $0400 - $07FF (write)
72 uint8_t * mainMemoryHGRR = &ram[0x2000]; // $2000 - $3FFF (read)
73 uint8_t * mainMemoryHGRW = &ram[0x2000]; // $2000 - $3FFF (write)
75 uint8_t * slotMemory = &rom[0xC100]; // $C100 - $C7FF
76 uint8_t * peripheralMemory= &rom[0xC800]; // $C800 - $CFFF
77 uint8_t * slot3Memory = &rom[0xC300]; // $C300 - $C3FF
78 uint8_t * slot4Memory = &rom[0xC400]; // $C400 - $C4FF
79 uint8_t * slot6Memory = &diskROM[0]; // $C600 - $C6FF
80 uint8_t * lcBankMemoryR = &ram[0xD000]; // $D000 - $DFFF (read)
81 uint8_t * lcBankMemoryW = &ram[0xD000]; // $D000 - $DFFF (write)
82 uint8_t * upperMemoryR = &ram[0xE000]; // $E000 - $FFFF (read)
83 uint8_t * upperMemoryW = &ram[0xE000]; // $E000 - $FFFF (write)
86 // Function prototypes
87 uint8_t ReadNOP(uint16_t);
88 void WriteNOP(uint16_t, uint8_t);
89 uint8_t ReadMemory(uint16_t);
90 void WriteMemory(uint16_t, uint8_t);
91 uint8_t SlotR(uint16_t address);
92 void SlotW(uint16_t address, uint8_t byte);
93 uint8_t Slot2KR(uint16_t address);
94 void Slot2KW(uint16_t address, uint8_t byte);
95 uint8_t ReadKeyboard(uint16_t);
96 void Switch80STORE(uint16_t, uint8_t);
97 void SwitchRAMRD(uint16_t, uint8_t);
98 void SwitchRAMWRT(uint16_t, uint8_t);
99 void SwitchSLOTCXROM(uint16_t, uint8_t);
100 void SwitchALTZP(uint16_t, uint8_t);
101 void SwitchSLOTC3ROM(uint16_t, uint8_t);
102 uint8_t SwitchINTC8ROMR(uint16_t);
103 void SwitchINTC8ROMW(uint16_t, uint8_t);
104 void Switch80COL(uint16_t, uint8_t);
105 void SwitchALTCHARSET(uint16_t, uint8_t);
106 uint8_t ReadKeyStrobe(uint16_t);
107 uint8_t ReadBANK2(uint16_t);
108 uint8_t ReadLCRAM(uint16_t);
109 uint8_t ReadRAMRD(uint16_t);
110 uint8_t ReadRAMWRT(uint16_t);
111 uint8_t ReadSLOTCXROM(uint16_t);
112 uint8_t ReadALTZP(uint16_t);
113 uint8_t ReadSLOTC3ROM(uint16_t);
114 uint8_t Read80STORE(uint16_t);
115 uint8_t ReadVBL(uint16_t);
116 uint8_t ReadTEXT(uint16_t);
117 uint8_t ReadMIXED(uint16_t);
118 uint8_t ReadPAGE2(uint16_t);
119 uint8_t ReadHIRES(uint16_t);
120 uint8_t ReadALTCHARSET(uint16_t);
121 uint8_t Read80COL(uint16_t);
122 void WriteKeyStrobe(uint16_t, uint8_t);
123 uint8_t ReadSpeaker(uint16_t);
124 void WriteSpeaker(uint16_t, uint8_t);
125 uint8_t SwitchLCR(uint16_t);
126 void SwitchLCW(uint16_t, uint8_t);
128 uint8_t SwitchTEXTR(uint16_t);
129 void SwitchTEXTW(uint16_t, uint8_t);
130 uint8_t SwitchMIXEDR(uint16_t);
131 void SwitchMIXEDW(uint16_t, uint8_t);
132 uint8_t SwitchPAGE2R(uint16_t);
133 void SwitchPAGE2W(uint16_t, uint8_t);
134 uint8_t SwitchHIRESR(uint16_t);
135 void SwitchHIRESW(uint16_t, uint8_t);
136 uint8_t SwitchDHIRESR(uint16_t);
137 void SwitchDHIRESW(uint16_t, uint8_t);
138 void SwitchIOUDIS(uint16_t, uint8_t);
139 uint8_t ReadButton0(uint16_t);
140 uint8_t ReadButton1(uint16_t);
141 uint8_t ReadPaddle0(uint16_t);
142 uint8_t ReadIOUDIS(uint16_t);
143 uint8_t ReadDHIRES(uint16_t);
146 // The main Apple //e memory map
147 AddressMap memoryMap[] = {
148 { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
149 { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
151 // These will overlay over the previously written memory accessors
152 { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
153 { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
155 { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
156 { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
157 { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
158 { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
159 { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
160 { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
161 { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
162 { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
163 { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
164 { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
165 { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
166 { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
167 { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
168 { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
169 { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
170 { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
171 { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
172 { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
173 { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
174 { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
175 { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
176 { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
177 { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
178 { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
179 // $C020 is "Cassette Out (RO)"
180 { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
181 // May have to put a "floating bus" read there... :-/
182 // Apparently, video RAM is put on 'non-responding address'. So will
183 // need to time those out.
184 // So... $C020-$C08F, when read, return video data.
185 // $C090-$C7FF do also, as long as the slot the range refers to is empty
186 // and last and least is $CFFF, which is the Expansion ROM disable.
187 { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
188 { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
189 { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
190 { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
191 { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
192 { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
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)
234 void SetupAddressMap(void)
236 for(uint32_t i=0; i<0x10000; i++)
238 funcMapRead[i] = ReadNOP;
239 funcMapWrite[i] = WriteNOP;
245 for(uint32_t i=0; i<8; i++)
247 slotHandlerR[i] = ReadNOP;
248 slotHandlerW[i] = WriteNOP;
249 slotHandler2KR[i] = ReadNOP;
250 slotHandler2KW[i] = WriteNOP;
255 while (memoryMap[i].type != AM_END_OF_LIST)
257 switch (memoryMap[i].type)
260 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
262 funcMapRead[j] = ReadMemory;
263 funcMapWrite[j] = WriteMemory;
264 addrPtrRead[j] = memoryMap[i].memory;
265 addrPtrWrite[j] = memoryMap[i].memory;
266 addrOffset[j] = j - memoryMap[i].start;
267 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
272 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
274 funcMapRead[j] = ReadMemory;
275 addrPtrRead[j] = memoryMap[i].memory;
276 addrOffset[j] = j - memoryMap[i].start;
281 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
283 funcMapRead[j] = ReadMemory;
284 funcMapWrite[j] = WriteMemory;
285 addrPtrRead[j] = memoryMap[i].memory;
286 addrPtrWrite[j] = memoryMap[i].altMemory;
287 addrOffset[j] = j - memoryMap[i].start;
292 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
293 funcMapRead[j] = memoryMap[i].read;
297 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
298 funcMapWrite[j] = memoryMap[i].write;
302 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
304 funcMapRead[j] = memoryMap[i].read;
305 funcMapWrite[j] = memoryMap[i].write;
314 // This should correctly set up the LC pointers, but it doesn't
315 // for some reason... :-/
316 // It's because we were storing pointers directly, instead of pointers
317 // to the pointer... It's complicated. :-)
323 // Reset the MMU state after a power down event
325 void ResetMMUPointers(void)
329 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
330 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
334 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
335 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
338 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
339 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
340 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
341 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
343 // slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
344 // slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
345 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
348 WriteLog("RAMWRT = %s\n", (ramwrt ? "ON" : "off"));
349 WriteLog("RAMRD = %s\n", (ramrd ? "ON" : "off"));
350 WriteLog("SLOTCXROM = %s\n", (intCXROM ? "ON" : "off"));
351 WriteLog("SLOTC3ROM = %s\n", (slotC3ROM ? "ON" : "off"));
352 WriteLog("ALTZP = %s\n", (altzp ? "ON" : "off"));
358 // Set up slot access
360 void InstallSlotHandler(uint8_t slot, SlotData * slotData)
365 WriteLog("InstallSlotHanlder: Caller attempted to put device into slot #%u...\n", slot);
369 // Set up I/O read & write functions
370 for(uint32_t i=0; i<16; i++)
373 funcMapRead[0xC080 + (slot * 16) + i] = slotData->ioR;
376 funcMapWrite[0xC080 + (slot * 16) + i] = slotData->ioW;
379 // Set up memory access read/write functions
381 slotHandlerR[slot] = slotData->pageR;
384 slotHandlerW[slot] = slotData->pageW;
386 if (slotData->extraR)
387 slotHandler2KR[slot] = slotData->extraR;
389 if (slotData->extraW)
390 slotHandler2KW[slot] = slotData->extraW;
395 // Built-in functions
397 uint8_t ReadNOP(uint16_t)
399 // This is for unconnected reads, and some software looks at addresses like these. In particular, Mr. Robot and His Robot Factory failed in that it was looking at the first byte of each slots 256 byte driver space and failing if it saw a zero there. Now I have no idea what happens in the real hardware, but I suspect it would return something that looks like ReadFloatingBus().
404 void WriteNOP(uint16_t, uint8_t)
409 uint8_t ReadMemory(uint16_t address)
411 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
412 // We are guaranteed a valid address here by the setup function, so there's
413 // no need to do any checking here.
414 return (*addrPtrRead[address])[addrOffset[address]];
418 void WriteMemory(uint16_t address, uint8_t byte)
420 // We can write protect memory this way, but it adds a branch to the mix.
421 // :-/ (this can be avoided by setting up another bank of memory which we
423 if ((*addrPtrWrite[address]) == 0)
426 (*addrPtrWrite[address])[addrOffset[address]] = byte;
431 // The main memory access functions used by V65C02
433 uint8_t AppleReadMem(uint16_t address)
436 if (address == 0xD4 || address == 0xAC20)
437 WriteLog("Reading $%X...\n", address);
440 uint8_t memRead = (*(funcMapRead[address]))(address);
441 static uint16_t lastAddr = 0;
442 static uint32_t lastCount = 0;
443 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
445 if (lastAddr == address)
450 WriteLog("%d times...\n", lastCount);
452 WriteLog("Reading $%02X from $%X ($%02X, $%02X)\n", memRead, address, diskROM[1], rom[0xC601]);
459 return (*(funcMapRead[address]))(address);
464 void AppleWriteMem(uint16_t address, uint8_t byte)
467 static uint16_t lastAddr = 0;
468 static uint32_t lastCount = 0;
469 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
471 if (lastAddr == address)
476 WriteLog("%d times...\n", lastCount);
478 WriteLog("Writing to $%X\n", address);
485 if (address == 0xD4 || address == 0xAC20)
486 WriteLog("Writing $%02X @ $%X...\n", byte, address);
489 //if (address >= 0x0827 && address <= 0x082A)
490 if (address == 0x000D)
491 WriteLog("Writing $%02X @ $%X (PC=$%04X)...\n", byte, address, mainCPU.pc);
493 (*(funcMapWrite[address]))(address, byte);
498 // Generic slot handlers. These are set up here so that we can catch INTCXROM,
499 // INTC8ROM & SLOTC3ROM here instead of having to catch them in each slot handler.
501 uint8_t SlotR(uint16_t address)
503 //WriteLog("SlotR: address=$%04X, intCXROM=%d, slotC3ROM=%d, intC8ROM=%d\n", address, intCXROM, slotC3ROM, intC8ROM);
507 uint8_t slot = (address & 0xF00) >> 8;
510 if ((slotC3ROM == 0) && (slot == 3))
516 return (*(slotHandlerR[slot]))(address & 0xFF);
520 void SlotW(uint16_t address, uint8_t byte)
525 uint8_t slot = (address & 0xF00) >> 8;
528 if ((slotC3ROM == 0) && (slot == 3))
534 (*(slotHandlerW[slot]))(address & 0xFF, byte);
539 // Slot handling for 2K address space at $C800-$CFFF
541 uint8_t Slot2KR(uint16_t address)
543 if (intCXROM || intC8ROM)
546 return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
550 void Slot2KW(uint16_t address, uint8_t byte)
552 if (intCXROM || intC8ROM)
555 (*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
560 // Actual emulated I/O functions follow
562 uint8_t ReadKeyboard(uint16_t /*addr*/)
564 return lastKeyPressed | ((uint8_t)keyDown << 7);
568 void Switch80STORE(uint16_t address, uint8_t)
570 store80Mode = (bool)(address & 0x01);
571 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
575 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
576 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
580 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
581 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
586 void SwitchRAMRD(uint16_t address, uint8_t)
588 ramrd = (bool)(address & 0x01);
589 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
590 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
595 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
599 void SwitchRAMWRT(uint16_t address, uint8_t)
601 ramwrt = (bool)(address & 0x01);
602 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
603 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
608 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
613 // Since any slots that aren't populated are set to read from the ROM anyway,
614 // we only concern ourselves with switching populated slots here. (Note that
615 // the MB slot is a split ROM / I/O device, and it's taken care of in the
618 // N.B.: SLOTCXROM is also INTCXROM
620 void SwitchSLOTCXROM(uint16_t address, uint8_t)
622 WriteLog("Setting SLOTCXROM to %s...\n", (address & 0x01 ? "ON" : "off"));
623 intCXROM = (bool)(address & 0x01);
625 // INTC8ROM trumps all (only in the $C800--$CFFF range... which we don't account for yet... :-/)
632 slot3Memory = &rom[0xC300];
633 slot6Memory = &rom[0xC600];
637 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
638 slot6Memory = &diskROM[0];
641 // slot3Memory = (intCXROM ? &rom[0xC300] : &rom[0]);
642 slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
648 void SwitchALTZP(uint16_t address, uint8_t)
650 altzp = (bool)(address & 0x01);
651 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
655 //extern bool dumpDis;
657 // The interpretation of this name is that if it's set then we access the ROM
658 // for the card actually sitting in SLOT 3 (if any)
660 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
663 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
664 slotC3ROM = (bool)(address & 0x01);
667 slot3Memory = &rom[0xC300];
669 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
671 // slotC3ROM = false;
672 // Seems the h/w forces this with an 80 column card in slot 3...
673 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
674 // slot3Memory = &rom[0xC300];
680 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.
683 // This is a problem with split ROM / I/O regions. Because we can't do that
684 // cleanly, we have to have a read handler for this.
686 // N.B.: We could add AM_IOREAD_WRITE and AM_READ_IOWRITE to the memory handlers
687 // to take care of split ROM / I/O regions...
689 uint8_t SwitchINTC8ROMR(uint16_t)
691 WriteLog("Hitting INTC8ROM (read)...\n");
698 // This resets the INTC8ROM switch (RW)
700 void SwitchINTC8ROMW(uint16_t, uint8_t)
702 WriteLog("Hitting INTC8ROM (write)...\n");
707 void Switch80COL(uint16_t address, uint8_t)
709 col80Mode = (bool)(address & 0x01);
713 void SwitchALTCHARSET(uint16_t address, uint8_t)
715 alternateCharset = (bool)(address & 0x01);
716 WriteLog("Setting ALTCHARSET to %s...\n", (alternateCharset ? "ON" : "off"));
720 uint8_t ReadKeyStrobe(uint16_t)
722 // No character data is read from here, just the 'any key was pressed'
724 uint8_t byte = (uint8_t)keyDown << 7;
730 uint8_t ReadBANK2(uint16_t)
732 return (lcState < 0x04 ? 0x80 : 0x00);
736 uint8_t ReadLCRAM(uint16_t)
738 // If bits 0 & 1 are set, but not at the same time, then it's ROM
739 uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
740 return (lcROM ? 0x00 : 0x80);
744 uint8_t ReadRAMRD(uint16_t)
746 return (uint8_t)ramrd << 7;
750 uint8_t ReadRAMWRT(uint16_t)
752 return (uint8_t)ramwrt << 7;
756 uint8_t ReadSLOTCXROM(uint16_t)
758 return (uint8_t)intCXROM << 7;
762 uint8_t ReadALTZP(uint16_t)
764 return (uint8_t)altzp << 7;
768 uint8_t ReadSLOTC3ROM(uint16_t)
770 return (uint8_t)slotC3ROM << 7;
774 uint8_t Read80STORE(uint16_t)
776 return (uint8_t)store80Mode << 7;
780 uint8_t ReadVBL(uint16_t)
782 return (uint8_t)vbl << 7;
786 uint8_t ReadTEXT(uint16_t)
788 return (uint8_t)textMode << 7;
792 uint8_t ReadMIXED(uint16_t)
794 return (uint8_t)mixedMode << 7;
798 uint8_t ReadPAGE2(uint16_t)
800 return (uint8_t)displayPage2 << 7;
804 uint8_t ReadHIRES(uint16_t)
806 return (uint8_t)hiRes << 7;
810 uint8_t ReadALTCHARSET(uint16_t)
812 return (uint8_t)alternateCharset << 7;
816 uint8_t Read80COL(uint16_t)
818 return (uint8_t)col80Mode << 7;
822 void WriteKeyStrobe(uint16_t, uint8_t)
828 uint8_t ReadSpeaker(uint16_t)
835 void WriteSpeaker(uint16_t, uint8_t)
841 uint8_t SwitchLCR(uint16_t address)
843 lcState = address & 0x0B;
849 void SwitchLCW(uint16_t address, uint8_t)
851 lcState = address & 0x0B;
862 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
864 // [R ] Read RAM bank 2; no write
865 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
867 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
872 WriteLog("SwitchLC: Read ROM, write bank 2\n");
874 // [RR] Read ROM; write RAM bank 2
875 lcBankMemoryR = &rom[0xD000];
876 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
877 upperMemoryR = &rom[0xE000];
878 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
882 WriteLog("SwitchLC: Read ROM, no write\n");
884 // [R ] Read ROM; no write
885 lcBankMemoryR = &rom[0xD000];
887 upperMemoryR = &rom[0xE000];
892 WriteLog("SwitchLC: Read/write bank 2\n");
894 // [RR] Read RAM bank 2; write RAM bank 2
895 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
896 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
897 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
898 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
901 // [R ] Read RAM bank 1; no write
902 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
904 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
908 // [RR] Read ROM; write RAM bank 1
909 lcBankMemoryR = &rom[0xD000];
910 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
911 upperMemoryR = &rom[0xE000];
912 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
915 // [R ] Read ROM; no write
916 lcBankMemoryR = &rom[0xD000];
918 upperMemoryR = &rom[0xE000];
922 // [RR] Read RAM bank 1; write RAM bank 1
923 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
924 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
925 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
926 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);
940 void SwitchTEXTW(uint16_t address, uint8_t)
942 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
943 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);
955 void SwitchMIXEDW(uint16_t address, uint8_t)
957 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
958 mixedMode = (bool)(address & 0x01);
962 uint8_t SwitchPAGE2R(uint16_t address)
964 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
965 displayPage2 = (bool)(address & 0x01);
969 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
970 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
977 void SwitchPAGE2W(uint16_t address, uint8_t)
979 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
980 displayPage2 = (bool)(address & 0x01);
984 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
985 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
990 uint8_t SwitchHIRESR(uint16_t address)
992 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
993 hiRes = (bool)(address & 0x01);
998 void SwitchHIRESW(uint16_t address, uint8_t)
1000 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1001 hiRes = (bool)(address & 0x01);
1005 uint8_t SwitchDHIRESR(uint16_t address)
1007 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1008 // Hmm, this breaks convention too, like SLOTCXROM
1010 dhires = !((bool)(address & 0x01));
1016 void SwitchDHIRESW(uint16_t address, uint8_t)
1018 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1020 dhires = !((bool)(address & 0x01));
1024 void SwitchIOUDIS(uint16_t address, uint8_t)
1026 ioudis = !((bool)(address & 0x01));
1030 uint8_t ReadButton0(uint16_t)
1032 return (uint8_t)openAppleDown << 7;
1036 uint8_t ReadButton1(uint16_t)
1038 return (uint8_t)closedAppleDown << 7;
1042 // The way the paddles work is that a strobe is written (or read) to $C070,
1043 // then software counts down the time that it takes for the paddle outputs
1044 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1046 // NB: This is really paddles 0-3, not just 0 :-P
1047 uint8_t ReadPaddle0(uint16_t)
1053 uint8_t ReadIOUDIS(uint16_t)
1055 return (uint8_t)ioudis << 7;
1059 uint8_t ReadDHIRES(uint16_t)
1061 return (uint8_t)dhires << 7;
1065 // Whenever a read is done to a MMIO location that is unconnected to anything,
1066 // it actually sees the RAM access done by the video generation hardware. Some
1067 // programs exploit this, so we emulate it here.
1069 // N.B.: frameCycles will be off by the true amount because this only
1070 // increments by the amount of a speaker cycle, not the cycle count when
1071 // the access happens... !!! FIX !!!
1072 uint8_t ReadFloatingBus(uint16_t)
1074 // Get the currently elapsed cycle count for this frame
1075 uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1077 // Make counters out of the cycle count. There are 65 cycles per line.
1078 uint32_t numLines = frameCycles / 65;
1079 uint32_t numHTicks = frameCycles - (numLines * 65);
1081 // Convert these to H/V counters
1082 uint32_t hcount = numHTicks - 1;
1084 // HC sees zero twice:
1085 if (hcount == 0xFFFFFFFF)
1088 uint32_t vcount = numLines + 0xFA;
1090 // Now do the address calculations
1091 uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1092 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1093 uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1095 // Add in particulars for the gfx mode we're in...
1096 if (textMode || (!textMode && !hiRes))
1097 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1098 | (!store80Mode && displayPage2 ? 0x800 : 0);
1100 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1101 | (!store80Mode && displayPage2 ? 0x4000 : 0)
1102 | ((vcount & 0x07) << 10);
1104 // The address so read is *always* in main RAM, not alt RAM
1105 return ram[address];