]> Shamusworld >> Repos - apple2/blob - src/mmu.cpp
Misc. improvements, added WOZ file support to floppy emulation
[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 "firmware.h"
17 #include "log.h"
18 #include "mockingboard.h"
19 #include "sound.h"
20 #include "video.h"
21
22
23 // Debug defines
24 //#define LC_DEBUG
25
26 // Address Map enumeration
27 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
28
29 // Internal vars
30 uint8_t ** addrPtrRead[0x10000];
31 uint8_t ** addrPtrWrite[0x10000];
32 uint16_t addrOffset[0x10000];
33
34 READFUNC(funcMapRead[0x10000]);
35 WRITEFUNC(funcMapWrite[0x10000]);
36
37 READFUNC(slotHandlerR[8]);
38 WRITEFUNC(slotHandlerW[8]);
39
40 READFUNC(slotHandler2KR[8]);
41 WRITEFUNC(slotHandler2KW[8]);
42
43 uint8_t enabledSlot;
44
45 struct AddressMap
46 {
47         uint16_t start;
48         uint16_t end;
49         int type;
50         uint8_t ** memory;
51         uint8_t ** altMemory;
52         READFUNC(read);
53         WRITEFUNC(write);
54 };
55
56 #define ADDRESS_MAP_END         { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
57
58 // Dunno if I like this approach or not...
59 //ADDRESS_MAP_START()
60 //      AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
61 //      AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
62 //ADDRESS_MAP_END
63
64 // Would need a pointer for 80STORE as well...
65
66 uint8_t * pageZeroMemory  = &ram[0x0000];       // $0000 - $01FF
67 uint8_t * mainMemoryR     = &ram[0x0200];       // $0200 - $BFFF (read)
68 uint8_t * mainMemoryW     = &ram[0x0200];       // $0200 - $BFFF (write)
69
70 uint8_t * mainMemoryTextR = &ram[0x0400];       // $0400 - $07FF (read)
71 uint8_t * mainMemoryTextW = &ram[0x0400];       // $0400 - $07FF (write)
72 uint8_t * mainMemoryHGRR  = &ram[0x2000];       // $2000 - $3FFF (read)
73 uint8_t * mainMemoryHGRW  = &ram[0x2000];       // $2000 - $3FFF (write)
74
75 uint8_t * slotMemory      = &rom[0xC100];       // $C100 - $C7FF
76 uint8_t * peripheralMemory= &rom[0xC800];       // $C800 - $CFFF
77 uint8_t * slot3Memory     = &rom[0xC300];       // $C300 - $C3FF
78 uint8_t * slot4Memory     = &rom[0xC400];       // $C400 - $C4FF
79 uint8_t * slot6Memory     = &diskROM[0];        // $C600 - $C6FF
80 uint8_t * lcBankMemoryR   = &ram[0xD000];       // $D000 - $DFFF (read)
81 uint8_t * lcBankMemoryW   = &ram[0xD000];       // $D000 - $DFFF (write)
82 uint8_t * upperMemoryR    = &ram[0xE000];       // $E000 - $FFFF (read)
83 uint8_t * upperMemoryW    = &ram[0xE000];       // $E000 - $FFFF (write)
84
85
86 // Function prototypes
87 uint8_t ReadNOP(uint16_t);
88 void WriteNOP(uint16_t, uint8_t);
89 uint8_t ReadMemory(uint16_t);
90 void WriteMemory(uint16_t, uint8_t);
91 uint8_t SlotR(uint16_t address);
92 void SlotW(uint16_t address, uint8_t byte);
93 uint8_t Slot2KR(uint16_t address);
94 void Slot2KW(uint16_t address, uint8_t byte);
95 uint8_t ReadKeyboard(uint16_t);
96 void Switch80STORE(uint16_t, uint8_t);
97 void SwitchRAMRD(uint16_t, uint8_t);
98 void SwitchRAMWRT(uint16_t, uint8_t);
99 void SwitchSLOTCXROM(uint16_t, uint8_t);
100 void SwitchALTZP(uint16_t, uint8_t);
101 void SwitchSLOTC3ROM(uint16_t, uint8_t);
102 uint8_t SwitchINTC8ROMR(uint16_t);
103 void SwitchINTC8ROMW(uint16_t, uint8_t);
104 void Switch80COL(uint16_t, uint8_t);
105 void SwitchALTCHARSET(uint16_t, uint8_t);
106 uint8_t ReadKeyStrobe(uint16_t);
107 uint8_t ReadBANK2(uint16_t);
108 uint8_t ReadLCRAM(uint16_t);
109 uint8_t ReadRAMRD(uint16_t);
110 uint8_t ReadRAMWRT(uint16_t);
111 uint8_t ReadSLOTCXROM(uint16_t);
112 uint8_t ReadALTZP(uint16_t);
113 uint8_t ReadSLOTC3ROM(uint16_t);
114 uint8_t Read80STORE(uint16_t);
115 uint8_t ReadVBL(uint16_t);
116 uint8_t ReadTEXT(uint16_t);
117 uint8_t ReadMIXED(uint16_t);
118 uint8_t ReadPAGE2(uint16_t);
119 uint8_t ReadHIRES(uint16_t);
120 uint8_t ReadALTCHARSET(uint16_t);
121 uint8_t Read80COL(uint16_t);
122 void WriteKeyStrobe(uint16_t, uint8_t);
123 uint8_t ReadSpeaker(uint16_t);
124 void WriteSpeaker(uint16_t, uint8_t);
125 uint8_t SwitchLCR(uint16_t);
126 void SwitchLCW(uint16_t, uint8_t);
127 void SwitchLC(void);
128 uint8_t SwitchTEXTR(uint16_t);
129 void SwitchTEXTW(uint16_t, uint8_t);
130 uint8_t SwitchMIXEDR(uint16_t);
131 void SwitchMIXEDW(uint16_t, uint8_t);
132 uint8_t SwitchPAGE2R(uint16_t);
133 void SwitchPAGE2W(uint16_t, uint8_t);
134 uint8_t SwitchHIRESR(uint16_t);
135 void SwitchHIRESW(uint16_t, uint8_t);
136 uint8_t SwitchDHIRESR(uint16_t);
137 void SwitchDHIRESW(uint16_t, uint8_t);
138 void SwitchIOUDIS(uint16_t, uint8_t);
139 uint8_t ReadButton0(uint16_t);
140 uint8_t ReadButton1(uint16_t);
141 uint8_t ReadPaddle0(uint16_t);
142 uint8_t ReadIOUDIS(uint16_t);
143 uint8_t ReadDHIRES(uint16_t);
144
145
146 // The main Apple //e memory map
147 AddressMap memoryMap[] = {
148         { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
149         { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
150
151         // These will overlay over the previously written memory accessors
152         { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
153         { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
154
155         { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
156         { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
157         { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
158         { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
159         { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
160         { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
161         { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
162         { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
163         { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
164         { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
165         { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
166         { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
167         { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
168         { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
169         { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
170         { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
171         { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
172         { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
173         { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
174         { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
175         { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
176         { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
177         { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
178         { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
179         // $C020 is "Cassette Out (RO)"
180         { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
181         // May have to put a "floating bus" read there... :-/
182         // Apparently, video RAM is put on 'non-responding address'. So will
183         // need to time those out.
184         // So... $C020-$C08F, when read, return video data.
185         // $C090-$C7FF do also, as long as the slot the range refers to is empty
186         // and last and least is $CFFF, which is the Expansion ROM disable.
187         { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
188         { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
189         { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
190         { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
191         { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
192         { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
193         { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
194         { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
195         { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
196         { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
197         { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
198         { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
199
200         { 0xC100, 0xC7FF, AM_READ_WRITE, 0, 0, SlotR, SlotW },
201         { 0xC800, 0xCFFE, AM_READ_WRITE, 0, 0, Slot2KR, Slot2KW },
202         { 0xCFFF, 0xCFFF, AM_READ_WRITE, 0, 0, SwitchINTC8ROMR, SwitchINTC8ROMW },
203
204         { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
205         { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
206         ADDRESS_MAP_END
207 };
208 /*
209 Some stuff that may be useful:
210
211 N.B.: Page 5-22 of UTA2E has INTC8ROM ON/OFF backwards
212 INTC8ROM is turned OFF by R/W access to $CFFF
213 INTC8ROM is turned ON by $C3xx access and SLOTC3ROM' (off)
214 WRONG: (INTC8ROM on puts card's slot ROM/RAM(?) access in $C800-$CFFF)
215
216 OK, so it's slightly more complex than that.  Basically, when there is an access to $CFFF, all peripheral cards must *stop* responding to  I/O STROBE'.  Only when a card gets an I/O SELECT' signal, can it respond to I/O STROBE'.
217
218 INTC8ROM inhibits I/O STROBE' and activates the MB ROM in $C800-$CFFF
219 INTC8ROM is 1 by access to $C3xx when SLOTC3ROM is 0
220 INTC8ROM is 0 by access to $CFFF
221
222 ICX = INTCXROM (aka SLOTCXROM), SC3 = SLOTC3ROM
223
224              ICX=0,SC3=0  ICX=0,SC3=1  ICX=1,SC3=0  ICX=1,SC3=1
225 $C100-$C2FF   slot         slot         internal     internal
226 $C300-$C3FF   internal     slot         internal     internal
227 $C400-$CFFF   slot         slot         internal     internal
228
229 Read from $C800-$CFFF causes I/O STROBE to go low (and INTCXROM and INTC8ROM are not set)
230
231 */
232
233
234 void SetupAddressMap(void)
235 {
236         for(uint32_t i=0; i<0x10000; i++)
237         {
238                 funcMapRead[i] = ReadNOP;
239                 funcMapWrite[i] = WriteNOP;
240                 addrPtrRead[i] = 0;
241                 addrPtrWrite[i] = 0;
242                 addrOffset[i] = 0;
243         }
244
245         for(uint32_t i=0; i<8; i++)
246         {
247                 slotHandlerR[i] = ReadNOP;
248                 slotHandlerW[i] = WriteNOP;
249                 slotHandler2KR[i] = ReadNOP;
250                 slotHandler2KW[i] = WriteNOP;
251         }
252
253         uint32_t i=0;
254
255         while (memoryMap[i].type != AM_END_OF_LIST)
256         {
257                 switch (memoryMap[i].type)
258                 {
259                 case AM_RAM:
260                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
261                         {
262                                 funcMapRead[j] = ReadMemory;
263                                 funcMapWrite[j] = WriteMemory;
264                                 addrPtrRead[j] = memoryMap[i].memory;
265                                 addrPtrWrite[j] = memoryMap[i].memory;
266                                 addrOffset[j] = j - memoryMap[i].start;
267 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
268                         }
269
270                         break;
271                 case AM_ROM:
272                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
273                         {
274                                 funcMapRead[j] = ReadMemory;
275                                 addrPtrRead[j] = memoryMap[i].memory;
276                                 addrOffset[j] = j - memoryMap[i].start;
277                         }
278
279                         break;
280                 case AM_BANKED:
281                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
282                         {
283                                 funcMapRead[j] = ReadMemory;
284                                 funcMapWrite[j] = WriteMemory;
285                                 addrPtrRead[j] = memoryMap[i].memory;
286                                 addrPtrWrite[j] = memoryMap[i].altMemory;
287                                 addrOffset[j] = j - memoryMap[i].start;
288                         }
289
290                         break;
291                 case AM_READ:
292                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
293                                 funcMapRead[j] = memoryMap[i].read;
294
295                         break;
296                 case AM_WRITE:
297                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
298                                 funcMapWrite[j] = memoryMap[i].write;
299
300                         break;
301                 case AM_READ_WRITE:
302                         for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
303                         {
304                                 funcMapRead[j] = memoryMap[i].read;
305                                 funcMapWrite[j] = memoryMap[i].write;
306                         }
307
308                         break;
309                 }
310
311                 i++;
312         };
313
314         // This should correctly set up the LC pointers, but it doesn't
315         // for some reason... :-/
316         // It's because we were storing pointers directly, instead of pointers
317         // to the pointer... It's complicated. :-)
318         SwitchLC();
319 }
320
321
322 //
323 // Reset the MMU state after a power down event
324 //
325 void ResetMMUPointers(void)
326 {
327         if (store80Mode)
328         {
329                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
330                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
331         }
332         else
333         {
334                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
335                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
336         }
337
338         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
339         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
340         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
341         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
342
343 //      slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
344 //      slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
345         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
346         SwitchLC();
347 #if 1
348 WriteLog("RAMWRT = %s\n", (ramwrt ? "ON" : "off"));
349 WriteLog("RAMRD = %s\n", (ramrd ? "ON" : "off"));
350 WriteLog("SLOTCXROM = %s\n", (intCXROM ? "ON" : "off"));
351 WriteLog("SLOTC3ROM = %s\n", (slotC3ROM ? "ON" : "off"));
352 WriteLog("ALTZP = %s\n", (altzp ? "ON" : "off"));
353 #endif
354 }
355
356
357 //
358 // Set up slot access
359 //
360 void InstallSlotHandler(uint8_t slot, SlotData * slotData)
361 {
362         // Sanity check
363         if (slot > 7)
364         {
365                 WriteLog("InstallSlotHanlder: Caller attempted to put device into slot #%u...\n", slot);
366                 return;
367         }
368
369         // Set up I/O read & write functions
370         for(uint32_t i=0; i<16; i++)
371         {
372                 if (slotData->ioR)
373                         funcMapRead[0xC080 + (slot * 16) + i] = slotData->ioR;
374
375                 if (slotData->ioW)
376                         funcMapWrite[0xC080 + (slot * 16) + i] = slotData->ioW;
377         }
378
379         // Set up memory access read/write functions
380         if (slotData->pageR)
381                 slotHandlerR[slot] = slotData->pageR;
382
383         if (slotData->pageW)
384                 slotHandlerW[slot] = slotData->pageW;
385
386         if (slotData->extraR)
387                 slotHandler2KR[slot] = slotData->extraR;
388
389         if (slotData->extraW)
390                 slotHandler2KW[slot] = slotData->extraW;
391 }
392
393
394 //
395 // Built-in functions
396 //
397 uint8_t ReadNOP(uint16_t)
398 {
399         // This is for unconnected reads, and some software looks at addresses like these.  In particular, Mr. Robot and His Robot Factory failed in that it was looking at the first byte of each slots 256 byte driver space and failing if it saw a zero there.  Now I have no idea what happens in the real hardware, but I suspect it would return something that looks like ReadFloatingBus().
400         return 0xFF;
401 }
402
403
404 void WriteNOP(uint16_t, uint8_t)
405 {
406 }
407
408
409 uint8_t ReadMemory(uint16_t address)
410 {
411 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
412         // We are guaranteed a valid address here by the setup function, so there's
413         // no need to do any checking here.
414         return (*addrPtrRead[address])[addrOffset[address]];
415 }
416
417
418 void WriteMemory(uint16_t address, uint8_t byte)
419 {
420         // We can write protect memory this way, but it adds a branch to the mix.
421         // :-/ (this can be avoided by setting up another bank of memory which we
422         //  ignore... hmm...)
423         if ((*addrPtrWrite[address]) == 0)
424                 return;
425
426         (*addrPtrWrite[address])[addrOffset[address]] = byte;
427 }
428
429
430 //
431 // The main memory access functions used by V65C02
432 //
433 uint8_t AppleReadMem(uint16_t address)
434 {
435 #if 0
436 if (address == 0xD4 || address == 0xAC20)
437         WriteLog("Reading $%X...\n", address);
438 #endif
439 #if 0
440         uint8_t memRead = (*(funcMapRead[address]))(address);
441 static uint16_t lastAddr = 0;
442 static uint32_t lastCount = 0;
443 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
444 {
445         if (lastAddr == address)
446                 lastCount++;
447         else
448         {
449                 if (lastCount > 1)
450                         WriteLog("%d times...\n", lastCount);
451
452                 WriteLog("Reading $%02X from $%X ($%02X, $%02X)\n", memRead, address, diskROM[1], rom[0xC601]);
453                 lastCount = 1;
454                 lastAddr = address;
455         }
456 }
457         return memRead;
458 #else
459         return (*(funcMapRead[address]))(address);
460 #endif
461 }
462
463
464 void AppleWriteMem(uint16_t address, uint8_t byte)
465 {
466 #if 0
467 static uint16_t lastAddr = 0;
468 static uint32_t lastCount = 0;
469 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
470 {
471         if (lastAddr == address)
472                 lastCount++;
473         else
474         {
475                 if (lastCount > 1)
476                         WriteLog("%d times...\n", lastCount);
477
478                 WriteLog("Writing to $%X\n", address);
479                 lastCount = 1;
480                 lastAddr = address;
481         }
482 }
483 #endif
484 #if 0
485 if (address == 0xD4 || address == 0xAC20)
486         WriteLog("Writing $%02X @ $%X...\n", byte, address);
487 #endif
488 #if 0
489 //if (address >= 0x0827 && address <= 0x082A)
490 if (address == 0x000D)
491         WriteLog("Writing $%02X @ $%X (PC=$%04X)...\n", byte, address, mainCPU.pc);
492 #endif
493         (*(funcMapWrite[address]))(address, byte);
494 }
495
496
497 //
498 // Generic slot handlers.  These are set up here so that we can catch INTCXROM,
499 // INTC8ROM & SLOTC3ROM here instead of having to catch them in each slot handler.
500 //
501 uint8_t SlotR(uint16_t address)
502 {
503 //WriteLog("SlotR: address=$%04X, intCXROM=%d, slotC3ROM=%d, intC8ROM=%d\n", address, intCXROM, slotC3ROM, intC8ROM);
504         if (intCXROM)
505                 return rom[address];
506
507         uint8_t slot = (address & 0xF00) >> 8;
508         enabledSlot = slot;
509
510         if ((slotC3ROM == 0) && (slot == 3))
511         {
512                 intC8ROM = 1;
513                 return rom[address];
514         }
515
516         return (*(slotHandlerR[slot]))(address & 0xFF);
517 }
518
519
520 void SlotW(uint16_t address, uint8_t byte)
521 {
522         if (intCXROM)
523                 return;
524
525         uint8_t slot = (address & 0xF00) >> 8;
526         enabledSlot = slot;
527
528         if ((slotC3ROM == 0) && (slot == 3))
529         {
530                 intC8ROM = 1;
531                 return;
532         }
533
534         (*(slotHandlerW[slot]))(address & 0xFF, byte);
535 }
536
537
538 //
539 // Slot handling for 2K address space at $C800-$CFFF
540 //
541 uint8_t Slot2KR(uint16_t address)
542 {
543         if (intCXROM || intC8ROM)
544                 return rom[address];
545
546         return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
547 }
548
549
550 void Slot2KW(uint16_t address, uint8_t byte)
551 {
552         if (intCXROM || intC8ROM)
553                 return;
554
555         (*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
556 }
557
558
559 //
560 // Actual emulated I/O functions follow
561 //
562 uint8_t ReadKeyboard(uint16_t /*addr*/)
563 {
564         return lastKeyPressed | ((uint8_t)keyDown << 7);
565 }
566
567
568 void Switch80STORE(uint16_t address, uint8_t)
569 {
570         store80Mode = (bool)(address & 0x01);
571 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
572
573         if (store80Mode)
574         {
575                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
576                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
577         }
578         else
579         {
580                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
581                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
582         }
583 }
584
585
586 void SwitchRAMRD(uint16_t address, uint8_t)
587 {
588         ramrd = (bool)(address & 0x01);
589         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
590         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
591
592         if (store80Mode)
593                 return;
594
595         mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
596 }
597
598
599 void SwitchRAMWRT(uint16_t address, uint8_t)
600 {
601         ramwrt = (bool)(address & 0x01);
602         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
603         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
604
605         if (store80Mode)
606                 return;
607
608         mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
609 }
610
611
612 //
613 // Since any slots that aren't populated are set to read from the ROM anyway,
614 // we only concern ourselves with switching populated slots here.  (Note that
615 // the MB slot is a split ROM / I/O device, and it's taken care of in the
616 // MB handler.)
617 //
618 // N.B.: SLOTCXROM is also INTCXROM
619 //
620 void SwitchSLOTCXROM(uint16_t address, uint8_t)
621 {
622 WriteLog("Setting SLOTCXROM to %s...\n", (address & 0x01 ? "ON" : "off"));
623         intCXROM = (bool)(address & 0x01);
624
625         // INTC8ROM trumps all (only in the $C800--$CFFF range... which we don't account for yet...  :-/)
626 //      if (intC8ROM)
627 //              return;
628 #if 0
629 #if 1
630         if (intCXROM)
631         {
632                 slot3Memory = &rom[0xC300];
633                 slot6Memory = &rom[0xC600];
634         }
635         else
636         {
637                 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
638                 slot6Memory = &diskROM[0];
639         }
640 #else
641 //      slot3Memory = (intCXROM ? &rom[0xC300] : &rom[0]);
642         slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
643 #endif
644 #endif
645 }
646
647
648 void SwitchALTZP(uint16_t address, uint8_t)
649 {
650         altzp = (bool)(address & 0x01);
651         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
652         SwitchLC();
653 }
654
655 //extern bool dumpDis;
656 //
657 // The interpretation of this name is that if it's set then we access the ROM
658 // for the card actually sitting in SLOT 3 (if any)
659 //
660 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
661 {
662 //dumpDis = true;
663 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
664         slotC3ROM = (bool)(address & 0x01);
665 #if 1
666         if (intCXROM)
667                 slot3Memory = &rom[0xC300];
668         else
669                 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
670 #else
671 //      slotC3ROM = false;
672 // Seems the h/w forces this with an 80 column card in slot 3...
673         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
674 //      slot3Memory = &rom[0xC300];
675 #endif
676 }
677
678
679 /*
680 We need to see where this is being switched from; if we know that, we can switch in the appropriate ROM to $C800-$CFFF.  N.B.: Will probably need a custom handler routine, as some cards (like the Apple Hi-Speed SCSI card) split the 2K range into a 1K RAM space and a 1K bank switch ROM space.
681 */
682 //
683 // This is a problem with split ROM / I/O regions.  Because we can't do that
684 // cleanly, we have to have a read handler for this.
685 //
686 // N.B.: We could add AM_IOREAD_WRITE and AM_READ_IOWRITE to the memory handlers
687 //       to take care of split ROM / I/O regions...
688 //
689 uint8_t SwitchINTC8ROMR(uint16_t)
690 {
691 WriteLog("Hitting INTC8ROM (read)...\n");
692         intC8ROM = false;
693         return rom[0xCFFF];
694 }
695
696
697 //
698 // This resets the INTC8ROM switch (RW)
699 //
700 void SwitchINTC8ROMW(uint16_t, uint8_t)
701 {
702 WriteLog("Hitting INTC8ROM (write)...\n");
703         intC8ROM = false;
704 }
705
706
707 void Switch80COL(uint16_t address, uint8_t)
708 {
709         col80Mode = (bool)(address & 0x01);
710 }
711
712
713 void SwitchALTCHARSET(uint16_t address, uint8_t)
714 {
715         alternateCharset = (bool)(address & 0x01);
716 WriteLog("Setting ALTCHARSET to %s...\n", (alternateCharset ? "ON" : "off"));
717 }
718
719
720 uint8_t ReadKeyStrobe(uint16_t)
721 {
722         // No character data is read from here, just the 'any key was pressed'
723         // signal...
724         uint8_t byte = (uint8_t)keyDown << 7;
725         keyDown = false;
726         return byte;
727 }
728
729
730 uint8_t ReadBANK2(uint16_t)
731 {
732         return (lcState < 0x04 ? 0x80 : 0x00);
733 }
734
735
736 uint8_t ReadLCRAM(uint16_t)
737 {
738         // If bits 0 & 1 are set, but not at the same time, then it's ROM
739         uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
740         return (lcROM ? 0x00 : 0x80);
741 }
742
743
744 uint8_t ReadRAMRD(uint16_t)
745 {
746         return (uint8_t)ramrd << 7;
747 }
748
749
750 uint8_t ReadRAMWRT(uint16_t)
751 {
752         return (uint8_t)ramwrt << 7;
753 }
754
755
756 uint8_t ReadSLOTCXROM(uint16_t)
757 {
758         return (uint8_t)intCXROM << 7;
759 }
760
761
762 uint8_t ReadALTZP(uint16_t)
763 {
764         return (uint8_t)altzp << 7;
765 }
766
767
768 uint8_t ReadSLOTC3ROM(uint16_t)
769 {
770         return (uint8_t)slotC3ROM << 7;
771 }
772
773
774 uint8_t Read80STORE(uint16_t)
775 {
776         return (uint8_t)store80Mode << 7;
777 }
778
779
780 uint8_t ReadVBL(uint16_t)
781 {
782         return (uint8_t)vbl << 7;
783 }
784
785
786 uint8_t ReadTEXT(uint16_t)
787 {
788         return (uint8_t)textMode << 7;
789 }
790
791
792 uint8_t ReadMIXED(uint16_t)
793 {
794         return (uint8_t)mixedMode << 7;
795 }
796
797
798 uint8_t ReadPAGE2(uint16_t)
799 {
800         return (uint8_t)displayPage2 << 7;
801 }
802
803
804 uint8_t ReadHIRES(uint16_t)
805 {
806         return (uint8_t)hiRes << 7;
807 }
808
809
810 uint8_t ReadALTCHARSET(uint16_t)
811 {
812         return (uint8_t)alternateCharset << 7;
813 }
814
815
816 uint8_t Read80COL(uint16_t)
817 {
818         return (uint8_t)col80Mode << 7;
819 }
820
821
822 void WriteKeyStrobe(uint16_t, uint8_t)
823 {
824         keyDown = false;
825 }
826
827
828 uint8_t ReadSpeaker(uint16_t)
829 {
830         ToggleSpeaker();
831         return 0;
832 }
833
834
835 void WriteSpeaker(uint16_t, uint8_t)
836 {
837         ToggleSpeaker();
838 }
839
840
841 uint8_t SwitchLCR(uint16_t address)
842 {
843         lcState = address & 0x0B;
844         SwitchLC();
845         return 0;
846 }
847
848
849 void SwitchLCW(uint16_t address, uint8_t)
850 {
851         lcState = address & 0x0B;
852         SwitchLC();
853 }
854
855
856 void SwitchLC(void)
857 {
858         switch (lcState)
859         {
860         case 0x00:
861 #ifdef LC_DEBUG
862 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
863 #endif
864                 // [R ] Read RAM bank 2; no write
865                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
866                 lcBankMemoryW = 0;
867                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
868                 upperMemoryW = 0;
869                 break;
870         case 0x01:
871 #ifdef LC_DEBUG
872 WriteLog("SwitchLC: Read ROM, write bank 2\n");
873 #endif
874                 // [RR] Read ROM; write RAM bank 2
875                 lcBankMemoryR = &rom[0xD000];
876                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
877                 upperMemoryR = &rom[0xE000];
878                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
879                 break;
880         case 0x02:
881 #ifdef LC_DEBUG
882 WriteLog("SwitchLC: Read ROM, no write\n");
883 #endif
884                 // [R ] Read ROM; no write
885                 lcBankMemoryR = &rom[0xD000];
886                 lcBankMemoryW = 0;
887                 upperMemoryR = &rom[0xE000];
888                 upperMemoryW = 0;
889                 break;
890         case 0x03:
891 #ifdef LC_DEBUG
892 WriteLog("SwitchLC: Read/write bank 2\n");
893 #endif
894                 // [RR] Read RAM bank 2; write RAM bank 2
895                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
896                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
897                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
898                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
899                 break;
900         case 0x08:
901                 // [R ] Read RAM bank 1; no write
902                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
903                 lcBankMemoryW = 0;
904                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
905                 upperMemoryW = 0;
906                 break;
907         case 0x09:
908                 // [RR] Read ROM; write RAM bank 1
909                 lcBankMemoryR = &rom[0xD000];
910                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
911                 upperMemoryR = &rom[0xE000];
912                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
913                 break;
914         case 0x0A:
915                 // [R ] Read ROM; no write
916                 lcBankMemoryR = &rom[0xD000];
917                 lcBankMemoryW = 0;
918                 upperMemoryR = &rom[0xE000];
919                 upperMemoryW = 0;
920                 break;
921         case 0x0B:
922                 // [RR] Read RAM bank 1; write RAM bank 1
923                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
924                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
925                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
926                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
927                 break;
928         }
929 }
930
931
932 uint8_t SwitchTEXTR(uint16_t address)
933 {
934 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
935         textMode = (bool)(address & 0x01);
936         return 0;
937 }
938
939
940 void SwitchTEXTW(uint16_t address, uint8_t)
941 {
942 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
943         textMode = (bool)(address & 0x01);
944 }
945
946
947 uint8_t SwitchMIXEDR(uint16_t address)
948 {
949 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
950         mixedMode = (bool)(address & 0x01);
951         return 0;
952 }
953
954
955 void SwitchMIXEDW(uint16_t address, uint8_t)
956 {
957 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
958         mixedMode = (bool)(address & 0x01);
959 }
960
961
962 uint8_t SwitchPAGE2R(uint16_t address)
963 {
964 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
965         displayPage2 = (bool)(address & 0x01);
966
967         if (store80Mode)
968         {
969                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
970                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
971         }
972
973         return 0;
974 }
975
976
977 void SwitchPAGE2W(uint16_t address, uint8_t)
978 {
979 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
980         displayPage2 = (bool)(address & 0x01);
981
982         if (store80Mode)
983         {
984                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
985                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
986         }
987 }
988
989
990 uint8_t SwitchHIRESR(uint16_t address)
991 {
992 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
993         hiRes = (bool)(address & 0x01);
994         return 0;
995 }
996
997
998 void SwitchHIRESW(uint16_t address, uint8_t)
999 {
1000 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1001         hiRes = (bool)(address & 0x01);
1002 }
1003
1004
1005 uint8_t SwitchDHIRESR(uint16_t address)
1006 {
1007 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1008         // Hmm, this breaks convention too, like SLOTCXROM
1009         if (ioudis)
1010                 dhires = !((bool)(address & 0x01));
1011
1012         return 0;
1013 }
1014
1015
1016 void SwitchDHIRESW(uint16_t address, uint8_t)
1017 {
1018 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1019         if (ioudis)
1020                 dhires = !((bool)(address & 0x01));
1021 }
1022
1023
1024 void SwitchIOUDIS(uint16_t address, uint8_t)
1025 {
1026         ioudis = !((bool)(address & 0x01));
1027 }
1028
1029
1030 uint8_t ReadButton0(uint16_t)
1031 {
1032         return (uint8_t)openAppleDown << 7;
1033 }
1034
1035
1036 uint8_t ReadButton1(uint16_t)
1037 {
1038         return (uint8_t)closedAppleDown << 7;
1039 }
1040
1041
1042 // The way the paddles work is that a strobe is written (or read) to $C070,
1043 // then software counts down the time that it takes for the paddle outputs
1044 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1045 // stays at 1.
1046 // NB: This is really paddles 0-3, not just 0 :-P
1047 uint8_t ReadPaddle0(uint16_t)
1048 {
1049         return 0xFF;
1050 }
1051
1052
1053 uint8_t ReadIOUDIS(uint16_t)
1054 {
1055         return (uint8_t)ioudis << 7;
1056 }
1057
1058
1059 uint8_t ReadDHIRES(uint16_t)
1060 {
1061         return (uint8_t)dhires << 7;
1062 }
1063
1064
1065 // Whenever a read is done to a MMIO location that is unconnected to anything,
1066 // it actually sees the RAM access done by the video generation hardware. Some
1067 // programs exploit this, so we emulate it here.
1068
1069 // N.B.: frameCycles will be off by the true amount because this only
1070 //       increments by the amount of a speaker cycle, not the cycle count when
1071 //       the access happens... !!! FIX !!!
1072 uint8_t ReadFloatingBus(uint16_t)
1073 {
1074         // Get the currently elapsed cycle count for this frame
1075         uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1076
1077         // Make counters out of the cycle count. There are 65 cycles per line.
1078         uint32_t numLines = frameCycles / 65;
1079         uint32_t numHTicks = frameCycles - (numLines * 65);
1080
1081         // Convert these to H/V counters
1082         uint32_t hcount = numHTicks - 1;
1083
1084         // HC sees zero twice:
1085         if (hcount == 0xFFFFFFFF)
1086                 hcount = 0;
1087
1088         uint32_t vcount = numLines + 0xFA;
1089
1090         // Now do the address calculations
1091         uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1092                 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1093         uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1094
1095         // Add in particulars for the gfx mode we're in...
1096         if (textMode || (!textMode && !hiRes))
1097                 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1098                         | (!store80Mode && displayPage2 ? 0x800 : 0);
1099         else
1100                 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1101                         | (!store80Mode && displayPage2 ? 0x4000 : 0)
1102                         | ((vcount & 0x07) << 10);
1103
1104         // The address so read is *always* in main RAM, not alt RAM
1105         return ram[address];
1106 }
1107