2 // mmu.cpp: Memory management
5 // (C) 2013 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 09/27/2013 Created this file
25 // Address Map enumeration
26 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
28 // Macros for function pointers
29 #define READFUNC(x) uint8_t (* x)(uint16_t)
30 #define WRITEFUNC(x) void (* x)(uint16_t, uint8_t)
33 uint8_t ** addrPtrRead[0x10000];
34 uint8_t ** addrPtrWrite[0x10000];
35 uint16_t addrOffset[0x10000];
37 READFUNC(funcMapRead[0x10000]);
38 WRITEFUNC(funcMapWrite[0x10000]);
51 #define ADDRESS_MAP_END { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
53 // Dunno if I like this approach or not...
55 // AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
56 // AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
59 // Would need a pointer for 80STORE as well...
61 uint8_t * pageZeroMemory = &ram[0x0000]; // $0000 - $01FF
62 uint8_t * mainMemoryR = &ram[0x0200]; // $0200 - $BFFF (read)
63 uint8_t * mainMemoryW = &ram[0x0200]; // $0200 - $BFFF (write)
65 uint8_t * mainMemoryTextR = &ram[0x0400]; // $0400 - $07FF (read)
66 uint8_t * mainMemoryTextW = &ram[0x0400]; // $0400 - $07FF (write)
67 uint8_t * mainMemoryHGRR = &ram[0x2000]; // $2000 - $3FFF (read)
68 uint8_t * mainMemoryHGRW = &ram[0x2000]; // $2000 - $3FFF (write)
70 uint8_t * slotMemory = &rom[0xC100]; // $C100 - $CFFF
71 uint8_t * slot3Memory = &rom[0xC300]; // $C300 - $C3FF
72 uint8_t * slot6Memory = &diskROM[0]; // $C600 - $C6FF
73 uint8_t * lcBankMemoryR = &ram[0xD000]; // $D000 - $DFFF (read)
74 uint8_t * lcBankMemoryW = &ram[0xD000]; // $D000 - $DFFF (write)
75 uint8_t * upperMemoryR = &ram[0xE000]; // $E000 - $FFFF (read)
76 uint8_t * upperMemoryW = &ram[0xE000]; // $E000 - $FFFF (write)
79 // Function prototypes
80 uint8_t ReadNOP(uint16_t);
81 void WriteNOP(uint16_t, uint8_t);
82 uint8_t ReadMemory(uint16_t);
83 void WriteMemory(uint16_t, uint8_t);
84 uint8_t ReadKeyboard(uint16_t);
85 void Switch80STORE(uint16_t, uint8_t);
86 void SwitchRAMRD(uint16_t, uint8_t);
87 void SwitchRAMWRT(uint16_t, uint8_t);
88 void SwitchSLOTCXROM(uint16_t, uint8_t);
89 void SwitchALTZP(uint16_t, uint8_t);
90 void SwitchSLOTC3ROM(uint16_t, uint8_t);
91 void Switch80COL(uint16_t, uint8_t);
92 void SwitchALTCHARSET(uint16_t, uint8_t);
93 uint8_t ReadKeyStrobe(uint16_t);
94 uint8_t ReadBANK2(uint16_t);
95 uint8_t ReadLCRAM(uint16_t);
96 uint8_t ReadRAMRD(uint16_t);
97 uint8_t ReadRAMWRT(uint16_t);
98 uint8_t ReadSLOTCXROM(uint16_t);
99 uint8_t ReadALTZP(uint16_t);
100 uint8_t ReadSLOTC3ROM(uint16_t);
101 uint8_t Read80STORE(uint16_t);
102 uint8_t ReadVBL(uint16_t);
103 uint8_t ReadTEXT(uint16_t);
104 uint8_t ReadMIXED(uint16_t);
105 uint8_t ReadPAGE2(uint16_t);
106 uint8_t ReadHIRES(uint16_t);
107 uint8_t ReadALTCHARSET(uint16_t);
108 uint8_t Read80COL(uint16_t);
109 void WriteKeyStrobe(uint16_t, uint8_t);
110 uint8_t ReadSpeaker(uint16_t);
111 void WriteSpeaker(uint16_t, uint8_t);
112 uint8_t SwitchLCR(uint16_t);
113 void SwitchLCW(uint16_t, uint8_t);
115 uint8_t SwitchTEXTR(uint16_t);
116 void SwitchTEXTW(uint16_t, uint8_t);
117 uint8_t SwitchMIXEDR(uint16_t);
118 void SwitchMIXEDW(uint16_t, uint8_t);
119 uint8_t SwitchPAGE2R(uint16_t);
120 void SwitchPAGE2W(uint16_t, uint8_t);
121 uint8_t SwitchHIRESR(uint16_t);
122 void SwitchHIRESW(uint16_t, uint8_t);
123 uint8_t SwitchDHIRESR(uint16_t);
124 void SwitchDHIRESW(uint16_t, uint8_t);
125 void SwitchIOUDIS(uint16_t, uint8_t);
126 uint8_t Slot6R(uint16_t);
127 void Slot6W(uint16_t, uint8_t);
128 void HandleSlot6(uint16_t, uint8_t);
129 uint8_t ReadButton0(uint16_t);
130 uint8_t ReadButton1(uint16_t);
131 uint8_t ReadPaddle0(uint16_t);
132 uint8_t ReadIOUDIS(uint16_t);
133 uint8_t ReadDHIRES(uint16_t);
134 uint8_t ReadFloatingBus(uint16_t);
135 //uint8_t SwitchR(uint16_t);
136 //void SwitchW(uint16_t, uint8_t);
139 // The main Apple //e memory map
140 AddressMap memoryMap[] = {
141 { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
142 { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
144 // These will overlay over the previously written memory accessors
145 { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
146 { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
148 { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
149 { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
150 { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
151 { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
152 { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
153 { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
154 { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
155 { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
156 { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
157 { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
158 { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
159 { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
160 { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
161 { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
162 { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
163 { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
164 { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
165 { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
166 { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
167 { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
168 { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
169 { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
170 { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
171 { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
172 // $C020 is "Cassette Out (RO)"
173 { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
174 // May have to put a "floating bus" read there... :-/
175 // Apparently, video RAM is put on 'non-responding address'. So will
176 // need to time those out.
177 // So... $C020-$C08F, when read, return video data.
178 // $C090-$C7FF do also, as long as the slot the range refers to is empty
179 // and last and least is $CFFF, which is the Expansion ROM disable.
180 { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
181 { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
182 { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
183 { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
184 { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
185 { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
186 { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
187 { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
188 { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
189 // { 0xC07E, 0xC07F, AM_READ_WRITE, 0, 0, SwitchIOUDISR, SwitchIOUDISW },
190 { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
191 { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
192 { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
193 { 0xC0E0, 0xC0EF, AM_READ_WRITE, 0, 0, Slot6R, Slot6W },
194 { 0xC100, 0xCFFF, AM_ROM, &slotMemory, 0, 0, 0 },
196 // This will overlay the slotMemory accessors for slot 6 ROM
197 { 0xC300, 0xC3FF, AM_ROM, &slot3Memory, 0, 0, 0 },
198 { 0xC600, 0xC6FF, AM_ROM, &slot6Memory, 0, 0, 0 },
200 { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
201 { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
202 // { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
207 void SetupAddressMap(void)
209 for(uint32_t i=0; i<0x10000; i++)
211 funcMapRead[i] = ReadNOP;
212 funcMapWrite[i] = WriteNOP;
220 while (memoryMap[i].type != AM_END_OF_LIST)
222 switch (memoryMap[i].type)
225 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
227 funcMapRead[j] = ReadMemory;
228 funcMapWrite[j] = WriteMemory;
229 addrPtrRead[j] = memoryMap[i].memory;
230 addrPtrWrite[j] = memoryMap[i].memory;
231 addrOffset[j] = j - memoryMap[i].start;
232 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
237 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
239 funcMapRead[j] = ReadMemory;
240 addrPtrRead[j] = memoryMap[i].memory;
241 addrOffset[j] = j - memoryMap[i].start;
246 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
248 funcMapRead[j] = ReadMemory;
249 funcMapWrite[j] = WriteMemory;
250 addrPtrRead[j] = memoryMap[i].memory;
251 addrPtrWrite[j] = memoryMap[i].altMemory;
252 addrOffset[j] = j - memoryMap[i].start;
257 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
258 funcMapRead[j] = memoryMap[i].read;
262 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
263 funcMapWrite[j] = memoryMap[i].write;
267 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
269 funcMapRead[j] = memoryMap[i].read;
270 funcMapWrite[j] = memoryMap[i].write;
279 // This should correctly set up the LC pointers, but it doesn't
280 // for some reason... :-/
281 // It's because we were storing pointers directly, instead of pointers
282 // to the pointer... It's complicated. :-)
288 // Reset the MMU state after a power down event
290 void ResetMMUPointers(void)
294 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
295 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
299 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
300 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
303 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
304 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
305 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
306 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
308 slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
309 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
310 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
316 // Built-in functions
318 uint8_t ReadNOP(uint16_t)
324 void WriteNOP(uint16_t, uint8_t)
329 uint8_t ReadMemory(uint16_t address)
331 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
332 // We are guaranteed a valid address here by the setup function, so there's
333 // no need to do any checking here.
334 return (*addrPtrRead[address])[addrOffset[address]];
338 void WriteMemory(uint16_t address, uint8_t byte)
340 // We can write protect memory this way, but it adds a branch to the mix.
341 // :-/ (this can be avoided by setting up another bank of memory which we
343 if ((*addrPtrWrite[address]) == 0)
346 (*addrPtrWrite[address])[addrOffset[address]] = byte;
351 // The main memory access functions used by V65C02
353 uint8_t AppleReadMem(uint16_t address)
355 return (*(funcMapRead[address]))(address);
359 void AppleWriteMem(uint16_t address, uint8_t byte)
361 (*(funcMapWrite[address]))(address, byte);
366 // Actual emulated I/O functions follow
368 uint8_t ReadKeyboard(uint16_t /*addr*/)
370 return lastKeyPressed | ((uint8_t)keyDown << 7);
374 void Switch80STORE(uint16_t address, uint8_t)
376 store80Mode = (bool)(address & 0x01);
377 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
381 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
382 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
386 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
387 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
392 void SwitchRAMRD(uint16_t address, uint8_t)
394 ramrd = (bool)(address & 0x01);
395 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
396 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
401 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
405 void SwitchRAMWRT(uint16_t address, uint8_t)
407 ramwrt = (bool)(address & 0x01);
408 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
409 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
414 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
418 void SwitchSLOTCXROM(uint16_t address, uint8_t)
420 //WriteLog("Setting SLOTCXROM to %s...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"));
421 // This is the only soft switch that breaks the usual convention.
422 slotCXROM = !((bool)(address & 0x01));
423 // slot3Memory = (slotCXROM ? &rom[0] : &rom[0xC300]);
424 slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
428 void SwitchALTZP(uint16_t address, uint8_t)
430 altzp = (bool)(address & 0x01);
431 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
435 //extern bool dumpDis;
437 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
440 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
441 slotC3ROM = (bool)(address & 0x01);
442 // slotC3ROM = false;
443 // Seems the h/w forces this with an 80 column card in slot 3...
444 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
445 // slot3Memory = &rom[0xC300];
449 void Switch80COL(uint16_t address, uint8_t)
451 col80Mode = (bool)(address & 0x01);
455 void SwitchALTCHARSET(uint16_t address, uint8_t)
457 alternateCharset = (bool)(address & 0x01);
461 uint8_t ReadKeyStrobe(uint16_t)
463 // No character data is read from here, just the 'any key was pressed' signal...
464 // uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
465 uint8_t byte = (uint8_t)keyDown << 7;
471 uint8_t ReadBANK2(uint16_t)
473 return (lcState < 0x04 ? 0x80 : 0x00);
477 uint8_t ReadLCRAM(uint16_t)
479 // If bits 0 & 1 are set, but not at the same time, then it's ROM
480 uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
481 return (lcROM ? 0x00 : 0x80);
485 uint8_t ReadRAMRD(uint16_t)
487 return (uint8_t)ramrd << 7;
491 uint8_t ReadRAMWRT(uint16_t)
493 return (uint8_t)ramwrt << 7;
497 uint8_t ReadSLOTCXROM(uint16_t)
499 return (uint8_t)slotCXROM << 7;
503 uint8_t ReadALTZP(uint16_t)
505 return (uint8_t)altzp << 7;
509 uint8_t ReadSLOTC3ROM(uint16_t)
512 return (uint8_t)slotC3ROM << 7;
516 uint8_t Read80STORE(uint16_t)
518 return (uint8_t)store80Mode << 7;
522 uint8_t ReadVBL(uint16_t)
524 return (uint8_t)vbl << 7;
528 uint8_t ReadTEXT(uint16_t)
530 return (uint8_t)textMode << 7;
534 uint8_t ReadMIXED(uint16_t)
536 return (uint8_t)mixedMode << 7;
540 uint8_t ReadPAGE2(uint16_t)
542 return (uint8_t)displayPage2 << 7;
546 uint8_t ReadHIRES(uint16_t)
548 return (uint8_t)hiRes << 7;
552 uint8_t ReadALTCHARSET(uint16_t)
554 return (uint8_t)alternateCharset << 7;
558 uint8_t Read80COL(uint16_t)
560 return (uint8_t)col80Mode << 7;
564 void WriteKeyStrobe(uint16_t, uint8_t)
570 uint8_t ReadSpeaker(uint16_t)
577 void WriteSpeaker(uint16_t, uint8_t)
583 uint8_t SwitchLCR(uint16_t address)
585 lcState = address & 0x0B;
591 void SwitchLCW(uint16_t address, uint8_t)
593 lcState = address & 0x0B;
604 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
606 // [R ] Read RAM bank 2; no write
607 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
609 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
614 WriteLog("SwitchLC: Read ROM, write bank 2\n");
616 // [RR] Read ROM; write RAM bank 2
617 lcBankMemoryR = &rom[0xD000];
618 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
619 upperMemoryR = &rom[0xE000];
620 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
624 WriteLog("SwitchLC: Read ROM, no write\n");
626 // [R ] Read ROM; no write
627 lcBankMemoryR = &rom[0xD000];
629 upperMemoryR = &rom[0xE000];
634 WriteLog("SwitchLC: Read/write bank 2\n");
636 // [RR] Read RAM bank 2; write RAM bank 2
637 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
638 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
639 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
640 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
643 // [R ] Read RAM bank 1; no write
644 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
646 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
650 // [RR] Read ROM; write RAM bank 1
651 lcBankMemoryR = &rom[0xD000];
652 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
653 upperMemoryR = &rom[0xE000];
654 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
657 // [R ] Read ROM; no write
658 lcBankMemoryR = &rom[0xD000];
660 upperMemoryR = &rom[0xE000];
664 // [RR] Read RAM bank 1; write RAM bank 1
665 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
666 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
667 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
668 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
674 uint8_t SwitchTEXTR(uint16_t address)
676 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
677 textMode = (bool)(address & 0x01);
682 void SwitchTEXTW(uint16_t address, uint8_t)
684 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
685 textMode = (bool)(address & 0x01);
689 uint8_t SwitchMIXEDR(uint16_t address)
691 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
692 mixedMode = (bool)(address & 0x01);
697 void SwitchMIXEDW(uint16_t address, uint8_t)
699 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
700 mixedMode = (bool)(address & 0x01);
704 uint8_t SwitchPAGE2R(uint16_t address)
706 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
707 displayPage2 = (bool)(address & 0x01);
711 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
712 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
719 void SwitchPAGE2W(uint16_t address, uint8_t)
721 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
722 displayPage2 = (bool)(address & 0x01);
726 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
727 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
732 uint8_t SwitchHIRESR(uint16_t address)
734 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
735 hiRes = (bool)(address & 0x01);
740 void SwitchHIRESW(uint16_t address, uint8_t)
742 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
743 hiRes = (bool)(address & 0x01);
747 uint8_t SwitchDHIRESR(uint16_t address)
749 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
750 // Hmm, this breaks convention too, like SLOTCXROM
752 dhires = !((bool)(address & 0x01));
758 void SwitchDHIRESW(uint16_t address, uint8_t)
760 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
762 dhires = !((bool)(address & 0x01));
766 void SwitchIOUDIS(uint16_t address, uint8_t)
768 ioudis = !((bool)(address & 0x01));
772 uint8_t Slot6R(uint16_t address)
774 //WriteLog("Slot6R: address = %X\n", address & 0x0F);
775 // HandleSlot6(address, 0);
777 uint8_t state = address & 0x0F;
789 floppyDrive.ControlStepper(state);
793 floppyDrive.ControlMotor(state & 0x01);
797 floppyDrive.DriveEnable(state & 0x01);
800 return floppyDrive.ReadWrite();
803 return floppyDrive.GetLatchValue();
806 floppyDrive.SetReadMode();
809 floppyDrive.SetWriteMode();
817 void Slot6W(uint16_t address, uint8_t byte)
819 //WriteLog("Slot6W: address = %X, byte= %X\n", address & 0x0F, byte);
820 // HandleSlot6(address, byte);
821 uint8_t state = address & 0x0F;
833 floppyDrive.ControlStepper(state);
837 floppyDrive.ControlMotor(state & 0x01);
841 floppyDrive.DriveEnable(state & 0x01);
844 floppyDrive.ReadWrite();
847 floppyDrive.SetLatchValue(byte);
850 floppyDrive.SetReadMode();
853 floppyDrive.SetWriteMode();
859 void HandleSlot6(uint16_t address, uint8_t byte)
864 uint8_t ReadButton0(uint16_t)
866 return (uint8_t)openAppleDown << 7;
870 uint8_t ReadButton1(uint16_t)
872 return (uint8_t)closedAppleDown << 7;
876 // The way the paddles work is that a strobe is written (or read) to $C070,
877 // then software counts down the time that it takes for the paddle outputs
878 // to have bit 7 return to 0. If there are no paddles connected, bit 7
880 // NB: This is really paddles 0-3, not just 0 :-P
881 uint8_t ReadPaddle0(uint16_t)
887 uint8_t ReadIOUDIS(uint16_t)
889 return (uint8_t)ioudis << 7;
893 uint8_t ReadDHIRES(uint16_t)
895 return (uint8_t)dhires << 7;
899 // Whenever a read is done to a MMIO location that is unconnected to anything,
900 // it actually sees the RAM access done by the video generation hardware. Some
901 // programs exploit this, so we emulate it here.
903 // N.B.: frameCycles will be off by the true amount because this only increments
904 // by the amount of a speaker cycle, not the cycle count when the access
905 // happens... !!! FIX !!!
906 uint8_t ReadFloatingBus(uint16_t)
908 // Get the currently elapsed cycle count for this frame
909 uint32_t frameCycles = mainCPU.clock - frameCycleStart;
911 // Make counters out of the cycle count. There are 65 cycles per line.
912 uint32_t numLines = frameCycles / 65;
913 uint32_t numHTicks = frameCycles - (numLines * 65);
915 // Convert these to H/V counters
916 uint32_t hcount = numHTicks - 1;
918 // HC sees zero twice:
919 if (hcount == 0xFFFFFFFF)
922 uint32_t vcount = numLines + 0xFA;
924 // Now do the address calculations
925 uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
926 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
927 uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
929 // Add in particulars for the gfx mode we're in...
930 if (textMode || (!textMode && !hiRes))
931 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
932 | (!store80Mode && displayPage2 ? 0x800 : 0);
934 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
935 | (!store80Mode && displayPage2 ? 0x4000 : 0)
936 | ((vcount & 0x07) << 10);
938 // The address so read is *always* in main RAM, not alt RAM