]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Moved CPU cycle subtraction into CPU core.
[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 FOR FIRST 6522
324         6       ORB             EQU     $C400           ;PORT B
325         7       ORA             EQU     $C401           ;PORT A
326         8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
327         9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
328         10      *                                       ;ADDRESSES FOR SECOND 6522
329         11      ORB2            EQU     $C480           ;PORT B
330         12      ORA2            EQU     $C481           ;PORT A
331         13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
332         14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
333 */
334 void WrMem(uint16 addr, uint8 b)
335 {
336 //temp...
337 //extern V6809REGS regs;
338 //if (addr >= 0xC800 && addr <= 0xCBFE)
339 //if (addr == 0xC80F || addr == 0xC80D)
340 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
341
342 #if 0
343 if (addr >= 0xC000 && addr <= 0xC0FF)
344         WriteLog("\n*** Write at I/O address %04X\n", addr);
345 #endif
346 /*
347 Check the BIKO version on Asimov to see if it's been cracked or not...
348
349 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
350 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
351 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
352 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
353 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
354
355 ; INX here *ensures* 1 - 6!!! BUG!!!
356 ; Or is it? Could this be part of a braindead copy protection scheme? It's
357 ; awfully close to NOP ($EA)...
358 ; Nothing else touches it once it's been written... Hmm...
359
360 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
361 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
362 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
363 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
364
365 ; 4E13: 03 00
366 ; 4E15: 25 25 15 15 10 20
367 ; 4E1B: 03 41 99 99 01 00 12
368 ; 4E22: 99 70
369
370 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
371 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
372 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
373 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
374
375 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
376
377 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
378 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
379
380 ; Print "ALAKAZAM!" and so on...
381
382 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
383 */
384 #if 0
385 if (addr == 0x7F47)
386         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
387 #endif
388 /*
389 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
390 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
391
392 CLRAUXRD = $C002 ;read from main 48K (WR-only)
393 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
394
395 CLRAUXWR = $C004 ;write to main 48K (WR-only)
396 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
397
398 CLRCXROM = $C006 ;use ROM on cards (WR-only)
399 SETCXROM = $C007 ;use internal ROM (WR-only)
400
401 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
402 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
403
404 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
405 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
406
407 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
408 SET80VID = $C00D ;enable 80-column display mode (WR-only)
409
410 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
411 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
412 */
413         if (addr == 0xC00E)
414         {
415                 alternateCharset = false;
416         }
417         else if (addr == 0xC00F)
418         {
419                 alternateCharset = true;
420         }
421         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
422         {
423                 keyDown = false;
424         }
425         else if (addr == 0xC050)
426         {
427                 textMode = false;
428         }
429         else if (addr == 0xC051)
430         {
431                 textMode = true;
432         }
433         else if (addr == 0xC052)
434         {
435                 mixedMode = false;
436         }
437         else if (addr == 0xC053)
438         {
439                 mixedMode = true;
440         }
441         else if (addr == 0xC054)
442         {
443                 displayPage2 = false;
444         }
445         else if (addr == 0xC055)
446         {
447                 displayPage2 = true;
448         }
449         else if (addr == 0xC056)
450         {
451                 hiRes = false;
452         }
453         else if (addr == 0xC057)
454         {
455                 hiRes = true;
456         }
457         else if ((addr & 0xFFF8) == 0xC0E0)
458         {
459                 floppyDrive.ControlStepper(addr & 0x07);
460         }
461         else if ((addr & 0xFFFE) == 0xC0E8)
462         {
463                 floppyDrive.ControlMotor(addr & 0x01);
464         }
465         else if ((addr & 0xFFFE) == 0xC0EA)
466         {
467                 floppyDrive.DriveEnable(addr & 0x01);
468         }
469         else if (addr == 0xC0EC)
470         {
471 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
472                 floppyDrive.ReadWrite();
473         }
474         else if (addr == 0xC0ED)
475         {
476                 floppyDrive.SetLatchValue(b);
477         }
478         else if (addr == 0xC0EE)
479         {
480                 floppyDrive.SetReadMode();
481         }
482         else if (addr == 0xC0EF)
483         {
484                 floppyDrive.SetWriteMode();
485         }
486 //Still need to add missing I/O switches here...
487
488         if (addr >= 0xD000)
489         {
490                 if (writeRAM)
491                 {
492                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
493                                 ram[addr - 0x1000] = b;
494                         else
495                                 ram[addr] = b;
496                 }
497
498                 return;
499         }
500
501         ram[addr] = b;
502 }
503
504 //
505 // Load a file into RAM/ROM image space
506 //
507 bool LoadImg(char * filename, uint8 * ram, int size)
508 {
509         FILE * fp = fopen(filename, "rb");
510
511         if (fp == NULL)
512                 return false;
513
514         fread(ram, 1, size, fp);
515         fclose(fp);
516
517         return true;
518 }
519
520 static void SaveApple2State(const char * filename)
521 {
522 }
523
524 static bool LoadApple2State(const char * filename)
525 {
526         return false;
527 }
528
529 //
530 // Main loop
531 //
532 int main(int /*argc*/, char * /*argv*/[])
533 {
534         InitLog("./apple2.log");
535         LoadSettings();
536         srand(time(NULL));                                                                      // Initialize RNG
537
538         // Zero out memory
539 //Need to bankify this stuff for the IIe emulation...
540         memset(ram, 0, 0x10000);
541         memset(rom, 0, 0x10000);
542
543         // Set up V65C02 execution context
544         memset(&mainCPU, 0, sizeof(V65C02REGS));
545         mainCPU.RdMem = RdMem;
546         mainCPU.WrMem = WrMem;
547         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
548
549         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
550         {
551                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
552                 return -1;
553         }
554
555 //This is now included...
556 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
557         {
558                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
559 //              return -1;
560         }//*/
561
562 //Load up disk image from config file (for now)...
563         floppyDrive.LoadImage(settings.diskImagePath1, 0);
564         floppyDrive.LoadImage(settings.diskImagePath2, 1);
565 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
566
567 //Kill the DOS ROM in slot 6 for now...
568 //not
569         memcpy(rom + 0xC600, diskROM, 0x100);
570
571         WriteLog("About to initialize video...\n");
572         if (!InitVideo())
573         {
574                 std::cout << "Aborting!" << std::endl;
575                 return -1;
576         }
577
578         // Have to do this *after* video init but *before* sound init...!
579 //Shouldn't be necessary since we're not doing emulation in the ISR...
580         if (settings.autoStateSaving)
581         {
582                 // Load last state from file...
583                 if (!LoadApple2State(settings.autoStatePath))
584                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
585         }
586
587
588 #if 0
589 // State loading!
590 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
591 {
592         cout << "Couldn't load state file!" << endl;
593         cout << "Aborting!!" << endl;
594         return -1;
595 }
596
597 //A  P  Y  X  S     PC
598 //-- -- -- -- ----- -----
599 //00 75 3B 53 FD 01 41 44
600
601 mainCPU.cpuFlags = 0;
602 mainCPU.a = 0x00;
603 mainCPU.x = 0x53;
604 mainCPU.y = 0x3B;
605 mainCPU.cc = 0x75;
606 mainCPU.sp = 0xFD;
607 mainCPU.pc = 0x4441;
608
609 textMode = false;
610 mixedMode = false;
611 displayPage2 = false;
612 hiRes = true;
613
614 //kludge...
615 readHiRam = true;
616 //dumpDis=true;
617 //kludge II...
618 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
619 #endif
620
621         WriteLog("About to initialize audio...\n");
622         SoundInit();
623         SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
624
625         gui = new GUI(surface);                                         // Set up the GUI system object...
626         gui->AddMenuTitle("Apple2");
627         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
628         gui->AddMenuItem("");
629         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
630         gui->CommitItemsToMenu();
631
632         SetupBlurTable();                                                       // Set up the color TV emulation blur table
633         running = true;                                                         // Set running status...
634
635         InitializeEventList();                                          // Clear the event list before we use it...
636         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
637         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
638         startTicks = SDL_GetTicks();
639
640         WriteLog("Entering main loop...\n");
641         while (running)
642         {
643                 double timeToNextEvent = GetTimeToNextEvent();
644                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
645 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
646 //(Fix so that this is not a requirement!)
647 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
648 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
649                 HandleNextEvent();
650         }
651
652         if (settings.autoStateSaving)
653         {
654                 // Save state here...
655                 SaveApple2State(settings.autoStatePath);
656         }
657 floppyDrive.SaveImage();
658
659         SoundDone();
660         VideoDone();
661         SaveSettings();
662         LogDone();
663
664         return 0;
665 }
666
667 /*
668 Apple II keycodes
669 -----------------
670
671 Key     Aln CTL SHF BTH
672 -----------------------
673 space   $A0     $A0     $A0 $A0         No xlation
674 RETURN  $8D     $8D     $8D     $8D             No xlation
675 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
676 1!              $B1 $B1 $A1 $A1         No xlation
677 2"              $B2     $B2     $A2     $A2             No xlation
678 3#              $B3     $B3     $A3     $A3             No xlation
679 4$              $B4     $B4     $A4     $A4             No xlation
680 5%              $B5     $B5     $A5     $A5             No xlation
681 6&              $B6     $B6     $A6     $A6             No xlation
682 7'              $B7     $B7     $A7     $A7             No xlation
683 8(              $B8     $B8     $A8     $A8             No xlation
684 9)              $B9     $B9     $A9     $A9             No xlation
685 :*              $BA     $BA     $AA     $AA             No xlation
686 ;+              $BB     $BB     $AB     $AB             No xlation
687 ,<              $AC     $AC     $BC     $BC             No xlation
688 -=              $AD     $AD     $BD     $BD             No xlation
689 .>              $AE     $AE     $BE     $BE             No xlation
690 /?              $AF     $AF     $BF     $BF             No xlation
691 A               $C1     $81     $C1     $81
692 B               $C2     $82     $C2     $82
693 C               $C3     $83     $C3     $83
694 D               $C4     $84     $C4     $84
695 E               $C5     $85     $C5     $85
696 F               $C6     $86     $C6     $86
697 G               $C7     $87     $C7     $87
698 H               $C8     $88     $C8     $88
699 I               $C9     $89     $C9     $89
700 J               $CA     $8A     $CA     $8A
701 K               $CB     $8B     $CB     $8B
702 L               $CC     $8C     $CC     $8C
703 M               $CD     $8D     $DD     $9D             -> ODD
704 N^              $CE     $8E     $DE     $9E             -> ODD
705 O               $CF     $8F     $CF     $8F
706 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
707 Q               $D1     $91     $D1     $91
708 R               $D2     $92     $D2     $92
709 S               $D3     $93     $D3     $93
710 T               $D4     $94     $D4     $94
711 U               $D5     $95     $D5     $95
712 V               $D6     $96     $D6     $96
713 W               $D7     $97     $D7     $97
714 X               $D8     $98     $D8     $98
715 Y               $D9     $99     $D9     $99
716 Z               $DA     $9A     $DA     $9A
717 <-              $88     $88     $88     $88
718 ->              $95     $95     $95     $95
719 ESC             $9B     $9B     $9B     $9B             No xlation
720
721 */
722 static void FrameCallback(void)
723 {
724         SDL_Event event;
725
726         while (SDL_PollEvent(&event))
727         {
728                 switch (event.type)
729                 {
730                 case SDL_KEYDOWN:
731                         if (event.key.keysym.unicode != 0)
732                         {
733 //Need to do some key translation here, and screen out non-apple keys as well...
734                                 if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
735                                         break;
736
737                                 lastKeyPressed = event.key.keysym.unicode;
738                                 keyDown = true;
739                                 //kludge: should have a caps lock thingy here...
740                                 //or all uppercase for ][+...
741                                 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
742                                         lastKeyPressed &= 0xDF;         // Convert to upper case...
743                         }
744
745                         // CTRL+RESET key emulation (mapped to CTRL+`)
746 // This doesn't work...
747 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
748 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
749                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
750 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
751 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
752                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
753
754                         if (event.key.keysym.sym == SDLK_RIGHT)
755                                 lastKeyPressed = 0x15, keyDown = true;
756                         else if (event.key.keysym.sym == SDLK_LEFT)
757                                 lastKeyPressed = 0x08, keyDown = true;
758
759                         // Use ALT+Q to exit, as well as the usual window decoration method
760                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
761                                 running = false;
762
763                         if (event.key.keysym.sym == SDLK_F12)
764                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
765                         else if (event.key.keysym.sym == SDLK_F11)
766                                 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
767 else if (event.key.keysym.sym == SDLK_F9)
768 {
769         floppyDrive.CreateBlankImage();
770 //      SpawnMessage("Image cleared...");
771 }//*/
772 else if (event.key.keysym.sym == SDLK_F10)
773 {
774         floppyDrive.SwapImages();
775 //      SpawnMessage("Image swapped...");
776 }//*/
777
778                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
779                                 TogglePalette();
780                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
781                                 CycleScreenTypes();
782
783 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
784                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
785 //NOTE: Should parse the output to determine whether or not the user requested
786 //      to quit completely... !!! FIX !!!
787                                 gui->Run();
788
789                         break;
790                 case SDL_QUIT:
791                         running = false;
792                 }
793         }
794
795         HandleSoundAtFrameEdge();                                       // Sound stuff... (ick)
796         RenderVideoFrame();
797         SetCallbackTime(FrameCallback, 16666.66666667);
798
799 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
800         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
801         startTicks = SDL_GetTicks();
802 }
803
804 static void BlinkTimer(void)
805 {
806         flash = !flash;
807         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
808 }