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