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