]> Shamusworld >> Repos - apple2/blob - src/mmu.cpp
49143c090f1217fedd898020a032cb0e31b577fd
[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 Was thinking about how to make these things more self-contained, so that the management overhead would be less.  IOW, you should be able to make an object (struct) that holds everything needed to interface the MMU with itself--InstallSlotHandler *almost* does this, but not quite.  A consequence of this approach is that we would have to add generic slot I/O handlers into the mix, but that shouldn't be too horrible.  So it could be something like so:
393
394 struct Card
395 {
396         void * object;
397         uint16_t type;  // Probably an enum so we can figure out what 'object' is
398         READFUNC(slotIOR);
399         WRITEFUNC(slotIOW);
400         READFUNC(slotPageR);
401         WRITEFUNC(slotPageW);
402         READFUNC(slot2KR);
403         WRITEFUNC(slot2KW);
404 }
405
406 So instead of a bunch of crappy shit that sucks in here, we would have a simple thing like:
407
408 Card * slots[8];
409
410 to encapsulate slots.  This also makes it easier to move them around and makes things less error prone.
411
412 So maybe...
413
414
415 */
416 }
417
418
419 //
420 // Built-in functions
421 //
422 uint8_t ReadNOP(uint16_t)
423 {
424         // This is for unconnected reads, and some software looks at addresses like
425         // these.  In particular, Mr. Robot and His Robot Factory failed in that it
426         // was looking at the first byte of each slots 256 byte driver space and
427         // failing if it saw a zero there.  Now I have no idea what happens in the
428         // real hardware, but I suspect it would return something that looks like
429         // ReadFloatingBus().
430         return 0xFF;
431 }
432
433
434 void WriteNOP(uint16_t, uint8_t)
435 {
436 }
437
438
439 uint8_t ReadMemory(uint16_t address)
440 {
441 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
442         // We are guaranteed a valid address here by the setup function, so there's
443         // no need to do any checking here.
444         return (*addrPtrRead[address])[addrOffset[address]];
445 }
446
447
448 void WriteMemory(uint16_t address, uint8_t byte)
449 {
450         // We can write protect memory this way, but it adds a branch to the mix.
451         // :-/ (this can be avoided by setting up another bank of memory which we
452         //  ignore... hmm...)
453         if ((*addrPtrWrite[address]) == 0)
454                 return;
455
456         (*addrPtrWrite[address])[addrOffset[address]] = byte;
457 }
458
459
460 //
461 // The main memory access functions used by V65C02
462 //
463 uint8_t AppleReadMem(uint16_t address)
464 {
465 #if 0
466 if (address == 0xD4 || address == 0xAC20)
467         WriteLog("Reading $%X...\n", address);
468 #endif
469 #if 0
470         uint8_t memRead = (*(funcMapRead[address]))(address);
471 static uint16_t lastAddr = 0;
472 static uint32_t lastCount = 0;
473 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
474 {
475         if (lastAddr == address)
476                 lastCount++;
477         else
478         {
479                 if (lastCount > 1)
480                         WriteLog("%d times...\n", lastCount);
481
482                 WriteLog("Reading $%02X from $%X ($%02X, $%02X)\n", memRead, address, diskROM[1], rom[0xC601]);
483                 lastCount = 1;
484                 lastAddr = address;
485         }
486 }
487         return memRead;
488 #else
489         return (*(funcMapRead[address]))(address);
490 #endif
491 }
492
493
494 void AppleWriteMem(uint16_t address, uint8_t byte)
495 {
496 #if 0
497 static uint16_t lastAddr = 0;
498 static uint32_t lastCount = 0;
499 if ((address > 0xC000 && address < 0xC100) || address == 0xC601)
500 {
501         if (lastAddr == address)
502                 lastCount++;
503         else
504         {
505                 if (lastCount > 1)
506                         WriteLog("%d times...\n", lastCount);
507
508                 WriteLog("Writing to $%X\n", address);
509                 lastCount = 1;
510                 lastAddr = address;
511         }
512 }
513 #endif
514 #if 0
515 if (address == 0xD4 || address == 0xAC20)
516         WriteLog("Writing $%02X @ $%X...\n", byte, address);
517 #endif
518 #if 0
519 //if (address >= 0x0827 && address <= 0x082A)
520 if (address == 0x000D)
521         WriteLog("Writing $%02X @ $%X (PC=$%04X)...\n", byte, address, mainCPU.pc);
522 #endif
523         (*(funcMapWrite[address]))(address, byte);
524 }
525
526
527 //
528 // Generic slot handlers.  These are set up here so that we can catch INTCXROM,
529 // INTC8ROM & SLOTC3ROM here instead of having to catch them in each slot handler.
530 //
531 uint8_t SlotR(uint16_t address)
532 {
533 //WriteLog("SlotR: address=$%04X, intCXROM=%d, slotC3ROM=%d, intC8ROM=%d\n", address, intCXROM, slotC3ROM, intC8ROM);
534         if (intCXROM)
535                 return rom[address];
536
537         uint8_t slot = (address & 0xF00) >> 8;
538         enabledSlot = slot;
539
540         if ((slotC3ROM == 0) && (slot == 3))
541         {
542                 intC8ROM = 1;
543                 return rom[address];
544         }
545
546         return (*(slotHandlerR[slot]))(address & 0xFF);
547 }
548
549
550 void SlotW(uint16_t address, uint8_t byte)
551 {
552         if (intCXROM)
553                 return;
554
555         uint8_t slot = (address & 0xF00) >> 8;
556         enabledSlot = slot;
557
558         if ((slotC3ROM == 0) && (slot == 3))
559         {
560                 intC8ROM = 1;
561                 return;
562         }
563
564         (*(slotHandlerW[slot]))(address & 0xFF, byte);
565 }
566
567
568 //
569 // Slot handling for 2K address space at $C800-$CFFF
570 //
571 uint8_t Slot2KR(uint16_t address)
572 {
573         if (intCXROM || intC8ROM)
574                 return rom[address];
575
576         return (*(slotHandler2KR[enabledSlot]))(address & 0x7FF);
577 }
578
579
580 void Slot2KW(uint16_t address, uint8_t byte)
581 {
582         if (intCXROM || intC8ROM)
583                 return;
584
585         (*(slotHandler2KW[enabledSlot]))(address & 0x7FF, byte);
586 }
587
588
589 //
590 // Actual emulated I/O functions follow
591 //
592 uint8_t ReadKeyboard(uint16_t /*addr*/)
593 {
594         return lastKeyPressed | ((uint8_t)keyDown << 7);
595 }
596
597
598 void Switch80STORE(uint16_t address, uint8_t)
599 {
600         store80Mode = (bool)(address & 0x01);
601 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
602
603         if (store80Mode)
604         {
605                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
606                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
607         }
608         else
609         {
610                 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
611                 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
612         }
613 }
614
615
616 void SwitchRAMRD(uint16_t address, uint8_t)
617 {
618         ramrd = (bool)(address & 0x01);
619         mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
620         mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
621
622         if (store80Mode)
623                 return;
624
625         mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
626 }
627
628
629 void SwitchRAMWRT(uint16_t address, uint8_t)
630 {
631         ramwrt = (bool)(address & 0x01);
632         mainMemoryW = (ramwrt ?  &ram2[0x0200] : &ram[0x0200]);
633         mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
634
635         if (store80Mode)
636                 return;
637
638         mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
639 }
640
641
642 //
643 // Since any slots that aren't populated are set to read from the ROM anyway,
644 // we only concern ourselves with switching populated slots here.  (Note that
645 // the MB slot is a split ROM / I/O device, and it's taken care of in the
646 // MB handler.)
647 //
648 // N.B.: SLOTCXROM is also INTCXROM
649 //
650 void SwitchSLOTCXROM(uint16_t address, uint8_t)
651 {
652 WriteLog("Setting SLOTCXROM to %s...\n", (address & 0x01 ? "ON" : "off"));
653         intCXROM = (bool)(address & 0x01);
654
655         // INTC8ROM trumps all (only in the $C800--$CFFF range... which we don't account for yet...  :-/)
656 //      if (intC8ROM)
657 //              return;
658 #if 0
659 #if 1
660         if (intCXROM)
661         {
662                 slot3Memory = &rom[0xC300];
663                 slot6Memory = &rom[0xC600];
664         }
665         else
666         {
667                 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
668                 slot6Memory = &diskROM[0];
669         }
670 #else
671 //      slot3Memory = (intCXROM ? &rom[0xC300] : &rom[0]);
672         slot6Memory = (intCXROM ? &rom[0xC600] : &diskROM[0]);
673 #endif
674 #endif
675 }
676
677
678 void SwitchALTZP(uint16_t address, uint8_t)
679 {
680         altzp = (bool)(address & 0x01);
681         pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
682         SwitchLC();
683 }
684
685 //extern bool dumpDis;
686 //
687 // The interpretation of this name is that if it's set then we access the ROM
688 // for the card actually sitting in SLOT 3 (if any)
689 //
690 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
691 {
692 //dumpDis = true;
693 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
694         slotC3ROM = (bool)(address & 0x01);
695 #if 1
696         if (intCXROM)
697                 slot3Memory = &rom[0xC300];
698         else
699                 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
700 #else
701 //      slotC3ROM = false;
702 // Seems the h/w forces this with an 80 column card in slot 3...
703         slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
704 //      slot3Memory = &rom[0xC300];
705 #endif
706 }
707
708
709 /*
710 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.
711 */
712 //
713 // This is a problem with split ROM / I/O regions.  Because we can't do that
714 // cleanly, we have to have a read handler for this.
715 //
716 // N.B.: We could add AM_IOREAD_WRITE and AM_READ_IOWRITE to the memory handlers
717 //       to take care of split ROM / I/O regions...
718 //
719 uint8_t SwitchINTC8ROMR(uint16_t)
720 {
721 WriteLog("Hitting INTC8ROM (read)...\n");
722         intC8ROM = false;
723         return rom[0xCFFF];
724 }
725
726
727 //
728 // This resets the INTC8ROM switch (RW)
729 //
730 void SwitchINTC8ROMW(uint16_t, uint8_t)
731 {
732 WriteLog("Hitting INTC8ROM (write)...\n");
733         intC8ROM = false;
734 }
735
736
737 void Switch80COL(uint16_t address, uint8_t)
738 {
739         col80Mode = (bool)(address & 0x01);
740 }
741
742
743 void SwitchALTCHARSET(uint16_t address, uint8_t)
744 {
745         alternateCharset = (bool)(address & 0x01);
746 WriteLog("Setting ALTCHARSET to %s...\n", (alternateCharset ? "ON" : "off"));
747 }
748
749
750 uint8_t ReadKeyStrobe(uint16_t)
751 {
752         // No character data is read from here, just the 'any key was pressed'
753         // signal...
754         uint8_t byte = (uint8_t)keyDown << 7;
755         keyDown = false;
756         return byte;
757 }
758
759
760 uint8_t ReadBANK2(uint16_t)
761 {
762         return (lcState < 0x04 ? 0x80 : 0x00);
763 }
764
765
766 uint8_t ReadLCRAM(uint16_t)
767 {
768         // If bits 0 & 1 are set, but not at the same time, then it's ROM
769         uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
770         return (lcROM ? 0x00 : 0x80);
771 }
772
773
774 uint8_t ReadRAMRD(uint16_t)
775 {
776         return (uint8_t)ramrd << 7;
777 }
778
779
780 uint8_t ReadRAMWRT(uint16_t)
781 {
782         return (uint8_t)ramwrt << 7;
783 }
784
785
786 uint8_t ReadSLOTCXROM(uint16_t)
787 {
788         return (uint8_t)intCXROM << 7;
789 }
790
791
792 uint8_t ReadALTZP(uint16_t)
793 {
794         return (uint8_t)altzp << 7;
795 }
796
797
798 uint8_t ReadSLOTC3ROM(uint16_t)
799 {
800         return (uint8_t)slotC3ROM << 7;
801 }
802
803
804 uint8_t Read80STORE(uint16_t)
805 {
806         return (uint8_t)store80Mode << 7;
807 }
808
809
810 uint8_t ReadVBL(uint16_t)
811 {
812         return (uint8_t)vbl << 7;
813 }
814
815
816 uint8_t ReadTEXT(uint16_t)
817 {
818         return (uint8_t)textMode << 7;
819 }
820
821
822 uint8_t ReadMIXED(uint16_t)
823 {
824         return (uint8_t)mixedMode << 7;
825 }
826
827
828 uint8_t ReadPAGE2(uint16_t)
829 {
830         return (uint8_t)displayPage2 << 7;
831 }
832
833
834 uint8_t ReadHIRES(uint16_t)
835 {
836         return (uint8_t)hiRes << 7;
837 }
838
839
840 uint8_t ReadALTCHARSET(uint16_t)
841 {
842         return (uint8_t)alternateCharset << 7;
843 }
844
845
846 uint8_t Read80COL(uint16_t)
847 {
848         return (uint8_t)col80Mode << 7;
849 }
850
851
852 void WriteKeyStrobe(uint16_t, uint8_t)
853 {
854         keyDown = false;
855 }
856
857
858 uint8_t ReadSpeaker(uint16_t)
859 {
860         ToggleSpeaker();
861         return 0;
862 }
863
864
865 void WriteSpeaker(uint16_t, uint8_t)
866 {
867         ToggleSpeaker();
868 }
869
870
871 uint8_t SwitchLCR(uint16_t address)
872 {
873         lcState = address & 0x0B;
874         SwitchLC();
875         return 0;
876 }
877
878
879 void SwitchLCW(uint16_t address, uint8_t)
880 {
881         lcState = address & 0x0B;
882         SwitchLC();
883 }
884
885
886 void SwitchLC(void)
887 {
888         switch (lcState)
889         {
890         case 0x00:
891 #ifdef LC_DEBUG
892 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
893 #endif
894                 // [R ] Read RAM bank 2; no write
895                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
896                 lcBankMemoryW = 0;
897                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
898                 upperMemoryW = 0;
899                 break;
900         case 0x01:
901 #ifdef LC_DEBUG
902 WriteLog("SwitchLC: Read ROM, write bank 2\n");
903 #endif
904                 // [RR] Read ROM; write RAM bank 2
905                 lcBankMemoryR = &rom[0xD000];
906                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
907                 upperMemoryR = &rom[0xE000];
908                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
909                 break;
910         case 0x02:
911 #ifdef LC_DEBUG
912 WriteLog("SwitchLC: Read ROM, no write\n");
913 #endif
914                 // [R ] Read ROM; no write
915                 lcBankMemoryR = &rom[0xD000];
916                 lcBankMemoryW = 0;
917                 upperMemoryR = &rom[0xE000];
918                 upperMemoryW = 0;
919                 break;
920         case 0x03:
921 #ifdef LC_DEBUG
922 WriteLog("SwitchLC: Read/write bank 2\n");
923 #endif
924                 // [RR] Read RAM bank 2; write RAM bank 2
925                 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
926                 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
927                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
928                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
929                 break;
930         case 0x08:
931                 // [R ] Read RAM bank 1; no write
932                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
933                 lcBankMemoryW = 0;
934                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
935                 upperMemoryW = 0;
936                 break;
937         case 0x09:
938                 // [RR] Read ROM; write RAM bank 1
939                 lcBankMemoryR = &rom[0xD000];
940                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
941                 upperMemoryR = &rom[0xE000];
942                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
943                 break;
944         case 0x0A:
945                 // [R ] Read ROM; no write
946                 lcBankMemoryR = &rom[0xD000];
947                 lcBankMemoryW = 0;
948                 upperMemoryR = &rom[0xE000];
949                 upperMemoryW = 0;
950                 break;
951         case 0x0B:
952                 // [RR] Read RAM bank 1; write RAM bank 1
953                 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
954                 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
955                 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
956                 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
957                 break;
958         }
959 }
960
961
962 uint8_t SwitchTEXTR(uint16_t address)
963 {
964 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
965         textMode = (bool)(address & 0x01);
966         return 0;
967 }
968
969
970 void SwitchTEXTW(uint16_t address, uint8_t)
971 {
972 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
973         textMode = (bool)(address & 0x01);
974 }
975
976
977 uint8_t SwitchMIXEDR(uint16_t address)
978 {
979 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
980         mixedMode = (bool)(address & 0x01);
981         return 0;
982 }
983
984
985 void SwitchMIXEDW(uint16_t address, uint8_t)
986 {
987 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
988         mixedMode = (bool)(address & 0x01);
989 }
990
991
992 uint8_t SwitchPAGE2R(uint16_t address)
993 {
994 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
995         displayPage2 = (bool)(address & 0x01);
996
997         if (store80Mode)
998         {
999                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
1000                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
1001         }
1002
1003         return 0;
1004 }
1005
1006
1007 void SwitchPAGE2W(uint16_t address, uint8_t)
1008 {
1009 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
1010         displayPage2 = (bool)(address & 0x01);
1011
1012         if (store80Mode)
1013         {
1014                 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
1015                 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
1016         }
1017 }
1018
1019
1020 uint8_t SwitchHIRESR(uint16_t address)
1021 {
1022 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1023         hiRes = (bool)(address & 0x01);
1024         return 0;
1025 }
1026
1027
1028 void SwitchHIRESW(uint16_t address, uint8_t)
1029 {
1030 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
1031         hiRes = (bool)(address & 0x01);
1032 }
1033
1034
1035 uint8_t SwitchDHIRESR(uint16_t address)
1036 {
1037 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1038         // Hmm, this breaks convention too, like SLOTCXROM
1039         if (ioudis)
1040                 dhires = !((bool)(address & 0x01));
1041
1042         return 0;
1043 }
1044
1045
1046 void SwitchDHIRESW(uint16_t address, uint8_t)
1047 {
1048 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
1049         if (ioudis)
1050                 dhires = !((bool)(address & 0x01));
1051 }
1052
1053
1054 void SwitchIOUDIS(uint16_t address, uint8_t)
1055 {
1056         ioudis = !((bool)(address & 0x01));
1057 }
1058
1059
1060 uint8_t ReadButton0(uint16_t)
1061 {
1062         return (uint8_t)openAppleDown << 7;
1063 }
1064
1065
1066 uint8_t ReadButton1(uint16_t)
1067 {
1068         return (uint8_t)closedAppleDown << 7;
1069 }
1070
1071
1072 // The way the paddles work is that a strobe is written (or read) to $C070,
1073 // then software counts down the time that it takes for the paddle outputs
1074 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1075 // stays at 1.
1076 // NB: This is really paddles 0-3, not just 0 :-P
1077 uint8_t ReadPaddle0(uint16_t)
1078 {
1079         return 0xFF;
1080 }
1081
1082
1083 uint8_t ReadIOUDIS(uint16_t)
1084 {
1085         return (uint8_t)ioudis << 7;
1086 }
1087
1088
1089 uint8_t ReadDHIRES(uint16_t)
1090 {
1091         return (uint8_t)dhires << 7;
1092 }
1093
1094
1095 // Whenever a read is done to a MMIO location that is unconnected to anything,
1096 // it actually sees the RAM access done by the video generation hardware. Some
1097 // programs exploit this, so we emulate it here.
1098
1099 // N.B.: frameCycles will be off by the true amount because this only
1100 //       increments by the amount of a speaker cycle, not the cycle count when
1101 //       the access happens... !!! FIX !!!
1102 uint8_t ReadFloatingBus(uint16_t)
1103 {
1104         // Get the currently elapsed cycle count for this frame
1105         uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1106
1107         // Make counters out of the cycle count. There are 65 cycles per line.
1108         uint32_t numLines = frameCycles / 65;
1109         uint32_t numHTicks = frameCycles - (numLines * 65);
1110
1111         // Convert these to H/V counters
1112         uint32_t hcount = numHTicks - 1;
1113
1114         // HC sees zero twice:
1115         if (hcount == 0xFFFFFFFF)
1116                 hcount = 0;
1117
1118         uint32_t vcount = numLines + 0xFA;
1119
1120         // Now do the address calculations
1121         uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1122                 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1123         uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1124
1125         // Add in particulars for the gfx mode we're in...
1126         if (textMode || (!textMode && !hiRes))
1127                 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1128                         | (!store80Mode && displayPage2 ? 0x800 : 0);
1129         else
1130                 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1131                         | (!store80Mode && displayPage2 ? 0x4000 : 0)
1132                         | ((vcount & 0x07) << 10);
1133
1134         // The address so read is *always* in main RAM, not alt RAM
1135         return ram[address];
1136 }
1137