]> Shamusworld >> Repos - apple2/blob - src/mmu.cpp
653eac9eeb61e392da8d1255faa6e5a652b5a126
[apple2] / src / mmu.cpp
1 //
2 // mmu.cpp: Memory management
3 //
4 // by James Hammons
5 // (C) 2013-2018 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 "ay8910.h"
17 #include "firmware.h"
18 #include "log.h"
19 #include "mos6522via.h"
20 #include "sound.h"
21 #include "video.h"
22
23
24 // Debug defines
25 //#define LC_DEBUG
26
27 // Address Map enumeration
28 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
29
30 // Macros for function pointers
31 #define READFUNC(x) uint8_t (* x)(uint16_t)
32 #define WRITEFUNC(x) void (* x)(uint16_t, uint8_t)
33
34 // Internal vars
35 uint8_t ** addrPtrRead[0x10000];
36 uint8_t ** addrPtrWrite[0x10000];
37 uint16_t addrOffset[0x10000];
38
39 READFUNC(funcMapRead[0x10000]);
40 WRITEFUNC(funcMapWrite[0x10000]);
41
42 struct AddressMap
43 {
44         uint16_t start;
45         uint16_t end;
46         int type;
47         uint8_t ** memory;
48         uint8_t ** altMemory;
49         READFUNC(read);
50         WRITEFUNC(write);
51 };
52
53 #define ADDRESS_MAP_END         { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
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 * slot4Memory     = &rom[0xC400];       // $C400 - $C4FF
75 uint8_t * slot6Memory     = &diskROM[0];        // $C600 - $C6FF
76 uint8_t * lcBankMemoryR   = &ram[0xD000];       // $D000 - $DFFF (read)
77 uint8_t * lcBankMemoryW   = &ram[0xD000];       // $D000 - $DFFF (write)
78 uint8_t * upperMemoryR    = &ram[0xE000];       // $E000 - $FFFF (read)
79 uint8_t * upperMemoryW    = &ram[0xE000];       // $E000 - $FFFF (write)
80
81
82 // Function prototypes
83 uint8_t ReadNOP(uint16_t);
84 void WriteNOP(uint16_t, uint8_t);
85 uint8_t ReadMemory(uint16_t);
86 void WriteMemory(uint16_t, uint8_t);
87 uint8_t ReadKeyboard(uint16_t);
88 void Switch80STORE(uint16_t, uint8_t);
89 void SwitchRAMRD(uint16_t, uint8_t);
90 void SwitchRAMWRT(uint16_t, uint8_t);
91 void SwitchSLOTCXROM(uint16_t, uint8_t);
92 void SwitchALTZP(uint16_t, uint8_t);
93 void SwitchSLOTC3ROM(uint16_t, uint8_t);
94 void Switch80COL(uint16_t, uint8_t);
95 void SwitchALTCHARSET(uint16_t, uint8_t);
96 uint8_t ReadKeyStrobe(uint16_t);
97 uint8_t ReadBANK2(uint16_t);
98 uint8_t ReadLCRAM(uint16_t);
99 uint8_t ReadRAMRD(uint16_t);
100 uint8_t ReadRAMWRT(uint16_t);
101 uint8_t ReadSLOTCXROM(uint16_t);
102 uint8_t ReadALTZP(uint16_t);
103 uint8_t ReadSLOTC3ROM(uint16_t);
104 uint8_t Read80STORE(uint16_t);
105 uint8_t ReadVBL(uint16_t);
106 uint8_t ReadTEXT(uint16_t);
107 uint8_t ReadMIXED(uint16_t);
108 uint8_t ReadPAGE2(uint16_t);
109 uint8_t ReadHIRES(uint16_t);
110 uint8_t ReadALTCHARSET(uint16_t);
111 uint8_t Read80COL(uint16_t);
112 void WriteKeyStrobe(uint16_t, uint8_t);
113 uint8_t ReadSpeaker(uint16_t);
114 void WriteSpeaker(uint16_t, uint8_t);
115 uint8_t SwitchLCR(uint16_t);
116 void SwitchLCW(uint16_t, uint8_t);
117 void SwitchLC(void);
118 uint8_t SwitchTEXTR(uint16_t);
119 void SwitchTEXTW(uint16_t, uint8_t);
120 uint8_t SwitchMIXEDR(uint16_t);
121 void SwitchMIXEDW(uint16_t, uint8_t);
122 uint8_t SwitchPAGE2R(uint16_t);
123 void SwitchPAGE2W(uint16_t, uint8_t);
124 uint8_t SwitchHIRESR(uint16_t);
125 void SwitchHIRESW(uint16_t, uint8_t);
126 uint8_t SwitchDHIRESR(uint16_t);
127 void SwitchDHIRESW(uint16_t, uint8_t);
128 void SwitchIOUDIS(uint16_t, uint8_t);
129 uint8_t Slot6R(uint16_t);
130 void Slot6W(uint16_t, uint8_t);
131 void HandleSlot6(uint16_t, uint8_t);
132 uint8_t MBRead(uint16_t);
133 void MBWrite(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 ReadFloatingBus(uint16_t);
140 //uint8_t SwitchR(uint16_t);
141 //void SwitchW(uint16_t, uint8_t);
142
143
144 // The main Apple //e memory map
145 AddressMap memoryMap[] = {
146         { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
147         { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
148
149         // These will overlay over the previously written memory accessors
150         { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
151         { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
152
153         { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
154         { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
155         { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
156         { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
157         { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
158         { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
159         { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
160         { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
161         { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
162         { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
163         { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
164         { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
165         { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
166         { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
167         { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
168         { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
169         { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
170         { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
171         { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
172         { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
173         { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
174         { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
175         { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
176         { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
177         // $C020 is "Cassette Out (RO)"
178         { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
179         // May have to put a "floating bus" read there... :-/
180         // Apparently, video RAM is put on 'non-responding address'. So will
181         // need to time those out.
182         // So... $C020-$C08F, when read, return video data.
183         // $C090-$C7FF do also, as long as the slot the range refers to is empty
184         // and last and least is $CFFF, which is the Expansion ROM disable.
185         { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
186         { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
187         { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
188         { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
189         { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
190         { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
191         { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
192         { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
193         { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
194 //      { 0xC07E, 0xC07F, AM_READ_WRITE, 0, 0, SwitchIOUDISR, SwitchIOUDISW },
195         { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
196         { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
197         { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
198         { 0xC0E0, 0xC0EF, AM_READ_WRITE, 0, 0, Slot6R, Slot6W },
199         { 0xC100, 0xCFFF, AM_ROM, &slotMemory, 0, 0, 0 },
200
201         // This will overlay the slotMemory accessors for slot 6 ROM
202         { 0xC300, 0xC3FF, AM_ROM, &slot3Memory, 0, 0, 0 },
203         { 0xC600, 0xC6FF, AM_ROM, &slot6Memory, 0, 0, 0 },
204         { 0xC400, 0xC4FF, AM_READ_WRITE, 0, 0, MBRead, MBWrite },
205
206         { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
207         { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
208         ADDRESS_MAP_END
209 };
210
211
212 void SetupAddressMap(void)
213 {
214         for(uint32_t i=0; i<0x10000; i++)
215         {
216                 funcMapRead[i] = ReadNOP;
217                 funcMapWrite[i] = WriteNOP;
218                 addrPtrRead[i] = 0;
219                 addrPtrWrite[i] = 0;
220                 addrOffset[i] = 0;
221         }
222
223         uint32_t i=0;
224
225         while (memoryMap[i].type != AM_END_OF_LIST)
226         {
227                 switch (memoryMap[i].type)
228                 {
229                 case AM_RAM:
230                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
231                         {
232                                 funcMapRead[j] = ReadMemory;
233                                 funcMapWrite[j] = WriteMemory;
234                                 addrPtrRead[j] = memoryMap[i].memory;
235                                 addrPtrWrite[j] = memoryMap[i].memory;
236                                 addrOffset[j] = j - memoryMap[i].start;
237 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
238                         }
239
240                         break;
241                 case AM_ROM:
242                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
243                         {
244                                 funcMapRead[j] = ReadMemory;
245                                 addrPtrRead[j] = memoryMap[i].memory;
246                                 addrOffset[j] = j - memoryMap[i].start;
247                         }
248
249                         break;
250                 case AM_BANKED:
251                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
252                         {
253                                 funcMapRead[j] = ReadMemory;
254                                 funcMapWrite[j] = WriteMemory;
255                                 addrPtrRead[j] = memoryMap[i].memory;
256                                 addrPtrWrite[j] = memoryMap[i].altMemory;
257                                 addrOffset[j] = j - memoryMap[i].start;
258                         }
259
260                         break;
261                 case AM_READ:
262                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
263                                 funcMapRead[j] = memoryMap[i].read;
264
265                         break;
266                 case AM_WRITE:
267                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
268                                 funcMapWrite[j] = memoryMap[i].write;
269
270                         break;
271                 case AM_READ_WRITE:
272                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
273                         {
274                                 funcMapRead[j] = memoryMap[i].read;
275                                 funcMapWrite[j] = memoryMap[i].write;
276                         }
277
278                         break;
279                 }
280
281                 i++;
282         };
283
284         // This should correctly set up the LC pointers, but it doesn't
285         // for some reason... :-/
286         // It's because we were storing pointers directly, instead of pointers
287         // to the pointer... It's complicated. :-)
288         SwitchLC();
289 }
290
291
292 //
293 // Reset the MMU state after a power down event
294 //
295 void ResetMMUPointers(void)
296 {
297         if (store80Mode)
298         {
299                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
300                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
301         }
302         else
303         {
304                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
305                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
306         }
307
308         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
309         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
310         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
311         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
312
313         slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
314         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
315         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
316         SwitchLC();
317 }
318
319
320 //
321 // Built-in functions
322 //
323 uint8_t ReadNOP(uint16_t)
324 {
325         return 0;
326 }
327
328
329 void WriteNOP(uint16_t, uint8_t)
330 {
331 }
332
333
334 uint8_t ReadMemory(uint16_t address)
335 {
336 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
337         // We are guaranteed a valid address here by the setup function, so there's
338         // no need to do any checking here.
339         return (*addrPtrRead[address])[addrOffset[address]];
340 }
341
342
343 void WriteMemory(uint16_t address, uint8_t byte)
344 {
345         // We can write protect memory this way, but it adds a branch to the mix.
346         // :-/ (this can be avoided by setting up another bank of memory which we
347         //  ignore... hmm...)
348         if ((*addrPtrWrite[address]) == 0)
349                 return;
350
351         (*addrPtrWrite[address])[addrOffset[address]] = byte;
352 }
353
354
355 //
356 // The main memory access functions used by V65C02
357 //
358 uint8_t AppleReadMem(uint16_t address)
359 {
360         return (*(funcMapRead[address]))(address);
361 }
362
363
364 void AppleWriteMem(uint16_t address, uint8_t byte)
365 {
366         (*(funcMapWrite[address]))(address, byte);
367 }
368
369
370 //
371 // Actual emulated I/O functions follow
372 //
373 uint8_t ReadKeyboard(uint16_t /*addr*/)
374 {
375         return lastKeyPressed | ((uint8_t)keyDown << 7);
376 }
377
378
379 void Switch80STORE(uint16_t address, uint8_t)
380 {
381         store80Mode = (bool)(address & 0x01);
382 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
383
384         if (store80Mode)
385         {
386                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
387                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
388         }
389         else
390         {
391                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
392                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
393         }
394 }
395
396
397 void SwitchRAMRD(uint16_t address, uint8_t)
398 {
399         ramrd = (bool)(address & 0x01);
400         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
401         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
402
403         if (store80Mode)
404                 return;
405
406         mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
407 }
408
409
410 void SwitchRAMWRT(uint16_t address, uint8_t)
411 {
412         ramwrt = (bool)(address & 0x01);
413         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
414         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
415
416         if (store80Mode)
417                 return;
418
419         mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
420 }
421
422
423 void SwitchSLOTCXROM(uint16_t address, uint8_t)
424 {
425 //WriteLog("Setting SLOTCXROM to %s...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"));
426         // This is the only soft switch that breaks the usual convention.
427         slotCXROM = !((bool)(address & 0x01));
428 //      slot3Memory = (slotCXROM ? &rom[0] : &rom[0xC300]);
429         slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
430 }
431
432
433 void SwitchALTZP(uint16_t address, uint8_t)
434 {
435         altzp = (bool)(address & 0x01);
436         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
437         SwitchLC();
438 }
439
440 //extern bool dumpDis;
441
442 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
443 {
444 //dumpDis = true;
445 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
446         slotC3ROM = (bool)(address & 0x01);
447 //      slotC3ROM = false;
448 // Seems the h/w forces this with an 80 column card in slot 3...
449         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
450 //      slot3Memory = &rom[0xC300];
451 }
452
453
454 void Switch80COL(uint16_t address, uint8_t)
455 {
456         col80Mode = (bool)(address & 0x01);
457 }
458
459
460 void SwitchALTCHARSET(uint16_t address, uint8_t)
461 {
462         alternateCharset = (bool)(address & 0x01);
463 }
464
465
466 uint8_t ReadKeyStrobe(uint16_t)
467 {
468 // No character data is read from here, just the 'any key was pressed' signal...
469 //      uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
470         uint8_t byte = (uint8_t)keyDown << 7;
471         keyDown = false;
472         return byte;
473 }
474
475
476 uint8_t ReadBANK2(uint16_t)
477 {
478         return (lcState < 0x04 ? 0x80 : 0x00);
479 }
480
481
482 uint8_t ReadLCRAM(uint16_t)
483 {
484         // If bits 0 & 1 are set, but not at the same time, then it's ROM
485         uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
486         return (lcROM ? 0x00 : 0x80);
487 }
488
489
490 uint8_t ReadRAMRD(uint16_t)
491 {
492         return (uint8_t)ramrd << 7;
493 }
494
495
496 uint8_t ReadRAMWRT(uint16_t)
497 {
498         return (uint8_t)ramwrt << 7;
499 }
500
501
502 uint8_t ReadSLOTCXROM(uint16_t)
503 {
504         return (uint8_t)slotCXROM << 7;
505 }
506
507
508 uint8_t ReadALTZP(uint16_t)
509 {
510         return (uint8_t)altzp << 7;
511 }
512
513
514 uint8_t ReadSLOTC3ROM(uint16_t)
515 {
516 //      return 0;
517         return (uint8_t)slotC3ROM << 7;
518 }
519
520
521 uint8_t Read80STORE(uint16_t)
522 {
523         return (uint8_t)store80Mode << 7;
524 }
525
526
527 uint8_t ReadVBL(uint16_t)
528 {
529         return (uint8_t)vbl << 7;
530 }
531
532
533 uint8_t ReadTEXT(uint16_t)
534 {
535         return (uint8_t)textMode << 7;
536 }
537
538
539 uint8_t ReadMIXED(uint16_t)
540 {
541         return (uint8_t)mixedMode << 7;
542 }
543
544
545 uint8_t ReadPAGE2(uint16_t)
546 {
547         return (uint8_t)displayPage2 << 7;
548 }
549
550
551 uint8_t ReadHIRES(uint16_t)
552 {
553         return (uint8_t)hiRes << 7;
554 }
555
556
557 uint8_t ReadALTCHARSET(uint16_t)
558 {
559         return (uint8_t)alternateCharset << 7;
560 }
561
562
563 uint8_t Read80COL(uint16_t)
564 {
565         return (uint8_t)col80Mode << 7;
566 }
567
568
569 void WriteKeyStrobe(uint16_t, uint8_t)
570 {
571         keyDown = false;
572 }
573
574
575 uint8_t ReadSpeaker(uint16_t)
576 {
577         ToggleSpeaker();
578         return 0;
579 }
580
581
582 void WriteSpeaker(uint16_t, uint8_t)
583 {
584         ToggleSpeaker();
585 }
586
587
588 uint8_t SwitchLCR(uint16_t address)
589 {
590         lcState = address & 0x0B;
591         SwitchLC();
592         return 0;
593 }
594
595
596 void SwitchLCW(uint16_t address, uint8_t)
597 {
598         lcState = address & 0x0B;
599         SwitchLC();
600 }
601
602
603 void SwitchLC(void)
604 {
605         switch (lcState)
606         {
607         case 0x00:
608 #ifdef LC_DEBUG
609 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
610 #endif
611                 // [R ] Read RAM bank 2; no write
612                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
613                 lcBankMemoryW = 0;
614                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
615                 upperMemoryW = 0;
616                 break;
617         case 0x01:
618 #ifdef LC_DEBUG
619 WriteLog("SwitchLC: Read ROM, write bank 2\n");
620 #endif
621                 // [RR] Read ROM; write RAM bank 2
622                 lcBankMemoryR = &rom[0xD000];
623                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
624                 upperMemoryR = &rom[0xE000];
625                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
626                 break;
627         case 0x02:
628 #ifdef LC_DEBUG
629 WriteLog("SwitchLC: Read ROM, no write\n");
630 #endif
631                 // [R ] Read ROM; no write
632                 lcBankMemoryR = &rom[0xD000];
633                 lcBankMemoryW = 0;
634                 upperMemoryR = &rom[0xE000];
635                 upperMemoryW = 0;
636                 break;
637         case 0x03:
638 #ifdef LC_DEBUG
639 WriteLog("SwitchLC: Read/write bank 2\n");
640 #endif
641                 // [RR] Read RAM bank 2; write RAM bank 2
642                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
643                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
644                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
645                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
646                 break;
647         case 0x08:
648                 // [R ] Read RAM bank 1; no write
649                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
650                 lcBankMemoryW = 0;
651                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
652                 upperMemoryW = 0;
653                 break;
654         case 0x09:
655                 // [RR] Read ROM; write RAM bank 1
656                 lcBankMemoryR = &rom[0xD000];
657                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
658                 upperMemoryR = &rom[0xE000];
659                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
660                 break;
661         case 0x0A:
662                 // [R ] Read ROM; no write
663                 lcBankMemoryR = &rom[0xD000];
664                 lcBankMemoryW = 0;
665                 upperMemoryR = &rom[0xE000];
666                 upperMemoryW = 0;
667                 break;
668         case 0x0B:
669                 // [RR] Read RAM bank 1; write RAM bank 1
670                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
671                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
672                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
673                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
674                 break;
675         }
676 }
677
678
679 uint8_t SwitchTEXTR(uint16_t address)
680 {
681 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
682         textMode = (bool)(address & 0x01);
683         return 0;
684 }
685
686
687 void SwitchTEXTW(uint16_t address, uint8_t)
688 {
689 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
690         textMode = (bool)(address & 0x01);
691 }
692
693
694 uint8_t SwitchMIXEDR(uint16_t address)
695 {
696 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
697         mixedMode = (bool)(address & 0x01);
698         return 0;
699 }
700
701
702 void SwitchMIXEDW(uint16_t address, uint8_t)
703 {
704 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
705         mixedMode = (bool)(address & 0x01);
706 }
707
708
709 uint8_t SwitchPAGE2R(uint16_t address)
710 {
711 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
712         displayPage2 = (bool)(address & 0x01);
713
714         if (store80Mode)
715         {
716                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
717                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
718         }
719
720         return 0;
721 }
722
723
724 void SwitchPAGE2W(uint16_t address, uint8_t)
725 {
726 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
727         displayPage2 = (bool)(address & 0x01);
728
729         if (store80Mode)
730         {
731                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
732                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
733         }
734 }
735
736
737 uint8_t SwitchHIRESR(uint16_t address)
738 {
739 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
740         hiRes = (bool)(address & 0x01);
741         return 0;
742 }
743
744
745 void SwitchHIRESW(uint16_t address, uint8_t)
746 {
747 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
748         hiRes = (bool)(address & 0x01);
749 }
750
751
752 uint8_t SwitchDHIRESR(uint16_t address)
753 {
754 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
755         // Hmm, this breaks convention too, like SLOTCXROM
756         if (ioudis)
757                 dhires = !((bool)(address & 0x01));
758
759         return 0;
760 }
761
762
763 void SwitchDHIRESW(uint16_t address, uint8_t)
764 {
765 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
766         if (ioudis)
767                 dhires = !((bool)(address & 0x01));
768 }
769
770
771 void SwitchIOUDIS(uint16_t address, uint8_t)
772 {
773         ioudis = !((bool)(address & 0x01));
774 }
775
776
777 uint8_t Slot6R(uint16_t address)
778 {
779 //WriteLog("Slot6R: address = %X\n", address & 0x0F);
780 //      HandleSlot6(address, 0);
781 //      return 0;
782         uint8_t state = address & 0x0F;
783
784         switch (state)
785         {
786         case 0x00:
787         case 0x01:
788         case 0x02:
789         case 0x03:
790         case 0x04:
791         case 0x05:
792         case 0x06:
793         case 0x07:
794                 floppyDrive.ControlStepper(state);
795                 break;
796         case 0x08:
797         case 0x09:
798                 floppyDrive.ControlMotor(state & 0x01);
799                 break;
800         case 0x0A:
801         case 0x0B:
802                 floppyDrive.DriveEnable(state & 0x01);
803                 break;
804         case 0x0C:
805                 return floppyDrive.ReadWrite();
806                 break;
807         case 0x0D:
808                 return floppyDrive.GetLatchValue();
809                 break;
810         case 0x0E:
811                 floppyDrive.SetReadMode();
812                 break;
813         case 0x0F:
814                 floppyDrive.SetWriteMode();
815                 break;
816         }
817
818         return 0;
819 }
820
821
822 void Slot6W(uint16_t address, uint8_t byte)
823 {
824 //WriteLog("Slot6W: address = %X, byte= %X\n", address & 0x0F, byte);
825 //      HandleSlot6(address, byte);
826         uint8_t state = address & 0x0F;
827
828         switch (state)
829         {
830         case 0x00:
831         case 0x01:
832         case 0x02:
833         case 0x03:
834         case 0x04:
835         case 0x05:
836         case 0x06:
837         case 0x07:
838                 floppyDrive.ControlStepper(state);
839                 break;
840         case 0x08:
841         case 0x09:
842                 floppyDrive.ControlMotor(state & 0x01);
843                 break;
844         case 0x0A:
845         case 0x0B:
846                 floppyDrive.DriveEnable(state & 0x01);
847                 break;
848         case 0x0C:
849                 floppyDrive.ReadWrite();
850                 break;
851         case 0x0D:
852                 floppyDrive.SetLatchValue(byte);
853                 break;
854         case 0x0E:
855                 floppyDrive.SetReadMode();
856                 break;
857         case 0x0F:
858                 floppyDrive.SetWriteMode();
859                 break;
860         }
861 }
862
863
864 void HandleSlot6(uint16_t address, uint8_t byte)
865 {
866 }
867
868
869 uint8_t MBRead(uint16_t address)
870 {
871 #if 1
872         // Not sure [Seems to work OK]
873         if (!slotCXROM)
874         {
875                 return slot4Memory[address & 0x00FF];
876         }
877 #endif
878
879         uint8_t regNum = address & 0x0F;
880         uint8_t chipNum = (address & 0x80) >> 7;
881
882 #if 0
883         WriteLog("MBRead: address = %X [chip %d, reg %X, clock=$%X]\n", address & 0xFF, chipNum, regNum, GetCurrentV65C02Clock());
884 #endif
885
886         switch (regNum)
887         {
888         case 0x00:
889                 return mbvia[chipNum].orb & mbvia[chipNum].ddrb;
890
891         case 0x01:
892                 return mbvia[chipNum].ora & mbvia[chipNum].ddra;
893
894         case 0x02:
895                 return mbvia[chipNum].ddrb;
896
897         case 0x03:
898                 return mbvia[chipNum].ddra;
899
900         case 0x04:
901                 return mbvia[chipNum].timer1counter & 0xFF;
902
903         case 0x05:
904                 return (mbvia[chipNum].timer1counter & 0xFF00) >> 8;
905
906         case 0x06:
907                 return mbvia[chipNum].timer1latch & 0xFF;
908
909         case 0x07:
910                 return (mbvia[chipNum].timer1latch & 0xFF00) >> 8;
911
912         case 0x08:
913                 return mbvia[chipNum].timer2counter & 0xFF;
914
915         case 0x09:
916                 return (mbvia[chipNum].timer2counter & 0xFF00) >> 8;
917
918         case 0x0B:
919                 return mbvia[chipNum].acr;
920
921         case 0x0D:
922                 return (mbvia[chipNum].ifr & 0x7F)
923                         | (mbvia[chipNum].ifr & 0x7F ? 0x80 : 0);
924
925         case 0x0E:
926                 return mbvia[chipNum].ier | 0x80;
927
928         default:
929                 WriteLog("Unhandled 6522 register %X read (chip %d)\n", regNum, chipNum);
930         }
931
932         return 0;
933 }
934
935
936 static uint8_t regLatch[2];
937 void MBWrite(uint16_t address, uint8_t byte)
938 {
939         uint8_t regNum = address & 0x0F;
940         uint8_t chipNum = (address & 0x80) >> 7;
941 /*
942 NOTES:
943 bit 7 = L/R channel select (AY chip 1 versus AY chip 2)
944         0 = Left, 1 = Right
945
946 Reg. B is connected to BC1, BDIR, RST' (bits 0, 1, 2)
947
948 Left VIA IRQ line is tied to 6502 IRQ line
949 Rght VIA IRQ line is tied to 6502 NMI line
950
951 Register  Function
952 --------  -------------------------
953 0         Output Register B
954 1         Output Register A
955 2         Data Direction Register B
956 3         Data Direction Register A
957 4         Timer 1 Low byte counter (& latch)
958 5         Timer 1 Hgh byte counter (& latch)
959 6         Timer 1 Low byte latch
960 7         Timer 1 Hgh byte latch (& reset IRQ flag)
961 B         Aux Control Register
962 D         Interrupt Flag Register
963 E         Interrupt Enable Register
964
965 bit 6 of ACR is like so:
966 0: Timed interrupt each time Timer 1 is loaded
967 1: Continuous interrupts
968
969 bit 7 enables PB7 (bit 6 controls output type):
970 0: One shot output
971 1: Square wave output
972
973
974 */
975 #if 0
976         WriteLog("MBWrite: address = %X, byte= %X [clock=$%X]", address & 0xFF, byte, GetCurrentV65C02Clock());
977
978         if (regNum == 0)
979                 WriteLog("[OUTB -> %s%s%s]\n", (byte & 0x01 ? "BC1" : ""), (byte & 0x02 ? " BDIR" : ""), (byte & 0x04 ? " RST'" : ""));
980         else if (regNum == 1)
981                 WriteLog("[OUTA -> %02X]\n", byte);
982         else if (regNum == 2)
983                 WriteLog("[DDRB -> %02X]\n", byte);
984         else if (regNum == 3)
985                 WriteLog("[DDRA -> %02X]\n", byte);
986         else
987                 WriteLog("\n");
988 #endif
989
990         switch (regNum)
991         {
992         case 0x00:
993                 // Control of the AY-3-8912 is thru this port pretty much...
994                 mbvia[chipNum].orb = byte;
995
996                 if ((byte & 0x04) == 0)
997 #ifdef USE_NEW_AY8910
998                         AYReset(chipNum);
999 #else
1000                         AY8910_reset(chipNum);
1001 #endif
1002                 else if ((byte & 0x03) == 0x03)
1003                         regLatch[chipNum] = mbvia[chipNum].ora;
1004                 else if ((byte & 0x03) == 0x02)
1005 #ifdef USE_NEW_AY8910
1006                         AYWrite(chipNum, regLatch[chipNum], mbvia[chipNum].ora);
1007 #else
1008                         _AYWriteReg(chipNum, regLatch[chipNum], mbvia[chipNum].ora);
1009 #endif
1010
1011                 break;
1012
1013         case 0x01:
1014                 mbvia[chipNum].ora = byte;
1015                 break;
1016
1017         case 0x02:
1018                 mbvia[chipNum].ddrb = byte;
1019                 break;
1020
1021         case 0x03:
1022                 mbvia[chipNum].ddra = byte;
1023                 break;
1024
1025         case 0x04:
1026                 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0xFF00)
1027                         | byte;
1028                 break;
1029
1030         case 0x05:
1031                 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0x00FF)
1032                         | (((uint16_t)byte) << 8);
1033                 mbvia[chipNum].timer1counter = mbvia[chipNum].timer1latch;
1034                 mbvia[chipNum].ifr &= 0x3F; // Clear T1 interrupt flag
1035                 break;
1036
1037         case 0x06:
1038                 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0xFF00)
1039                         | byte;
1040                 break;
1041
1042         case 0x07:
1043                 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0x00FF)
1044                         | (((uint16_t)byte) << 8);
1045                 mbvia[chipNum].ifr &= 0x3F; // Clear T1 interrupt flag
1046                 break;
1047
1048         case 0x0B:
1049                 mbvia[chipNum].acr = byte;
1050                 break;
1051
1052         case 0x0D:
1053                 mbvia[chipNum].ifr &= ~byte;
1054                 break;
1055
1056         case 0x0E:
1057                 if (byte & 0x80)
1058                         // Setting bits in the IER
1059                         mbvia[chipNum].ier |= byte;
1060                 else
1061                         // Clearing bits in the IER
1062                         mbvia[chipNum].ier &= ~byte;
1063
1064                 break;
1065         default:
1066                 WriteLog("Unhandled 6522 register $%X write $%02X (chip %d)\n", regNum, byte, chipNum);
1067         }
1068 }
1069
1070
1071 uint8_t ReadButton0(uint16_t)
1072 {
1073         return (uint8_t)openAppleDown << 7;
1074 }
1075
1076
1077 uint8_t ReadButton1(uint16_t)
1078 {
1079         return (uint8_t)closedAppleDown << 7;
1080 }
1081
1082
1083 // The way the paddles work is that a strobe is written (or read) to $C070,
1084 // then software counts down the time that it takes for the paddle outputs
1085 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1086 // stays at 1.
1087 // NB: This is really paddles 0-3, not just 0 :-P
1088 uint8_t ReadPaddle0(uint16_t)
1089 {
1090         return 0xFF;
1091 }
1092
1093
1094 uint8_t ReadIOUDIS(uint16_t)
1095 {
1096         return (uint8_t)ioudis << 7;
1097 }
1098
1099
1100 uint8_t ReadDHIRES(uint16_t)
1101 {
1102         return (uint8_t)dhires << 7;
1103 }
1104
1105
1106 // Whenever a read is done to a MMIO location that is unconnected to anything,
1107 // it actually sees the RAM access done by the video generation hardware. Some
1108 // programs exploit this, so we emulate it here.
1109
1110 // N.B.: frameCycles will be off by the true amount because this only
1111 //       increments by the amount of a speaker cycle, not the cycle count when
1112 //       the access happens... !!! FIX !!!
1113 uint8_t ReadFloatingBus(uint16_t)
1114 {
1115         // Get the currently elapsed cycle count for this frame
1116         uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1117
1118         // Make counters out of the cycle count. There are 65 cycles per line.
1119         uint32_t numLines = frameCycles / 65;
1120         uint32_t numHTicks = frameCycles - (numLines * 65);
1121
1122         // Convert these to H/V counters
1123         uint32_t hcount = numHTicks - 1;
1124
1125         // HC sees zero twice:
1126         if (hcount == 0xFFFFFFFF)
1127                 hcount = 0;
1128
1129         uint32_t vcount = numLines + 0xFA;
1130
1131         // Now do the address calculations
1132         uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1133                 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1134         uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1135
1136         // Add in particulars for the gfx mode we're in...
1137         if (textMode || (!textMode && !hiRes))
1138                 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1139                         | (!store80Mode && displayPage2 ? 0x800 : 0);
1140         else
1141                 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1142                         | (!store80Mode && displayPage2 ? 0x4000 : 0)
1143                         | ((vcount & 0x07) << 10);
1144
1145         // The address so read is *always* in main RAM, not alt RAM
1146         return ram[address];
1147 }
1148