]> Shamusworld >> Repos - apple2/blob - src/mmu.cpp
7070fda34b93347f6bc539b3c8cf1147a5c6397d
[apple2] / src / mmu.cpp
1 //
2 // mmu.cpp: Memory management
3 //
4 // by James Hammons
5 // (C) 2013 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  09/27/2013  Created this file
12
13
14 #include "mmu.h"
15 #include "apple2.h"
16 #include "applevideo.h"
17 #include "firmware.h"
18 #include "log.h"
19 #include "sound.h"
20
21
22 // Debug defines
23 //#define LC_DEBUG
24
25 // Address Map enumeration
26 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
27
28 // Macros for function pointers
29 // The typedef would be something like:
30 //typedef ReadFunc (uint8_t (*)(uint16_t));
31 //typedef WriteFunc (void (*)(uint16_t, uint8_t));
32 #define READFUNC(x) uint8_t (* x)(uint16_t)
33 #define WRITEFUNC(x) void (* x)(uint16_t, uint8_t)
34
35 // Internal vars
36 uint8_t ** addrPtrRead[0x10000];
37 uint8_t ** addrPtrWrite[0x10000];
38 uint16_t addrOffset[0x10000];
39
40 READFUNC(funcMapRead[0x10000]);
41 WRITEFUNC(funcMapWrite[0x10000]);
42
43 struct AddressMap
44 {
45         uint16_t start;
46         uint16_t end;
47         int type;
48         uint8_t ** memory;
49         uint8_t ** altMemory;
50         READFUNC(read);
51         WRITEFUNC(write);
52 };
53
54
55 // Dunno if I like this approach or not...
56 //ADDRESS_MAP_START()
57 //      AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
58 //      AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
59 //ADDRESS_MAP_END
60
61 // Would need a pointer for 80STORE as well...
62
63 uint8_t * pageZeroMemory  = &ram[0x0000];       // $0000 - $01FF
64 uint8_t * mainMemoryR     = &ram[0x0200];       // $0200 - $BFFF (read)
65 uint8_t * mainMemoryW     = &ram[0x0200];       // $0200 - $BFFF (write)
66
67 uint8_t * mainMemoryTextR = &ram[0x0400];       // $0400 - $07FF (read)
68 uint8_t * mainMemoryTextW = &ram[0x0400];       // $0400 - $07FF (write)
69 uint8_t * mainMemoryHGRR  = &ram[0x2000];       // $2000 - $3FFF (read)
70 uint8_t * mainMemoryHGRW  = &ram[0x2000];       // $2000 - $3FFF (write)
71
72 uint8_t * slotMemory      = &rom[0xC100];       // $C100 - $CFFF
73 uint8_t * slot3Memory     = &rom[0xC300];       // $C300 - $C3FF
74 uint8_t * slot6Memory     = &diskROM[0];        // $C600 - $C6FF
75 uint8_t * lcBankMemoryR   = &ram[0xD000];       // $D000 - $DFFF (read)
76 uint8_t * lcBankMemoryW   = &ram[0xD000];       // $D000 - $DFFF (write)
77 uint8_t * upperMemoryR    = &ram[0xE000];       // $E000 - $FFFF (read)
78 uint8_t * upperMemoryW    = &ram[0xE000];       // $E000 - $FFFF (write)
79
80
81 // Function prototypes
82 uint8_t ReadNOP(uint16_t);
83 void WriteNOP(uint16_t, uint8_t);
84 uint8_t ReadMemory(uint16_t);
85 void WriteMemory(uint16_t, uint8_t);
86 uint8_t ReadKeyboard(uint16_t);
87 void Switch80STORE(uint16_t, uint8_t);
88 void SwitchRAMRD(uint16_t, uint8_t);
89 void SwitchRAMWRT(uint16_t, uint8_t);
90 void SwitchSLOTCXROM(uint16_t, uint8_t);
91 void SwitchALTZP(uint16_t, uint8_t);
92 void SwitchSLOTC3ROM(uint16_t, uint8_t);
93 void Switch80COL(uint16_t, uint8_t);
94 void SwitchALTCHARSET(uint16_t, uint8_t);
95 uint8_t ReadKeyStrobe(uint16_t);
96 uint8_t ReadBANK2(uint16_t);
97 uint8_t ReadLCRAM(uint16_t);
98 uint8_t ReadRAMRD(uint16_t);
99 uint8_t ReadRAMWRT(uint16_t);
100 uint8_t ReadSLOTCXROM(uint16_t);
101 uint8_t ReadALTZP(uint16_t);
102 uint8_t ReadSLOTC3ROM(uint16_t);
103 uint8_t Read80STORE(uint16_t);
104 uint8_t ReadVBL(uint16_t);
105 uint8_t ReadTEXT(uint16_t);
106 uint8_t ReadMIXED(uint16_t);
107 uint8_t ReadPAGE2(uint16_t);
108 uint8_t ReadHIRES(uint16_t);
109 uint8_t ReadALTCHARSET(uint16_t);
110 uint8_t Read80COL(uint16_t);
111 void WriteKeyStrobe(uint16_t, uint8_t);
112 uint8_t ReadSpeaker(uint16_t);
113 void WriteSpeaker(uint16_t, uint8_t);
114 uint8_t SwitchLCR(uint16_t);
115 void SwitchLCW(uint16_t, uint8_t);
116 void SwitchLC(void);
117 uint8_t SwitchTEXTR(uint16_t);
118 void SwitchTEXTW(uint16_t, uint8_t);
119 uint8_t SwitchMIXEDR(uint16_t);
120 void SwitchMIXEDW(uint16_t, uint8_t);
121 uint8_t SwitchPAGE2R(uint16_t);
122 void SwitchPAGE2W(uint16_t, uint8_t);
123 uint8_t SwitchHIRESR(uint16_t);
124 void SwitchHIRESW(uint16_t, uint8_t);
125 uint8_t SwitchDHIRESR(uint16_t);
126 void SwitchDHIRESW(uint16_t, uint8_t);
127 void SwitchIOUDIS(uint16_t, uint8_t);
128 uint8_t Slot6R(uint16_t);
129 void Slot6W(uint16_t, uint8_t);
130 void HandleSlot6(uint16_t, uint8_t);
131 uint8_t ReadButton0(uint16_t);
132 uint8_t ReadButton1(uint16_t);
133 uint8_t ReadPaddle0(uint16_t);
134 uint8_t ReadIOUDIS(uint16_t);
135 uint8_t ReadDHIRES(uint16_t);
136 //uint8_t SwitchR(uint16_t);
137 //void SwitchW(uint16_t, uint8_t);
138
139
140 // The main Apple //e memory map
141 AddressMap memoryMap[] = {
142         { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
143         { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
144
145         // These will overlay over the previously written memory accessors
146         { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
147         { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
148
149         { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
150         { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
151         { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
152         { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
153         { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
154         { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
155         { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
156         { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
157         { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
158         { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
159         { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
160         { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
161         { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
162         { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
163         { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
164         { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
165         { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
166         { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
167         { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
168         { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
169         { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
170         { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
171         { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
172         { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
173         { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
174         { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
175         { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
176         { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
177         { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
178         { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
179         { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
180         { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
181         { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
182 //      { 0xC07E, 0xC07F, AM_READ_WRITE, 0, 0, SwitchIOUDISR, SwitchIOUDISW },
183         { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
184         { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
185         { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
186         { 0xC0E0, 0xC0EF, AM_READ_WRITE, 0, 0, Slot6R, Slot6W },
187         { 0xC100, 0xCFFF, AM_ROM, &slotMemory, 0, 0, 0 },
188
189         // This will overlay the slotMemory accessors for slot 6 ROM
190         { 0xC300, 0xC3FF, AM_ROM, &slot3Memory, 0, 0, 0 },
191         { 0xC600, 0xC6FF, AM_ROM, &slot6Memory, 0, 0, 0 },
192
193         { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
194         { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
195         { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
196 };
197
198
199 void SetupAddressMap(void)
200 {
201         for(uint32_t i=0; i<0x10000; i++)
202         {
203                 funcMapRead[i] = ReadNOP;
204                 funcMapWrite[i] = WriteNOP;
205                 addrPtrRead[i] = 0;
206                 addrPtrWrite[i] = 0;
207                 addrOffset[i] = 0;
208         }
209
210         uint32_t i=0;
211
212         while (memoryMap[i].type != AM_END_OF_LIST)
213         {
214                 switch (memoryMap[i].type)
215                 {
216                 case AM_RAM:
217                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
218                         {
219                                 funcMapRead[j] = ReadMemory;
220                                 funcMapWrite[j] = WriteMemory;
221                                 addrPtrRead[j] = memoryMap[i].memory;
222                                 addrPtrWrite[j] = memoryMap[i].memory;
223                                 addrOffset[j] = j - memoryMap[i].start;
224 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
225                         }
226
227                         break;
228                 case AM_ROM:
229                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
230                         {
231                                 funcMapRead[j] = ReadMemory;
232                                 addrPtrRead[j] = memoryMap[i].memory;
233                                 addrOffset[j] = j - memoryMap[i].start;
234                         }
235
236                         break;
237                 case AM_BANKED:
238                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
239                         {
240                                 funcMapRead[j] = ReadMemory;
241                                 funcMapWrite[j] = WriteMemory;
242                                 addrPtrRead[j] = memoryMap[i].memory;
243                                 addrPtrWrite[j] = memoryMap[i].altMemory;
244                                 addrOffset[j] = j - memoryMap[i].start;
245                         }
246
247                         break;
248                 case AM_READ:
249                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
250                                 funcMapRead[j] = memoryMap[i].read;
251
252                         break;
253                 case AM_WRITE:
254                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
255                                 funcMapWrite[j] = memoryMap[i].write;
256
257                         break;
258                 case AM_READ_WRITE:
259                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
260                         {
261                                 funcMapRead[j] = memoryMap[i].read;
262                                 funcMapWrite[j] = memoryMap[i].write;
263                         }
264
265                         break;
266                 }
267
268                 i++;
269         };
270
271         // This should correctly set up the LC pointers, but it doesn't
272         // for some reason... :-/
273         // It's because we were storing pointers directly, instead of pointers
274         // to the pointer... It's complicated. :-)
275         SwitchLC();
276 }
277
278
279 //
280 // Reset the MMU state after a power down event
281 //
282 void ResetMMUPointers(void)
283 {
284         if (store80Mode)
285         {
286                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
287                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
288         }
289         else
290         {
291                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
292                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
293         }
294
295         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
296         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
297         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
298         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
299
300         slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
301         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
302         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
303         SwitchLC();
304 }
305
306
307 //
308 // Built-in functions
309 //
310 uint8_t ReadNOP(uint16_t)
311 {
312         return 0;
313 }
314
315
316 void WriteNOP(uint16_t, uint8_t)
317 {
318 }
319
320
321 uint8_t ReadMemory(uint16_t address)
322 {
323 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
324         // We are guaranteed a valid address here by the setup function, so there's
325         // no need to do any checking here.
326         return (*addrPtrRead[address])[addrOffset[address]];
327 }
328
329
330 void WriteMemory(uint16_t address, uint8_t byte)
331 {
332         // We can write protect memory this way, but it adds a branch to the mix. :-/
333         // (this can be avoided by setting up another bank of memory which we
334         //  ignore... hmm...)
335         if ((*addrPtrWrite[address]) == 0)
336                 return;
337
338         (*addrPtrWrite[address])[addrOffset[address]] = byte;
339 }
340
341
342 //
343 // The main memory access functions used by V65C02
344 //
345 uint8_t AppleReadMem(uint16_t address)
346 {
347         return (*(funcMapRead[address]))(address);
348 }
349
350
351 void AppleWriteMem(uint16_t address, uint8_t byte)
352 {
353         (*(funcMapWrite[address]))(address, byte);
354 }
355
356
357 //
358 // Actual emulated I/O functions follow
359 //
360 uint8_t ReadKeyboard(uint16_t /*addr*/)
361 {
362         return lastKeyPressed | ((uint8_t)keyDown << 7);
363 }
364
365
366 void Switch80STORE(uint16_t address, uint8_t)
367 {
368         store80Mode = (bool)(address & 0x01);
369 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
370
371         if (store80Mode)
372         {
373                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
374                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
375 //              mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
376 //              mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
377         }
378         else
379         {
380                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
381                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
382 //              mainMemoryHGRR = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
383 //              mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
384         }
385 }
386
387
388 void SwitchRAMRD(uint16_t address, uint8_t)
389 {
390         ramrd = (bool)(address & 0x01);
391         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
392         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
393
394         if (store80Mode)
395                 return;
396
397         mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
398 }
399
400
401 void SwitchRAMWRT(uint16_t address, uint8_t)
402 {
403         ramwrt = (bool)(address & 0x01);
404         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
405         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
406
407         if (store80Mode)
408                 return;
409
410         mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
411 }
412
413
414 void SwitchSLOTCXROM(uint16_t address, uint8_t)
415 {
416 //WriteLog("Setting SLOTCXROM to %s...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"));
417         // This is the only soft switch that breaks the usual convention.
418         slotCXROM = !((bool)(address & 0x01));
419 //      slot3Memory = (slotCXROM ? &rom[0] : &rom[0xC300]);
420         slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
421 }
422
423
424 void SwitchALTZP(uint16_t address, uint8_t)
425 {
426         altzp = (bool)(address & 0x01);
427         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
428         SwitchLC();
429 }
430
431 //extern bool dumpDis;
432
433 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
434 {
435 //dumpDis = true;
436 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
437         slotC3ROM = (bool)(address & 0x01);
438 //      slotC3ROM = false;
439 // Seems the h/w forces this with an 80 column card in slot 3...
440         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
441 //      slot3Memory = &rom[0xC300];
442 }
443
444
445 void Switch80COL(uint16_t address, uint8_t)
446 {
447         col80Mode = (bool)(address & 0x01);
448 }
449
450
451 void SwitchALTCHARSET(uint16_t address, uint8_t)
452 {
453         alternateCharset = (bool)(address & 0x01);
454 }
455
456
457 uint8_t ReadKeyStrobe(uint16_t)
458 {
459         uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
460         keyDown = false;
461         return byte;
462 }
463
464
465 uint8_t ReadBANK2(uint16_t)
466 {
467         return (lcState < 0x04 ? 0x80 : 0x00);
468 }
469
470
471 uint8_t ReadLCRAM(uint16_t)
472 {
473         // If bits 0 & 1 are set, but not at the same time, then it's ROM
474         uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
475         return (lcROM ? 0x00 : 0x80);
476 }
477
478
479 uint8_t ReadRAMRD(uint16_t)
480 {
481         return (uint8_t)ramrd << 7;
482 }
483
484
485 uint8_t ReadRAMWRT(uint16_t)
486 {
487         return (uint8_t)ramwrt << 7;
488 }
489
490
491 uint8_t ReadSLOTCXROM(uint16_t)
492 {
493         return (uint8_t)slotCXROM << 7;
494 }
495
496
497 uint8_t ReadALTZP(uint16_t)
498 {
499         return (uint8_t)altzp << 7;
500 }
501
502
503 uint8_t ReadSLOTC3ROM(uint16_t)
504 {
505 //      return 0;
506         return (uint8_t)slotC3ROM << 7;
507 }
508
509
510 uint8_t Read80STORE(uint16_t)
511 {
512         return (uint8_t)store80Mode << 7;
513 }
514
515
516 uint8_t ReadVBL(uint16_t)
517 {
518         return (uint8_t)vbl << 7;
519 }
520
521
522 uint8_t ReadTEXT(uint16_t)
523 {
524         return (uint8_t)textMode << 7;
525 }
526
527
528 uint8_t ReadMIXED(uint16_t)
529 {
530         return (uint8_t)mixedMode << 7;
531 }
532
533
534 uint8_t ReadPAGE2(uint16_t)
535 {
536         return (uint8_t)displayPage2 << 7;
537 }
538
539
540 uint8_t ReadHIRES(uint16_t)
541 {
542         return (uint8_t)hiRes << 7;
543 }
544
545
546 uint8_t ReadALTCHARSET(uint16_t)
547 {
548         return (uint8_t)alternateCharset << 7;
549 }
550
551
552 uint8_t Read80COL(uint16_t)
553 {
554         return (uint8_t)col80Mode << 7;
555 }
556
557
558 void WriteKeyStrobe(uint16_t, uint8_t)
559 {
560         keyDown = false;
561 }
562
563
564 uint8_t ReadSpeaker(uint16_t)
565 {
566         ToggleSpeaker();
567         return 0;
568 }
569
570
571 void WriteSpeaker(uint16_t, uint8_t)
572 {
573         ToggleSpeaker();
574 }
575
576
577 uint8_t SwitchLCR(uint16_t address)
578 {
579         lcState = address & 0x0B;
580         SwitchLC();
581         return 0;
582 }
583
584
585 void SwitchLCW(uint16_t address, uint8_t)
586 {
587         lcState = address & 0x0B;
588         SwitchLC();
589 }
590
591
592 void SwitchLC(void)
593 {
594         switch (lcState)
595         {
596         case 0x00:
597 #ifdef LC_DEBUG
598 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
599 #endif
600                 // [R ] Read RAM bank 2; no write
601                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
602                 lcBankMemoryW = 0;
603                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
604                 upperMemoryW = 0;
605                 break;
606         case 0x01:
607 #ifdef LC_DEBUG
608 WriteLog("SwitchLC: Read ROM, write bank 2\n");
609 #endif
610                 // [RR] Read ROM; write RAM bank 2
611                 lcBankMemoryR = &rom[0xD000];
612                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
613                 upperMemoryR = &rom[0xE000];
614                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
615                 break;
616         case 0x02:
617 #ifdef LC_DEBUG
618 WriteLog("SwitchLC: Read ROM, no write\n");
619 #endif
620                 // [R ] Read ROM; no write
621                 lcBankMemoryR = &rom[0xD000];
622                 lcBankMemoryW = 0;
623                 upperMemoryR = &rom[0xE000];
624                 upperMemoryW = 0;
625                 break;
626         case 0x03:
627 #ifdef LC_DEBUG
628 WriteLog("SwitchLC: Read/write bank 2\n");
629 #endif
630                 // [RR] Read RAM bank 2; write RAM bank 2
631                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
632                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
633                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
634                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
635                 break;
636         case 0x08:
637                 // [R ] Read RAM bank 1; no write
638                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
639                 lcBankMemoryW = 0;
640                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
641                 upperMemoryW = 0;
642                 break;
643         case 0x09:
644                 // [RR] Read ROM; write RAM bank 1
645                 lcBankMemoryR = &rom[0xD000];
646                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
647                 upperMemoryR = &rom[0xE000];
648                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
649                 break;
650         case 0x0A:
651                 // [R ] Read ROM; no write
652                 lcBankMemoryR = &rom[0xD000];
653                 lcBankMemoryW = 0;
654                 upperMemoryR = &rom[0xE000];
655                 upperMemoryW = 0;
656                 break;
657         case 0x0B:
658                 // [RR] Read RAM bank 1; write RAM bank 1
659                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
660                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
661                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
662                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
663                 break;
664         }
665 }
666
667
668 uint8_t SwitchTEXTR(uint16_t address)
669 {
670 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
671         textMode = (bool)(address & 0x01);
672         return 0;
673 }
674
675
676 void SwitchTEXTW(uint16_t address, uint8_t)
677 {
678 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
679         textMode = (bool)(address & 0x01);
680 }
681
682
683 uint8_t SwitchMIXEDR(uint16_t address)
684 {
685 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
686         mixedMode = (bool)(address & 0x01);
687         return 0;
688 }
689
690
691 void SwitchMIXEDW(uint16_t address, uint8_t)
692 {
693 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
694         mixedMode = (bool)(address & 0x01);
695 }
696
697
698 uint8_t SwitchPAGE2R(uint16_t address)
699 {
700 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
701         displayPage2 = (bool)(address & 0x01);
702
703         if (store80Mode)
704         {
705                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
706                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
707 //              mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
708 //              mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
709         }
710
711         return 0;
712 }
713
714
715 void SwitchPAGE2W(uint16_t address, uint8_t)
716 {
717 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
718         displayPage2 = (bool)(address & 0x01);
719
720         if (store80Mode)
721         {
722                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
723                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
724 //              mainMemoryHGRR = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
725 //              mainMemoryHGRW = (displayPage2 ? &ram2[0x2000] : &ram[0x2000]);
726         }
727 }
728
729
730 uint8_t SwitchHIRESR(uint16_t address)
731 {
732 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
733         hiRes = (bool)(address & 0x01);
734         return 0;
735 }
736
737
738 void SwitchHIRESW(uint16_t address, uint8_t)
739 {
740 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
741         hiRes = (bool)(address & 0x01);
742 }
743
744
745 uint8_t SwitchDHIRESR(uint16_t address)
746 {
747 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
748         // Hmm, this breaks convention too, like SLOTCXROM
749         if (ioudis)
750                 dhires = !((bool)(address & 0x01));
751
752         return 0;
753 }
754
755
756 void SwitchDHIRESW(uint16_t address, uint8_t)
757 {
758 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
759         if (ioudis)
760                 dhires = !((bool)(address & 0x01));
761 }
762
763
764 void SwitchIOUDIS(uint16_t address, uint8_t)
765 {
766         ioudis = !((bool)(address & 0x01));
767 }
768
769
770 uint8_t Slot6R(uint16_t address)
771 {
772 //WriteLog("Slot6R: address = %X\n", address & 0x0F);
773 //      HandleSlot6(address, 0);
774 //      return 0;
775         uint8_t state = address & 0x0F;
776
777         switch (state)
778         {
779         case 0x00:
780         case 0x01:
781         case 0x02:
782         case 0x03:
783         case 0x04:
784         case 0x05:
785         case 0x06:
786         case 0x07:
787                 floppyDrive.ControlStepper(state);
788                 break;
789         case 0x08:
790         case 0x09:
791                 floppyDrive.ControlMotor(state & 0x01);
792                 break;
793         case 0x0A:
794         case 0x0B:
795                 floppyDrive.DriveEnable(state & 0x01);
796                 break;
797         case 0x0C:
798                 return floppyDrive.ReadWrite();
799                 break;
800         case 0x0D:
801                 return floppyDrive.GetLatchValue();
802                 break;
803         case 0x0E:
804                 floppyDrive.SetReadMode();
805                 break;
806         case 0x0F:
807                 floppyDrive.SetWriteMode();
808                 break;
809         }
810
811         return 0;
812 }
813
814
815 void Slot6W(uint16_t address, uint8_t byte)
816 {
817 //WriteLog("Slot6W: address = %X, byte= %X\n", address & 0x0F, byte);
818 //      HandleSlot6(address, byte);
819         uint8_t state = address & 0x0F;
820
821         switch (state)
822         {
823         case 0x00:
824         case 0x01:
825         case 0x02:
826         case 0x03:
827         case 0x04:
828         case 0x05:
829         case 0x06:
830         case 0x07:
831                 floppyDrive.ControlStepper(state);
832                 break;
833         case 0x08:
834         case 0x09:
835                 floppyDrive.ControlMotor(state & 0x01);
836                 break;
837         case 0x0A:
838         case 0x0B:
839                 floppyDrive.DriveEnable(state & 0x01);
840                 break;
841         case 0x0C:
842                 floppyDrive.ReadWrite();
843                 break;
844         case 0x0D:
845                 floppyDrive.SetLatchValue(byte);
846                 break;
847         case 0x0E:
848                 floppyDrive.SetReadMode();
849                 break;
850         case 0x0F:
851                 floppyDrive.SetWriteMode();
852                 break;
853         }
854 }
855
856
857 void HandleSlot6(uint16_t address, uint8_t byte)
858 {
859 }
860
861
862 uint8_t ReadButton0(uint16_t)
863 {
864         return (uint8_t)openAppleDown << 7;
865 }
866
867
868 uint8_t ReadButton1(uint16_t)
869 {
870         return (uint8_t)closedAppleDown << 7;
871 }
872
873
874 // The way the paddles work is that a strobe is written (or read) to $C070,
875 // then software counts down the time that it takes for the paddle outputs
876 // to have bit 7 return to 0. If there are no paddles connected, bit 7
877 // stays at 1.
878 // NB: This is really paddles 0-3, not just 0 :-P
879 uint8_t ReadPaddle0(uint16_t)
880 {
881         return 0xFF;
882 }
883
884
885 uint8_t ReadIOUDIS(uint16_t)
886 {
887         return (uint8_t)ioudis << 7;
888 }
889
890
891 uint8_t ReadDHIRES(uint16_t)
892 {
893         return (uint8_t)dhires << 7;
894 }
895
896