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);
98 #define THREADED_65C02
100 // Test of threaded execution of 6502
101 static SDL_Thread * cpuThread = NULL;
102 //static SDL_mutex * cpuMutex = NULL;
103 static SDL_cond * cpuCond = NULL;
104 static bool cpuFinished = false;
105 static bool cpuSleep = false;
107 // Let's try a thread...
109 Here's how it works: Execute 1 frame's worth, then sleep.
110 Other stuff wakes it up
112 int CPUThreadFunc(void * data)
114 // Mutex must be locked for conditional to work...
115 // Also, must be created in the thread that uses it...
116 SDL_mutex * cpuMutex = SDL_CreateMutex();
121 SDL_CondWait(cpuCond, cpuMutex);
123 Execute65C02(&mainCPU, 17066); // how much? 1 frame (after 1 s, off by 40 cycles)
124 SDL_mutexP(cpuMutex);
125 if (SDL_CondWait(cpuCond, cpuMutex) != 0)
127 printf("SDL_CondWait != 0! (Error: '%s')\n", SDL_GetError());
130 SDL_mutexV(cpuMutex);
132 while (!cpuFinished);
140 Element * TestWindow(void)
142 Element * win = new DraggableWindow2(10, 10, 128, 128);
143 // ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
148 Element * QuitEmulator(void)
157 Small Apple II memory map:
159 $C010 - Clear bit 7 of keyboard data ($C000)
160 $C030 - Toggle speaker diaphragm
162 $C054 - Select page 1
163 $C056 - Select lo-res
164 $C058 - Set annuciator-0 output to 0
165 $C05A - Set annuciator-0 output to 0
166 $C05D - Set annuciator-0 output to 1
167 $C05F - Set annuciator-0 output to 1
168 $C0E0 - Disk control stepper ($C0E0-7)
169 $C0E9 - Disk control motor (on)
170 $C0EA - Disk enable (drive 1)
172 $C0EE - Disk set read mode
176 // V65C02 read/write memory functions
179 uint8 RdMem(uint16 addr)
184 if (addr >= 0xC000 && addr <= 0xC0FF)
185 WriteLog("\n*** Read at I/O address %04X\n", addr);
188 if (addr >= 0xC080 && addr <= 0xC08F)
189 WriteLog("\n*** Read at I/O address %04X\n", addr);
192 if ((addr & 0xFFF0) == 0xC000)
194 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
196 else if ((addr & 0xFFF0) == 0xC010)
198 //This is bogus: keyDown is set to false, so return val NEVER is set...
200 //Also, this is IIe/IIc only...!
201 uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
205 else if ((addr & 0xFFF0) == 0xC030)
208 This is problematic, mainly because the v65C02 removes actual cycles run after each call.
209 Therefore, we don't really have a reliable method of sending a timestamp to the sound routine.
212 What we need to send is a delta T value but related to the IRQ buffer routine. E.g., if the buffer
213 hasn't had any changes in it then we just fill it with the last sample value and are done. Then
214 we need to adjust our delta T accordingly. What we could do is keep a running total of time since the
215 last change and adjust it accordingly, i.e., whenever a sound IRQ happens.
218 Have deltaT somewhere. Then, whenever there's a toggle, backfill buffer with last spkr state and reset
219 deltaT to zero. In the sound IRQ, if deltaT > buffer size, then subtract buffer size from deltaT. (?)
224 ToggleSpeaker(GetCurrentV65C02Clock());
225 //should it return something else here???
228 else if (addr == 0xC050)
232 else if (addr == 0xC051)
236 else if (addr == 0xC052)
240 else if (addr == 0xC053)
244 else if (addr == 0xC054)
246 displayPage2 = false;
248 else if (addr == 0xC055)
252 else if (addr == 0xC056)
256 else if (addr == 0xC057)
261 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
262 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
263 //[SHOULD BE FIXED NOW]
264 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
265 //visible, two makes it R/W.
301 else if ((addr & 0xFFFB) == 0xC080)
304 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
306 //$C080 49280 OECG R Read RAM bank 2; no write
307 visibleBank = LC_BANK_2;
311 else if ((addr & 0xFFFB) == 0xC081)
314 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
316 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
317 visibleBank = LC_BANK_2;
321 else if ((addr & 0xFFFB) == 0xC082)
324 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
326 //$C082 49282 OECG R Read ROM; no write
327 visibleBank = LC_BANK_2;
331 else if ((addr & 0xFFFB) == 0xC083)
334 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
336 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
337 visibleBank = LC_BANK_2;
341 else if ((addr & 0xFFFB) == 0xC088)
344 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
346 //$C088 49288 OECG R Read RAM bank 1; no write
347 visibleBank = LC_BANK_1;
350 //Hm. Some stuff seems to want this.
351 //nope, was looking at $C0E8... return 0xFF;
353 else if ((addr & 0xFFFB) == 0xC089)
356 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
358 //$C089 49289 OECG RR Read ROM; write RAM bank 1
359 visibleBank = LC_BANK_1;
363 else if ((addr & 0xFFFB) == 0xC08A)
366 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
368 //$C08A 49290 OECG R Read ROM; no write
369 visibleBank = LC_BANK_1;
373 else if ((addr & 0xFFFB) == 0xC08B)
376 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
378 //$C08B 49291 OECG RR Read/write RAM bank 1
379 visibleBank = LC_BANK_1;
383 else if ((addr & 0xFFF8) == 0xC0E0)
385 floppyDrive.ControlStepper(addr & 0x07);
387 else if ((addr & 0xFFFE) == 0xC0E8)
389 floppyDrive.ControlMotor(addr & 0x01);
391 else if ((addr & 0xFFFE) == 0xC0EA)
393 floppyDrive.DriveEnable(addr & 0x01);
395 else if (addr == 0xC0EC)
397 return floppyDrive.ReadWrite();
398 //Hm, some stuff is looking at the return value. Dunno what it *should* be...
399 // OK, it's from the ReadWrite routine...
402 else if (addr == 0xC0ED)
404 return floppyDrive.GetLatchValue();
406 else if (addr == 0xC0EE)
408 floppyDrive.SetReadMode();
410 else if (addr == 0xC0EF)
412 floppyDrive.SetWriteMode();
415 //#define LC_DEBUGGING
417 bool showpath = false;
418 if (addr >= 0xD000 && addr <= 0xD00F)
422 if (addr >= 0xC100 && addr <= 0xCFFF) // The $C000-$CFFF block is *never* RAM
429 WriteLog("b is from $C100-$CFFF block...\n");
432 else if (addr >= 0xD000)
436 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
440 b = ram[addr - 0x1000];
443 WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
453 WriteLog("b is from LC bank #2 (ram[addr])...\n");
464 WriteLog("b is from LC ROM (rom[addr])...\n");
475 WriteLog("b is from ram[addr]...\n");
480 if (addr >= 0xD000 && addr <= 0xD00F)
482 WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
491 APPENDIX F Assembly Language Program Listings
497 5 * ;ADDRESSES FOR FIRST 6522
498 6 ORB EQU $C400 ;PORT B
499 7 ORA EQU $C401 ;PORT A
500 8 DDRB EQU $C402 ;DATA DIRECTION REGISTER (A)
501 9 DDRA EQU $C403 ;DATA DIRECTION REGISTER (B)
502 10 * ;ADDRESSES FOR SECOND 6522
503 11 ORB2 EQU $C480 ;PORT B
504 12 ORA2 EQU $C481 ;PORT A
505 13 DDRB2 EQU $C482 ;DATA DIRECTION REGISTER (B)
506 14 DDRA2 EQU $C483 ;DATA DIRECTION REGISTER (A)
508 void WrMem(uint16 addr, uint8 b)
511 //extern V6809REGS regs;
512 //if (addr >= 0xC800 && addr <= 0xCBFE)
513 //if (addr == 0xC80F || addr == 0xC80D)
514 // WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
517 if (addr >= 0xC000 && addr <= 0xC0FF)
518 WriteLog("\n*** Write at I/O address %04X\n", addr);
521 Check the BIKO version on Asimov to see if it's been cracked or not...
523 7F3D: 29 07 AND #$07 [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
524 7F3F: C9 06 CMP #$06 [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
525 7F41: 90 03 BCC $7F46 [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
526 [7F43: 4C 83 7E JMP $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
527 7F46: AA TAX [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
529 ; INX here *ensures* 1 - 6!!! BUG!!!
530 ; Or is it? Could this be part of a braindead copy protection scheme? It's
531 ; awfully close to NOP ($EA)...
532 ; Nothing else touches it once it's been written... Hmm...
534 7F47: E8 INX [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
535 7F48: F8 SED [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
536 7F49: 18 CLC [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
537 7F4A: BD 15 4E LDA $4E15,X [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
540 ; 4E15: 25 25 15 15 10 20
541 ; 4E1B: 03 41 99 99 01 00 12
544 7F4D: 65 FC ADC $FC [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
545 7F4F: 65 FC ADC $FC [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
546 7F51: 65 FC ADC $FC [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
547 7F53: 65 FC ADC $FC [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
549 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
551 7F55: 9D 15 4E STA $4E15,X [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
552 7F58: D8 CLD [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
554 ; Print "ALAKAZAM!" and so on...
556 7F59: 20 2C 40 JSR $402C [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
560 WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
563 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
564 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
566 CLRAUXRD = $C002 ;read from main 48K (WR-only)
567 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
569 CLRAUXWR = $C004 ;write to main 48K (WR-only)
570 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
572 CLRCXROM = $C006 ;use ROM on cards (WR-only)
573 SETCXROM = $C007 ;use internal ROM (WR-only)
575 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
576 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
578 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
579 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
581 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
582 SET80VID = $C00D ;enable 80-column display mode (WR-only)
584 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
585 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
589 alternateCharset = false;
591 else if (addr == 0xC00F)
593 alternateCharset = true;
595 else if ((addr & 0xFFF0) == 0xC010) // Keyboard strobe
597 //Actually, according to the A2 ref, this should do nothing since a write
598 //is immediately preceded by a read leaving it in the same state it was...
599 //But leaving this out seems to fuck up the key handling of some games...
602 else if (addr == 0xC050)
606 else if (addr == 0xC051)
610 else if (addr == 0xC052)
614 else if (addr == 0xC053)
618 else if (addr == 0xC054)
620 displayPage2 = false;
622 else if (addr == 0xC055)
626 else if (addr == 0xC056)
630 else if (addr == 0xC057)
634 else if ((addr & 0xFFFB) == 0xC080)
637 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
639 //$C080 49280 OECG R Read RAM bank 2; no write
640 visibleBank = LC_BANK_2;
644 else if ((addr & 0xFFFB) == 0xC081)
647 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
649 //$C081 49281 ROMIN OECG RR Read ROM; write RAM bank 2
650 visibleBank = LC_BANK_2;
654 else if ((addr & 0xFFFB) == 0xC082)
657 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
659 //$C082 49282 OECG R Read ROM; no write
660 visibleBank = LC_BANK_2;
664 else if ((addr & 0xFFFB) == 0xC083)
667 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
669 //$C083 49283 LCBANK2 OECG RR Read/write RAM bank 2
670 visibleBank = LC_BANK_2;
674 else if ((addr & 0xFFFB) == 0xC088)
677 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
679 //$C088 49288 OECG R Read RAM bank 1; no write
680 visibleBank = LC_BANK_1;
684 else if ((addr & 0xFFFB) == 0xC089)
687 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
689 //$C089 49289 OECG RR Read ROM; write RAM bank 1
690 visibleBank = LC_BANK_1;
694 else if ((addr & 0xFFFB) == 0xC08A)
697 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
699 //$C08A 49290 OECG R Read ROM; no write
700 visibleBank = LC_BANK_1;
704 else if ((addr & 0xFFFB) == 0xC08B)
707 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
709 //$C08B 49291 OECG RR Read/write RAM bank 1
710 visibleBank = LC_BANK_1;
714 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
715 else if ((addr & 0xFFF8) == 0xC0E0)
717 floppyDrive.ControlStepper(addr & 0x07);
719 else if ((addr & 0xFFFE) == 0xC0E8)
721 floppyDrive.ControlMotor(addr & 0x01);
723 else if ((addr & 0xFFFE) == 0xC0EA)
725 floppyDrive.DriveEnable(addr & 0x01);
727 else if (addr == 0xC0EC)
729 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
730 floppyDrive.ReadWrite();
732 else if (addr == 0xC0ED)
734 floppyDrive.SetLatchValue(b);
736 else if (addr == 0xC0EE)
738 floppyDrive.SetReadMode();
740 else if (addr == 0xC0EF)
742 floppyDrive.SetWriteMode();
744 //Still need to add missing I/O switches here...
746 //DEEE: BD 10 BF LDA $BF10,X [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
748 if (addr >= 0xD000 && addr <= 0xD00F)
750 WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
753 if (addr >= 0xC000 && addr <= 0xCFFF)
754 return; // Protect LC bank #1 from being written to!
760 if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
761 ram[addr - 0x1000] = b;
773 // Load a file into RAM/ROM image space
775 bool LoadImg(char * filename, uint8 * ram, int size)
777 FILE * fp = fopen(filename, "rb");
782 fread(ram, 1, size, fp);
788 static void SaveApple2State(const char * filename)
792 static bool LoadApple2State(const char * filename)
797 //#define CPU_CLOCK_CHECKING
798 #ifdef CPU_CLOCK_CHECKING
805 int main(int /*argc*/, char * /*argv*/[])
807 InitLog("./apple2.log");
809 srand(time(NULL)); // Initialize RNG
812 //Need to bankify this stuff for the IIe emulation...
813 memset(ram, 0, 0x10000);
814 memset(rom, 0, 0x10000);
816 // Set up V65C02 execution context
817 memset(&mainCPU, 0, sizeof(V65C02REGS));
818 mainCPU.RdMem = RdMem;
819 mainCPU.WrMem = WrMem;
820 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
822 if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
824 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
828 //This is now included...
829 /* if (!LoadImg(settings.diskPath, diskRom, 0x100))
831 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
835 //Load up disk image from config file (for now)...
836 floppyDrive.LoadImage(settings.diskImagePath1, 0);
837 floppyDrive.LoadImage(settings.diskImagePath2, 1);
838 // floppyDrive.LoadImage("./disks/temp.nib", 1); // Load temp .nib file into second drive...
840 //Kill the DOS ROM in slot 6 for now...
842 memcpy(rom + 0xC600, diskROM, 0x100);
844 WriteLog("About to initialize video...\n");
847 std::cout << "Aborting!" << std::endl;
851 // Have to do this *after* video init but *before* sound init...!
852 //Shouldn't be necessary since we're not doing emulation in the ISR...
853 if (settings.autoStateSaving)
855 // Load last state from file...
856 if (!LoadApple2State(settings.autoStatePath))
857 WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
863 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
865 cout << "Couldn't load state file!" << endl;
866 cout << "Aborting!!" << endl;
871 //-- -- -- -- ----- -----
872 //00 75 3B 53 FD 01 41 44
874 mainCPU.cpuFlags = 0;
884 displayPage2 = false;
891 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
894 WriteLog("About to initialize audio...\n");
896 SDL_EnableUNICODE(1); // Needed to do key translation shit
898 // gui = new GUI(surface); // Set up the GUI system object...
899 gui = new GUI(mainSurface); // Set up the GUI system object...
901 gui->AddMenuTitle("Apple2");
902 gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
903 gui->AddMenuItem("");
904 gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
905 gui->CommitItemsToMenu();
908 SetupBlurTable(); // Set up the color TV emulation blur table
909 running = true; // Set running status...
911 InitializeEventList(); // Clear the event list before we use it...
912 SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
913 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 s intervals
914 startTicks = SDL_GetTicks();
916 #ifdef THREADED_65C02
917 //cpuMutex = SDL_CreateMutex();
918 //mutex must be locked for conditional to work...
919 //if (SDL_mutexP(cpuMutex) == -1)
921 // printf("Couldn't lock CPU mutex!\n");
924 cpuCond = SDL_CreateCond();
925 //printf("mutex=$%08X, cond=$%08X\n", cpuMutex, cpuCond);
927 cpuThread = SDL_CreateThread(CPUThreadFunc, NULL);
929 //SDL_CondSignal(cpuCond);
932 WriteLog("Entering main loop...\n");
935 double timeToNextEvent = GetTimeToNextEvent();
936 #ifndef THREADED_65C02
937 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
939 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
940 //(Fix so that this is not a requirement!)
941 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
942 // mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
943 #ifdef CPU_CLOCK_CHECKING
944 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
946 // Handle CPU time delta for sound...
947 //Don't need this anymore now that we use absolute time...
948 // AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
952 #ifdef THREADED_65C02
954 SDL_CondSignal(cpuCond);//thread is asleep, wake it up
955 SDL_WaitThread(cpuThread, NULL);
956 SDL_DestroyCond(cpuCond);
957 //SDL_DestroyMutex(cpuMutex);
960 if (settings.autoStateSaving)
962 // Save state here...
963 SaveApple2State(settings.autoStatePath);
965 floppyDrive.SaveImage();
980 -----------------------
981 space $A0 $A0 $A0 $A0 No xlation
982 RETURN $8D $8D $8D $8D No xlation
983 0 $B0 $B0 $B0 $B0 Need to screen shift+0 (?)
984 1! $B1 $B1 $A1 $A1 No xlation
985 2" $B2 $B2 $A2 $A2 No xlation
986 3# $B3 $B3 $A3 $A3 No xlation
987 4$ $B4 $B4 $A4 $A4 No xlation
988 5% $B5 $B5 $A5 $A5 No xlation
989 6& $B6 $B6 $A6 $A6 No xlation
990 7' $B7 $B7 $A7 $A7 No xlation
991 8( $B8 $B8 $A8 $A8 No xlation
992 9) $B9 $B9 $A9 $A9 No xlation
993 :* $BA $BA $AA $AA No xlation
994 ;+ $BB $BB $AB $AB No xlation
995 ,< $AC $AC $BC $BC No xlation
996 -= $AD $AD $BD $BD No xlation
997 .> $AE $AE $BE $BE No xlation
998 /? $AF $AF $BF $BF No xlation
1011 M $CD $8D $DD $9D -> ODD
1012 N^ $CE $8E $DE $9E -> ODD
1014 P@ $D0 $90 $C0 $80 Need to xlate CTL+SHFT+P & SHFT+P (?)
1027 ESC $9B $9B $9B $9B No xlation
1030 static void FrameCallback(void)
1034 while (SDL_PollEvent(&event))
1039 if (event.key.keysym.unicode != 0)
1041 //Need to do some key translation here, and screen out non-apple keys as well...
1042 if (event.key.keysym.sym == SDLK_TAB) // Prelim key screening...
1045 lastKeyPressed = event.key.keysym.unicode;
1047 //kludge: should have a caps lock thingy here...
1048 //or all uppercase for ][+...
1049 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
1050 lastKeyPressed &= 0xDF; // Convert to upper case...
1053 // CTRL+RESET key emulation (mapped to CTRL+`)
1054 // This doesn't work...
1055 // if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
1056 // if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
1057 if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
1058 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
1059 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
1060 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1062 if (event.key.keysym.sym == SDLK_RIGHT)
1063 lastKeyPressed = 0x15, keyDown = true;
1064 else if (event.key.keysym.sym == SDLK_LEFT)
1065 lastKeyPressed = 0x08, keyDown = true;
1067 // Use ALT+Q to exit, as well as the usual window decoration method
1068 if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1071 if (event.key.keysym.sym == SDLK_F12)
1072 dumpDis = !dumpDis; // Toggle the disassembly process
1073 // else if (event.key.keysym.sym == SDLK_F11)
1074 // floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1075 else if (event.key.keysym.sym == SDLK_F9)
1077 floppyDrive.CreateBlankImage();
1078 // SpawnMessage("Image cleared...");
1080 else if (event.key.keysym.sym == SDLK_F10)
1082 floppyDrive.SwapImages();
1083 // SpawnMessage("Image swapped...");
1086 if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1088 else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1091 // if (event.key.keysym.sym == SDLK_F5) // Temp GUI launch key
1092 if (event.key.keysym.sym == SDLK_F1) // GUI launch key
1093 //NOTE: Should parse the output to determine whether or not the user requested
1094 // to quit completely... !!! FIX !!!
1097 if (event.key.keysym.sym == SDLK_F5)
1100 char volStr[10] = "*********";
1101 volStr[GetVolume()] = 0;
1102 SpawnMessage("Volume: %s", volStr);
1104 else if (event.key.keysym.sym == SDLK_F6)
1107 char volStr[10] = "*********";
1108 volStr[GetVolume()] = 0;
1109 SpawnMessage("Volume: %s", volStr);
1119 SetCallbackTime(FrameCallback, 16666.66666667);
1121 #ifdef CPU_CLOCK_CHECKING
1125 printf("Executed %u cycles...\n", totalCPU);
1130 #ifdef THREADED_65C02
1131 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
1133 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
1136 //Actually, slows things down too much...
1138 while (SDL_GetTicks() - startTicks < 16); // Wait for next frame...
1139 startTicks = SDL_GetTicks();
1142 static void BlinkTimer(void)
1145 SetCallbackTime(BlinkTimer, 250000); // Set up blinking at 1/4 sec intervals
1149 Next problem is this: How to have events occur and synchronize with the rest
1152 o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
1153 remainder CPU cycles over...)