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