]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
12eb8c92181d9263bebb61435e5d0e13778bad51
[apple2] / src / apple2.cpp
1 //
2 // Apple 2 SDL Portable Apple Emulator
3 //
4 // by James L. Hammons
5 // (C) 2005 Underground Software
6 //
7 // Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by
8 // Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are
9 // also derived from ApplePC. Too bad it was closed source--it could have been
10 // *the* premier Apple II emulator out there.
11 //
12 // JLH = James L. Hammons <jlhamm@acm.org>
13 //
14 // WHO  WHEN        WHAT
15 // ---  ----------  ------------------------------------------------------------
16 // JLH  11/12/2005  Initial port to SDL
17 // JLH  11/18/2005  Wired up graphic soft switches
18 // JLH  12/02/2005  Setup timer subsystem for more accurate time keeping
19 // JLH  12/12/2005  Added preliminary state saving support
20 //
21
22 // STILL TO DO:
23 //
24 // - Port to SDL [DONE]
25 // - GUI goodies
26 // - Weed out unneeded functions [DONE]
27 // - Disk I/O [DONE]
28 // - 128K IIe related stuff
29 // - State loading/saving
30 //
31
32 #include "apple2.h"
33
34 #include <SDL2/SDL.h>
35 #include <fstream>
36 #include <string>
37 #include <iomanip>
38 #include <iostream>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <time.h>
42 #include "log.h"
43 #include "video.h"
44 #include "sound.h"
45 #include "settings.h"
46 #include "v65c02.h"
47 #include "applevideo.h"
48 #include "timing.h"
49 #include "floppy.h"
50 #include "firmware.h"
51
52 #include "gui/gui.h"
53 #include "gui/window.h"
54 #include "gui/draggablewindow2.h"
55 #include "gui/textedit.h"
56
57 // Debug and misc. defines
58
59 #define THREADED_65C02
60 #define CPU_THREAD_OVERFLOW_COMPENSATION
61 #define DEBUG_LC
62 //#define CPU_CLOCK_CHECKING
63 //#define THREAD_DEBUGGING
64 #define SOFT_SWITCH_DEBUGGING
65
66 // Global variables
67
68 uint8_t ram[0x10000], rom[0x10000];                             // RAM & ROM spaces
69 uint8_t ram2[0x10000];                                                  // Auxillary RAM
70 uint8_t diskRom[0x100];                                                 // Disk ROM space
71 V65C02REGS mainCPU;                                                             // v65C02 execution context
72 uint8_t appleType = APPLE_TYPE_IIE;
73 FloppyDrive floppyDrive;
74
75 // Local variables
76
77 static uint8_t lastKeyPressed = 0;
78 static bool keyDown = false;
79 static bool openAppleDown = false;
80 static bool closedAppleDown = false;
81 static bool store80Mode = false;
82 static bool vbl = false;
83 static bool slotCXROM = false;
84 static bool slotC3ROM = false;
85 static bool ramrd = false;
86 static bool ramwrt = false;
87 static bool altzp = false;
88 static bool ioudis = true;
89 bool dhires = false;
90
91 //static FloppyDrive floppyDrive;
92
93 enum { LC_BANK_1, LC_BANK_2 };
94
95 static uint8_t visibleBank = LC_BANK_1;
96 static bool readRAM = false;
97 static bool writeRAM = false;
98
99 static bool running = true;                                             // Machine running state flag...
100 static uint32_t startTicks;
101
102 static GUI * gui = NULL;
103
104 // Local functions (technically, they're global...)
105
106 bool LoadImg(char * filename, uint8_t * ram, int size);
107 uint8_t RdMem(uint16_t addr);
108 void WrMem(uint16_t addr, uint8_t b);
109 static void SaveApple2State(const char * filename);
110 static bool LoadApple2State(const char * filename);
111
112 // Local timer callback functions
113
114 static void FrameCallback(void);
115 static void BlinkTimer(void);
116
117 #ifdef THREADED_65C02
118 // Test of threaded execution of 6502
119 static SDL_Thread * cpuThread = NULL;
120 //static SDL_mutex * cpuMutex = NULL;
121 static SDL_cond * cpuCond = NULL;
122 static SDL_sem * mainSem = NULL;
123 static bool cpuFinished = false;
124 static bool cpuSleep = false;
125
126
127 // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
128
129 // Let's try a thread...
130 /*
131 Here's how it works: Execute 1 frame's worth, then sleep.
132 Other stuff wakes it up
133 */
134 int CPUThreadFunc(void * data)
135 {
136         // Mutex must be locked for conditional to work...
137         // Also, must be created in the thread that uses it...
138         SDL_mutex * cpuMutex = SDL_CreateMutex();
139
140 // decrement mainSem...
141 //SDL_SemWait(mainSem);
142 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
143         float overflow = 0.0;
144 #endif
145
146         do
147         {
148                 if (cpuSleep)
149                         SDL_CondWait(cpuCond, cpuMutex);
150
151 // decrement mainSem...
152 #ifdef THREAD_DEBUGGING
153 WriteLog("CPU: SDL_SemWait(mainSem);\n");
154 #endif
155 SDL_SemWait(mainSem);
156
157 // There are exactly 800 slices of 21.333 cycles per frame, so it works out
158 // evenly.
159 #if 0
160                 uint32_t cycles = 17066;
161 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
162 // ODD! It's closer *without* this overflow compensation. ??? WHY ???
163                 overflow += 0.666666667;
164
165                 if (overflow > 1.0)
166                 {
167                         overflow -= 1.0;
168                         cycles++;
169                 }
170 #endif
171
172 #ifdef THREAD_DEBUGGING
173 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
174 #endif
175                 Execute65C02(&mainCPU, cycles); // how much? 1 frame (after 1 s, off by 40 cycles) not any more--it's off by as much as 240 now!
176
177                 // Adjust the sound routine's last cycle toggled time base
178                 // Also, since we're finished executing, .clock is now valid
179 #ifdef THREAD_DEBUGGING
180 WriteLog("CPU: AdjustLastToggleCycles(mainCPU.clock);\n");
181 #endif
182                 AdjustLastToggleCycles(mainCPU.clock);
183 #else
184 #ifdef THREAD_DEBUGGING
185 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
186 #endif
187                 for(int i=0; i<800; i++)
188                 {
189                         uint32_t cycles = 21;
190                         overflow += 0.333333334;
191
192                         if (overflow > 1.0)
193                         {
194                                 cycles++;
195                                 overflow -= 1.0;
196                         }
197
198                         Execute65C02(&mainCPU, cycles);
199                         WriteSampleToBuffer();
200                 }
201 #endif
202
203 //WriteLog("CPUThread: Supposedly end of frame...\n");
204
205 #ifdef THREAD_DEBUGGING
206 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
207 #endif
208                 SDL_mutexP(cpuMutex);
209 #if 0
210                 if (SDL_CondWait(cpuCond, cpuMutex) != 0)
211                 {
212                         printf("SDL_CondWait != 0! (Error: '%s')\n", SDL_GetError());
213                         exit(-1);
214                 }
215 #else
216 // increment mainSem...
217 #ifdef THREAD_DEBUGGING
218 WriteLog("CPU: SDL_SemPost(mainSem);\n");
219 #endif
220 SDL_SemPost(mainSem);
221 //              SDL_CondSignal(mainCond);       // In case something is waiting on us...
222
223 #ifdef THREAD_DEBUGGING
224 WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
225 #endif
226                 SDL_CondWait(cpuCond, cpuMutex);
227
228 #endif
229 #ifdef THREAD_DEBUGGING
230 WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
231 #endif
232                 SDL_mutexV(cpuMutex);
233         }
234         while (!cpuFinished);
235
236         SDL_DestroyMutex(cpuMutex);
237
238         return 0;
239 }
240 #endif
241
242
243 // Test GUI function
244
245 Element * TestWindow(void)
246 {
247         Element * win = new DraggableWindow2(10, 10, 128, 128);
248 //      ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
249
250         return win;
251 }
252
253
254 Element * QuitEmulator(void)
255 {
256         gui->Stop();
257         running = false;
258
259         return NULL;
260 }
261
262
263 /*
264  Small Apple II memory map:
265
266 $C010 - Clear bit 7 of keyboard data ($C000)
267 $C030 - Toggle speaker diaphragm
268 $C051 - Display text
269 $C054 - Select page 1
270 $C056 - Select lo-res
271 $C058 - Set annuciator-0 output to 0
272 $C05A - Set annuciator-0 output to 0
273 $C05D - Set annuciator-0 output to 1
274 $C05F - Set annuciator-0 output to 1
275 $C0E0 - Disk control stepper ($C0E0-7)
276 $C0E9 - Disk control motor (on)
277 $C0EA - Disk enable (drive 1)
278 $C0EC - Disk R/W
279 $C0EE - Disk set read mode
280 */
281
282 //
283 // V65C02 read/write memory functions
284 //
285
286 uint8_t RdMem(uint16_t addr)
287 {
288         uint8_t b;
289
290 #if 0
291 if (addr >= 0xC000 && addr <= 0xC0FF)
292         WriteLog("\n*** Read at I/O address %04X\n", addr);
293 #endif
294 #if 0
295 if (addr >= 0xC080 && addr <= 0xC08F)
296         WriteLog("\n*** Read at I/O address %04X\n", addr);
297 #endif
298
299         if ((addr & 0xFFF0) == 0xC000)                  // Read $C000-$C00F
300         {
301                 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
302         }
303 //      else if ((addr & 0xFFF8) == 0xC010)             // Read $C010-$C01F
304         else if (addr == 0xC010)
305         {
306 //This is bogus: keyDown is set to false, so return val NEVER is set...
307 //Fixed...
308 //Also, this is IIe/IIc only...!
309                 uint8_t retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
310                 keyDown = false;
311                 return retVal;
312         }
313         // These are //e locations
314         else if (addr == 0xC011)
315         {
316 #ifdef SOFT_SWITCH_DEBUGGING
317 WriteLog("RDBANK2 (read)\n");
318 #endif
319                 return (visibleBank == LC_BANK_2 ? 0x80 : 0x00);
320         }
321         else if (addr == 0xC012)
322         {
323 #ifdef SOFT_SWITCH_DEBUGGING
324 WriteLog("RDLCRAM (read)\n");
325 #endif
326                 return (readRAM ? 0x80 : 0x00);
327         }
328         else if (addr == 0xC013)
329         {
330 #ifdef SOFT_SWITCH_DEBUGGING
331 WriteLog("RAMRD (read)\n");
332 #endif
333                 return (ramrd ? 0x80 : 0x00);
334         }
335         else if (addr == 0xC014)
336         {
337 #ifdef SOFT_SWITCH_DEBUGGING
338 WriteLog("RAMWRT (read)\n");
339 #endif
340                 return (ramwrt ? 0x80 : 0x00);
341         }
342         else if (addr == 0xC015)
343         {
344 #ifdef SOFT_SWITCH_DEBUGGING
345 WriteLog("SLOTCXROM (read)\n");
346 #endif
347                 return (slotCXROM ? 0x80 : 0x00);
348         }
349         else if (addr == 0xC016)
350         {
351 #ifdef SOFT_SWITCH_DEBUGGING
352 WriteLog("ALTZP (read)\n");
353 #endif
354                 return (altzp ? 0x80 : 0x00);
355         }
356         else if (addr == 0xC017)
357         {
358 #ifdef SOFT_SWITCH_DEBUGGING
359 WriteLog("SLOTC3ROM (read)\n");
360 #endif
361                 return (slotC3ROM ? 0x80 : 0x00);
362         }
363         else if (addr == 0xC018)
364         {
365 #ifdef SOFT_SWITCH_DEBUGGING
366 WriteLog("80STORE (read)\n");
367 #endif
368                 return (store80Mode ? 0x80 : 0x00);
369         }
370         else if (addr == 0xC019)
371         {
372 #ifdef SOFT_SWITCH_DEBUGGING
373 WriteLog("VBL (read)\n");
374 #endif
375                 return (vbl ? 0x80 : 0x00);
376         }
377         else if (addr == 0xC01A)
378         {
379 #ifdef SOFT_SWITCH_DEBUGGING
380 WriteLog("TEXT (read)\n");
381 #endif
382                 return (textMode ? 0x80 : 0x00);
383         }
384         else if (addr == 0xC01B)
385         {
386 #ifdef SOFT_SWITCH_DEBUGGING
387 WriteLog("MIXED (read)\n");
388 #endif
389                 return (mixedMode ? 0x80 : 0x00);
390         }
391         else if (addr == 0xC01C)
392         {
393 #ifdef SOFT_SWITCH_DEBUGGING
394 WriteLog("PAGE2 (read)\n");
395 #endif
396                 return (displayPage2 ? 0x80 : 0x00);
397         }
398         else if (addr == 0xC01D)
399         {
400 #ifdef SOFT_SWITCH_DEBUGGING
401 WriteLog("HIRES (read)\n");
402 #endif
403                 return (hiRes ? 0x80 : 0x00);
404         }
405         else if (addr == 0xC01E)
406         {
407 #ifdef SOFT_SWITCH_DEBUGGING
408 WriteLog("ALTCHARSET (read)\n");
409 #endif
410                 return (alternateCharset ? 0x80 : 0x00);
411         }
412         else if (addr == 0xC01F)
413         {
414 #ifdef SOFT_SWITCH_DEBUGGING
415 WriteLog("80COL (read)\n");
416 #endif
417                 return (col80Mode ? 0x80 : 0x00);
418         }
419         else if ((addr & 0xFFF0) == 0xC030)             // Read $C030-$C03F
420         {
421 /*
422 This is problematic, mainly because the v65C02 removes actual cycles run after each call.
423 Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
424 How to fix?
425
426 What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
427 hasn't had any changes in it then we just fill it with the last sample value and are done. Then
428 we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
429 last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
430 How to keep track?
431
432 Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
433 deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
434
435
436
437 */
438                 ToggleSpeaker(GetCurrentV65C02Clock());
439 //should it return something else here???
440                 return 0x00;
441         }
442         else if (addr == 0xC050)                                // Read $C050
443         {
444 #ifdef SOFT_SWITCH_DEBUGGING
445 WriteLog("TEXT off (read)\n");
446 #endif
447                 textMode = false;
448         }
449         else if (addr == 0xC051)                                // Read $C051
450         {
451 #ifdef SOFT_SWITCH_DEBUGGING
452 WriteLog("TEXT on (read)\n");
453 #endif
454                 textMode = true;
455         }
456         else if (addr == 0xC052)                                // Read $C052
457         {
458 #ifdef SOFT_SWITCH_DEBUGGING
459 WriteLog("MIXED off (read)\n");
460 #endif
461                 mixedMode = false;
462         }
463         else if (addr == 0xC053)                                // Read $C053
464         {
465 #ifdef SOFT_SWITCH_DEBUGGING
466 WriteLog("MIXED on (read)\n");
467 #endif
468                 mixedMode = true;
469         }
470         else if (addr == 0xC054)                                // Read $C054
471         {
472 #ifdef SOFT_SWITCH_DEBUGGING
473 WriteLog("PAGE2 off (read)\n");
474 #endif
475                 displayPage2 = false;
476         }
477         else if (addr == 0xC055)                                // Read $C055
478         {
479 #ifdef SOFT_SWITCH_DEBUGGING
480 WriteLog("PAGE2 on (read)\n");
481 #endif
482                 displayPage2 = true;
483         }
484         else if (addr == 0xC056)                                // Read $C056
485         {
486 #ifdef SOFT_SWITCH_DEBUGGING
487 WriteLog("HIRES off (read)\n");
488 #endif
489                 hiRes = false;
490         }
491         else if (addr == 0xC057)                                // Read $C057
492         {
493 #ifdef SOFT_SWITCH_DEBUGGING
494 WriteLog("HIRES on (read)\n");
495 #endif
496                 hiRes = true;
497         }
498         else if (addr == 0xC05E)
499         {
500 #ifdef SOFT_SWITCH_DEBUGGING
501 WriteLog("DHIRES on (read)\n");
502 #endif
503                 if (ioudis)
504                         dhires = true;
505         }
506         else if (addr == 0xC05F)
507         {
508 #ifdef SOFT_SWITCH_DEBUGGING
509 WriteLog("DHIRES off (read)\n");
510 #endif
511                 if (ioudis)
512                         dhires = false;
513         }
514         else if (addr == 0xC061)                                // Read $C061
515         {
516                 // Open Apple key (or push button 0)
517                 return (openAppleDown ? 0x80 : 0x00);
518         }
519         else if (addr == 0xC062)                                // Read $C062
520         {
521                 // Open Apple key (or push button 0)
522                 return (closedAppleDown ? 0x80 : 0x00);
523         }
524         // The way the paddles work is that a strobe is written (or read) to $C070,
525         // then software counts down the time that it takes for the paddle outputs
526         // to have bit 7 return to 0. If there are no paddles connected, bit 7
527         // stays at 1.
528         else if (addr == 0xC064)        // Paddles 0-3
529         {
530                 return 0xFF;
531         }
532         else if (addr == 0xC065)
533         {
534                 return 0xFF;
535         }
536         else if (addr == 0xC066)
537         {
538                 return 0xFF;
539         }
540         else if (addr == 0xC067)
541         {
542                 return 0xFF;
543         }
544         else if (addr == 0xC07E)
545         {
546 #ifdef SOFT_SWITCH_DEBUGGING
547 WriteLog("IOUDIS (read)\n");
548 #endif
549                 return (ioudis ? 0x80 : 0x00);
550         }
551         else if (addr == 0xC07F)
552         {
553 #ifdef SOFT_SWITCH_DEBUGGING
554 WriteLog("DHIRES (read)\n");
555 #endif
556                 return (dhires ? 0x80 : 0x00);
557         }
558
559 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
560 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
561 //[SHOULD BE FIXED NOW]
562 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
563 //visible, two makes it R/W.
564
565 /*
566 301  LDA $E000
567 304  PHA
568 305  LDA $C081
569 308  PLA
570 309  PHA
571 30A  CMP $E000
572 30D  BNE $332
573 30F  LDA $C083
574 312  LDA $C083
575 315  LDA #$A5
576 317  STA $D000
577 31A  CMP $D000
578 31D  BNE $332
579 31F  LSR A
580 320  STA $D000
581 323  CMP $D000
582 326  BNE $332
583 328  LDA $C081
584 32B  LDA $C081
585 32E  LDA #$01
586 330  BNE $334
587 332  LDA #$00
588 334  STA $300
589 337  PLA
590 338  CMP $E000
591 33B  BEQ $340
592 33D  LDA $C080
593 340  RTS
594
595 A = PEEK($C082)
596 */
597
598         else if ((addr & 0xFFFB) == 0xC080)
599         {
600 #ifdef DEBUG_LC
601 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
602 #endif
603 //$C080 49280              OECG  R   Read RAM bank 2; no write
604                 visibleBank = LC_BANK_2;
605                 readRAM = true;
606                 writeRAM = false;
607         }
608         else if ((addr & 0xFFFB) == 0xC081)
609         {
610 #ifdef DEBUG_LC
611 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
612 #endif
613 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
614                 visibleBank = LC_BANK_2;
615                 readRAM = false;
616                 writeRAM = true;
617         }
618         else if ((addr & 0xFFFB) == 0xC082)
619         {
620 #ifdef DEBUG_LC
621 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
622 #endif
623 //$C082 49282              OECG  R   Read ROM; no write
624                 visibleBank = LC_BANK_2;
625                 readRAM = false;
626                 writeRAM = false;
627         }
628         else if ((addr & 0xFFFB) == 0xC083)
629         {
630 #ifdef DEBUG_LC
631 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
632 #endif
633 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
634                 visibleBank = LC_BANK_2;
635                 readRAM = true;
636                 writeRAM = true;
637         }
638         else if ((addr & 0xFFFB) == 0xC088)
639         {
640 #ifdef DEBUG_LC
641 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
642 #endif
643 //$C088 49288              OECG  R   Read RAM bank 1; no write
644                 visibleBank = LC_BANK_1;
645                 readRAM = true;
646                 writeRAM = false;
647 //Hm. Some stuff seems to want this.
648 //nope, was looking at $C0E8... return 0xFF;
649         }
650         else if ((addr & 0xFFFB) == 0xC089)
651         {
652 #ifdef DEBUG_LC
653 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
654 #endif
655 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
656                 visibleBank = LC_BANK_1;
657                 readRAM = false;
658                 writeRAM = true;
659         }
660         else if ((addr & 0xFFFB) == 0xC08A)
661         {
662 #ifdef DEBUG_LC
663 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
664 #endif
665 //$C08A 49290              OECG  R   Read ROM; no write
666                 visibleBank = LC_BANK_1;
667                 readRAM = false;
668                 writeRAM = false;
669         }
670         else if ((addr & 0xFFFB) == 0xC08B)
671         {
672 #ifdef DEBUG_LC
673 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
674 #endif
675 //$C08B 49291              OECG  RR  Read/write RAM bank 1
676                 visibleBank = LC_BANK_1;
677                 readRAM = true;
678                 writeRAM = true;
679         }
680         else if ((addr & 0xFFF8) == 0xC0E0)
681         {
682                 floppyDrive.ControlStepper(addr & 0x07);
683         }
684         else if ((addr & 0xFFFE) == 0xC0E8)
685         {
686                 floppyDrive.ControlMotor(addr & 0x01);
687         }
688         else if ((addr & 0xFFFE) == 0xC0EA)
689         {
690                 floppyDrive.DriveEnable(addr & 0x01);
691         }
692         else if (addr == 0xC0EC)
693         {
694                 return floppyDrive.ReadWrite();
695         }
696         else if (addr == 0xC0ED)
697         {
698                 return floppyDrive.GetLatchValue();
699         }
700         else if (addr == 0xC0EE)
701         {
702                 floppyDrive.SetReadMode();
703         }
704         else if (addr == 0xC0EF)
705         {
706                 floppyDrive.SetWriteMode();
707         }
708
709 //#define LC_DEBUGGING
710 #ifdef LC_DEBUGGING
711 bool showpath = false;
712 if (addr >= 0xD000 && addr <= 0xD00F)
713         showpath = true;
714 #endif
715 //This sux...
716         if (addr >= 0xC100 && addr <= 0xC7FF)   // The $C000-$CFFF block is *never* RAM
717         {
718 // Looks like the ][e ref manual got this one wrong: slotCXROM set should mean
719 // use internal ROM, NOT slot ROM. :-/
720 // (fixed now, by setting the switch correctly in the write mem section :-P)
721                 if (!slotCXROM)
722 //              if (slotCXROM)
723                         b = rom[addr];
724                 else
725                 {
726                         if (addr >= 0xC100 && addr <= 0xC1FF)
727                                 b = parallelROM[addr & 0xFF];
728                         else if (addr >= 0xC600 && addr <= 0xC6FF)
729                                 b = diskROM[addr & 0xFF];
730                         else if (addr >= 0xC300 && addr <= 0xC3FF && !slotC3ROM)
731                                 b = rom[addr];
732                         else
733                                 b = 0xFF;
734 //                              b = rom[addr];
735                 }
736 #ifdef LC_DEBUGGING
737 if (showpath)
738         WriteLog("b is from $C100-$CFFF block...\n");
739 #endif
740         }
741         else if (addr >= 0xC800 && addr <= 0xCFFF)      // 2K peripheral or OS ROM
742         {
743                 b = rom[addr];
744         }
745         else if (addr >= 0xD000)
746         {
747                 if (readRAM)
748                 {
749                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
750 #ifdef LC_DEBUGGING
751                         {
752 #endif
753 //                              b = ram[addr - 0x1000];
754                                 b = (altzp ? ram2[addr - 0x1000] : ram[addr - 0x1000]);
755 #ifdef LC_DEBUGGING
756 if (showpath)
757         WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
758                         }
759 #endif
760                         else
761 #ifdef LC_DEBUGGING
762                         {
763 #endif
764 //                              b = ram[addr];
765                                 b = (altzp ? ram2[addr] : ram[addr]);
766 #ifdef LC_DEBUGGING
767 if (showpath)
768         WriteLog("b is from LC bank #2 (ram[addr])...\n");
769                         }
770 #endif
771                 }
772                 else
773 #ifdef LC_DEBUGGING
774                 {
775 #endif
776                         b = rom[addr];
777 #ifdef LC_DEBUGGING
778 if (showpath)
779         WriteLog("b is from LC ROM (rom[addr])...\n");
780                 }
781 #endif
782         }
783         else
784         {
785                 // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
786                 if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
787                 {
788                         if (displayPage2)
789                                 b = ram2[addr];
790                         else
791                                 b = ram[addr];
792
793                         return b;
794                 }
795
796                 // Finally, check for auxillary/altzp write switches
797                 if (addr < 0x0200)
798                         b = (altzp ? ram2[addr] : ram[addr]);
799                 else
800                         b = (ramrd ? ram2[addr] : ram[addr]);
801 #ifdef LC_DEBUGGING
802 if (showpath)
803         WriteLog("b is from ram[addr]...\n");
804 #endif
805         }
806
807 #ifdef LC_DEBUGGING
808 if (addr >= 0xD000 && addr <= 0xD00F)
809 {
810         WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
811 }
812 #endif
813
814         return b;
815 }
816
817 /*
818 A-9 (Mockingboard)
819 APPENDIX F Assembly Language Program Listings
820
821         1       *PRIMARY ROUTINES
822         2       *FOR SLOT 4
823         3       *
824         4                       ORG     $9000
825         5       *                               ;ADDRESSES FOR FIRST 6522
826         6       ORB             EQU     $C400           ;PORT B
827         7       ORA             EQU     $C401           ;PORT A
828         8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
829         9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
830         10      *                                       ;ADDRESSES FOR SECOND 6522
831         11      ORB2            EQU     $C480           ;PORT B
832         12      ORA2            EQU     $C481           ;PORT A
833         13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
834         14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
835 */
836 void WrMem(uint16_t addr, uint8_t b)
837 {
838 //temp...
839 //extern V6809REGS regs;
840 //if (addr >= 0xC800 && addr <= 0xCBFE)
841 //if (addr == 0xC80F || addr == 0xC80D)
842 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
843
844 #if 0
845 if (addr >= 0xC000 && addr <= 0xC0FF)
846         WriteLog("\n*** Write at I/O address %04X\n", addr);
847 #endif
848 /*
849 Check the BIKO version on Asimov to see if it's been cracked or not...
850
851 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
852 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
853 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
854 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
855 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
856
857 ; INX here *ensures* 1 - 6!!! BUG!!!
858 ; Or is it? Could this be part of a braindead copy protection scheme? It's
859 ; awfully close to NOP ($EA)...
860 ; Nothing else touches it once it's been written... Hmm...
861
862 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
863 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
864 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
865 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
866
867 ; 4E13: 03 00
868 ; 4E15: 25 25 15 15 10 20
869 ; 4E1B: 03 41 99 99 01 00 12
870 ; 4E22: 99 70
871
872 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
873 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
874 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
875 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
876
877 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
878
879 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
880 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
881
882 ; Print "ALAKAZAM!" and so on...
883
884 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
885 */
886 #if 0
887 if (addr == 0x7F47)
888         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
889 #endif
890 /*
891 I think this is IIc/IIe only...
892
893 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
894 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
895
896 CLRAUXRD = $C002 ;read from main 48K (WR-only)
897 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
898
899 CLRAUXWR = $C004 ;write to main 48K (WR-only)
900 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
901
902 CLRCXROM = $C006 ;use ROM on cards (WR-only)
903 SETCXROM = $C007 ;use internal ROM (WR-only)
904
905 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
906 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
907
908 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
909 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
910
911 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
912 SET80VID = $C00D ;enable 80-column display mode (WR-only)
913
914 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
915 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
916 */
917         if (addr == 0xC000)
918         {
919 #ifdef SOFT_SWITCH_DEBUGGING
920 WriteLog("80STORE off (write)\n");
921 #endif
922                 store80Mode = false;
923         }
924         else if (addr == 0xC001)
925         {
926 #ifdef SOFT_SWITCH_DEBUGGING
927 WriteLog("80STORE on (write)\n");
928 #endif
929                 store80Mode = true;
930         }
931         else if (addr == 0xC002)
932         {
933 #ifdef SOFT_SWITCH_DEBUGGING
934 WriteLog("RAMRD off (write)\n");
935 #endif
936                 ramrd = false;
937         }
938         else if (addr == 0xC003)
939         {
940 #ifdef SOFT_SWITCH_DEBUGGING
941 WriteLog("RAMRD on (write)\n");
942 #endif
943                 ramrd = true;
944         }
945         else if (addr == 0xC004)
946         {
947 #ifdef SOFT_SWITCH_DEBUGGING
948 WriteLog("RAMWRT off (write)\n");
949 #endif
950                 ramwrt = false;
951         }
952         else if (addr == 0xC005)
953         {
954 #ifdef SOFT_SWITCH_DEBUGGING
955 WriteLog("RAMWRT on (write)\n");
956 #endif
957                 ramwrt = true;
958         }
959         else if (addr == 0xC006)
960         {
961                 // This is the only soft switch that breaks the usual convention.
962 #ifdef SOFT_SWITCH_DEBUGGING
963 WriteLog("SLOTCXROM on (write)\n");
964 #endif
965                 slotCXROM = true;
966         }
967         else if (addr == 0xC007)
968         {
969 #ifdef SOFT_SWITCH_DEBUGGING
970 WriteLog("SLOTCXROM off (write)\n");
971 #endif
972                 slotCXROM = false;
973         }
974         else if (addr == 0xC008)
975         {
976 #ifdef SOFT_SWITCH_DEBUGGING
977 WriteLog("ALTZP off (write)\n");
978 #endif
979                 altzp = false;
980         }
981         else if (addr == 0xC009)
982         {
983 #ifdef SOFT_SWITCH_DEBUGGING
984 WriteLog("ALTZP on (write)\n");
985 #endif
986                 altzp = true;
987         }
988         else if (addr == 0xC00A)
989         {
990 #ifdef SOFT_SWITCH_DEBUGGING
991 WriteLog("SLOTC3ROM off (write)\n");
992 #endif
993                 slotC3ROM = false;
994         }
995         else if (addr == 0xC00B)
996         {
997 #ifdef SOFT_SWITCH_DEBUGGING
998 WriteLog("SLOTC3ROM on (write)\n");
999 #endif
1000                 slotC3ROM = true;
1001         }
1002         else if (addr == 0xC00C)
1003         {
1004 #ifdef SOFT_SWITCH_DEBUGGING
1005 WriteLog("80COL off (write)\n");
1006 #endif
1007                 col80Mode = false;
1008         }
1009         else if (addr == 0xC00D)
1010         {
1011 #ifdef SOFT_SWITCH_DEBUGGING
1012 WriteLog("80COL on (write)\n");
1013 #endif
1014                 col80Mode = true;
1015         }
1016         else if (addr == 0xC00E)
1017         {
1018 #ifdef SOFT_SWITCH_DEBUGGING
1019 WriteLog("ALTCHARSET off (write)\n");
1020 #endif
1021                 alternateCharset = false;
1022         }
1023         else if (addr == 0xC00F)
1024         {
1025 #ifdef SOFT_SWITCH_DEBUGGING
1026 WriteLog("ALTCHARSET on (write)\n");
1027 #endif
1028                 alternateCharset = true;
1029         }
1030         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
1031         {
1032 //Actually, according to the A2 ref, this should do nothing since a write
1033 //is immediately preceded by a read leaving it in the same state it was...
1034 //But leaving this out seems to fuck up the key handling of some games...
1035                 keyDown = false;
1036         }
1037         else if (addr == 0xC050)
1038         {
1039 #ifdef SOFT_SWITCH_DEBUGGING
1040 WriteLog("TEXT off (write)\n");
1041 #endif
1042                 textMode = false;
1043         }
1044         else if (addr == 0xC051)
1045         {
1046 #ifdef SOFT_SWITCH_DEBUGGING
1047 WriteLog("TEXT on (write)\n");
1048 #endif
1049                 textMode = true;
1050         }
1051         else if (addr == 0xC052)
1052         {
1053 #ifdef SOFT_SWITCH_DEBUGGING
1054 WriteLog("MIXED off (write)\n");
1055 #endif
1056                 mixedMode = false;
1057         }
1058         else if (addr == 0xC053)
1059         {
1060 #ifdef SOFT_SWITCH_DEBUGGING
1061 WriteLog("MIXED on (write)\n");
1062 #endif
1063                 mixedMode = true;
1064         }
1065         else if (addr == 0xC054)
1066         {
1067 #ifdef SOFT_SWITCH_DEBUGGING
1068 WriteLog("PAGE2 off (write)\n");
1069 #endif
1070                 displayPage2 = false;
1071         }
1072         else if (addr == 0xC055)
1073         {
1074 #ifdef SOFT_SWITCH_DEBUGGING
1075 WriteLog("PAGE2 on (write)\n");
1076 #endif
1077                 displayPage2 = true;
1078         }
1079         else if (addr == 0xC056)
1080         {
1081 #ifdef SOFT_SWITCH_DEBUGGING
1082 WriteLog("HIRES off (write)\n");
1083 #endif
1084                 hiRes = false;
1085         }
1086         else if (addr == 0xC057)
1087         {
1088 #ifdef SOFT_SWITCH_DEBUGGING
1089 WriteLog("HIRES on (write)\n");
1090 #endif
1091                 hiRes = true;
1092         }
1093         else if (addr == 0xC05E)
1094         {
1095 #ifdef SOFT_SWITCH_DEBUGGING
1096 WriteLog("DHIRES on (write)\n");
1097 #endif
1098                 if (ioudis)
1099                         dhires = true;
1100
1101 //static int goDumpDis = 0;
1102 //goDumpDis++;
1103 //if (goDumpDis == 2)
1104 //      dumpDis = true;
1105         }
1106         else if (addr == 0xC05F)
1107         {
1108 #ifdef SOFT_SWITCH_DEBUGGING
1109 WriteLog("DHIRES off (write)\n");
1110 #endif
1111                 if (ioudis)
1112                         dhires = false;
1113         }
1114         else if (addr == 0xC07E)
1115         {
1116 #ifdef SOFT_SWITCH_DEBUGGING
1117 WriteLog("IOUDIS on (write)\n");
1118 #endif
1119                 ioudis = true;
1120         }
1121         else if (addr == 0xC07F)
1122         {
1123 #ifdef SOFT_SWITCH_DEBUGGING
1124 WriteLog("IOUDIS off (write)\n");
1125 #endif
1126                 ioudis = false;
1127         }
1128         else if ((addr & 0xFFFB) == 0xC080)
1129         {
1130 #ifdef DEBUG_LC
1131 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
1132 #endif
1133 //$C080 49280              OECG  R   Read RAM bank 2; no write
1134                 visibleBank = LC_BANK_2;
1135                 readRAM = true;
1136                 writeRAM = false;
1137         }
1138         else if ((addr & 0xFFFB) == 0xC081)
1139         {
1140 #ifdef DEBUG_LC
1141 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
1142 #endif
1143 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
1144                 visibleBank = LC_BANK_2;
1145                 readRAM = false;
1146                 writeRAM = true;
1147         }
1148         else if ((addr & 0xFFFB) == 0xC082)
1149         {
1150 #ifdef DEBUG_LC
1151 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
1152 #endif
1153 //$C082 49282              OECG  R   Read ROM; no write
1154                 visibleBank = LC_BANK_2;
1155                 readRAM = false;
1156                 writeRAM = false;
1157         }
1158         else if ((addr & 0xFFFB) == 0xC083)
1159         {
1160 #ifdef DEBUG_LC
1161 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
1162 #endif
1163 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
1164                 visibleBank = LC_BANK_2;
1165                 readRAM = true;
1166                 writeRAM = true;
1167         }
1168         else if ((addr & 0xFFFB) == 0xC088)
1169         {
1170 #ifdef DEBUG_LC
1171 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
1172 #endif
1173 //$C088 49288              OECG  R   Read RAM bank 1; no write
1174                 visibleBank = LC_BANK_1;
1175                 readRAM = true;
1176                 writeRAM = false;
1177         }
1178         else if ((addr & 0xFFFB) == 0xC089)
1179         {
1180 #ifdef DEBUG_LC
1181 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
1182 #endif
1183 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
1184                 visibleBank = LC_BANK_1;
1185                 readRAM = false;
1186                 writeRAM = true;
1187         }
1188         else if ((addr & 0xFFFB) == 0xC08A)
1189         {
1190 #ifdef DEBUG_LC
1191 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
1192 #endif
1193 //$C08A 49290              OECG  R   Read ROM; no write
1194                 visibleBank = LC_BANK_1;
1195                 readRAM = false;
1196                 writeRAM = false;
1197         }
1198         else if ((addr & 0xFFFB) == 0xC08B)
1199         {
1200 #ifdef DEBUG_LC
1201 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
1202 #endif
1203 //$C08B 49291              OECG  RR  Read/write RAM bank 1
1204                 visibleBank = LC_BANK_1;
1205                 readRAM = true;
1206                 writeRAM = true;
1207         }
1208 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
1209         else if ((addr & 0xFFF8) == 0xC0E0)
1210         {
1211                 floppyDrive.ControlStepper(addr & 0x07);
1212         }
1213         else if ((addr & 0xFFFE) == 0xC0E8)
1214         {
1215                 floppyDrive.ControlMotor(addr & 0x01);
1216         }
1217         else if ((addr & 0xFFFE) == 0xC0EA)
1218         {
1219                 floppyDrive.DriveEnable(addr & 0x01);
1220         }
1221         else if (addr == 0xC0EC)
1222         {
1223 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
1224 //or DoIO
1225                 floppyDrive.ReadWrite();
1226         }
1227         else if (addr == 0xC0ED)
1228         {
1229                 floppyDrive.SetLatchValue(b);
1230         }
1231         else if (addr == 0xC0EE)
1232         {
1233                 floppyDrive.SetReadMode();
1234         }
1235         else if (addr == 0xC0EF)
1236         {
1237                 floppyDrive.SetWriteMode();
1238         }
1239 //Still need to add missing I/O switches here...
1240
1241 //DEEE: BD 10 BF       LDA   $BF10,X    [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
1242 #if 0
1243 if (addr >= 0xD000 && addr <= 0xD00F)
1244 {
1245         WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
1246 }
1247 #endif
1248         if (addr >= 0xC000 && addr <= 0xCFFF)
1249                 return; // Protect LC bank #1 from being written to!
1250
1251         if (addr >= 0xD000)
1252         {
1253                 if (writeRAM)
1254                 {
1255 #if 0
1256                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
1257                                 ram[addr - 0x1000] = b;
1258                         else
1259                                 ram[addr] = b;
1260 #else
1261                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
1262                         {
1263                                 if (altzp)
1264                                         ram2[addr - 0x1000] = b;
1265                                 else
1266                                         ram[addr - 0x1000] = b;
1267                         }
1268                         else
1269                         {
1270                                 if (altzp)
1271                                         ram2[addr] = b;
1272                                 else
1273                                         ram[addr] = b;
1274                         }
1275 #endif
1276                 }
1277
1278                 return;
1279         }
1280
1281         // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
1282         if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
1283         {
1284                 if (displayPage2)
1285                         ram2[addr] = b;
1286                 else
1287                         ram[addr] = b;
1288
1289                 return;
1290         }
1291
1292         // Finally, check for auxillary/altzp write switches
1293 #if 0
1294         if (ramwrt)
1295                 ram2[addr] = b;
1296         else
1297         {
1298                 if (altzp)
1299                         ram2[addr] = b;
1300                 else
1301                         ram[addr] = b;
1302         }
1303 #else
1304         if (addr < 0x0200)
1305 //      if (addr < 0x0200 || addr >= 0xD000)
1306         {
1307                 if (altzp)
1308                         ram2[addr] = b;
1309                 else
1310                         ram[addr] = b;
1311         }
1312         else
1313         {
1314                 if (ramwrt)
1315                         ram2[addr] = b;
1316                 else
1317                         ram[addr] = b;
1318         }
1319 #endif
1320 }
1321
1322
1323 //
1324 // Load a file into RAM/ROM image space
1325 //
1326 bool LoadImg(char * filename, uint8_t * ram, int size)
1327 {
1328         FILE * fp = fopen(filename, "rb");
1329
1330         if (fp == NULL)
1331                 return false;
1332
1333         fread(ram, 1, size, fp);
1334         fclose(fp);
1335
1336         return true;
1337 }
1338
1339
1340 static void SaveApple2State(const char * filename)
1341 {
1342 }
1343
1344
1345 static bool LoadApple2State(const char * filename)
1346 {
1347         return false;
1348 }
1349
1350
1351 #ifdef CPU_CLOCK_CHECKING
1352 uint8_t counter = 0;
1353 uint32_t totalCPU = 0;
1354 uint64_t lastClock = 0;
1355 #endif
1356 //
1357 // Main loop
1358 //
1359 int main(int /*argc*/, char * /*argv*/[])
1360 {
1361         InitLog("./apple2.log");
1362         LoadSettings();
1363         srand(time(NULL));                                                                      // Initialize RNG
1364
1365         // Zero out memory
1366 //Need to bankify this stuff for the IIe emulation...
1367         memset(ram, 0, 0x10000);
1368         memset(rom, 0, 0x10000);
1369         memset(ram2, 0, 0x10000);
1370
1371         // Set up V65C02 execution context
1372         memset(&mainCPU, 0, sizeof(V65C02REGS));
1373         mainCPU.RdMem = RdMem;
1374         mainCPU.WrMem = WrMem;
1375         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1376
1377 //      alternateCharset = true;
1378 //      if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
1379         if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000))
1380         {
1381                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
1382                 return -1;
1383         }
1384
1385 //This is now included...
1386 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
1387         {
1388                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
1389 //              return -1;
1390         }//*/
1391
1392 //Load up disk image from config file (for now)...
1393         floppyDrive.LoadImage(settings.diskImagePath1, 0);
1394         floppyDrive.LoadImage(settings.diskImagePath2, 1);
1395 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
1396
1397 //Kill the DOS ROM in slot 6 for now...
1398 //not
1399 //      memcpy(rom + 0xC600, diskROM, 0x100);
1400 //      memcpy(rom + 0xC700, diskROM, 0x100);   // Slot 7???
1401
1402         WriteLog("About to initialize video...\n");
1403
1404         if (!InitVideo())
1405         {
1406                 std::cout << "Aborting!" << std::endl;
1407                 return -1;
1408         }
1409
1410         // Have to do this *after* video init but *before* sound init...!
1411 //Shouldn't be necessary since we're not doing emulation in the ISR...
1412         if (settings.autoStateSaving)
1413         {
1414                 // Load last state from file...
1415                 if (!LoadApple2State(settings.autoStatePath))
1416                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
1417         }
1418
1419
1420 #if 0
1421 // State loading!
1422 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
1423 {
1424         cout << "Couldn't load state file!" << endl;
1425         cout << "Aborting!!" << endl;
1426         return -1;
1427 }
1428
1429 //A  P  Y  X  S     PC
1430 //-- -- -- -- ----- -----
1431 //00 75 3B 53 FD 01 41 44
1432
1433 mainCPU.cpuFlags = 0;
1434 mainCPU.a = 0x00;
1435 mainCPU.x = 0x53;
1436 mainCPU.y = 0x3B;
1437 mainCPU.cc = 0x75;
1438 mainCPU.sp = 0xFD;
1439 mainCPU.pc = 0x4441;
1440
1441 textMode = false;
1442 mixedMode = false;
1443 displayPage2 = false;
1444 hiRes = true;
1445
1446 //kludge...
1447 readHiRam = true;
1448 //dumpDis=true;
1449 //kludge II...
1450 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
1451 #endif
1452
1453         WriteLog("About to initialize audio...\n");
1454         SoundInit();
1455 //nope  SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
1456
1457 //      gui = new GUI(surface);                                         // Set up the GUI system object...
1458 //      gui = new GUI(mainSurface);                                     // Set up the GUI system object...
1459 // SDL 2... this will likely cause Apple 2 to crash
1460 //      gui = new GUI(NULL);                                    // Set up the GUI system object...
1461 #if 0
1462         gui->AddMenuTitle("Apple2");
1463         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
1464         gui->AddMenuItem("");
1465         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
1466         gui->CommitItemsToMenu();
1467 #endif
1468
1469         SetupBlurTable();                                                       // Set up the color TV emulation blur table
1470         running = true;                                                         // Set running status...
1471
1472         InitializeEventList();                                          // Clear the event list before we use it...
1473         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
1474         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
1475         startTicks = SDL_GetTicks();
1476
1477 #ifdef THREADED_65C02
1478         cpuCond = SDL_CreateCond();
1479         mainSem = SDL_CreateSemaphore(1);
1480         cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
1481 //Hmm... CPU does POST (+1), wait, then WAIT (-1)
1482 //      SDL_sem * mainMutex = SDL_CreateMutex();
1483 #endif
1484
1485         WriteLog("Entering main loop...\n");
1486         while (running)
1487         {
1488                 double timeToNextEvent = GetTimeToNextEvent();
1489 #ifndef THREADED_65C02
1490                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
1491 #endif
1492 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
1493 //(Fix so that this is not a requirement!)
1494 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
1495 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
1496
1497 #ifdef CPU_CLOCK_CHECKING
1498 #ifndef THREADED_65C02
1499 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
1500 #endif
1501 #endif
1502                 // Handle CPU time delta for sound...
1503 //Don't need this anymore now that we use absolute time...
1504 //              AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
1505                 HandleNextEvent();
1506         }
1507
1508 #ifdef THREADED_65C02
1509 WriteLog("Main: cpuFinished = true;\n");
1510 cpuFinished = true;
1511 //#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE]
1512 //What to do? How do you know when the CPU is sleeping???
1513 //USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1!
1514 #if 0
1515 SDL_mutexP(mainMutex);
1516 SDL_CondWait(mainCond, mainMutex);      // Wait for CPU thread to get to signal point...
1517 SDL_mutexV(mainMutex);
1518 #else
1519 //Nope, use a semaphore...
1520 WriteLog("Main: SDL_SemWait(mainSem);\n");
1521 SDL_SemWait(mainSem);//should lock until CPU thread is waiting...
1522 #endif
1523
1524 WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
1525 SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
1526 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
1527 SDL_WaitThread(cpuThread, NULL);
1528 //nowok:SDL_WaitThread(CPUThreadFunc, NULL);
1529 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
1530 SDL_DestroyCond(cpuCond);
1531
1532 //SDL_DestroyMutex(mainMutex);
1533 SDL_DestroySemaphore(mainSem);
1534 #endif
1535
1536         if (settings.autoStateSaving)
1537         {
1538                 // Save state here...
1539                 SaveApple2State(settings.autoStatePath);
1540         }
1541 floppyDrive.SaveImage();
1542
1543         SoundDone();
1544         VideoDone();
1545         SaveSettings();
1546         LogDone();
1547
1548         return 0;
1549 }
1550
1551
1552 /*
1553 Apple II keycodes
1554 -----------------
1555
1556 Key     Aln CTL SHF BTH
1557 -----------------------
1558 space   $A0     $A0     $A0 $A0         No xlation
1559 RETURN  $8D     $8D     $8D     $8D             No xlation
1560 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
1561 1!              $B1 $B1 $A1 $A1         No xlation
1562 2"              $B2     $B2     $A2     $A2             No xlation
1563 3#              $B3     $B3     $A3     $A3             No xlation
1564 4$              $B4     $B4     $A4     $A4             No xlation
1565 5%              $B5     $B5     $A5     $A5             No xlation
1566 6&              $B6     $B6     $A6     $A6             No xlation
1567 7'              $B7     $B7     $A7     $A7             No xlation
1568 8(              $B8     $B8     $A8     $A8             No xlation
1569 9)              $B9     $B9     $A9     $A9             No xlation
1570 :*              $BA     $BA     $AA     $AA             No xlation
1571 ;+              $BB     $BB     $AB     $AB             No xlation
1572 ,<              $AC     $AC     $BC     $BC             No xlation
1573 -=              $AD     $AD     $BD     $BD             No xlation
1574 .>              $AE     $AE     $BE     $BE             No xlation
1575 /?              $AF     $AF     $BF     $BF             No xlation
1576 A               $C1     $81     $C1     $81
1577 B               $C2     $82     $C2     $82
1578 C               $C3     $83     $C3     $83
1579 D               $C4     $84     $C4     $84
1580 E               $C5     $85     $C5     $85
1581 F               $C6     $86     $C6     $86
1582 G               $C7     $87     $C7     $87
1583 H               $C8     $88     $C8     $88
1584 I               $C9     $89     $C9     $89
1585 J               $CA     $8A     $CA     $8A
1586 K               $CB     $8B     $CB     $8B
1587 L               $CC     $8C     $CC     $8C
1588 M               $CD     $8D     $DD     $9D             -> ODD
1589 N^              $CE     $8E     $DE     $9E             -> ODD
1590 O               $CF     $8F     $CF     $8F
1591 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
1592 Q               $D1     $91     $D1     $91
1593 R               $D2     $92     $D2     $92
1594 S               $D3     $93     $D3     $93
1595 T               $D4     $94     $D4     $94
1596 U               $D5     $95     $D5     $95
1597 V               $D6     $96     $D6     $96
1598 W               $D7     $97     $D7     $97
1599 X               $D8     $98     $D8     $98
1600 Y               $D9     $99     $D9     $99
1601 Z               $DA     $9A     $DA     $9A
1602 <-              $88     $88     $88     $88
1603 ->              $95     $95     $95     $95
1604 ESC             $9B     $9B     $9B     $9B             No xlation
1605
1606 */
1607 //static uint64_t lastCPUCycles = 0;
1608 static uint32_t frameCount = 0;
1609 static void FrameCallback(void)
1610 {
1611         SDL_Event event;
1612
1613         while (SDL_PollEvent(&event))
1614         {
1615                 switch (event.type)
1616                 {
1617                 case SDL_TEXTINPUT:
1618 //Need to do some key translation here, and screen out non-apple keys as well...
1619 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
1620 // everything else done separately. this is slightly easier. :-P)
1621 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
1622                         if (event.edit.text[0] == '\t') // Prelim key screening...
1623                                 break;
1624
1625                         lastKeyPressed = event.edit.text[0];
1626                         keyDown = true;
1627
1628                         //kludge: should have a caps lock thingy here...
1629                         //or all uppercase for ][+...
1630 //                      if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
1631 //                              lastKeyPressed &= 0xDF;         // Convert to upper case...
1632
1633                         break;
1634                 case SDL_KEYDOWN:
1635                         // CTRL+RESET key emulation (mapped to CTRL+`)
1636 // This doesn't work...
1637 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
1638 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
1639                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
1640 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
1641 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
1642                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1643
1644                         if (event.key.keysym.sym == SDLK_RIGHT)
1645                                 lastKeyPressed = 0x15, keyDown = true;
1646                         else if (event.key.keysym.sym == SDLK_LEFT)
1647                                 lastKeyPressed = 0x08, keyDown = true;
1648                         else if (event.key.keysym.sym == SDLK_UP)
1649                                 lastKeyPressed = 0x0B, keyDown = true;
1650                         else if (event.key.keysym.sym == SDLK_DOWN)
1651                                 lastKeyPressed = 0x0A, keyDown = true;
1652                         else if (event.key.keysym.sym == SDLK_RETURN)
1653                                 lastKeyPressed = 0x0D, keyDown = true;
1654                         else if (event.key.keysym.sym == SDLK_ESCAPE)
1655                                 lastKeyPressed = 0x1B, keyDown = true;
1656                         else if (event.key.keysym.sym == SDLK_BACKSPACE)
1657                                 lastKeyPressed = 0x7F, keyDown = true;
1658
1659                         // Fix CTRL+key combo...
1660                         if (event.key.keysym.mod & KMOD_CTRL)
1661                         {
1662                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1663                                 {
1664                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
1665                                         keyDown = true;
1666 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
1667                                 }
1668                         }
1669
1670                         // Use ALT+Q to exit, as well as the usual window decoration method
1671                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1672                                 running = false;
1673
1674                         // Paddle buttons 0 & 1
1675                         if (event.key.keysym.sym == SDLK_INSERT)
1676                                 openAppleDown = true;
1677                         if (event.key.keysym.sym == SDLK_PAGEUP)
1678                                 closedAppleDown = true;
1679
1680                         if (event.key.keysym.sym == SDLK_F11)
1681                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
1682 //                      else if (event.key.keysym.sym == SDLK_F11)
1683 //                              floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1684 else if (event.key.keysym.sym == SDLK_F9)
1685 {
1686         floppyDrive.CreateBlankImage();
1687 //      SpawnMessage("Image cleared...");
1688 }//*/
1689 else if (event.key.keysym.sym == SDLK_F10)
1690 {
1691         floppyDrive.SwapImages();
1692 //      SpawnMessage("Image swapped...");
1693 }//*/
1694
1695                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1696                                 TogglePalette();
1697                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1698                                 CycleScreenTypes();
1699
1700 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1701                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1702 //NOTE: Should parse the output to determine whether or not the user requested
1703 //      to quit completely... !!! FIX !!!
1704                                 gui->Run();
1705
1706                         if (event.key.keysym.sym == SDLK_F5)
1707                         {
1708                                 VolumeDown();
1709                                 char volStr[19] = "[****************]";
1710 //                              volStr[GetVolume()] = 0;
1711                                 for(int i=GetVolume(); i<16; i++)
1712                                         volStr[1 + i] = '-';
1713                                 SpawnMessage("Volume: %s", volStr);
1714                         }
1715                         else if (event.key.keysym.sym == SDLK_F6)
1716                         {
1717                                 VolumeUp();
1718                                 char volStr[19] = "[****************]";
1719 //                              volStr[GetVolume()] = 0;
1720                                 for(int i=GetVolume(); i<16; i++)
1721                                         volStr[1 + i] = '-';
1722                                 SpawnMessage("Volume: %s", volStr);
1723                         }
1724
1725                         static bool fullscreenDebounce = false;
1726
1727                         if (event.key.keysym.sym == SDLK_F12)
1728                         {
1729                                 if (!fullscreenDebounce)
1730                                 {
1731                                         ToggleFullScreen();
1732                                         fullscreenDebounce = true;
1733                                 }
1734                         }
1735 //                      else
1736
1737                         break;
1738                 case SDL_KEYUP:
1739                         if (event.key.keysym.sym == SDLK_F12)
1740                                 fullscreenDebounce = false;
1741
1742                         // Paddle buttons 0 & 1
1743                         if (event.key.keysym.sym == SDLK_INSERT)
1744                                 openAppleDown = false;
1745                         if (event.key.keysym.sym == SDLK_PAGEUP)
1746                                 closedAppleDown = false;
1747
1748 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1749 //                              keyDown = false;
1750
1751                         break;
1752                 case SDL_QUIT:
1753                         running = false;
1754                 }
1755         }
1756
1757 //#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
1758         RenderVideoFrame();
1759         SetCallbackTime(FrameCallback, 16666.66666667);
1760
1761 #ifdef CPU_CLOCK_CHECKING
1762 //We know it's stopped, so we can get away with this...
1763 counter++;
1764 if (counter == 60)
1765 {
1766         uint64_t clock = GetCurrentV65C02Clock();
1767 //totalCPU += (uint32_t)(clock - lastClock);
1768
1769         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
1770         lastClock = clock;
1771 //      totalCPU = 0;
1772         counter = 0;
1773 }
1774 #endif
1775 //Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE]
1776 //lessee...
1777 //nope.
1778 //Actually, slows things down too much...
1779 //SDL_Delay(10);
1780 //      while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1781
1782 // This is the problem: If you set the interval to 16, it runs faster than
1783 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
1784 // have it do 16 for one frame, then 17 for two others. Then it should average
1785 // out to 1/60s per frame every 3 frames.
1786         frameCount = (frameCount + 1) % 3;
1787
1788         uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
1789
1790         while (SDL_GetTicks() - startTicks < waitFrameTime)
1791                 SDL_Delay(1);                                                   // Wait for next frame...
1792
1793         startTicks = SDL_GetTicks();
1794 #if 0
1795         uint64_t cpuCycles = GetCurrentV65C02Clock();
1796         uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
1797         WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
1798         lastCPUCycles = cpuCycles;
1799 #endif
1800
1801 //let's wait, then signal...
1802 //works longer, but then still falls behind...
1803 #ifdef THREADED_65C02
1804         SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
1805 #endif
1806 }
1807
1808
1809 static void BlinkTimer(void)
1810 {
1811         flash = !flash;
1812         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1813 }
1814
1815
1816 /*
1817 Next problem is this: How to have events occur and synchronize with the rest
1818 of the threads?
1819
1820   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
1821     remainder CPU cycles over...)
1822
1823 One way would be to use a fractional accumulator, then subtract 1 every
1824 time it overflows. Like so:
1825
1826 double overflow = 0;
1827 uint32_t time = 20;
1828 while (!done)
1829 {
1830         Execute6808(&soundCPU, time);
1831         overflow += 0.289115646;
1832         if (overflow > 1.0)
1833         {
1834                 overflow -= 1.0;
1835                 time = 21;
1836         }
1837         else
1838                 time = 20;
1839 }
1840 */