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