]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Undoing changes (accidentally) committed from r31.
[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 <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 using namespace std;
58
59 // Global variables
60
61 uint8 ram[0x10000], rom[0x10000];                               // RAM & ROM spaces
62 uint8 diskRom[0x100];                                                   // Disk ROM space
63 V65C02REGS mainCPU;
64 uint8 appleType = APPLE_TYPE_II;
65
66 // Local variables
67
68 static uint8 lastKeyPressed = 0;
69 static bool keyDown = false;
70
71 static FloppyDrive floppyDrive;
72
73 enum { LC_BANK_1, LC_BANK_2 };
74
75 static uint8 visibleBank = LC_BANK_1;
76 static bool readRAM = false;
77 static bool writeRAM = false;
78
79 static bool running = true;                                             // Machine running state flag...
80 static uint32 startTicks;
81
82 static GUI * gui = NULL;
83
84 // Local functions (technically, they're global...)
85
86 bool LoadImg(char * filename, uint8 * ram, int size);
87 uint8 RdMem(uint16 addr);
88 void WrMem(uint16 addr, uint8 b);
89 static void SaveApple2State(const char * filename);
90 static bool LoadApple2State(const char * filename);
91
92 // Local timer callback functions
93
94 static void FrameCallback(void);
95 static void BlinkTimer(void);
96
97 // Test GUI function
98
99 Element * TestWindow(void)
100 {
101         Element * win = new DraggableWindow2(10, 10, 128, 128);
102 //      ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
103
104         return win;
105 }
106
107 Element * QuitEmulator(void)
108 {
109         gui->Stop();
110         running = false;
111
112         return NULL;
113 }
114
115 /*
116  Small Apple II memory map:
117
118 $C010 - Clear bit 7 of keyboard data ($C000)
119 $C030 - Toggle speaker diaphragm
120 $C051 - Display text
121 $C054 - Select page 1
122 $C056 - Select lo-res
123 $C058 - Set annuciator-0 output to 0
124 $C05A - Set annuciator-0 output to 0
125 $C05D - Set annuciator-0 output to 1
126 $C05F - Set annuciator-0 output to 1
127 $C0E0 - Disk control stepper ($C0E0-7)
128 $C0E9 - Disk control motor (on)
129 $C0EA - Disk enable (drive 1)
130 $C0EC - Disk R/W
131 $C0EE - Disk set read mode
132 */
133
134 //
135 // V65C02 read/write memory functions
136 //
137
138 uint8 RdMem(uint16 addr)
139 {
140         uint8 b;
141
142 #if 0
143 if (addr >= 0xC000 && addr <= 0xC0FF)
144         WriteLog("\n*** Read at I/O address %04X\n", addr);
145 #endif
146 #if 0
147 if (addr >= 0xC080 && addr <= 0xC08F)
148         WriteLog("\n*** Read at I/O address %04X\n", addr);
149 #endif
150
151         if ((addr & 0xFFF0) == 0xC000)
152         {
153                 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
154         }
155         else if ((addr & 0xFFF0) == 0xC010)
156         {
157 //This is bogus: keyDown is set to false, so return val NEVER is set...
158 //Fixed...
159 //Also, this is IIe/IIc only...!
160                 uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
161                 keyDown = false;
162                 return retVal;
163         }
164         else if ((addr & 0xFFF0) == 0xC030)
165         {
166                 ToggleSpeaker(GetCurrentV65C02Clock());
167 //should it return something else here???
168                 return 0x00;
169         }
170         else if (addr == 0xC050)
171         {
172                 textMode = false;
173         }
174         else if (addr == 0xC051)
175         {
176                 textMode = true;
177         }
178         else if (addr == 0xC052)
179         {
180                 mixedMode = false;
181         }
182         else if (addr == 0xC053)
183         {
184                 mixedMode = true;
185         }
186         else if (addr == 0xC054)
187         {
188                 displayPage2 = false;
189         }
190         else if (addr == 0xC055)
191         {
192                 displayPage2 = true;
193         }
194         else if (addr == 0xC056)
195         {
196                 hiRes = false;
197         }
198         else if (addr == 0xC057)
199         {
200                 hiRes = true;
201         }
202
203 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
204 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
205 //[SHOULD BE FIXED NOW]
206 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
207 //visible, two makes it R/W.
208
209         else if ((addr & 0xFFFB) == 0xC080)
210         {
211 //$C080 49280              OECG  R   Read RAM bank 2; no write
212                 visibleBank = LC_BANK_2;
213                 readRAM = true;
214                 writeRAM = false;
215         }
216         else if ((addr & 0xFFFB) == 0xC081)
217         {
218 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
219                 visibleBank = LC_BANK_2;
220                 readRAM = false;
221                 writeRAM = true;
222         }
223         else if ((addr & 0xFFFB) == 0xC082)
224         {
225 //$C082 49282              OECG  R   Read ROM; no write
226                 visibleBank = LC_BANK_2;
227                 readRAM = false;
228                 writeRAM = false;
229         }
230         else if ((addr & 0xFFFB) == 0xC083)
231         {
232 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
233                 visibleBank = LC_BANK_2;
234                 readRAM = true;
235                 writeRAM = true;
236         }
237         else if ((addr & 0xFFFB) == 0xC088)
238         {
239 //$C088 49288              OECG  R   Read RAM bank 1; no write
240                 visibleBank = LC_BANK_1;
241                 readRAM = true;
242                 writeRAM = false;
243         }
244         else if ((addr & 0xFFFB) == 0xC089)
245         {
246 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
247                 visibleBank = LC_BANK_1;
248                 readRAM = false;
249                 writeRAM = true;
250         }
251         else if ((addr & 0xFFFB) == 0xC08A)
252         {
253 //$C08A 49290              OECG  R   Read ROM; no write
254                 visibleBank = LC_BANK_1;
255                 readRAM = false;
256                 writeRAM = false;
257         }
258         else if ((addr & 0xFFFB) == 0xC08B)
259         {
260 //$C08B 49291              OECG  RR  Read/write RAM bank 1
261                 visibleBank = LC_BANK_1;
262                 readRAM = true;
263                 writeRAM = true;
264         }
265         else if ((addr & 0xFFF8) == 0xC0E0)
266         {
267                 floppyDrive.ControlStepper(addr & 0x07);
268         }
269         else if ((addr & 0xFFFE) == 0xC0E8)
270         {
271                 floppyDrive.ControlMotor(addr & 0x01);
272         }
273         else if ((addr & 0xFFFE) == 0xC0EA)
274         {
275                 floppyDrive.DriveEnable(addr & 0x01);
276         }
277         else if (addr == 0xC0EC)
278         {
279                 return floppyDrive.ReadWrite();
280         }
281         else if (addr == 0xC0ED)
282         {
283                 return floppyDrive.GetLatchValue();
284         }
285         else if (addr == 0xC0EE)
286         {
287                 floppyDrive.SetReadMode();
288         }
289         else if (addr == 0xC0EF)
290         {
291                 floppyDrive.SetWriteMode();
292         }
293
294 //This sux...
295         if (addr >= 0xC100 && addr <= 0xCFFF)   // The $C000-$CFFF block is *never* RAM
296                 b = rom[addr];
297         else if (addr >= 0xD000)
298         {
299                 if (readRAM)
300                 {
301                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
302                                 b = ram[addr - 0x1000];
303                         else
304                                 b = ram[addr];
305                 }
306                 else
307                         b = rom[addr];
308         }
309         else
310                 b = ram[addr];
311
312         return b;
313 }
314
315 /*
316 A-9 (Mockingboard)
317 APPENDIX F Assembly Language Program Listings
318
319                         1       *PRIMARY ROUTINES
320                         2       *FOR SLOT 4
321                         3       *
322                         4                       ORG     $9000
323                         5       *                               ;ADDRESSES
324                                                                                 FOR FIRST
325                                                                                 6522
326                         6       ORB             EQU     $C400           ;PORT B
327                         7       ORA             EQU     $C401           ;PORT A
328                         8       DDRB            EQU     $C402           ;DATA DIRECTION
329                                                                                 REGISTER (A)
330                         9       DDRA            EQU     $C403           ;DATA DIRECTION
331                                                                                 REGISTER (B)
332                         10      *                                       ;ADDRESSES
333                                                                                 FOR SECOND
334                                                                                 6522
335                         11      ORB2            EQU     $C480           ;PORT B
336                         12      ORA2            EQU     $C481           ;PORT A
337                         13      DDRB2   EQU     $C482           ;DATA DIRECTION
338                                                                                 REGISTER (B)
339                         14      DDRA2   EQU     $C483           ;DATA DIRECTION
340                                                                                 REGISTER (A)
341 */
342 void WrMem(uint16 addr, uint8 b)
343 {
344 //temp...
345 //extern V6809REGS regs;
346 //if (addr >= 0xC800 && addr <= 0xCBFE)
347 //if (addr == 0xC80F || addr == 0xC80D)
348 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
349
350 #if 0
351 if (addr >= 0xC000 && addr <= 0xC0FF)
352         WriteLog("\n*** Write at I/O address %04X\n", addr);
353 #endif
354 /*
355 Check the BIKO version on Asimov to see if it's been cracked or not...
356
357 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
358 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
359 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
360 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
361 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
362
363 ; INX here *ensures* 1 - 6!!! BUG!!!
364 ; Or is it? Could this be part of a braindead copy protection scheme? It's
365 ; awfully close to NOP ($EA)...
366 ; Nothing else touches it once it's been written... Hmm...
367
368 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
369 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
370 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
371 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
372
373 ; 4E13: 03 00
374 ; 4E15: 25 25 15 15 10 20
375 ; 4E1B: 03 41 99 99 01 00 12
376 ; 4E22: 99 70
377
378 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
379 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
380 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
381 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
382
383 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
384
385 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
386 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
387
388 ; Print "ALAKAZAM!" and so on...
389
390 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
391 */
392 #if 0
393 if (addr == 0x7F47)
394         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
395 #endif
396 /*
397 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
398 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
399
400 CLRAUXRD = $C002 ;read from main 48K (WR-only)
401 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
402
403 CLRAUXWR = $C004 ;write to main 48K (WR-only)
404 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
405
406 CLRCXROM = $C006 ;use ROM on cards (WR-only)
407 SETCXROM = $C007 ;use internal ROM (WR-only)
408
409 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
410 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
411
412 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
413 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
414
415 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
416 SET80VID = $C00D ;enable 80-column display mode (WR-only)
417
418 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
419 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
420 */
421         if (addr == 0xC00E)
422         {
423                 alternateCharset = false;
424         }
425         else if (addr == 0xC00F)
426         {
427                 alternateCharset = true;
428         }
429         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
430         {
431                 keyDown = false;
432         }
433         else if (addr == 0xC050)
434         {
435                 textMode = false;
436         }
437         else if (addr == 0xC051)
438         {
439                 textMode = true;
440         }
441         else if (addr == 0xC052)
442         {
443                 mixedMode = false;
444         }
445         else if (addr == 0xC053)
446         {
447                 mixedMode = true;
448         }
449         else if (addr == 0xC054)
450         {
451                 displayPage2 = false;
452         }
453         else if (addr == 0xC055)
454         {
455                 displayPage2 = true;
456         }
457         else if (addr == 0xC056)
458         {
459                 hiRes = false;
460         }
461         else if (addr == 0xC057)
462         {
463                 hiRes = true;
464         }
465         else if ((addr & 0xFFF8) == 0xC0E0)
466         {
467                 floppyDrive.ControlStepper(addr & 0x07);
468         }
469         else if ((addr & 0xFFFE) == 0xC0E8)
470         {
471                 floppyDrive.ControlMotor(addr & 0x01);
472         }
473         else if ((addr & 0xFFFE) == 0xC0EA)
474         {
475                 floppyDrive.DriveEnable(addr & 0x01);
476         }
477         else if (addr == 0xC0EC)
478         {
479 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
480                 floppyDrive.ReadWrite();
481         }
482         else if (addr == 0xC0ED)
483         {
484                 floppyDrive.SetLatchValue(b);
485         }
486         else if (addr == 0xC0EE)
487         {
488                 floppyDrive.SetReadMode();
489         }
490         else if (addr == 0xC0EF)
491         {
492                 floppyDrive.SetWriteMode();
493         }
494 //Still need to add missing I/O switches here...
495
496         if (addr >= 0xD000)
497         {
498                 if (writeRAM)
499                 {
500                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
501                                 ram[addr - 0x1000] = b;
502                         else
503                                 ram[addr] = b;
504                 }
505
506                 return;
507         }
508
509         ram[addr] = b;
510 }
511
512 //
513 // Load a file into RAM/ROM image space
514 //
515 bool LoadImg(char * filename, uint8 * ram, int size)
516 {
517         FILE * fp = fopen(filename, "rb");
518
519         if (fp == NULL)
520                 return false;
521
522         fread(ram, 1, size, fp);
523         fclose(fp);
524
525         return true;
526 }
527
528 static void SaveApple2State(const char * filename)
529 {
530 }
531
532 static bool LoadApple2State(const char * filename)
533 {
534         return false;
535 }
536
537 //
538 // Main loop
539 //
540 int main(int /*argc*/, char * /*argv*/[])
541 {
542         InitLog("./apple2.log");
543         LoadSettings();
544         srand(time(NULL));                                                                      // Initialize RNG
545
546         // Zero out memory
547 //Need to bankify this stuff for the IIe emulation...
548         memset(ram, 0, 0x10000);
549         memset(rom, 0, 0x10000);
550
551         // Set up V65C02 execution context
552         memset(&mainCPU, 0, sizeof(V65C02REGS));
553         mainCPU.RdMem = RdMem;
554         mainCPU.WrMem = WrMem;
555         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
556
557         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
558         {
559                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
560                 return -1;
561         }
562
563 //This is now included...
564 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
565         {
566                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
567 //              return -1;
568         }//*/
569
570 //Load up disk image from config file (for now)...
571         floppyDrive.LoadImage(settings.diskImagePath1, 0);
572         floppyDrive.LoadImage(settings.diskImagePath2, 1);
573 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
574
575 //Kill the DOS ROM in slot 6 for now...
576 //not
577         memcpy(rom + 0xC600, diskROM, 0x100);
578
579         WriteLog("About to initialize video...\n");
580         if (!InitVideo())
581         {
582                 cout << "Aborting!" << endl;
583                 return -1;
584         }
585
586         // Have to do this *after* video init but *before* sound init...!
587 //Shouldn't be necessary since we're not doing emulation in the ISR...
588         if (settings.autoStateSaving)
589         {
590                 // Load last state from file...
591                 if (!LoadApple2State(settings.autoStatePath))
592                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
593         }
594
595
596 #if 0
597 // State loading!
598 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
599 {
600         cout << "Couldn't load state file!" << endl;
601         cout << "Aborting!!" << endl;
602         return -1;
603 }
604
605 //A  P  Y  X  S     PC
606 //-- -- -- -- ----- -----
607 //00 75 3B 53 FD 01 41 44
608
609 mainCPU.cpuFlags = 0;
610 mainCPU.a = 0x00;
611 mainCPU.x = 0x53;
612 mainCPU.y = 0x3B;
613 mainCPU.cc = 0x75;
614 mainCPU.sp = 0xFD;
615 mainCPU.pc = 0x4441;
616
617 textMode = false;
618 mixedMode = false;
619 displayPage2 = false;
620 hiRes = true;
621
622 //kludge...
623 readHiRam = true;
624 //dumpDis=true;
625 //kludge II...
626 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
627 #endif
628
629         WriteLog("About to initialize audio...\n");
630         SoundInit();
631         SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
632
633         gui = new GUI(surface);                                         // Set up the GUI system object...
634         gui->AddMenuTitle("Apple2");
635         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
636         gui->AddMenuItem("");
637         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
638         gui->CommitItemsToMenu();
639
640         SetupBlurTable();                                                       // Set up the color TV emulation blur table
641         running = true;                                                         // Set running status...
642
643         InitializeEventList();                                          // Clear the event list before we use it...
644         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
645         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
646         startTicks = SDL_GetTicks();
647
648         WriteLog("Entering main loop...\n");
649         while (running)
650         {
651                 double timeToNextEvent = GetTimeToNextEvent();
652                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
653 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
654 //(Fix so that this is not a requirement!)
655                 mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
656                 HandleNextEvent();
657         }
658
659         if (settings.autoStateSaving)
660         {
661                 // Save state here...
662                 SaveApple2State(settings.autoStatePath);
663         }
664 floppyDrive.SaveImage();
665
666         SoundDone();
667         VideoDone();
668         SaveSettings();
669         LogDone();
670
671         return 0;
672 }
673
674 /*
675 Apple II keycodes
676 -----------------
677
678 Key     Aln CTL SHF BTH
679 -----------------------
680 space   $A0     $A0     $A0 $A0         No xlation
681 RETURN  $8D     $8D     $8D     $8D             No xlation
682 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
683 1!              $B1 $B1 $A1 $A1         No xlation
684 2"              $B2     $B2     $A2     $A2             No xlation
685 3#              $B3     $B3     $A3     $A3             No xlation
686 4$              $B4     $B4     $A4     $A4             No xlation
687 5%              $B5     $B5     $A5     $A5             No xlation
688 6&              $B6     $B6     $A6     $A6             No xlation
689 7'              $B7     $B7     $A7     $A7             No xlation
690 8(              $B8     $B8     $A8     $A8             No xlation
691 9)              $B9     $B9     $A9     $A9             No xlation
692 :*              $BA     $BA     $AA     $AA             No xlation
693 ;+              $BB     $BB     $AB     $AB             No xlation
694 ,<              $AC     $AC     $BC     $BC             No xlation
695 -=              $AD     $AD     $BD     $BD             No xlation
696 .>              $AE     $AE     $BE     $BE             No xlation
697 /?              $AF     $AF     $BF     $BF             No xlation
698 A               $C1     $81     $C1     $81
699 B               $C2     $82     $C2     $82
700 C               $C3     $83     $C3     $83
701 D               $C4     $84     $C4     $84
702 E               $C5     $85     $C5     $85
703 F               $C6     $86     $C6     $86
704 G               $C7     $87     $C7     $87
705 H               $C8     $88     $C8     $88
706 I               $C9     $89     $C9     $89
707 J               $CA     $8A     $CA     $8A
708 K               $CB     $8B     $CB     $8B
709 L               $CC     $8C     $CC     $8C
710 M               $CD     $8D     $DD     $9D             -> ODD
711 N^              $CE     $8E     $DE     $9E             -> ODD
712 O               $CF     $8F     $CF     $8F
713 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
714 Q               $D1     $91     $D1     $91
715 R               $D2     $92     $D2     $92
716 S               $D3     $93     $D3     $93
717 T               $D4     $94     $D4     $94
718 U               $D5     $95     $D5     $95
719 V               $D6     $96     $D6     $96
720 W               $D7     $97     $D7     $97
721 X               $D8     $98     $D8     $98
722 Y               $D9     $99     $D9     $99
723 Z               $DA     $9A     $DA     $9A
724 <-              $88     $88     $88     $88
725 ->              $95     $95     $95     $95
726 ESC             $9B     $9B     $9B     $9B             No xlation
727
728 */
729 static void FrameCallback(void)
730 {
731         SDL_Event event;
732
733         while (SDL_PollEvent(&event))
734         {
735                 switch (event.type)
736                 {
737                 case SDL_KEYDOWN:
738                         if (event.key.keysym.unicode != 0)
739                         {
740 //Need to do some key translation here, and screen out non-apple keys as well...
741                                 if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
742                                         break;
743
744                                 lastKeyPressed = event.key.keysym.unicode;
745                                 keyDown = true;
746                                 //kludge: should have a caps lock thingy here...
747                                 //or all uppercase for ][+...
748                                 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
749                                         lastKeyPressed &= 0xDF;         // Convert to upper case...
750                         }
751
752                         // CTRL+RESET key emulation (mapped to CTRL+`)
753 // This doesn't work...
754 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
755 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
756                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
757 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
758 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
759                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
760
761                         if (event.key.keysym.sym == SDLK_RIGHT)
762                                 lastKeyPressed = 0x15, keyDown = true;
763                         else if (event.key.keysym.sym == SDLK_LEFT)
764                                 lastKeyPressed = 0x08, keyDown = true;
765
766                         // Use ALT+Q to exit, as well as the usual window decoration method
767                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
768                                 running = false;
769
770                         if (event.key.keysym.sym == SDLK_F12)
771                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
772                         else if (event.key.keysym.sym == SDLK_F11)
773                                 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
774 else if (event.key.keysym.sym == SDLK_F9)
775 {
776         floppyDrive.CreateBlankImage();
777 //      SpawnMessage("Image cleared...");
778 }//*/
779 else if (event.key.keysym.sym == SDLK_F10)
780 {
781         floppyDrive.SwapImages();
782 //      SpawnMessage("Image swapped...");
783 }//*/
784
785                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
786                                 TogglePalette();
787                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
788                                 CycleScreenTypes();
789
790 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
791                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
792 //NOTE: Should parse the output to determine whether or not the user requested
793 //      to quit completely... !!! FIX !!!
794                                 gui->Run();
795
796                         break;
797                 case SDL_QUIT:
798                         running = false;
799                 }
800         }
801
802         HandleSoundAtFrameEdge();                                       // Sound stuff... (ick)
803         RenderVideoFrame();
804         SetCallbackTime(FrameCallback, 16666.66666667);
805
806         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
807         startTicks = SDL_GetTicks();
808 }
809
810 static void BlinkTimer(void)
811 {
812         flash = !flash;
813         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
814 }