]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Convert sound driving method to direct sampling.
[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
65 // Global variables
66
67 uint8_t ram[0x10000], rom[0x10000];                             // RAM & ROM spaces
68 uint8_t ram2[0x10000];
69 uint8_t diskRom[0x100];                                                 // Disk ROM space
70 V65C02REGS mainCPU;                                                             // v65C02 execution context
71 uint8_t appleType = APPLE_TYPE_II;
72 FloppyDrive floppyDrive;
73
74 // Local variables
75
76 static uint8_t lastKeyPressed = 0;
77 static bool keyDown = false;
78 static bool openAppleDown = false;
79 static bool closedAppleDown = false;
80
81 //static FloppyDrive floppyDrive;
82
83 enum { LC_BANK_1, LC_BANK_2 };
84
85 static uint8_t visibleBank = LC_BANK_1;
86 static bool readRAM = false;
87 static bool writeRAM = false;
88
89 static bool running = true;                                             // Machine running state flag...
90 static uint32_t startTicks;
91
92 static GUI * gui = NULL;
93
94 // Local functions (technically, they're global...)
95
96 bool LoadImg(char * filename, uint8_t * ram, int size);
97 uint8_t RdMem(uint16_t addr);
98 void WrMem(uint16_t addr, uint8_t b);
99 static void SaveApple2State(const char * filename);
100 static bool LoadApple2State(const char * filename);
101
102 // Local timer callback functions
103
104 static void FrameCallback(void);
105 static void BlinkTimer(void);
106
107 #ifdef THREADED_65C02
108 // Test of threaded execution of 6502
109 static SDL_Thread * cpuThread = NULL;
110 //static SDL_mutex * cpuMutex = NULL;
111 static SDL_cond * cpuCond = NULL;
112 static SDL_sem * mainSem = NULL;
113 static bool cpuFinished = false;
114 static bool cpuSleep = false;
115
116
117 // Let's try a thread...
118 /*
119 Here's how it works: Execute 1 frame's worth, then sleep.
120 Other stuff wakes it up
121 */
122 int CPUThreadFunc(void * data)
123 {
124         // Mutex must be locked for conditional to work...
125         // Also, must be created in the thread that uses it...
126         SDL_mutex * cpuMutex = SDL_CreateMutex();
127
128 // decrement mainSem...
129 //SDL_SemWait(mainSem);
130 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
131         float overflow = 0.0;
132 #endif
133
134         do
135         {
136                 if (cpuSleep)
137                         SDL_CondWait(cpuCond, cpuMutex);
138
139 // decrement mainSem...
140 #ifdef THREAD_DEBUGGING
141 WriteLog("CPU: SDL_SemWait(mainSem);\n");
142 #endif
143 SDL_SemWait(mainSem);
144
145 // There are exactly 800 slices of 21.333 cycles per frame, so it works out
146 // evenly.
147 #if 0
148                 uint32_t cycles = 17066;
149 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
150 // ODD! It's closer *without* this overflow compensation. ??? WHY ???
151                 overflow += 0.666666667;
152
153                 if (overflow > 1.0)
154                 {
155                         overflow -= 1.0;
156                         cycles++;
157                 }
158 #endif
159
160 #ifdef THREAD_DEBUGGING
161 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
162 #endif
163                 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!
164
165                 // Adjust the sound routine's last cycle toggled time base
166                 // Also, since we're finished executing, .clock is now valid
167 #ifdef THREAD_DEBUGGING
168 WriteLog("CPU: AdjustLastToggleCycles(mainCPU.clock);\n");
169 #endif
170                 AdjustLastToggleCycles(mainCPU.clock);
171 #else
172 #ifdef THREAD_DEBUGGING
173 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
174 #endif
175                 for(int i=0; i<800; i++)
176                 {
177                         uint32_t cycles = 21;
178                         overflow += 0.333333334;
179
180                         if (overflow > 1.0)
181                         {
182                                 cycles++;
183                                 overflow -= 1.0;
184                         }
185
186                         Execute65C02(&mainCPU, cycles);
187                         WriteSampleToBuffer();
188                 }
189 #endif
190
191
192 #ifdef THREAD_DEBUGGING
193 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
194 #endif
195                 SDL_mutexP(cpuMutex);
196 #if 0
197                 if (SDL_CondWait(cpuCond, cpuMutex) != 0)
198                 {
199                         printf("SDL_CondWait != 0! (Error: '%s')\n", SDL_GetError());
200                         exit(-1);
201                 }
202 #else
203 // increment mainSem...
204 #ifdef THREAD_DEBUGGING
205 WriteLog("CPU: SDL_SemPost(mainSem);\n");
206 #endif
207 SDL_SemPost(mainSem);
208 //              SDL_CondSignal(mainCond);       // In case something is waiting on us...
209
210 #ifdef THREAD_DEBUGGING
211 WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
212 #endif
213                 SDL_CondWait(cpuCond, cpuMutex);
214
215 #endif
216 #ifdef THREAD_DEBUGGING
217 WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
218 #endif
219                 SDL_mutexV(cpuMutex);
220         }
221         while (!cpuFinished);
222
223         SDL_DestroyMutex(cpuMutex);
224
225         return 0;
226 }
227 #endif
228
229
230 // Test GUI function
231
232 Element * TestWindow(void)
233 {
234         Element * win = new DraggableWindow2(10, 10, 128, 128);
235 //      ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
236
237         return win;
238 }
239
240
241 Element * QuitEmulator(void)
242 {
243         gui->Stop();
244         running = false;
245
246         return NULL;
247 }
248
249
250 /*
251  Small Apple II memory map:
252
253 $C010 - Clear bit 7 of keyboard data ($C000)
254 $C030 - Toggle speaker diaphragm
255 $C051 - Display text
256 $C054 - Select page 1
257 $C056 - Select lo-res
258 $C058 - Set annuciator-0 output to 0
259 $C05A - Set annuciator-0 output to 0
260 $C05D - Set annuciator-0 output to 1
261 $C05F - Set annuciator-0 output to 1
262 $C0E0 - Disk control stepper ($C0E0-7)
263 $C0E9 - Disk control motor (on)
264 $C0EA - Disk enable (drive 1)
265 $C0EC - Disk R/W
266 $C0EE - Disk set read mode
267 */
268
269 //
270 // V65C02 read/write memory functions
271 //
272
273 uint8_t RdMem(uint16_t addr)
274 {
275         uint8_t b;
276
277 #if 0
278 if (addr >= 0xC000 && addr <= 0xC0FF)
279         WriteLog("\n*** Read at I/O address %04X\n", addr);
280 #endif
281 #if 0
282 if (addr >= 0xC080 && addr <= 0xC08F)
283         WriteLog("\n*** Read at I/O address %04X\n", addr);
284 #endif
285
286         if ((addr & 0xFFF0) == 0xC000)                  // Read $C000-$C00F
287         {
288                 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
289         }
290         else if ((addr & 0xFFF0) == 0xC010)             // Read $C010-$C01F
291         {
292 //This is bogus: keyDown is set to false, so return val NEVER is set...
293 //Fixed...
294 //Also, this is IIe/IIc only...!
295                 uint8_t retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
296                 keyDown = false;
297                 return retVal;
298         }
299         else if ((addr & 0xFFF0) == 0xC030)             // Read $C030-$C03F
300         {
301 /*
302 This is problematic, mainly because the v65C02 removes actual cycles run after each call.
303 Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
304 How to fix?
305
306 What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
307 hasn't had any changes in it then we just fill it with the last sample value and are done. Then
308 we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
309 last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
310 How to keep track?
311
312 Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
313 deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
314
315
316
317 */
318                 ToggleSpeaker(GetCurrentV65C02Clock());
319 //should it return something else here???
320                 return 0x00;
321         }
322         else if (addr == 0xC050)                                // Read $C050
323         {
324                 textMode = false;
325         }
326         else if (addr == 0xC051)                                // Read $C051
327         {
328                 textMode = true;
329         }
330         else if (addr == 0xC052)                                // Read $C052
331         {
332                 mixedMode = false;
333         }
334         else if (addr == 0xC053)                                // Read $C053
335         {
336                 mixedMode = true;
337         }
338         else if (addr == 0xC054)                                // Read $C054
339         {
340                 displayPage2 = false;
341         }
342         else if (addr == 0xC055)                                // Read $C055
343         {
344                 displayPage2 = true;
345         }
346         else if (addr == 0xC056)                                // Read $C056
347         {
348                 hiRes = false;
349         }
350         else if (addr == 0xC057)                                // Read $C057
351         {
352                 hiRes = true;
353         }
354         else if (addr == 0xC061)                                // Read $C061
355         {
356                 // Open Apple key (or push button 0)
357                 return (openAppleDown ? 0x80 : 0x00);
358         }
359         else if (addr == 0xC062)                                // Read $C062
360         {
361                 // Open Apple key (or push button 0)
362                 return (closedAppleDown ? 0x80 : 0x00);
363         }
364
365 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
366 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
367 //[SHOULD BE FIXED NOW]
368 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
369 //visible, two makes it R/W.
370
371 /*
372 301  LDA $E000
373 304  PHA
374 305  LDA $C081
375 308  PLA
376 309  PHA
377 30A  CMP $E000
378 30D  BNE $332
379 30F  LDA $C083
380 312  LDA $C083
381 315  LDA #$A5
382 317  STA $D000
383 31A  CMP $D000
384 31D  BNE $332
385 31F  LSR A
386 320  STA $D000
387 323  CMP $D000
388 326  BNE $332
389 328  LDA $C081
390 32B  LDA $C081
391 32E  LDA #$01
392 330  BNE $334
393 332  LDA #$00
394 334  STA $300
395 337  PLA
396 338  CMP $E000
397 33B  BEQ $340
398 33D  LDA $C080
399 340  RTS
400
401 A = PEEK($C082)
402 */
403
404         else if ((addr & 0xFFFB) == 0xC080)
405         {
406 #ifdef DEBUG_LC
407 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
408 #endif
409 //$C080 49280              OECG  R   Read RAM bank 2; no write
410                 visibleBank = LC_BANK_2;
411                 readRAM = true;
412                 writeRAM = false;
413         }
414         else if ((addr & 0xFFFB) == 0xC081)
415         {
416 #ifdef DEBUG_LC
417 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
418 #endif
419 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
420                 visibleBank = LC_BANK_2;
421                 readRAM = false;
422                 writeRAM = true;
423         }
424         else if ((addr & 0xFFFB) == 0xC082)
425         {
426 #ifdef DEBUG_LC
427 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
428 #endif
429 //$C082 49282              OECG  R   Read ROM; no write
430                 visibleBank = LC_BANK_2;
431                 readRAM = false;
432                 writeRAM = false;
433         }
434         else if ((addr & 0xFFFB) == 0xC083)
435         {
436 #ifdef DEBUG_LC
437 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
438 #endif
439 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
440                 visibleBank = LC_BANK_2;
441                 readRAM = true;
442                 writeRAM = true;
443         }
444         else if ((addr & 0xFFFB) == 0xC088)
445         {
446 #ifdef DEBUG_LC
447 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
448 #endif
449 //$C088 49288              OECG  R   Read RAM bank 1; no write
450                 visibleBank = LC_BANK_1;
451                 readRAM = true;
452                 writeRAM = false;
453 //Hm. Some stuff seems to want this.
454 //nope, was looking at $C0E8... return 0xFF;
455         }
456         else if ((addr & 0xFFFB) == 0xC089)
457         {
458 #ifdef DEBUG_LC
459 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
460 #endif
461 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
462                 visibleBank = LC_BANK_1;
463                 readRAM = false;
464                 writeRAM = true;
465         }
466         else if ((addr & 0xFFFB) == 0xC08A)
467         {
468 #ifdef DEBUG_LC
469 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
470 #endif
471 //$C08A 49290              OECG  R   Read ROM; no write
472                 visibleBank = LC_BANK_1;
473                 readRAM = false;
474                 writeRAM = false;
475         }
476         else if ((addr & 0xFFFB) == 0xC08B)
477         {
478 #ifdef DEBUG_LC
479 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
480 #endif
481 //$C08B 49291              OECG  RR  Read/write RAM bank 1
482                 visibleBank = LC_BANK_1;
483                 readRAM = true;
484                 writeRAM = true;
485         }
486         else if ((addr & 0xFFF8) == 0xC0E0)
487         {
488                 floppyDrive.ControlStepper(addr & 0x07);
489         }
490         else if ((addr & 0xFFFE) == 0xC0E8)
491         {
492                 floppyDrive.ControlMotor(addr & 0x01);
493         }
494         else if ((addr & 0xFFFE) == 0xC0EA)
495         {
496                 floppyDrive.DriveEnable(addr & 0x01);
497         }
498         else if (addr == 0xC0EC)
499         {
500                 return floppyDrive.ReadWrite();
501         }
502         else if (addr == 0xC0ED)
503         {
504                 return floppyDrive.GetLatchValue();
505         }
506         else if (addr == 0xC0EE)
507         {
508                 floppyDrive.SetReadMode();
509         }
510         else if (addr == 0xC0EF)
511         {
512                 floppyDrive.SetWriteMode();
513         }
514
515 //#define LC_DEBUGGING
516 #ifdef LC_DEBUGGING
517 bool showpath = false;
518 if (addr >= 0xD000 && addr <= 0xD00F)
519         showpath = true;
520 #endif
521 //This sux...
522         if (addr >= 0xC100 && addr <= 0xCFFF)   // The $C000-$CFFF block is *never* RAM
523 #ifdef LC_DEBUGGING
524         {
525 #endif
526                 b = rom[addr];
527 #ifdef LC_DEBUGGING
528 if (showpath)
529         WriteLog("b is from $C100-$CFFF block...\n");
530         }
531 #endif
532         else if (addr >= 0xD000)
533         {
534                 if (readRAM)
535                 {
536                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
537 #ifdef LC_DEBUGGING
538         {
539 #endif
540                                 b = ram[addr - 0x1000];
541 #ifdef LC_DEBUGGING
542 if (showpath)
543         WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
544         }
545 #endif
546                         else
547 #ifdef LC_DEBUGGING
548         {
549 #endif
550                                 b = ram[addr];
551 #ifdef LC_DEBUGGING
552 if (showpath)
553         WriteLog("b is from LC bank #2 (ram[addr])...\n");
554         }
555 #endif
556                 }
557                 else
558 #ifdef LC_DEBUGGING
559         {
560 #endif
561                         b = rom[addr];
562 #ifdef LC_DEBUGGING
563 if (showpath)
564         WriteLog("b is from LC ROM (rom[addr])...\n");
565         }
566 #endif
567         }
568         else
569 #ifdef LC_DEBUGGING
570         {
571 #endif
572                 b = ram[addr];
573 #ifdef LC_DEBUGGING
574 if (showpath)
575         WriteLog("b is from ram[addr]...\n");
576         }
577 #endif
578
579 #ifdef LC_DEBUGGING
580 if (addr >= 0xD000 && addr <= 0xD00F)
581 {
582         WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
583 }
584 #endif
585
586         return b;
587 }
588
589 /*
590 A-9 (Mockingboard)
591 APPENDIX F Assembly Language Program Listings
592
593         1       *PRIMARY ROUTINES
594         2       *FOR SLOT 4
595         3       *
596         4                       ORG     $9000
597         5       *                               ;ADDRESSES FOR FIRST 6522
598         6       ORB             EQU     $C400           ;PORT B
599         7       ORA             EQU     $C401           ;PORT A
600         8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
601         9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
602         10      *                                       ;ADDRESSES FOR SECOND 6522
603         11      ORB2            EQU     $C480           ;PORT B
604         12      ORA2            EQU     $C481           ;PORT A
605         13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
606         14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
607 */
608 void WrMem(uint16_t addr, uint8_t b)
609 {
610 //temp...
611 //extern V6809REGS regs;
612 //if (addr >= 0xC800 && addr <= 0xCBFE)
613 //if (addr == 0xC80F || addr == 0xC80D)
614 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
615
616 #if 0
617 if (addr >= 0xC000 && addr <= 0xC0FF)
618         WriteLog("\n*** Write at I/O address %04X\n", addr);
619 #endif
620 /*
621 Check the BIKO version on Asimov to see if it's been cracked or not...
622
623 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
624 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
625 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
626 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
627 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
628
629 ; INX here *ensures* 1 - 6!!! BUG!!!
630 ; Or is it? Could this be part of a braindead copy protection scheme? It's
631 ; awfully close to NOP ($EA)...
632 ; Nothing else touches it once it's been written... Hmm...
633
634 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
635 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
636 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
637 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
638
639 ; 4E13: 03 00
640 ; 4E15: 25 25 15 15 10 20
641 ; 4E1B: 03 41 99 99 01 00 12
642 ; 4E22: 99 70
643
644 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
645 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
646 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
647 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
648
649 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
650
651 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
652 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
653
654 ; Print "ALAKAZAM!" and so on...
655
656 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
657 */
658 #if 0
659 if (addr == 0x7F47)
660         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
661 #endif
662 /*
663 I think this is IIc/IIe only...
664
665 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
666 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
667
668 CLRAUXRD = $C002 ;read from main 48K (WR-only)
669 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
670
671 CLRAUXWR = $C004 ;write to main 48K (WR-only)
672 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
673
674 CLRCXROM = $C006 ;use ROM on cards (WR-only)
675 SETCXROM = $C007 ;use internal ROM (WR-only)
676
677 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
678 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
679
680 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
681 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
682
683 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
684 SET80VID = $C00D ;enable 80-column display mode (WR-only)
685
686 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
687 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
688 */
689         if (addr == 0xC00E)
690         {
691                 alternateCharset = false;
692         }
693         else if (addr == 0xC00F)
694         {
695                 alternateCharset = true;
696         }
697         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
698         {
699 //Actually, according to the A2 ref, this should do nothing since a write
700 //is immediately preceded by a read leaving it in the same state it was...
701 //But leaving this out seems to fuck up the key handling of some games...
702                 keyDown = false;
703         }
704         else if (addr == 0xC050)
705         {
706                 textMode = false;
707         }
708         else if (addr == 0xC051)
709         {
710                 textMode = true;
711         }
712         else if (addr == 0xC052)
713         {
714                 mixedMode = false;
715         }
716         else if (addr == 0xC053)
717         {
718                 mixedMode = true;
719         }
720         else if (addr == 0xC054)
721         {
722                 displayPage2 = false;
723         }
724         else if (addr == 0xC055)
725         {
726                 displayPage2 = true;
727         }
728         else if (addr == 0xC056)
729         {
730                 hiRes = false;
731         }
732         else if (addr == 0xC057)
733         {
734                 hiRes = true;
735         }
736         else if ((addr & 0xFFFB) == 0xC080)
737         {
738 #ifdef DEBUG_LC
739 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
740 #endif
741 //$C080 49280              OECG  R   Read RAM bank 2; no write
742                 visibleBank = LC_BANK_2;
743                 readRAM = true;
744                 writeRAM = false;
745         }
746         else if ((addr & 0xFFFB) == 0xC081)
747         {
748 #ifdef DEBUG_LC
749 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
750 #endif
751 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
752                 visibleBank = LC_BANK_2;
753                 readRAM = false;
754                 writeRAM = true;
755         }
756         else if ((addr & 0xFFFB) == 0xC082)
757         {
758 #ifdef DEBUG_LC
759 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
760 #endif
761 //$C082 49282              OECG  R   Read ROM; no write
762                 visibleBank = LC_BANK_2;
763                 readRAM = false;
764                 writeRAM = false;
765         }
766         else if ((addr & 0xFFFB) == 0xC083)
767         {
768 #ifdef DEBUG_LC
769 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
770 #endif
771 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
772                 visibleBank = LC_BANK_2;
773                 readRAM = true;
774                 writeRAM = true;
775         }
776         else if ((addr & 0xFFFB) == 0xC088)
777         {
778 #ifdef DEBUG_LC
779 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
780 #endif
781 //$C088 49288              OECG  R   Read RAM bank 1; no write
782                 visibleBank = LC_BANK_1;
783                 readRAM = true;
784                 writeRAM = false;
785         }
786         else if ((addr & 0xFFFB) == 0xC089)
787         {
788 #ifdef DEBUG_LC
789 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
790 #endif
791 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
792                 visibleBank = LC_BANK_1;
793                 readRAM = false;
794                 writeRAM = true;
795         }
796         else if ((addr & 0xFFFB) == 0xC08A)
797         {
798 #ifdef DEBUG_LC
799 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
800 #endif
801 //$C08A 49290              OECG  R   Read ROM; no write
802                 visibleBank = LC_BANK_1;
803                 readRAM = false;
804                 writeRAM = false;
805         }
806         else if ((addr & 0xFFFB) == 0xC08B)
807         {
808 #ifdef DEBUG_LC
809 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
810 #endif
811 //$C08B 49291              OECG  RR  Read/write RAM bank 1
812                 visibleBank = LC_BANK_1;
813                 readRAM = true;
814                 writeRAM = true;
815         }
816 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
817         else if ((addr & 0xFFF8) == 0xC0E0)
818         {
819                 floppyDrive.ControlStepper(addr & 0x07);
820         }
821         else if ((addr & 0xFFFE) == 0xC0E8)
822         {
823                 floppyDrive.ControlMotor(addr & 0x01);
824         }
825         else if ((addr & 0xFFFE) == 0xC0EA)
826         {
827                 floppyDrive.DriveEnable(addr & 0x01);
828         }
829         else if (addr == 0xC0EC)
830         {
831 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
832 //or DoIO
833                 floppyDrive.ReadWrite();
834         }
835         else if (addr == 0xC0ED)
836         {
837                 floppyDrive.SetLatchValue(b);
838         }
839         else if (addr == 0xC0EE)
840         {
841                 floppyDrive.SetReadMode();
842         }
843         else if (addr == 0xC0EF)
844         {
845                 floppyDrive.SetWriteMode();
846         }
847 //Still need to add missing I/O switches here...
848
849 //DEEE: BD 10 BF       LDA   $BF10,X    [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
850 #if 0
851 if (addr >= 0xD000 && addr <= 0xD00F)
852 {
853         WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
854 }
855 #endif
856         if (addr >= 0xC000 && addr <= 0xCFFF)
857                 return; // Protect LC bank #1 from being written to!
858
859         if (addr >= 0xD000)
860         {
861                 if (writeRAM)
862                 {
863                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
864                                 ram[addr - 0x1000] = b;
865                         else
866                                 ram[addr] = b;
867                 }
868
869                 return;
870         }
871
872         ram[addr] = b;
873 }
874
875
876 //
877 // Load a file into RAM/ROM image space
878 //
879 bool LoadImg(char * filename, uint8_t * ram, int size)
880 {
881         FILE * fp = fopen(filename, "rb");
882
883         if (fp == NULL)
884                 return false;
885
886         fread(ram, 1, size, fp);
887         fclose(fp);
888
889         return true;
890 }
891
892
893 static void SaveApple2State(const char * filename)
894 {
895 }
896
897
898 static bool LoadApple2State(const char * filename)
899 {
900         return false;
901 }
902
903
904 #ifdef CPU_CLOCK_CHECKING
905 uint8_t counter = 0;
906 uint32_t totalCPU = 0;
907 uint64_t lastClock = 0;
908 #endif
909 //
910 // Main loop
911 //
912 int main(int /*argc*/, char * /*argv*/[])
913 {
914         InitLog("./apple2.log");
915         LoadSettings();
916         srand(time(NULL));                                                                      // Initialize RNG
917
918         // Zero out memory
919 //Need to bankify this stuff for the IIe emulation...
920         memset(ram, 0, 0x10000);
921         memset(rom, 0, 0x10000);
922         memset(ram2, 0, 0x10000);
923
924         // Set up V65C02 execution context
925         memset(&mainCPU, 0, sizeof(V65C02REGS));
926         mainCPU.RdMem = RdMem;
927         mainCPU.WrMem = WrMem;
928         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
929
930         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
931         {
932                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
933                 return -1;
934         }
935
936 //This is now included...
937 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
938         {
939                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
940 //              return -1;
941         }//*/
942
943 //Load up disk image from config file (for now)...
944         floppyDrive.LoadImage(settings.diskImagePath1, 0);
945         floppyDrive.LoadImage(settings.diskImagePath2, 1);
946 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
947
948 //Kill the DOS ROM in slot 6 for now...
949 //not
950         memcpy(rom + 0xC600, diskROM, 0x100);
951 //      memcpy(rom + 0xC700, diskROM, 0x100);   // Slot 7???
952
953         WriteLog("About to initialize video...\n");
954
955         if (!InitVideo())
956         {
957                 std::cout << "Aborting!" << std::endl;
958                 return -1;
959         }
960
961         // Have to do this *after* video init but *before* sound init...!
962 //Shouldn't be necessary since we're not doing emulation in the ISR...
963         if (settings.autoStateSaving)
964         {
965                 // Load last state from file...
966                 if (!LoadApple2State(settings.autoStatePath))
967                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
968         }
969
970
971 #if 0
972 // State loading!
973 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
974 {
975         cout << "Couldn't load state file!" << endl;
976         cout << "Aborting!!" << endl;
977         return -1;
978 }
979
980 //A  P  Y  X  S     PC
981 //-- -- -- -- ----- -----
982 //00 75 3B 53 FD 01 41 44
983
984 mainCPU.cpuFlags = 0;
985 mainCPU.a = 0x00;
986 mainCPU.x = 0x53;
987 mainCPU.y = 0x3B;
988 mainCPU.cc = 0x75;
989 mainCPU.sp = 0xFD;
990 mainCPU.pc = 0x4441;
991
992 textMode = false;
993 mixedMode = false;
994 displayPage2 = false;
995 hiRes = true;
996
997 //kludge...
998 readHiRam = true;
999 //dumpDis=true;
1000 //kludge II...
1001 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
1002 #endif
1003
1004         WriteLog("About to initialize audio...\n");
1005         SoundInit();
1006 //nope  SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
1007
1008 //      gui = new GUI(surface);                                         // Set up the GUI system object...
1009 //      gui = new GUI(mainSurface);                                     // Set up the GUI system object...
1010 // SDL 2... this will likely cause Apple 2 to crash
1011 //      gui = new GUI(NULL);                                    // Set up the GUI system object...
1012 #if 0
1013         gui->AddMenuTitle("Apple2");
1014         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
1015         gui->AddMenuItem("");
1016         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
1017         gui->CommitItemsToMenu();
1018 #endif
1019
1020         SetupBlurTable();                                                       // Set up the color TV emulation blur table
1021         running = true;                                                         // Set running status...
1022
1023         InitializeEventList();                                          // Clear the event list before we use it...
1024         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
1025         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
1026         startTicks = SDL_GetTicks();
1027
1028 #ifdef THREADED_65C02
1029         cpuCond = SDL_CreateCond();
1030         mainSem = SDL_CreateSemaphore(1);
1031         cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
1032 //Hmm... CPU does POST (+1), wait, then WAIT (-1)
1033 //      SDL_sem * mainMutex = SDL_CreateMutex();
1034 #endif
1035
1036         WriteLog("Entering main loop...\n");
1037         while (running)
1038         {
1039                 double timeToNextEvent = GetTimeToNextEvent();
1040 #ifndef THREADED_65C02
1041                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
1042 #endif
1043 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
1044 //(Fix so that this is not a requirement!)
1045 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
1046 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
1047
1048 #ifdef CPU_CLOCK_CHECKING
1049 #ifndef THREADED_65C02
1050 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
1051 #endif
1052 #endif
1053                 // Handle CPU time delta for sound...
1054 //Don't need this anymore now that we use absolute time...
1055 //              AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
1056                 HandleNextEvent();
1057         }
1058
1059 #ifdef THREADED_65C02
1060 WriteLog("Main: cpuFinished = true;\n");
1061 cpuFinished = true;
1062 //#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE]
1063 //What to do? How do you know when the CPU is sleeping???
1064 //USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1!
1065 #if 0
1066 SDL_mutexP(mainMutex);
1067 SDL_CondWait(mainCond, mainMutex);      // Wait for CPU thread to get to signal point...
1068 SDL_mutexV(mainMutex);
1069 #else
1070 //Nope, use a semaphore...
1071 WriteLog("Main: SDL_SemWait(mainSem);\n");
1072 SDL_SemWait(mainSem);//should lock until CPU thread is waiting...
1073 #endif
1074
1075 WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
1076 SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
1077 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
1078 SDL_WaitThread(cpuThread, NULL);
1079 //nowok:SDL_WaitThread(CPUThreadFunc, NULL);
1080 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
1081 SDL_DestroyCond(cpuCond);
1082
1083 //SDL_DestroyMutex(mainMutex);
1084 SDL_DestroySemaphore(mainSem);
1085 #endif
1086
1087         if (settings.autoStateSaving)
1088         {
1089                 // Save state here...
1090                 SaveApple2State(settings.autoStatePath);
1091         }
1092 floppyDrive.SaveImage();
1093
1094         SoundDone();
1095         VideoDone();
1096         SaveSettings();
1097         LogDone();
1098
1099         return 0;
1100 }
1101
1102
1103 /*
1104 Apple II keycodes
1105 -----------------
1106
1107 Key     Aln CTL SHF BTH
1108 -----------------------
1109 space   $A0     $A0     $A0 $A0         No xlation
1110 RETURN  $8D     $8D     $8D     $8D             No xlation
1111 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
1112 1!              $B1 $B1 $A1 $A1         No xlation
1113 2"              $B2     $B2     $A2     $A2             No xlation
1114 3#              $B3     $B3     $A3     $A3             No xlation
1115 4$              $B4     $B4     $A4     $A4             No xlation
1116 5%              $B5     $B5     $A5     $A5             No xlation
1117 6&              $B6     $B6     $A6     $A6             No xlation
1118 7'              $B7     $B7     $A7     $A7             No xlation
1119 8(              $B8     $B8     $A8     $A8             No xlation
1120 9)              $B9     $B9     $A9     $A9             No xlation
1121 :*              $BA     $BA     $AA     $AA             No xlation
1122 ;+              $BB     $BB     $AB     $AB             No xlation
1123 ,<              $AC     $AC     $BC     $BC             No xlation
1124 -=              $AD     $AD     $BD     $BD             No xlation
1125 .>              $AE     $AE     $BE     $BE             No xlation
1126 /?              $AF     $AF     $BF     $BF             No xlation
1127 A               $C1     $81     $C1     $81
1128 B               $C2     $82     $C2     $82
1129 C               $C3     $83     $C3     $83
1130 D               $C4     $84     $C4     $84
1131 E               $C5     $85     $C5     $85
1132 F               $C6     $86     $C6     $86
1133 G               $C7     $87     $C7     $87
1134 H               $C8     $88     $C8     $88
1135 I               $C9     $89     $C9     $89
1136 J               $CA     $8A     $CA     $8A
1137 K               $CB     $8B     $CB     $8B
1138 L               $CC     $8C     $CC     $8C
1139 M               $CD     $8D     $DD     $9D             -> ODD
1140 N^              $CE     $8E     $DE     $9E             -> ODD
1141 O               $CF     $8F     $CF     $8F
1142 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
1143 Q               $D1     $91     $D1     $91
1144 R               $D2     $92     $D2     $92
1145 S               $D3     $93     $D3     $93
1146 T               $D4     $94     $D4     $94
1147 U               $D5     $95     $D5     $95
1148 V               $D6     $96     $D6     $96
1149 W               $D7     $97     $D7     $97
1150 X               $D8     $98     $D8     $98
1151 Y               $D9     $99     $D9     $99
1152 Z               $DA     $9A     $DA     $9A
1153 <-              $88     $88     $88     $88
1154 ->              $95     $95     $95     $95
1155 ESC             $9B     $9B     $9B     $9B             No xlation
1156
1157 */
1158 static void FrameCallback(void)
1159 {
1160         SDL_Event event;
1161
1162         while (SDL_PollEvent(&event))
1163         {
1164                 switch (event.type)
1165                 {
1166                 case SDL_TEXTINPUT:
1167 //Need to do some key translation here, and screen out non-apple keys as well...
1168 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
1169 // everything else done separately. this is slightly easier. :-P)
1170 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
1171                         if (event.edit.text[0] == '\t') // Prelim key screening...
1172                                 break;
1173
1174                         lastKeyPressed = event.edit.text[0];
1175                         keyDown = true;
1176
1177                         //kludge: should have a caps lock thingy here...
1178                         //or all uppercase for ][+...
1179                         if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
1180                                 lastKeyPressed &= 0xDF;         // Convert to upper case...
1181
1182                         break;
1183                 case SDL_KEYDOWN:
1184                         // CTRL+RESET key emulation (mapped to CTRL+`)
1185 // This doesn't work...
1186 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
1187 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
1188                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
1189 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
1190 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
1191                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1192
1193                         if (event.key.keysym.sym == SDLK_RIGHT)
1194                                 lastKeyPressed = 0x15, keyDown = true;
1195                         else if (event.key.keysym.sym == SDLK_LEFT)
1196                                 lastKeyPressed = 0x08, keyDown = true;
1197                         else if (event.key.keysym.sym == SDLK_RETURN)
1198                                 lastKeyPressed = 0x0D, keyDown = true;
1199                         else if (event.key.keysym.sym == SDLK_ESCAPE)
1200                                 lastKeyPressed = 0x1B, keyDown = true;
1201
1202                         // Fix CTRL+key combo...
1203                         if (event.key.keysym.mod & KMOD_CTRL)
1204                         {
1205                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1206                                 {
1207                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
1208                                         keyDown = true;
1209 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
1210                                 }
1211                         }
1212
1213                         // Use ALT+Q to exit, as well as the usual window decoration method
1214                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1215                                 running = false;
1216
1217                         // Paddle buttons 0 & 1
1218                         if (event.key.keysym.sym == SDLK_INSERT)
1219                                 openAppleDown = true;
1220                         if (event.key.keysym.sym == SDLK_PAGEUP)
1221                                 closedAppleDown = true;
1222
1223                         if (event.key.keysym.sym == SDLK_F11)
1224                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
1225 //                      else if (event.key.keysym.sym == SDLK_F11)
1226 //                              floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1227 else if (event.key.keysym.sym == SDLK_F9)
1228 {
1229         floppyDrive.CreateBlankImage();
1230 //      SpawnMessage("Image cleared...");
1231 }//*/
1232 else if (event.key.keysym.sym == SDLK_F10)
1233 {
1234         floppyDrive.SwapImages();
1235 //      SpawnMessage("Image swapped...");
1236 }//*/
1237
1238                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1239                                 TogglePalette();
1240                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1241                                 CycleScreenTypes();
1242
1243 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1244                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1245 //NOTE: Should parse the output to determine whether or not the user requested
1246 //      to quit completely... !!! FIX !!!
1247                                 gui->Run();
1248
1249                         if (event.key.keysym.sym == SDLK_F5)
1250                         {
1251                                 VolumeDown();
1252                                 char volStr[19] = "[****************]";
1253 //                              volStr[GetVolume()] = 0;
1254                                 for(int i=GetVolume(); i<16; i++)
1255                                         volStr[1 + i] = '-';
1256                                 SpawnMessage("Volume: %s", volStr);
1257                         }
1258                         else if (event.key.keysym.sym == SDLK_F6)
1259                         {
1260                                 VolumeUp();
1261                                 char volStr[19] = "[****************]";
1262 //                              volStr[GetVolume()] = 0;
1263                                 for(int i=GetVolume(); i<16; i++)
1264                                         volStr[1 + i] = '-';
1265                                 SpawnMessage("Volume: %s", volStr);
1266                         }
1267
1268                         static bool fullscreenDebounce = false;
1269
1270                         if (event.key.keysym.sym == SDLK_F12)
1271                         {
1272                                 if (!fullscreenDebounce)
1273                                 {
1274                                         ToggleFullScreen();
1275                                         fullscreenDebounce = true;
1276                                 }
1277                         }
1278 //                      else
1279
1280                         break;
1281                 case SDL_KEYUP:
1282                         if (event.key.keysym.sym == SDLK_F12)
1283                                 fullscreenDebounce = false;
1284
1285                         // Paddle buttons 0 & 1
1286                         if (event.key.keysym.sym == SDLK_INSERT)
1287                                 openAppleDown = false;
1288                         if (event.key.keysym.sym == SDLK_PAGEUP)
1289                                 closedAppleDown = false;
1290
1291 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1292 //                              keyDown = false;
1293
1294                         break;
1295                 case SDL_QUIT:
1296                         running = false;
1297                 }
1298         }
1299
1300 #warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
1301         RenderVideoFrame();
1302         SetCallbackTime(FrameCallback, 16666.66666667);
1303
1304 #ifdef CPU_CLOCK_CHECKING
1305 //We know it's stopped, so we can get away with this...
1306 counter++;
1307 if (counter == 60)
1308 {
1309         uint64_t clock = GetCurrentV65C02Clock();
1310 //totalCPU += (uint32_t)(clock - lastClock);
1311
1312         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
1313         lastClock = clock;
1314 //      totalCPU = 0;
1315         counter = 0;
1316 }
1317 #endif
1318 //Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE]
1319 //lessee...
1320 //nope.
1321 //Actually, slows things down too much...
1322 //SDL_Delay(10);
1323 //      while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1324         while (SDL_GetTicks() - startTicks < 16)
1325                 SDL_Delay(1);                                                   // Wait for next frame...
1326
1327         startTicks = SDL_GetTicks();
1328 //let's wait, then signal...
1329 //works longer, but then still falls behind...
1330 #ifdef THREADED_65C02
1331         SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
1332 #endif
1333 }
1334
1335
1336 static void BlinkTimer(void)
1337 {
1338         flash = !flash;
1339         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1340 }
1341
1342
1343 /*
1344 Next problem is this: How to have events occur and synchronize with the rest
1345 of the threads?
1346
1347   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
1348     remainder CPU cycles over...)
1349
1350 One way would be to use a fractional accumulator, then subtract 1 every
1351 time it overflows. Like so:
1352
1353 double overflow = 0;
1354 uint32_t time = 20;
1355 while (!done)
1356 {
1357         Execute6808(&soundCPU, time);
1358         overflow += 0.289115646;
1359         if (overflow > 1.0)
1360         {
1361                 overflow -= 1.0;
1362                 time = 21;
1363         }
1364         else
1365                 time = 20;
1366 }
1367 */