2 // mmu.cpp: Memory management
5 // (C) 2013-2018 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 09/27/2013 Created this file
19 #include "mos6522via.h"
27 // Address Map enumeration
28 enum { AM_RAM, AM_ROM, AM_BANKED, AM_READ, AM_WRITE, AM_READ_WRITE, AM_END_OF_LIST };
30 // Macros for function pointers
31 #define READFUNC(x) uint8_t (* x)(uint16_t)
32 #define WRITEFUNC(x) void (* x)(uint16_t, uint8_t)
35 uint8_t ** addrPtrRead[0x10000];
36 uint8_t ** addrPtrWrite[0x10000];
37 uint16_t addrOffset[0x10000];
39 READFUNC(funcMapRead[0x10000]);
40 WRITEFUNC(funcMapWrite[0x10000]);
53 #define ADDRESS_MAP_END { 0x0000, 0x0000, AM_END_OF_LIST, 0, 0, 0, 0 }
55 // Dunno if I like this approach or not...
57 // AM_RANGE(0x0000, 0xBFFF) AM_RAM AM_BASE(ram) AM_SHARE(1)
58 // AM_RANGE(0xC000, 0xC001) AM_READWRITE(readFunc, writeFunc)
61 // Would need a pointer for 80STORE as well...
63 uint8_t * pageZeroMemory = &ram[0x0000]; // $0000 - $01FF
64 uint8_t * mainMemoryR = &ram[0x0200]; // $0200 - $BFFF (read)
65 uint8_t * mainMemoryW = &ram[0x0200]; // $0200 - $BFFF (write)
67 uint8_t * mainMemoryTextR = &ram[0x0400]; // $0400 - $07FF (read)
68 uint8_t * mainMemoryTextW = &ram[0x0400]; // $0400 - $07FF (write)
69 uint8_t * mainMemoryHGRR = &ram[0x2000]; // $2000 - $3FFF (read)
70 uint8_t * mainMemoryHGRW = &ram[0x2000]; // $2000 - $3FFF (write)
72 uint8_t * slotMemory = &rom[0xC100]; // $C100 - $CFFF
73 uint8_t * slot3Memory = &rom[0xC300]; // $C300 - $C3FF
74 uint8_t * slot4Memory = &rom[0xC400]; // $C400 - $C4FF
75 uint8_t * slot6Memory = &diskROM[0]; // $C600 - $C6FF
76 uint8_t * lcBankMemoryR = &ram[0xD000]; // $D000 - $DFFF (read)
77 uint8_t * lcBankMemoryW = &ram[0xD000]; // $D000 - $DFFF (write)
78 uint8_t * upperMemoryR = &ram[0xE000]; // $E000 - $FFFF (read)
79 uint8_t * upperMemoryW = &ram[0xE000]; // $E000 - $FFFF (write)
82 // Function prototypes
83 uint8_t ReadNOP(uint16_t);
84 void WriteNOP(uint16_t, uint8_t);
85 uint8_t ReadMemory(uint16_t);
86 void WriteMemory(uint16_t, uint8_t);
87 uint8_t ReadKeyboard(uint16_t);
88 void Switch80STORE(uint16_t, uint8_t);
89 void SwitchRAMRD(uint16_t, uint8_t);
90 void SwitchRAMWRT(uint16_t, uint8_t);
91 void SwitchSLOTCXROM(uint16_t, uint8_t);
92 void SwitchALTZP(uint16_t, uint8_t);
93 void SwitchSLOTC3ROM(uint16_t, uint8_t);
94 void Switch80COL(uint16_t, uint8_t);
95 void SwitchALTCHARSET(uint16_t, uint8_t);
96 uint8_t ReadKeyStrobe(uint16_t);
97 uint8_t ReadBANK2(uint16_t);
98 uint8_t ReadLCRAM(uint16_t);
99 uint8_t ReadRAMRD(uint16_t);
100 uint8_t ReadRAMWRT(uint16_t);
101 uint8_t ReadSLOTCXROM(uint16_t);
102 uint8_t ReadALTZP(uint16_t);
103 uint8_t ReadSLOTC3ROM(uint16_t);
104 uint8_t Read80STORE(uint16_t);
105 uint8_t ReadVBL(uint16_t);
106 uint8_t ReadTEXT(uint16_t);
107 uint8_t ReadMIXED(uint16_t);
108 uint8_t ReadPAGE2(uint16_t);
109 uint8_t ReadHIRES(uint16_t);
110 uint8_t ReadALTCHARSET(uint16_t);
111 uint8_t Read80COL(uint16_t);
112 void WriteKeyStrobe(uint16_t, uint8_t);
113 uint8_t ReadSpeaker(uint16_t);
114 void WriteSpeaker(uint16_t, uint8_t);
115 uint8_t SwitchLCR(uint16_t);
116 void SwitchLCW(uint16_t, uint8_t);
118 uint8_t SwitchTEXTR(uint16_t);
119 void SwitchTEXTW(uint16_t, uint8_t);
120 uint8_t SwitchMIXEDR(uint16_t);
121 void SwitchMIXEDW(uint16_t, uint8_t);
122 uint8_t SwitchPAGE2R(uint16_t);
123 void SwitchPAGE2W(uint16_t, uint8_t);
124 uint8_t SwitchHIRESR(uint16_t);
125 void SwitchHIRESW(uint16_t, uint8_t);
126 uint8_t SwitchDHIRESR(uint16_t);
127 void SwitchDHIRESW(uint16_t, uint8_t);
128 void SwitchIOUDIS(uint16_t, uint8_t);
129 uint8_t Slot6R(uint16_t);
130 void Slot6W(uint16_t, uint8_t);
131 void HandleSlot6(uint16_t, uint8_t);
132 uint8_t MBRead(uint16_t);
133 void MBWrite(uint16_t, uint8_t);
134 uint8_t ReadButton0(uint16_t);
135 uint8_t ReadButton1(uint16_t);
136 uint8_t ReadPaddle0(uint16_t);
137 uint8_t ReadIOUDIS(uint16_t);
138 uint8_t ReadDHIRES(uint16_t);
139 uint8_t ReadFloatingBus(uint16_t);
140 //uint8_t SwitchR(uint16_t);
141 //void SwitchW(uint16_t, uint8_t);
144 // The main Apple //e memory map
145 AddressMap memoryMap[] = {
146 { 0x0000, 0x01FF, AM_RAM, &pageZeroMemory, 0, 0, 0 },
147 { 0x0200, 0xBFFF, AM_BANKED, &mainMemoryR, &mainMemoryW, 0, 0 },
149 // These will overlay over the previously written memory accessors
150 { 0x0400, 0x07FF, AM_BANKED, &mainMemoryTextR, &mainMemoryTextW, 0, 0 },
151 { 0x2000, 0x3FFF, AM_BANKED, &mainMemoryHGRR, &mainMemoryHGRW, 0, 0 },
153 { 0xC000, 0xC001, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80STORE },
154 { 0xC002, 0xC003, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMRD },
155 { 0xC004, 0xC005, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchRAMWRT },
156 { 0xC006, 0xC007, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTCXROM },
157 { 0xC008, 0xC009, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTZP },
158 { 0xC00A, 0xC00B, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchSLOTC3ROM },
159 { 0xC00C, 0xC00D, AM_READ_WRITE, 0, 0, ReadKeyboard, Switch80COL },
160 { 0xC00E, 0xC00F, AM_READ_WRITE, 0, 0, ReadKeyboard, SwitchALTCHARSET },
161 { 0xC010, 0xC010, AM_READ_WRITE, 0, 0, ReadKeyStrobe, WriteKeyStrobe },
162 { 0xC011, 0xC011, AM_READ_WRITE, 0, 0, ReadBANK2, WriteKeyStrobe },
163 { 0xC012, 0xC012, AM_READ_WRITE, 0, 0, ReadLCRAM, WriteKeyStrobe },
164 { 0xC013, 0xC013, AM_READ_WRITE, 0, 0, ReadRAMRD, WriteKeyStrobe },
165 { 0xC014, 0xC014, AM_READ_WRITE, 0, 0, ReadRAMWRT, WriteKeyStrobe },
166 { 0xC015, 0xC015, AM_READ_WRITE, 0, 0, ReadSLOTCXROM, WriteKeyStrobe },
167 { 0xC016, 0xC016, AM_READ_WRITE, 0, 0, ReadALTZP, WriteKeyStrobe },
168 { 0xC017, 0xC017, AM_READ_WRITE, 0, 0, ReadSLOTC3ROM, WriteKeyStrobe },
169 { 0xC018, 0xC018, AM_READ_WRITE, 0, 0, Read80STORE, WriteKeyStrobe },
170 { 0xC019, 0xC019, AM_READ_WRITE, 0, 0, ReadVBL, WriteKeyStrobe },
171 { 0xC01A, 0xC01A, AM_READ_WRITE, 0, 0, ReadTEXT, WriteKeyStrobe },
172 { 0xC01B, 0xC01B, AM_READ_WRITE, 0, 0, ReadMIXED, WriteKeyStrobe },
173 { 0xC01C, 0xC01C, AM_READ_WRITE, 0, 0, ReadPAGE2, WriteKeyStrobe },
174 { 0xC01D, 0xC01D, AM_READ_WRITE, 0, 0, ReadHIRES, WriteKeyStrobe },
175 { 0xC01E, 0xC01E, AM_READ_WRITE, 0, 0, ReadALTCHARSET, WriteKeyStrobe },
176 { 0xC01F, 0xC01F, AM_READ_WRITE, 0, 0, Read80COL, WriteKeyStrobe },
177 // $C020 is "Cassette Out (RO)"
178 { 0xC020, 0xC02F, AM_READ, 0, 0, ReadFloatingBus, 0 },
179 // May have to put a "floating bus" read there... :-/
180 // Apparently, video RAM is put on 'non-responding address'. So will
181 // need to time those out.
182 // So... $C020-$C08F, when read, return video data.
183 // $C090-$C7FF do also, as long as the slot the range refers to is empty
184 // and last and least is $CFFF, which is the Expansion ROM disable.
185 { 0xC030, 0xC03F, AM_READ_WRITE, 0, 0, ReadSpeaker, WriteSpeaker },
186 { 0xC050, 0xC051, AM_READ_WRITE, 0, 0, SwitchTEXTR, SwitchTEXTW },
187 { 0xC052, 0xC053, AM_READ_WRITE, 0, 0, SwitchMIXEDR, SwitchMIXEDW },
188 { 0xC054, 0xC055, AM_READ_WRITE, 0, 0, SwitchPAGE2R, SwitchPAGE2W },
189 { 0xC056, 0xC057, AM_READ_WRITE, 0, 0, SwitchHIRESR, SwitchHIRESW },
190 { 0xC05E, 0xC05F, AM_READ_WRITE, 0, 0, SwitchDHIRESR, SwitchDHIRESW },
191 { 0xC061, 0xC061, AM_READ, 0, 0, ReadButton0, 0 },
192 { 0xC062, 0xC062, AM_READ, 0, 0, ReadButton1, 0 },
193 { 0xC064, 0xC067, AM_READ, 0, 0, ReadPaddle0, 0 },
194 // { 0xC07E, 0xC07F, AM_READ_WRITE, 0, 0, SwitchIOUDISR, SwitchIOUDISW },
195 { 0xC07E, 0xC07E, AM_READ_WRITE, 0, 0, ReadIOUDIS, SwitchIOUDIS },
196 { 0xC07F, 0xC07F, AM_READ_WRITE, 0, 0, ReadDHIRES, SwitchIOUDIS },
197 { 0xC080, 0xC08F, AM_READ_WRITE, 0, 0, SwitchLCR, SwitchLCW },
198 { 0xC0E0, 0xC0EF, AM_READ_WRITE, 0, 0, Slot6R, Slot6W },
199 { 0xC100, 0xCFFF, AM_ROM, &slotMemory, 0, 0, 0 },
201 // This will overlay the slotMemory accessors for slot 6 ROM
202 { 0xC300, 0xC3FF, AM_ROM, &slot3Memory, 0, 0, 0 },
203 { 0xC600, 0xC6FF, AM_ROM, &slot6Memory, 0, 0, 0 },
204 { 0xC400, 0xC4FF, AM_READ_WRITE, 0, 0, MBRead, MBWrite },
206 { 0xD000, 0xDFFF, AM_BANKED, &lcBankMemoryR, &lcBankMemoryW, 0, 0 },
207 { 0xE000, 0xFFFF, AM_BANKED, &upperMemoryR, &upperMemoryW, 0, 0 },
212 void SetupAddressMap(void)
214 for(uint32_t i=0; i<0x10000; i++)
216 funcMapRead[i] = ReadNOP;
217 funcMapWrite[i] = WriteNOP;
225 while (memoryMap[i].type != AM_END_OF_LIST)
227 switch (memoryMap[i].type)
230 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
232 funcMapRead[j] = ReadMemory;
233 funcMapWrite[j] = WriteMemory;
234 addrPtrRead[j] = memoryMap[i].memory;
235 addrPtrWrite[j] = memoryMap[i].memory;
236 addrOffset[j] = j - memoryMap[i].start;
237 //WriteLog("SetupAddressMap: j=$%04X, addrOffset[j]=$%04X\n", j, addrOffset[j]);
242 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
244 funcMapRead[j] = ReadMemory;
245 addrPtrRead[j] = memoryMap[i].memory;
246 addrOffset[j] = j - memoryMap[i].start;
251 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
253 funcMapRead[j] = ReadMemory;
254 funcMapWrite[j] = WriteMemory;
255 addrPtrRead[j] = memoryMap[i].memory;
256 addrPtrWrite[j] = memoryMap[i].altMemory;
257 addrOffset[j] = j - memoryMap[i].start;
262 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
263 funcMapRead[j] = memoryMap[i].read;
267 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
268 funcMapWrite[j] = memoryMap[i].write;
272 for(uint32_t j=memoryMap[i].start; j<=memoryMap[i].end; j++)
274 funcMapRead[j] = memoryMap[i].read;
275 funcMapWrite[j] = memoryMap[i].write;
284 // This should correctly set up the LC pointers, but it doesn't
285 // for some reason... :-/
286 // It's because we were storing pointers directly, instead of pointers
287 // to the pointer... It's complicated. :-)
293 // Reset the MMU state after a power down event
295 void ResetMMUPointers(void)
299 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
300 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
304 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
305 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
308 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
309 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
310 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
311 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
313 slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
314 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
315 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
321 // Built-in functions
323 uint8_t ReadNOP(uint16_t)
329 void WriteNOP(uint16_t, uint8_t)
334 uint8_t ReadMemory(uint16_t address)
336 //WriteLog("ReadMemory: addr=$%04X, addrPtrRead[addr]=$%X, addrOffset[addr]=$%X, val=$%02X\n", address, addrPtrRead[address], addrOffset[address], addrPtrRead[address][addrOffset[address]]);
337 // We are guaranteed a valid address here by the setup function, so there's
338 // no need to do any checking here.
339 return (*addrPtrRead[address])[addrOffset[address]];
343 void WriteMemory(uint16_t address, uint8_t byte)
345 // We can write protect memory this way, but it adds a branch to the mix.
346 // :-/ (this can be avoided by setting up another bank of memory which we
348 if ((*addrPtrWrite[address]) == 0)
351 (*addrPtrWrite[address])[addrOffset[address]] = byte;
356 // The main memory access functions used by V65C02
358 uint8_t AppleReadMem(uint16_t address)
360 return (*(funcMapRead[address]))(address);
364 void AppleWriteMem(uint16_t address, uint8_t byte)
366 (*(funcMapWrite[address]))(address, byte);
371 // Actual emulated I/O functions follow
373 uint8_t ReadKeyboard(uint16_t /*addr*/)
375 return lastKeyPressed | ((uint8_t)keyDown << 7);
379 void Switch80STORE(uint16_t address, uint8_t)
381 store80Mode = (bool)(address & 0x01);
382 WriteLog("Setting 80STORE to %s...\n", (store80Mode ? "ON" : "off"));
386 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
387 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
391 mainMemoryTextR = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
392 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
397 void SwitchRAMRD(uint16_t address, uint8_t)
399 ramrd = (bool)(address & 0x01);
400 mainMemoryR = (ramrd ? &ram2[0x0200] : &ram[0x0200]);
401 mainMemoryHGRR = (ramrd ? &ram2[0x2000] : &ram[0x2000]);
406 mainMemoryTextR = (ramrd ? &ram2[0x0400] : &ram[0x0400]);
410 void SwitchRAMWRT(uint16_t address, uint8_t)
412 ramwrt = (bool)(address & 0x01);
413 mainMemoryW = (ramwrt ? &ram2[0x0200] : &ram[0x0200]);
414 mainMemoryHGRW = (ramwrt ? &ram2[0x2000] : &ram[0x2000]);
419 mainMemoryTextW = (ramwrt ? &ram2[0x0400] : &ram[0x0400]);
423 void SwitchSLOTCXROM(uint16_t address, uint8_t)
425 //WriteLog("Setting SLOTCXROM to %s...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"));
426 // This is the only soft switch that breaks the usual convention.
427 slotCXROM = !((bool)(address & 0x01));
428 // slot3Memory = (slotCXROM ? &rom[0] : &rom[0xC300]);
429 slot6Memory = (slotCXROM ? &diskROM[0] : &rom[0xC600]);
433 void SwitchALTZP(uint16_t address, uint8_t)
435 altzp = (bool)(address & 0x01);
436 pageZeroMemory = (altzp ? &ram2[0x0000] : &ram[0x0000]);
440 //extern bool dumpDis;
442 void SwitchSLOTC3ROM(uint16_t address, uint8_t)
445 //WriteLog("Setting SLOTC3ROM to %s...\n", (address & 0x01 ? "ON" : "off"));
446 slotC3ROM = (bool)(address & 0x01);
447 // slotC3ROM = false;
448 // Seems the h/w forces this with an 80 column card in slot 3...
449 slot3Memory = (slotC3ROM ? &rom[0] : &rom[0xC300]);
450 // slot3Memory = &rom[0xC300];
454 void Switch80COL(uint16_t address, uint8_t)
456 col80Mode = (bool)(address & 0x01);
460 void SwitchALTCHARSET(uint16_t address, uint8_t)
462 alternateCharset = (bool)(address & 0x01);
466 uint8_t ReadKeyStrobe(uint16_t)
468 // No character data is read from here, just the 'any key was pressed' signal...
469 // uint8_t byte = lastKeyPressed | ((uint8_t)keyDown << 7);
470 uint8_t byte = (uint8_t)keyDown << 7;
476 uint8_t ReadBANK2(uint16_t)
478 return (lcState < 0x04 ? 0x80 : 0x00);
482 uint8_t ReadLCRAM(uint16_t)
484 // If bits 0 & 1 are set, but not at the same time, then it's ROM
485 uint8_t lcROM = (lcState & 0x1) ^ ((lcState & 0x02) >> 1);
486 return (lcROM ? 0x00 : 0x80);
490 uint8_t ReadRAMRD(uint16_t)
492 return (uint8_t)ramrd << 7;
496 uint8_t ReadRAMWRT(uint16_t)
498 return (uint8_t)ramwrt << 7;
502 uint8_t ReadSLOTCXROM(uint16_t)
504 return (uint8_t)slotCXROM << 7;
508 uint8_t ReadALTZP(uint16_t)
510 return (uint8_t)altzp << 7;
514 uint8_t ReadSLOTC3ROM(uint16_t)
517 return (uint8_t)slotC3ROM << 7;
521 uint8_t Read80STORE(uint16_t)
523 return (uint8_t)store80Mode << 7;
527 uint8_t ReadVBL(uint16_t)
529 return (uint8_t)vbl << 7;
533 uint8_t ReadTEXT(uint16_t)
535 return (uint8_t)textMode << 7;
539 uint8_t ReadMIXED(uint16_t)
541 return (uint8_t)mixedMode << 7;
545 uint8_t ReadPAGE2(uint16_t)
547 return (uint8_t)displayPage2 << 7;
551 uint8_t ReadHIRES(uint16_t)
553 return (uint8_t)hiRes << 7;
557 uint8_t ReadALTCHARSET(uint16_t)
559 return (uint8_t)alternateCharset << 7;
563 uint8_t Read80COL(uint16_t)
565 return (uint8_t)col80Mode << 7;
569 void WriteKeyStrobe(uint16_t, uint8_t)
575 uint8_t ReadSpeaker(uint16_t)
582 void WriteSpeaker(uint16_t, uint8_t)
588 uint8_t SwitchLCR(uint16_t address)
590 lcState = address & 0x0B;
596 void SwitchLCW(uint16_t address, uint8_t)
598 lcState = address & 0x0B;
609 WriteLog("SwitchLC: Read RAM bank 2, no write\n");
611 // [R ] Read RAM bank 2; no write
612 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
614 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
619 WriteLog("SwitchLC: Read ROM, write bank 2\n");
621 // [RR] Read ROM; write RAM bank 2
622 lcBankMemoryR = &rom[0xD000];
623 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
624 upperMemoryR = &rom[0xE000];
625 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
629 WriteLog("SwitchLC: Read ROM, no write\n");
631 // [R ] Read ROM; no write
632 lcBankMemoryR = &rom[0xD000];
634 upperMemoryR = &rom[0xE000];
639 WriteLog("SwitchLC: Read/write bank 2\n");
641 // [RR] Read RAM bank 2; write RAM bank 2
642 lcBankMemoryR = (altzp ? &ram2[0xD000] : &ram[0xD000]);
643 lcBankMemoryW = (altzp ? &ram2[0xD000] : &ram[0xD000]);
644 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
645 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
648 // [R ] Read RAM bank 1; no write
649 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
651 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
655 // [RR] Read ROM; write RAM bank 1
656 lcBankMemoryR = &rom[0xD000];
657 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
658 upperMemoryR = &rom[0xE000];
659 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
662 // [R ] Read ROM; no write
663 lcBankMemoryR = &rom[0xD000];
665 upperMemoryR = &rom[0xE000];
669 // [RR] Read RAM bank 1; write RAM bank 1
670 lcBankMemoryR = (altzp ? &ram2[0xC000] : &ram[0xC000]);
671 lcBankMemoryW = (altzp ? &ram2[0xC000] : &ram[0xC000]);
672 upperMemoryR = (altzp ? &ram2[0xE000] : &ram[0xE000]);
673 upperMemoryW = (altzp ? &ram2[0xE000] : &ram[0xE000]);
679 uint8_t SwitchTEXTR(uint16_t address)
681 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
682 textMode = (bool)(address & 0x01);
687 void SwitchTEXTW(uint16_t address, uint8_t)
689 WriteLog("Setting TEXT to %s...\n", (address & 0x01 ? "ON" : "off"));
690 textMode = (bool)(address & 0x01);
694 uint8_t SwitchMIXEDR(uint16_t address)
696 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
697 mixedMode = (bool)(address & 0x01);
702 void SwitchMIXEDW(uint16_t address, uint8_t)
704 WriteLog("Setting MIXED to %s...\n", (address & 0x01 ? "ON" : "off"));
705 mixedMode = (bool)(address & 0x01);
709 uint8_t SwitchPAGE2R(uint16_t address)
711 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
712 displayPage2 = (bool)(address & 0x01);
716 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
717 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
724 void SwitchPAGE2W(uint16_t address, uint8_t)
726 WriteLog("Setting PAGE2 to %s...\n", (address & 0x01 ? "ON" : "off"));
727 displayPage2 = (bool)(address & 0x01);
731 mainMemoryTextR = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
732 mainMemoryTextW = (displayPage2 ? &ram2[0x0400] : &ram[0x0400]);
737 uint8_t SwitchHIRESR(uint16_t address)
739 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
740 hiRes = (bool)(address & 0x01);
745 void SwitchHIRESW(uint16_t address, uint8_t)
747 WriteLog("Setting HIRES to %s...\n", (address & 0x01 ? "ON" : "off"));
748 hiRes = (bool)(address & 0x01);
752 uint8_t SwitchDHIRESR(uint16_t address)
754 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
755 // Hmm, this breaks convention too, like SLOTCXROM
757 dhires = !((bool)(address & 0x01));
763 void SwitchDHIRESW(uint16_t address, uint8_t)
765 WriteLog("Setting DHIRES to %s (ioudis = %s)...\n", ((address & 0x01) ^ 0x01 ? "ON" : "off"), (ioudis ? "ON" : "off"));
767 dhires = !((bool)(address & 0x01));
771 void SwitchIOUDIS(uint16_t address, uint8_t)
773 ioudis = !((bool)(address & 0x01));
777 uint8_t Slot6R(uint16_t address)
779 //WriteLog("Slot6R: address = %X\n", address & 0x0F);
780 // HandleSlot6(address, 0);
782 uint8_t state = address & 0x0F;
794 floppyDrive.ControlStepper(state);
798 floppyDrive.ControlMotor(state & 0x01);
802 floppyDrive.DriveEnable(state & 0x01);
805 return floppyDrive.ReadWrite();
808 return floppyDrive.GetLatchValue();
811 floppyDrive.SetReadMode();
814 floppyDrive.SetWriteMode();
822 void Slot6W(uint16_t address, uint8_t byte)
824 //WriteLog("Slot6W: address = %X, byte= %X\n", address & 0x0F, byte);
825 // HandleSlot6(address, byte);
826 uint8_t state = address & 0x0F;
838 floppyDrive.ControlStepper(state);
842 floppyDrive.ControlMotor(state & 0x01);
846 floppyDrive.DriveEnable(state & 0x01);
849 floppyDrive.ReadWrite();
852 floppyDrive.SetLatchValue(byte);
855 floppyDrive.SetReadMode();
858 floppyDrive.SetWriteMode();
864 void HandleSlot6(uint16_t address, uint8_t byte)
869 uint8_t MBRead(uint16_t address)
872 // Not sure [Seems to work OK]
875 return slot4Memory[address & 0x00FF];
879 uint8_t regNum = address & 0x0F;
880 uint8_t chipNum = (address & 0x80) >> 7;
883 WriteLog("MBRead: address = %X [chip %d, reg %X, clock=$%X]\n", address & 0xFF, chipNum, regNum, GetCurrentV65C02Clock());
889 return mbvia[chipNum].orb & mbvia[chipNum].ddrb;
892 return mbvia[chipNum].ora & mbvia[chipNum].ddra;
895 return mbvia[chipNum].ddrb;
898 return mbvia[chipNum].ddra;
901 return mbvia[chipNum].timer1counter & 0xFF;
904 return (mbvia[chipNum].timer1counter & 0xFF00) >> 8;
907 return mbvia[chipNum].timer1latch & 0xFF;
910 return (mbvia[chipNum].timer1latch & 0xFF00) >> 8;
913 return mbvia[chipNum].timer2counter & 0xFF;
916 return (mbvia[chipNum].timer2counter & 0xFF00) >> 8;
919 return mbvia[chipNum].acr;
922 return (mbvia[chipNum].ifr & 0x7F)
923 | (mbvia[chipNum].ifr & 0x7F ? 0x80 : 0);
926 return mbvia[chipNum].ier | 0x80;
929 WriteLog("Unhandled 6522 register %X read (chip %d)\n", regNum, chipNum);
936 static uint8_t regLatch[2];
937 void MBWrite(uint16_t address, uint8_t byte)
939 uint8_t regNum = address & 0x0F;
940 uint8_t chipNum = (address & 0x80) >> 7;
943 bit 7 = L/R channel select (AY chip 1 versus AY chip 2)
946 Reg. B is connected to BC1, BDIR, RST' (bits 0, 1, 2)
948 Left VIA IRQ line is tied to 6502 IRQ line
949 Rght VIA IRQ line is tied to 6502 NMI line
952 -------- -------------------------
955 2 Data Direction Register B
956 3 Data Direction Register A
957 4 Timer 1 Low byte counter (& latch)
958 5 Timer 1 Hgh byte counter (& latch)
959 6 Timer 1 Low byte latch
960 7 Timer 1 Hgh byte latch (& reset IRQ flag)
961 B Aux Control Register
962 D Interrupt Flag Register
963 E Interrupt Enable Register
965 bit 6 of ACR is like so:
966 0: Timed interrupt each time Timer 1 is loaded
967 1: Continuous interrupts
969 bit 7 enables PB7 (bit 6 controls output type):
971 1: Square wave output
976 WriteLog("MBWrite: address = %X, byte= %X [clock=$%X]", address & 0xFF, byte, GetCurrentV65C02Clock());
979 WriteLog("[OUTB -> %s%s%s]\n", (byte & 0x01 ? "BC1" : ""), (byte & 0x02 ? " BDIR" : ""), (byte & 0x04 ? " RST'" : ""));
980 else if (regNum == 1)
981 WriteLog("[OUTA -> %02X]\n", byte);
982 else if (regNum == 2)
983 WriteLog("[DDRB -> %02X]\n", byte);
984 else if (regNum == 3)
985 WriteLog("[DDRA -> %02X]\n", byte);
993 // Control of the AY-3-8912 is thru this port pretty much...
994 mbvia[chipNum].orb = byte;
996 if ((byte & 0x04) == 0)
997 #ifdef USE_NEW_AY8910
1000 AY8910_reset(chipNum);
1002 else if ((byte & 0x03) == 0x03)
1003 regLatch[chipNum] = mbvia[chipNum].ora;
1004 else if ((byte & 0x03) == 0x02)
1005 #ifdef USE_NEW_AY8910
1006 AYWrite(chipNum, regLatch[chipNum], mbvia[chipNum].ora);
1008 _AYWriteReg(chipNum, regLatch[chipNum], mbvia[chipNum].ora);
1014 mbvia[chipNum].ora = byte;
1018 mbvia[chipNum].ddrb = byte;
1022 mbvia[chipNum].ddra = byte;
1026 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0xFF00)
1031 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0x00FF)
1032 | (((uint16_t)byte) << 8);
1033 mbvia[chipNum].timer1counter = mbvia[chipNum].timer1latch;
1034 mbvia[chipNum].ifr &= 0x3F; // Clear T1 interrupt flag
1038 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0xFF00)
1043 mbvia[chipNum].timer1latch = (mbvia[chipNum].timer1latch & 0x00FF)
1044 | (((uint16_t)byte) << 8);
1045 mbvia[chipNum].ifr &= 0x3F; // Clear T1 interrupt flag
1049 mbvia[chipNum].acr = byte;
1053 mbvia[chipNum].ifr &= ~byte;
1058 // Setting bits in the IER
1059 mbvia[chipNum].ier |= byte;
1061 // Clearing bits in the IER
1062 mbvia[chipNum].ier &= ~byte;
1066 WriteLog("Unhandled 6522 register $%X write $%02X (chip %d)\n", regNum, byte, chipNum);
1071 uint8_t ReadButton0(uint16_t)
1073 return (uint8_t)openAppleDown << 7;
1077 uint8_t ReadButton1(uint16_t)
1079 return (uint8_t)closedAppleDown << 7;
1083 // The way the paddles work is that a strobe is written (or read) to $C070,
1084 // then software counts down the time that it takes for the paddle outputs
1085 // to have bit 7 return to 0. If there are no paddles connected, bit 7
1087 // NB: This is really paddles 0-3, not just 0 :-P
1088 uint8_t ReadPaddle0(uint16_t)
1094 uint8_t ReadIOUDIS(uint16_t)
1096 return (uint8_t)ioudis << 7;
1100 uint8_t ReadDHIRES(uint16_t)
1102 return (uint8_t)dhires << 7;
1106 // Whenever a read is done to a MMIO location that is unconnected to anything,
1107 // it actually sees the RAM access done by the video generation hardware. Some
1108 // programs exploit this, so we emulate it here.
1110 // N.B.: frameCycles will be off by the true amount because this only
1111 // increments by the amount of a speaker cycle, not the cycle count when
1112 // the access happens... !!! FIX !!!
1113 uint8_t ReadFloatingBus(uint16_t)
1115 // Get the currently elapsed cycle count for this frame
1116 uint32_t frameCycles = mainCPU.clock - frameCycleStart;
1118 // Make counters out of the cycle count. There are 65 cycles per line.
1119 uint32_t numLines = frameCycles / 65;
1120 uint32_t numHTicks = frameCycles - (numLines * 65);
1122 // Convert these to H/V counters
1123 uint32_t hcount = numHTicks - 1;
1125 // HC sees zero twice:
1126 if (hcount == 0xFFFFFFFF)
1129 uint32_t vcount = numLines + 0xFA;
1131 // Now do the address calculations
1132 uint32_t sum = 0xD + ((hcount & 0x38) >> 3)
1133 + (((vcount & 0xC0) >> 6) | ((vcount & 0xC0) >> 4));
1134 uint32_t address = ((vcount & 0x38) << 4) | ((sum & 0x0F) << 3) | (hcount & 0x07);
1136 // Add in particulars for the gfx mode we're in...
1137 if (textMode || (!textMode && !hiRes))
1138 address |= (!(!store80Mode && displayPage2) ? 0x400 : 0)
1139 | (!store80Mode && displayPage2 ? 0x800 : 0);
1141 address |= (!(!store80Mode && displayPage2) ? 0x2000: 0)
1142 | (!store80Mode && displayPage2 ? 0x4000 : 0)
1143 | ((vcount & 0x07) << 10);
1145 // The address so read is *always* in main RAM, not alt RAM
1146 return ram[address];