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;
65 FloppyDrive floppyDrive;
69 static uint8 lastKeyPressed = 0;
70 static bool keyDown = false;
72 //static FloppyDrive floppyDrive;
74 enum { LC_BANK_1, LC_BANK_2 };
76 static uint8 visibleBank = LC_BANK_1;
77 static bool readRAM = false;
78 static bool writeRAM = false;
80 static bool running = true; // Machine running state flag...
81 static uint32 startTicks;
83 static GUI * gui = NULL;
85 // Local functions (technically, they're global...)
87 bool LoadImg(char * filename, uint8 * ram, int size);
88 uint8 RdMem(uint16 addr);
89 void WrMem(uint16 addr, uint8 b);
90 static void SaveApple2State(const char * filename);
91 static bool LoadApple2State(const char * filename);
93 // Local timer callback functions
95 static void FrameCallback(void);
96 static void BlinkTimer(void);
100 Element * TestWindow(void)
102 Element * win = new DraggableWindow2(10, 10, 128, 128);
103 // ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
108 Element * QuitEmulator(void)
117 Small Apple II memory map:
119 $C010 - Clear bit 7 of keyboard data ($C000)
120 $C030 - Toggle speaker diaphragm
122 $C054 - Select page 1
123 $C056 - Select lo-res
124 $C058 - Set annuciator-0 output to 0
125 $C05A - Set annuciator-0 output to 0
126 $C05D - Set annuciator-0 output to 1
127 $C05F - Set annuciator-0 output to 1
128 $C0E0 - Disk control stepper ($C0E0-7)
129 $C0E9 - Disk control motor (on)
130 $C0EA - Disk enable (drive 1)
132 $C0EE - Disk set read mode
136 // V65C02 read/write memory functions
139 uint8 RdMem(uint16 addr)
144 if (addr >= 0xC000 && addr <= 0xC0FF)
145 WriteLog("\n*** Read at I/O address %04X\n", addr);
148 if (addr >= 0xC080 && addr <= 0xC08F)
149 WriteLog("\n*** Read at I/O address %04X\n", addr);
152 if ((addr & 0xFFF0) == 0xC000)
154 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
156 else if ((addr & 0xFFF0) == 0xC010)
158 //This is bogus: keyDown is set to false, so return val NEVER is set...
160 //Also, this is IIe/IIc only...!
161 uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
165 else if ((addr & 0xFFF0) == 0xC030)
168 This is problematic, mainly because the v65C02 removes actual cycles run after each call.
169 Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
172 What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
173 hasn't had any changes in it then we just fill it with the last sample value and are done. Then
174 we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
175 last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
178 Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
179 deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
184 ToggleSpeaker(GetCurrentV65C02Clock());
185 //should it return something else here???
188 else if (addr == 0xC050)
192 else if (addr == 0xC051)
196 else if (addr == 0xC052)
200 else if (addr == 0xC053)
204 else if (addr == 0xC054)
206 displayPage2 = false;
208 else if (addr == 0xC055)
212 else if (addr == 0xC056)
216 else if (addr == 0xC057)
221 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
222 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
223 //[SHOULD BE FIXED NOW]
224 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
225 //visible, two makes it R/W.
261 else if ((addr & 0xFFFB) == 0xC080)
264 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
266 //$C080 49280 OECG R Read RAM bank 2; no write
267 visibleBank = LC_BANK_2;
271 else if ((addr & 0xFFFB) == 0xC081)
274 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
276 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
277 visibleBank = LC_BANK_2;
281 else if ((addr & 0xFFFB) == 0xC082)
284 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
286 //$C082 49282 OECG R Read ROM; no write
287 visibleBank = LC_BANK_2;
291 else if ((addr & 0xFFFB) == 0xC083)
294 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
296 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
297 visibleBank = LC_BANK_2;
301 else if ((addr & 0xFFFB) == 0xC088)
304 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
306 //$C088 49288 OECG R Read RAM bank 1; no write
307 visibleBank = LC_BANK_1;
310 //Hm. Some stuff seems to want this.
311 //nope, was looking at $C0E8... return 0xFF;
313 else if ((addr & 0xFFFB) == 0xC089)
316 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
318 //$C089 49289 OECG RR Read ROM; write RAM bank 1
319 visibleBank = LC_BANK_1;
323 else if ((addr & 0xFFFB) == 0xC08A)
326 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
328 //$C08A 49290 OECG R Read ROM; no write
329 visibleBank = LC_BANK_1;
333 else if ((addr & 0xFFFB) == 0xC08B)
336 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
338 //$C08B 49291 OECG RR Read/write RAM bank 1
339 visibleBank = LC_BANK_1;
343 else if ((addr & 0xFFF8) == 0xC0E0)
345 floppyDrive.ControlStepper(addr & 0x07);
347 else if ((addr & 0xFFFE) == 0xC0E8)
349 floppyDrive.ControlMotor(addr & 0x01);
351 else if ((addr & 0xFFFE) == 0xC0EA)
353 floppyDrive.DriveEnable(addr & 0x01);
355 else if (addr == 0xC0EC)
357 return floppyDrive.ReadWrite();
358 //Hm, some stuff is looking at the return value. Dunno what it *should* be...
359 // OK, it's from the ReadWrite routine...
362 else if (addr == 0xC0ED)
364 return floppyDrive.GetLatchValue();
366 else if (addr == 0xC0EE)
368 floppyDrive.SetReadMode();
370 else if (addr == 0xC0EF)
372 floppyDrive.SetWriteMode();
375 //#define LC_DEBUGGING
377 bool showpath = false;
378 if (addr >= 0xD000 && addr <= 0xD00F)
382 if (addr >= 0xC100 && addr <= 0xCFFF) // The $C000-$CFFF block is *never* RAM
389 WriteLog("b is from $C100-$CFFF block...\n");
392 else if (addr >= 0xD000)
396 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
400 b = ram[addr - 0x1000];
403 WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
413 WriteLog("b is from LC bank #2 (ram[addr])...\n");
424 WriteLog("b is from LC ROM (rom[addr])...\n");
435 WriteLog("b is from ram[addr]...\n");
440 if (addr >= 0xD000 && addr <= 0xD00F)
442 WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
451 APPENDIX F Assembly Language Program Listings
457 5 * ;ADDRESSES FOR FIRST 6522
458 6 ORB EQU $C400 ;PORT B
459 7 ORA EQU $C401 ;PORT A
460 8 DDRB EQU $C402 ;DATA DIRECTION REGISTER (A)
461 9 DDRA EQU $C403 ;DATA DIRECTION REGISTER (B)
462 10 * ;ADDRESSES FOR SECOND 6522
463 11 ORB2 EQU $C480 ;PORT B
464 12 ORA2 EQU $C481 ;PORT A
465 13 DDRB2 EQU $C482 ;DATA DIRECTION REGISTER (B)
466 14 DDRA2 EQU $C483 ;DATA DIRECTION REGISTER (A)
468 void WrMem(uint16 addr, uint8 b)
471 //extern V6809REGS regs;
472 //if (addr >= 0xC800 && addr <= 0xCBFE)
473 //if (addr == 0xC80F || addr == 0xC80D)
474 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
477 if (addr >= 0xC000 && addr <= 0xC0FF)
478 WriteLog("\n*** Write at I/O address %04X\n", addr);
481 Check the BIKO version on Asimov to see if it's been cracked or not...
483 7F3D: 29 07 AND #$07 [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
484 7F3F: C9 06 CMP #$06 [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
485 7F41: 90 03 BCC $7F46 [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
486 [7F43: 4C 83 7E JMP $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
487 7F46: AA TAX [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
489 ; INX here *ensures* 1 - 6!!! BUG!!!
490 ; Or is it? Could this be part of a braindead copy protection scheme? It's
491 ; awfully close to NOP ($EA)...
492 ; Nothing else touches it once it's been written... Hmm...
494 7F47: E8 INX [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
495 7F48: F8 SED [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
496 7F49: 18 CLC [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
497 7F4A: BD 15 4E LDA $4E15,X [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
500 ; 4E15: 25 25 15 15 10 20
501 ; 4E1B: 03 41 99 99 01 00 12
504 7F4D: 65 FC ADC $FC [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
505 7F4F: 65 FC ADC $FC [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
506 7F51: 65 FC ADC $FC [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
507 7F53: 65 FC ADC $FC [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
509 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
511 7F55: 9D 15 4E STA $4E15,X [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
512 7F58: D8 CLD [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
514 ; Print "ALAKAZAM!" and so on...
516 7F59: 20 2C 40 JSR $402C [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
520 WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
523 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
524 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
526 CLRAUXRD = $C002 ;read from main 48K (WR-only)
527 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
529 CLRAUXWR = $C004 ;write to main 48K (WR-only)
530 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
532 CLRCXROM = $C006 ;use ROM on cards (WR-only)
533 SETCXROM = $C007 ;use internal ROM (WR-only)
535 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
536 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
538 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
539 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
541 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
542 SET80VID = $C00D ;enable 80-column display mode (WR-only)
544 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
545 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
549 alternateCharset = false;
551 else if (addr == 0xC00F)
553 alternateCharset = true;
555 else if ((addr & 0xFFF0) == 0xC010) // Keyboard strobe
557 //Actually, according to the A2 ref, this should do nothing since a write
558 //is immediately preceded by a read leaving it in the same state it was...
559 //But leaving this out seems to fuck up the key handling of some games...
562 else if (addr == 0xC050)
566 else if (addr == 0xC051)
570 else if (addr == 0xC052)
574 else if (addr == 0xC053)
578 else if (addr == 0xC054)
580 displayPage2 = false;
582 else if (addr == 0xC055)
586 else if (addr == 0xC056)
590 else if (addr == 0xC057)
594 else if ((addr & 0xFFFB) == 0xC080)
597 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
599 //$C080 49280 OECG R Read RAM bank 2; no write
600 visibleBank = LC_BANK_2;
604 else if ((addr & 0xFFFB) == 0xC081)
607 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
609 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
610 visibleBank = LC_BANK_2;
614 else if ((addr & 0xFFFB) == 0xC082)
617 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
619 //$C082 49282 OECG R Read ROM; no write
620 visibleBank = LC_BANK_2;
624 else if ((addr & 0xFFFB) == 0xC083)
627 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
629 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
630 visibleBank = LC_BANK_2;
634 else if ((addr & 0xFFFB) == 0xC088)
637 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
639 //$C088 49288 OECG R Read RAM bank 1; no write
640 visibleBank = LC_BANK_1;
644 else if ((addr & 0xFFFB) == 0xC089)
647 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
649 //$C089 49289 OECG RR Read ROM; write RAM bank 1
650 visibleBank = LC_BANK_1;
654 else if ((addr & 0xFFFB) == 0xC08A)
657 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
659 //$C08A 49290 OECG R Read ROM; no write
660 visibleBank = LC_BANK_1;
664 else if ((addr & 0xFFFB) == 0xC08B)
667 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
669 //$C08B 49291 OECG RR Read/write RAM bank 1
670 visibleBank = LC_BANK_1;
674 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
675 else if ((addr & 0xFFF8) == 0xC0E0)
677 floppyDrive.ControlStepper(addr & 0x07);
679 else if ((addr & 0xFFFE) == 0xC0E8)
681 floppyDrive.ControlMotor(addr & 0x01);
683 else if ((addr & 0xFFFE) == 0xC0EA)
685 floppyDrive.DriveEnable(addr & 0x01);
687 else if (addr == 0xC0EC)
689 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
690 floppyDrive.ReadWrite();
692 else if (addr == 0xC0ED)
694 floppyDrive.SetLatchValue(b);
696 else if (addr == 0xC0EE)
698 floppyDrive.SetReadMode();
700 else if (addr == 0xC0EF)
702 floppyDrive.SetWriteMode();
704 //Still need to add missing I/O switches here...
706 //DEEE: BD 10 BF LDA $BF10,X [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
708 if (addr >= 0xD000 && addr <= 0xD00F)
710 WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
713 if (addr >= 0xC000 && addr <= 0xCFFF)
714 return; // Protect LC bank #1 from being written to!
720 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
721 ram[addr - 0x1000] = b;
733 // Load a file into RAM/ROM image space
735 bool LoadImg(char * filename, uint8 * ram, int size)
737 FILE * fp = fopen(filename, "rb");
742 fread(ram, 1, size, fp);
748 static void SaveApple2State(const char * filename)
752 static bool LoadApple2State(const char * filename)
757 //#define CPU_CLOCK_CHECKING
758 #ifdef CPU_CLOCK_CHECKING
765 int main(int /*argc*/, char * /*argv*/[])
767 InitLog("./apple2.log");
769 srand(time(NULL)); // Initialize RNG
772 //Need to bankify this stuff for the IIe emulation...
773 memset(ram, 0, 0x10000);
774 memset(rom, 0, 0x10000);
776 // Set up V65C02 execution context
777 memset(&mainCPU, 0, sizeof(V65C02REGS));
778 mainCPU.RdMem = RdMem;
779 mainCPU.WrMem = WrMem;
780 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
782 if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
784 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
788 //This is now included...
789 /* if (!LoadImg(settings.diskPath, diskRom, 0x100))
791 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
795 //Load up disk image from config file (for now)...
796 floppyDrive.LoadImage(settings.diskImagePath1, 0);
797 floppyDrive.LoadImage(settings.diskImagePath2, 1);
798 // floppyDrive.LoadImage("./disks/temp.nib", 1); // Load temp .nib file into second drive...
800 //Kill the DOS ROM in slot 6 for now...
802 memcpy(rom + 0xC600, diskROM, 0x100);
804 WriteLog("About to initialize video...\n");
807 std::cout << "Aborting!" << std::endl;
811 // Have to do this *after* video init but *before* sound init...!
812 //Shouldn't be necessary since we're not doing emulation in the ISR...
813 if (settings.autoStateSaving)
815 // Load last state from file...
816 if (!LoadApple2State(settings.autoStatePath))
817 WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
823 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
825 cout << "Couldn't load state file!" << endl;
826 cout << "Aborting!!" << endl;
831 //-- -- -- -- ----- -----
832 //00 75 3B 53 FD 01 41 44
834 mainCPU.cpuFlags = 0;
844 displayPage2 = false;
851 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
854 WriteLog("About to initialize audio...\n");
856 SDL_EnableUNICODE(1); // Needed to do key translation shit
858 // gui = new GUI(surface); // Set up the GUI system object...
859 gui = new GUI(mainSurface); // Set up the GUI system object...
861 gui->AddMenuTitle("Apple2");
862 gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
863 gui->AddMenuItem("");
864 gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
865 gui->CommitItemsToMenu();
868 SetupBlurTable(); // Set up the color TV emulation blur table
869 running = true; // Set running status...
871 InitializeEventList(); // Clear the event list before we use it...
872 SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
873 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals
874 startTicks = SDL_GetTicks();
876 WriteLog("Entering main loop...\n");
879 double timeToNextEvent = GetTimeToNextEvent();
880 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
881 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
882 //(Fix so that this is not a requirement!)
883 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
884 // mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
885 #ifdef CPU_CLOCK_CHECKING
886 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
888 // Handle CPU time delta for sound...
889 //Don't need this anymore now that we use absolute time...
890 // AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
894 if (settings.autoStateSaving)
896 // Save state here...
897 SaveApple2State(settings.autoStatePath);
899 floppyDrive.SaveImage();
914 -----------------------
915 space $A0 $A0 $A0 $A0 No xlation
916 RETURN $8D $8D $8D $8D No xlation
917 0 $B0 $B0 $B0 $B0 Need to screen shift+0 (?)
918 1! $B1 $B1 $A1 $A1 No xlation
919 2" $B2 $B2 $A2 $A2 No xlation
920 3# $B3 $B3 $A3 $A3 No xlation
921 4$ $B4 $B4 $A4 $A4 No xlation
922 5% $B5 $B5 $A5 $A5 No xlation
923 6& $B6 $B6 $A6 $A6 No xlation
924 7' $B7 $B7 $A7 $A7 No xlation
925 8( $B8 $B8 $A8 $A8 No xlation
926 9) $B9 $B9 $A9 $A9 No xlation
927 :* $BA $BA $AA $AA No xlation
928 ;+ $BB $BB $AB $AB No xlation
929 ,< $AC $AC $BC $BC No xlation
930 -= $AD $AD $BD $BD No xlation
931 .> $AE $AE $BE $BE No xlation
932 /? $AF $AF $BF $BF No xlation
945 M $CD $8D $DD $9D -> ODD
946 N^ $CE $8E $DE $9E -> ODD
948 P@ $D0 $90 $C0 $80 Need to xlate CTL+SHFT+P & SHFT+P (?)
961 ESC $9B $9B $9B $9B No xlation
964 static void FrameCallback(void)
968 while (SDL_PollEvent(&event))
973 if (event.key.keysym.unicode != 0)
975 //Need to do some key translation here, and screen out non-apple keys as well...
976 if (event.key.keysym.sym == SDLK_TAB) // Prelim key screening...
979 lastKeyPressed = event.key.keysym.unicode;
981 //kludge: should have a caps lock thingy here...
982 //or all uppercase for ][+...
983 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
984 lastKeyPressed &= 0xDF; // Convert to upper case...
987 // CTRL+RESET key emulation (mapped to CTRL+`)
988 // This doesn't work...
989 // if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
990 // if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
991 if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
992 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
993 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
994 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
996 if (event.key.keysym.sym == SDLK_RIGHT)
997 lastKeyPressed = 0x15, keyDown = true;
998 else if (event.key.keysym.sym == SDLK_LEFT)
999 lastKeyPressed = 0x08, keyDown = true;
1001 // Use ALT+Q to exit, as well as the usual window decoration method
1002 if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1005 if (event.key.keysym.sym == SDLK_F12)
1006 dumpDis = !dumpDis; // Toggle the disassembly process
1007 // else if (event.key.keysym.sym == SDLK_F11)
1008 // floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1009 else if (event.key.keysym.sym == SDLK_F9)
1011 floppyDrive.CreateBlankImage();
1012 // SpawnMessage("Image cleared...");
1014 else if (event.key.keysym.sym == SDLK_F10)
1016 floppyDrive.SwapImages();
1017 // SpawnMessage("Image swapped...");
1020 if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1022 else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1025 // if (event.key.keysym.sym == SDLK_F5) // Temp GUI launch key
1026 if (event.key.keysym.sym == SDLK_F1) // GUI launch key
1027 //NOTE: Should parse the output to determine whether or not the user requested
1028 // to quit completely... !!! FIX !!!
1031 if (event.key.keysym.sym == SDLK_F5)
1034 char volStr[10] = "*********";
1035 volStr[GetVolume()] = 0;
1036 SpawnMessage("Volume: %s", volStr);
1038 else if (event.key.keysym.sym == SDLK_F6)
1041 char volStr[10] = "*********";
1042 volStr[GetVolume()] = 0;
1043 SpawnMessage("Volume: %s", volStr);
1053 SetCallbackTime(FrameCallback, 16666.66666667);
1055 #ifdef CPU_CLOCK_CHECKING
1059 printf("Executed %u cycles...\n", totalCPU);
1064 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
1066 //nope. SDL_Delay(10);
1067 while (SDL_GetTicks() - startTicks < 16); // Wait for next frame...
1068 startTicks = SDL_GetTicks();
1071 static void BlinkTimer(void)
1074 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 sec intervals