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