2 // Apple 2 SDL Portable Apple Emulator
5 // (C) 2005 Underground Software
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.
12 // JLH = James L. Hammons <jlhamm@acm.org>
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
24 // - Port to SDL [DONE]
26 // - Weed out unneeded functions [DONE]
28 // - 128K IIe related stuff
29 // - State loading/saving
47 #include "applevideo.h"
53 #include "gui/window.h"
54 #include "gui/draggablewindow2.h"
55 #include "gui/textedit.h"
57 //using namespace std;
61 uint8 ram[0x10000], rom[0x10000]; // RAM & ROM spaces
62 uint8 diskRom[0x100]; // Disk ROM space
64 uint8 appleType = APPLE_TYPE_II;
68 static uint8 lastKeyPressed = 0;
69 static bool keyDown = false;
71 static FloppyDrive floppyDrive;
73 enum { LC_BANK_1, LC_BANK_2 };
75 static uint8 visibleBank = LC_BANK_1;
76 static bool readRAM = false;
77 static bool writeRAM = false;
79 static bool running = true; // Machine running state flag...
80 static uint32 startTicks;
82 static GUI * gui = NULL;
84 // Local functions (technically, they're global...)
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);
92 // Local timer callback functions
94 static void FrameCallback(void);
95 static void BlinkTimer(void);
99 Element * TestWindow(void)
101 Element * win = new DraggableWindow2(10, 10, 128, 128);
102 // ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
107 Element * QuitEmulator(void)
116 Small Apple II memory map:
118 $C010 - Clear bit 7 of keyboard data ($C000)
119 $C030 - Toggle speaker diaphragm
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)
131 $C0EE - Disk set read mode
135 // V65C02 read/write memory functions
138 uint8 RdMem(uint16 addr)
143 if (addr >= 0xC000 && addr <= 0xC0FF)
144 WriteLog("\n*** Read at I/O address %04X\n", addr);
147 if (addr >= 0xC080 && addr <= 0xC08F)
148 WriteLog("\n*** Read at I/O address %04X\n", addr);
151 if ((addr & 0xFFF0) == 0xC000)
153 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
155 else if ((addr & 0xFFF0) == 0xC010)
157 //This is bogus: keyDown is set to false, so return val NEVER is set...
159 //Also, this is IIe/IIc only...!
160 uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
164 else if ((addr & 0xFFF0) == 0xC030)
167 This is problematic, mainly because the v65C02 removes actual cycles run after each call.
168 Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
171 What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
172 hasn't had any changes in it then we just fill it with the last sample value and are done. Then
173 we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
174 last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
177 Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
178 deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
183 ToggleSpeaker(GetCurrentV65C02Clock());
184 //should it return something else here???
187 else if (addr == 0xC050)
191 else if (addr == 0xC051)
195 else if (addr == 0xC052)
199 else if (addr == 0xC053)
203 else if (addr == 0xC054)
205 displayPage2 = false;
207 else if (addr == 0xC055)
211 else if (addr == 0xC056)
215 else if (addr == 0xC057)
220 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
221 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
222 //[SHOULD BE FIXED NOW]
223 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
224 //visible, two makes it R/W.
260 else if ((addr & 0xFFFB) == 0xC080)
263 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
265 //$C080 49280 OECG R Read RAM bank 2; no write
266 visibleBank = LC_BANK_2;
270 else if ((addr & 0xFFFB) == 0xC081)
273 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
275 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
276 visibleBank = LC_BANK_2;
280 else if ((addr & 0xFFFB) == 0xC082)
283 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
285 //$C082 49282 OECG R Read ROM; no write
286 visibleBank = LC_BANK_2;
290 else if ((addr & 0xFFFB) == 0xC083)
293 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
295 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
296 visibleBank = LC_BANK_2;
300 else if ((addr & 0xFFFB) == 0xC088)
303 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
305 //$C088 49288 OECG R Read RAM bank 1; no write
306 visibleBank = LC_BANK_1;
309 //Hm. Some stuff seems to want this.
310 //nope, was looking at $C0E8... return 0xFF;
312 else if ((addr & 0xFFFB) == 0xC089)
315 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
317 //$C089 49289 OECG RR Read ROM; write RAM bank 1
318 visibleBank = LC_BANK_1;
322 else if ((addr & 0xFFFB) == 0xC08A)
325 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
327 //$C08A 49290 OECG R Read ROM; no write
328 visibleBank = LC_BANK_1;
332 else if ((addr & 0xFFFB) == 0xC08B)
335 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
337 //$C08B 49291 OECG RR Read/write RAM bank 1
338 visibleBank = LC_BANK_1;
342 else if ((addr & 0xFFF8) == 0xC0E0)
344 floppyDrive.ControlStepper(addr & 0x07);
346 else if ((addr & 0xFFFE) == 0xC0E8)
348 floppyDrive.ControlMotor(addr & 0x01);
350 else if ((addr & 0xFFFE) == 0xC0EA)
352 floppyDrive.DriveEnable(addr & 0x01);
354 else if (addr == 0xC0EC)
356 return floppyDrive.ReadWrite();
357 //Hm, some stuff is looking at the return value. Dunno what it *should* be...
358 // OK, it's from the ReadWrite routine...
361 else if (addr == 0xC0ED)
363 return floppyDrive.GetLatchValue();
365 else if (addr == 0xC0EE)
367 floppyDrive.SetReadMode();
369 else if (addr == 0xC0EF)
371 floppyDrive.SetWriteMode();
374 //#define LC_DEBUGGING
376 bool showpath = false;
377 if (addr >= 0xD000 && addr <= 0xD00F)
381 if (addr >= 0xC100 && addr <= 0xCFFF) // The $C000-$CFFF block is *never* RAM
388 WriteLog("b is from $C100-$CFFF block...\n");
391 else if (addr >= 0xD000)
395 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
399 b = ram[addr - 0x1000];
402 WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
412 WriteLog("b is from LC bank #2 (ram[addr])...\n");
423 WriteLog("b is from LC ROM (rom[addr])...\n");
434 WriteLog("b is from ram[addr]...\n");
439 if (addr >= 0xD000 && addr <= 0xD00F)
441 WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
450 APPENDIX F Assembly Language Program Listings
456 5 * ;ADDRESSES FOR FIRST 6522
457 6 ORB EQU $C400 ;PORT B
458 7 ORA EQU $C401 ;PORT A
459 8 DDRB EQU $C402 ;DATA DIRECTION REGISTER (A)
460 9 DDRA EQU $C403 ;DATA DIRECTION REGISTER (B)
461 10 * ;ADDRESSES FOR SECOND 6522
462 11 ORB2 EQU $C480 ;PORT B
463 12 ORA2 EQU $C481 ;PORT A
464 13 DDRB2 EQU $C482 ;DATA DIRECTION REGISTER (B)
465 14 DDRA2 EQU $C483 ;DATA DIRECTION REGISTER (A)
467 void WrMem(uint16 addr, uint8 b)
470 //extern V6809REGS regs;
471 //if (addr >= 0xC800 && addr <= 0xCBFE)
472 //if (addr == 0xC80F || addr == 0xC80D)
473 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
476 if (addr >= 0xC000 && addr <= 0xC0FF)
477 WriteLog("\n*** Write at I/O address %04X\n", addr);
480 Check the BIKO version on Asimov to see if it's been cracked or not...
482 7F3D: 29 07 AND #$07 [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
483 7F3F: C9 06 CMP #$06 [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
484 7F41: 90 03 BCC $7F46 [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
485 [7F43: 4C 83 7E JMP $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
486 7F46: AA TAX [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
488 ; INX here *ensures* 1 - 6!!! BUG!!!
489 ; Or is it? Could this be part of a braindead copy protection scheme? It's
490 ; awfully close to NOP ($EA)...
491 ; Nothing else touches it once it's been written... Hmm...
493 7F47: E8 INX [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
494 7F48: F8 SED [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
495 7F49: 18 CLC [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
496 7F4A: BD 15 4E LDA $4E15,X [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
499 ; 4E15: 25 25 15 15 10 20
500 ; 4E1B: 03 41 99 99 01 00 12
503 7F4D: 65 FC ADC $FC [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
504 7F4F: 65 FC ADC $FC [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
505 7F51: 65 FC ADC $FC [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
506 7F53: 65 FC ADC $FC [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
508 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
510 7F55: 9D 15 4E STA $4E15,X [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
511 7F58: D8 CLD [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
513 ; Print "ALAKAZAM!" and so on...
515 7F59: 20 2C 40 JSR $402C [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
519 WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
522 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
523 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
525 CLRAUXRD = $C002 ;read from main 48K (WR-only)
526 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
528 CLRAUXWR = $C004 ;write to main 48K (WR-only)
529 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
531 CLRCXROM = $C006 ;use ROM on cards (WR-only)
532 SETCXROM = $C007 ;use internal ROM (WR-only)
534 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
535 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
537 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
538 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
540 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
541 SET80VID = $C00D ;enable 80-column display mode (WR-only)
543 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
544 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
548 alternateCharset = false;
550 else if (addr == 0xC00F)
552 alternateCharset = true;
554 else if ((addr & 0xFFF0) == 0xC010) // Keyboard strobe
556 //Actually, according to the A2 ref, this should do nothing since a write
557 //is immediately preceded by a read leaving it in the same state it was...
558 //But leaving this out seems to fuck up the key handling of some games...
561 else if (addr == 0xC050)
565 else if (addr == 0xC051)
569 else if (addr == 0xC052)
573 else if (addr == 0xC053)
577 else if (addr == 0xC054)
579 displayPage2 = false;
581 else if (addr == 0xC055)
585 else if (addr == 0xC056)
589 else if (addr == 0xC057)
593 else if ((addr & 0xFFFB) == 0xC080)
596 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
598 //$C080 49280 OECG R Read RAM bank 2; no write
599 visibleBank = LC_BANK_2;
603 else if ((addr & 0xFFFB) == 0xC081)
606 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
608 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
609 visibleBank = LC_BANK_2;
613 else if ((addr & 0xFFFB) == 0xC082)
616 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
618 //$C082 49282 OECG R Read ROM; no write
619 visibleBank = LC_BANK_2;
623 else if ((addr & 0xFFFB) == 0xC083)
626 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
628 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
629 visibleBank = LC_BANK_2;
633 else if ((addr & 0xFFFB) == 0xC088)
636 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
638 //$C088 49288 OECG R Read RAM bank 1; no write
639 visibleBank = LC_BANK_1;
643 else if ((addr & 0xFFFB) == 0xC089)
646 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
648 //$C089 49289 OECG RR Read ROM; write RAM bank 1
649 visibleBank = LC_BANK_1;
653 else if ((addr & 0xFFFB) == 0xC08A)
656 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
658 //$C08A 49290 OECG R Read ROM; no write
659 visibleBank = LC_BANK_1;
663 else if ((addr & 0xFFFB) == 0xC08B)
666 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
668 //$C08B 49291 OECG RR Read/write RAM bank 1
669 visibleBank = LC_BANK_1;
673 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
674 else if ((addr & 0xFFF8) == 0xC0E0)
676 floppyDrive.ControlStepper(addr & 0x07);
678 else if ((addr & 0xFFFE) == 0xC0E8)
680 floppyDrive.ControlMotor(addr & 0x01);
682 else if ((addr & 0xFFFE) == 0xC0EA)
684 floppyDrive.DriveEnable(addr & 0x01);
686 else if (addr == 0xC0EC)
688 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
689 floppyDrive.ReadWrite();
691 else if (addr == 0xC0ED)
693 floppyDrive.SetLatchValue(b);
695 else if (addr == 0xC0EE)
697 floppyDrive.SetReadMode();
699 else if (addr == 0xC0EF)
701 floppyDrive.SetWriteMode();
703 //Still need to add missing I/O switches here...
705 //DEEE: BD 10 BF LDA $BF10,X [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
707 if (addr >= 0xD000 && addr <= 0xD00F)
709 WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
712 if (addr >= 0xC000 && addr <= 0xCFFF)
713 return; // Protect LC bank #1 from being written to!
719 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
720 ram[addr - 0x1000] = b;
732 // Load a file into RAM/ROM image space
734 bool LoadImg(char * filename, uint8 * ram, int size)
736 FILE * fp = fopen(filename, "rb");
741 fread(ram, 1, size, fp);
747 static void SaveApple2State(const char * filename)
751 static bool LoadApple2State(const char * filename)
759 int main(int /*argc*/, char * /*argv*/[])
761 InitLog("./apple2.log");
763 srand(time(NULL)); // Initialize RNG
766 //Need to bankify this stuff for the IIe emulation...
767 memset(ram, 0, 0x10000);
768 memset(rom, 0, 0x10000);
770 // Set up V65C02 execution context
771 memset(&mainCPU, 0, sizeof(V65C02REGS));
772 mainCPU.RdMem = RdMem;
773 mainCPU.WrMem = WrMem;
774 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
776 if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
778 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
782 //This is now included...
783 /* if (!LoadImg(settings.diskPath, diskRom, 0x100))
785 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
789 //Load up disk image from config file (for now)...
790 floppyDrive.LoadImage(settings.diskImagePath1, 0);
791 floppyDrive.LoadImage(settings.diskImagePath2, 1);
792 // floppyDrive.LoadImage("./disks/temp.nib", 1); // Load temp .nib file into second drive...
794 //Kill the DOS ROM in slot 6 for now...
796 memcpy(rom + 0xC600, diskROM, 0x100);
798 WriteLog("About to initialize video...\n");
801 std::cout << "Aborting!" << std::endl;
805 // Have to do this *after* video init but *before* sound init...!
806 //Shouldn't be necessary since we're not doing emulation in the ISR...
807 if (settings.autoStateSaving)
809 // Load last state from file...
810 if (!LoadApple2State(settings.autoStatePath))
811 WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
817 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
819 cout << "Couldn't load state file!" << endl;
820 cout << "Aborting!!" << endl;
825 //-- -- -- -- ----- -----
826 //00 75 3B 53 FD 01 41 44
828 mainCPU.cpuFlags = 0;
838 displayPage2 = false;
845 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
848 WriteLog("About to initialize audio...\n");
850 SDL_EnableUNICODE(1); // Needed to do key translation shit
852 // gui = new GUI(surface); // Set up the GUI system object...
853 gui = new GUI(mainSurface); // Set up the GUI system object...
854 gui->AddMenuTitle("Apple2");
855 gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
856 gui->AddMenuItem("");
857 gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
858 gui->CommitItemsToMenu();
860 SetupBlurTable(); // Set up the color TV emulation blur table
861 running = true; // Set running status...
863 InitializeEventList(); // Clear the event list before we use it...
864 SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
865 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals
866 startTicks = SDL_GetTicks();
868 WriteLog("Entering main loop...\n");
871 double timeToNextEvent = GetTimeToNextEvent();
872 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
873 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
874 //(Fix so that this is not a requirement!)
875 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
876 // mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
877 // Handle CPU time delta for sound...
878 AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
882 if (settings.autoStateSaving)
884 // Save state here...
885 SaveApple2State(settings.autoStatePath);
887 floppyDrive.SaveImage();
902 -----------------------
903 space $A0 $A0 $A0 $A0 No xlation
904 RETURN $8D $8D $8D $8D No xlation
905 0 $B0 $B0 $B0 $B0 Need to screen shift+0 (?)
906 1! $B1 $B1 $A1 $A1 No xlation
907 2" $B2 $B2 $A2 $A2 No xlation
908 3# $B3 $B3 $A3 $A3 No xlation
909 4$ $B4 $B4 $A4 $A4 No xlation
910 5% $B5 $B5 $A5 $A5 No xlation
911 6& $B6 $B6 $A6 $A6 No xlation
912 7' $B7 $B7 $A7 $A7 No xlation
913 8( $B8 $B8 $A8 $A8 No xlation
914 9) $B9 $B9 $A9 $A9 No xlation
915 :* $BA $BA $AA $AA No xlation
916 ;+ $BB $BB $AB $AB No xlation
917 ,< $AC $AC $BC $BC No xlation
918 -= $AD $AD $BD $BD No xlation
919 .> $AE $AE $BE $BE No xlation
920 /? $AF $AF $BF $BF No xlation
933 M $CD $8D $DD $9D -> ODD
934 N^ $CE $8E $DE $9E -> ODD
936 P@ $D0 $90 $C0 $80 Need to xlate CTL+SHFT+P & SHFT+P (?)
949 ESC $9B $9B $9B $9B No xlation
952 static void FrameCallback(void)
956 while (SDL_PollEvent(&event))
961 if (event.key.keysym.unicode != 0)
963 //Need to do some key translation here, and screen out non-apple keys as well...
964 if (event.key.keysym.sym == SDLK_TAB) // Prelim key screening...
967 lastKeyPressed = event.key.keysym.unicode;
969 //kludge: should have a caps lock thingy here...
970 //or all uppercase for ][+...
971 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
972 lastKeyPressed &= 0xDF; // Convert to upper case...
975 // CTRL+RESET key emulation (mapped to CTRL+`)
976 // This doesn't work...
977 // if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
978 // if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
979 if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
980 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
981 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
982 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
984 if (event.key.keysym.sym == SDLK_RIGHT)
985 lastKeyPressed = 0x15, keyDown = true;
986 else if (event.key.keysym.sym == SDLK_LEFT)
987 lastKeyPressed = 0x08, keyDown = true;
989 // Use ALT+Q to exit, as well as the usual window decoration method
990 if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
993 if (event.key.keysym.sym == SDLK_F12)
994 dumpDis = !dumpDis; // Toggle the disassembly process
995 else if (event.key.keysym.sym == SDLK_F11)
996 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
997 else if (event.key.keysym.sym == SDLK_F9)
999 floppyDrive.CreateBlankImage();
1000 // SpawnMessage("Image cleared...");
1002 else if (event.key.keysym.sym == SDLK_F10)
1004 floppyDrive.SwapImages();
1005 // SpawnMessage("Image swapped...");
1008 if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1010 else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1013 // if (event.key.keysym.sym == SDLK_F5) // Temp GUI launch key
1014 if (event.key.keysym.sym == SDLK_F1) // GUI launch key
1015 //NOTE: Should parse the output to determine whether or not the user requested
1016 // to quit completely... !!! FIX !!!
1025 //ick. HandleSoundAtFrameEdge(); // Sound stuff... (ick)
1027 SetCallbackTime(FrameCallback, 16666.66666667);
1029 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
1031 //nope. SDL_Delay(10);
1032 while (SDL_GetTicks() - startTicks < 16); // Wait for next frame...
1033 startTicks = SDL_GetTicks();
1036 static void BlinkTimer(void)
1039 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 sec intervals