]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
1021f2096356b9bc2a67f1fe6cd67f81d364c25d
[apple2] / src / apple2.cpp
1 //
2 // Apple 2 SDL Portable Apple Emulator
3 //
4 // by James L. Hammons
5 // (C) 2005 Underground Software
6 //
7 // Loosely based on AppleWin by Tom Charlesworth which was based on AppleWin by
8 // Oliver Schmidt which was based on AppleWin by Michael O'Brien. :-) Parts are
9 // also derived from ApplePC. Too bad it was closed source--it could have been
10 // *the* premier Apple II emulator out there.
11 //
12 // JLH = James L. Hammons <jlhamm@acm.org>
13 //
14 // WHO  WHEN        WHAT
15 // ---  ----------  ------------------------------------------------------------
16 // JLH  11/12/2005  Initial port to SDL
17 // JLH  11/18/2005  Wired up graphic soft switches
18 // JLH  12/02/2005  Setup timer subsystem for more accurate time keeping
19 // JLH  12/12/2005  Added preliminary state saving support
20 //
21
22 // STILL TO DO:
23 //
24 // - Port to SDL [DONE]
25 // - GUI goodies
26 // - Weed out unneeded functions [DONE]
27 // - Disk I/O [DONE]
28 // - 128K IIe related stuff
29 // - State loading/saving
30 //
31
32 #include "apple2.h"
33
34 #include <SDL.h>
35 #include <fstream>
36 #include <string>
37 #include <iomanip>
38 #include <iostream>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <time.h>
42 #include "log.h"
43 #include "video.h"
44 #include "sound.h"
45 #include "settings.h"
46 #include "v65c02.h"
47 #include "applevideo.h"
48 #include "timing.h"
49 #include "floppy.h"
50 #include "firmware.h"
51
52 #include "gui/gui.h"
53 #include "gui/window.h"
54 #include "gui/draggablewindow2.h"
55 #include "gui/textedit.h"
56
57 //using namespace std;
58
59 // Global variables
60
61 uint8 ram[0x10000], rom[0x10000];                               // RAM & ROM spaces
62 uint8 diskRom[0x100];                                                   // Disk ROM space
63 V65C02REGS mainCPU;
64 uint8 appleType = APPLE_TYPE_II;
65
66 // Local variables
67
68 static uint8 lastKeyPressed = 0;
69 static bool keyDown = false;
70
71 static FloppyDrive floppyDrive;
72
73 enum { LC_BANK_1, LC_BANK_2 };
74
75 static uint8 visibleBank = LC_BANK_1;
76 static bool readRAM = false;
77 static bool writeRAM = false;
78
79 static bool running = true;                                             // Machine running state flag...
80 static uint32 startTicks;
81
82 static GUI * gui = NULL;
83
84 // Local functions (technically, they're global...)
85
86 bool LoadImg(char * filename, uint8 * ram, int size);
87 uint8 RdMem(uint16 addr);
88 void WrMem(uint16 addr, uint8 b);
89 static void SaveApple2State(const char * filename);
90 static bool LoadApple2State(const char * filename);
91
92 // Local timer callback functions
93
94 static void FrameCallback(void);
95 static void BlinkTimer(void);
96
97 // Test GUI function
98
99 Element * TestWindow(void)
100 {
101         Element * win = new DraggableWindow2(10, 10, 128, 128);
102 //      ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
103
104         return win;
105 }
106
107 Element * QuitEmulator(void)
108 {
109         gui->Stop();
110         running = false;
111
112         return NULL;
113 }
114
115 /*
116  Small Apple II memory map:
117
118 $C010 - Clear bit 7 of keyboard data ($C000)
119 $C030 - Toggle speaker diaphragm
120 $C051 - Display text
121 $C054 - Select page 1
122 $C056 - Select lo-res
123 $C058 - Set annuciator-0 output to 0
124 $C05A - Set annuciator-0 output to 0
125 $C05D - Set annuciator-0 output to 1
126 $C05F - Set annuciator-0 output to 1
127 $C0E0 - Disk control stepper ($C0E0-7)
128 $C0E9 - Disk control motor (on)
129 $C0EA - Disk enable (drive 1)
130 $C0EC - Disk R/W
131 $C0EE - Disk set read mode
132 */
133
134 //
135 // V65C02 read/write memory functions
136 //
137
138 uint8 RdMem(uint16 addr)
139 {
140         uint8 b;
141
142 #if 0
143 if (addr >= 0xC000 && addr <= 0xC0FF)
144         WriteLog("\n*** Read at I/O address %04X\n", addr);
145 #endif
146 #if 0
147 if (addr >= 0xC080 && addr <= 0xC08F)
148         WriteLog("\n*** Read at I/O address %04X\n", addr);
149 #endif
150
151         if ((addr & 0xFFF0) == 0xC000)
152         {
153                 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
154         }
155         else if ((addr & 0xFFF0) == 0xC010)
156         {
157 //This is bogus: keyDown is set to false, so return val NEVER is set...
158 //Fixed...
159 //Also, this is IIe/IIc only...!
160                 uint8 retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
161                 keyDown = false;
162                 return retVal;
163         }
164         else if ((addr & 0xFFF0) == 0xC030)
165         {
166 /*
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.
169 How to fix?
170
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.
175 How to keep track?
176
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. (?)
179
180
181
182 */
183                 ToggleSpeaker(GetCurrentV65C02Clock());
184 //should it return something else here???
185                 return 0x00;
186         }
187         else if (addr == 0xC050)
188         {
189                 textMode = false;
190         }
191         else if (addr == 0xC051)
192         {
193                 textMode = true;
194         }
195         else if (addr == 0xC052)
196         {
197                 mixedMode = false;
198         }
199         else if (addr == 0xC053)
200         {
201                 mixedMode = true;
202         }
203         else if (addr == 0xC054)
204         {
205                 displayPage2 = false;
206         }
207         else if (addr == 0xC055)
208         {
209                 displayPage2 = true;
210         }
211         else if (addr == 0xC056)
212         {
213                 hiRes = false;
214         }
215         else if (addr == 0xC057)
216         {
217                 hiRes = true;
218         }
219
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.
225
226 /*
227 301  LDA $E000
228 304  PHA
229 305  LDA $C081
230 308  PLA
231 309  PHA
232 30A  CMP $E000
233 30D  BNE $332
234 30F  LDA $C083
235 312  LDA $C083
236 315  LDA #$A5
237 317  STA $D000
238 31A  CMP $D000
239 31D  BNE $332
240 31F  LSR A
241 320  STA $D000
242 323  CMP $D000
243 326  BNE $332
244 328  LDA $C081
245 32B  LDA $C081
246 32E  LDA #$01
247 330  BNE $334
248 332  LDA #$00
249 334  STA $300
250 337  PLA
251 338  CMP $E000
252 33B  BEQ $340
253 33D  LDA $C080
254 340  RTS
255
256 A = PEEK($C082)
257 */
258
259 //#define DEBUG_LC
260         else if ((addr & 0xFFFB) == 0xC080)
261         {
262 #ifdef DEBUG_LC
263 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
264 #endif
265 //$C080 49280              OECG  R   Read RAM bank 2; no write
266                 visibleBank = LC_BANK_2;
267                 readRAM = true;
268                 writeRAM = false;
269         }
270         else if ((addr & 0xFFFB) == 0xC081)
271         {
272 #ifdef DEBUG_LC
273 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
274 #endif
275 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
276                 visibleBank = LC_BANK_2;
277                 readRAM = false;
278                 writeRAM = true;
279         }
280         else if ((addr & 0xFFFB) == 0xC082)
281         {
282 #ifdef DEBUG_LC
283 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
284 #endif
285 //$C082 49282              OECG  R   Read ROM; no write
286                 visibleBank = LC_BANK_2;
287                 readRAM = false;
288                 writeRAM = false;
289         }
290         else if ((addr & 0xFFFB) == 0xC083)
291         {
292 #ifdef DEBUG_LC
293 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
294 #endif
295 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
296                 visibleBank = LC_BANK_2;
297                 readRAM = true;
298                 writeRAM = true;
299         }
300         else if ((addr & 0xFFFB) == 0xC088)
301         {
302 #ifdef DEBUG_LC
303 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
304 #endif
305 //$C088 49288              OECG  R   Read RAM bank 1; no write
306                 visibleBank = LC_BANK_1;
307                 readRAM = true;
308                 writeRAM = false;
309 //Hm. Some stuff seems to want this.
310 //nope, was looking at $C0E8... return 0xFF;
311         }
312         else if ((addr & 0xFFFB) == 0xC089)
313         {
314 #ifdef DEBUG_LC
315 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
316 #endif
317 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
318                 visibleBank = LC_BANK_1;
319                 readRAM = false;
320                 writeRAM = true;
321         }
322         else if ((addr & 0xFFFB) == 0xC08A)
323         {
324 #ifdef DEBUG_LC
325 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
326 #endif
327 //$C08A 49290              OECG  R   Read ROM; no write
328                 visibleBank = LC_BANK_1;
329                 readRAM = false;
330                 writeRAM = false;
331         }
332         else if ((addr & 0xFFFB) == 0xC08B)
333         {
334 #ifdef DEBUG_LC
335 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
336 #endif
337 //$C08B 49291              OECG  RR  Read/write RAM bank 1
338                 visibleBank = LC_BANK_1;
339                 readRAM = true;
340                 writeRAM = true;
341         }
342         else if ((addr & 0xFFF8) == 0xC0E0)
343         {
344                 floppyDrive.ControlStepper(addr & 0x07);
345         }
346         else if ((addr & 0xFFFE) == 0xC0E8)
347         {
348                 floppyDrive.ControlMotor(addr & 0x01);
349         }
350         else if ((addr & 0xFFFE) == 0xC0EA)
351         {
352                 floppyDrive.DriveEnable(addr & 0x01);
353         }
354         else if (addr == 0xC0EC)
355         {
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...
359 //return 0xFF;
360         }
361         else if (addr == 0xC0ED)
362         {
363                 return floppyDrive.GetLatchValue();
364         }
365         else if (addr == 0xC0EE)
366         {
367                 floppyDrive.SetReadMode();
368         }
369         else if (addr == 0xC0EF)
370         {
371                 floppyDrive.SetWriteMode();
372         }
373
374 //#define LC_DEBUGGING
375 #ifdef LC_DEBUGGING
376 bool showpath = false;
377 if (addr >= 0xD000 && addr <= 0xD00F)
378         showpath = true;
379 #endif
380 //This sux...
381         if (addr >= 0xC100 && addr <= 0xCFFF)   // The $C000-$CFFF block is *never* RAM
382 #ifdef LC_DEBUGGING
383         {
384 #endif
385                 b = rom[addr];
386 #ifdef LC_DEBUGGING
387 if (showpath)
388         WriteLog("b is from $C100-$CFFF block...\n");
389         }
390 #endif
391         else if (addr >= 0xD000)
392         {
393                 if (readRAM)
394                 {
395                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
396 #ifdef LC_DEBUGGING
397         {
398 #endif
399                                 b = ram[addr - 0x1000];
400 #ifdef LC_DEBUGGING
401 if (showpath)
402         WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
403         }
404 #endif
405                         else
406 #ifdef LC_DEBUGGING
407         {
408 #endif
409                                 b = ram[addr];
410 #ifdef LC_DEBUGGING
411 if (showpath)
412         WriteLog("b is from LC bank #2 (ram[addr])...\n");
413         }
414 #endif
415                 }
416                 else
417 #ifdef LC_DEBUGGING
418         {
419 #endif
420                         b = rom[addr];
421 #ifdef LC_DEBUGGING
422 if (showpath)
423         WriteLog("b is from LC ROM (rom[addr])...\n");
424         }
425 #endif
426         }
427         else
428 #ifdef LC_DEBUGGING
429         {
430 #endif
431                 b = ram[addr];
432 #ifdef LC_DEBUGGING
433 if (showpath)
434         WriteLog("b is from ram[addr]...\n");
435         }
436 #endif
437
438 #ifdef LC_DEBUGGING
439 if (addr >= 0xD000 && addr <= 0xD00F)
440 {
441         WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
442 }
443 #endif
444
445         return b;
446 }
447
448 /*
449 A-9 (Mockingboard)
450 APPENDIX F Assembly Language Program Listings
451
452         1       *PRIMARY ROUTINES
453         2       *FOR SLOT 4
454         3       *
455         4                       ORG     $9000
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)
466 */
467 void WrMem(uint16 addr, uint8 b)
468 {
469 //temp...
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]);//*/
474
475 #if 0
476 if (addr >= 0xC000 && addr <= 0xC0FF)
477         WriteLog("\n*** Write at I/O address %04X\n", addr);
478 #endif
479 /*
480 Check the BIKO version on Asimov to see if it's been cracked or not...
481
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]
487
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...
492
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]
497
498 ; 4E13: 03 00
499 ; 4E15: 25 25 15 15 10 20
500 ; 4E1B: 03 41 99 99 01 00 12
501 ; 4E22: 99 70
502
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]
507
508 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
509
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]
512
513 ; Print "ALAKAZAM!" and so on...
514
515 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
516 */
517 #if 0
518 if (addr == 0x7F47)
519         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
520 #endif
521 /*
522 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
523 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
524
525 CLRAUXRD = $C002 ;read from main 48K (WR-only)
526 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
527
528 CLRAUXWR = $C004 ;write to main 48K (WR-only)
529 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
530
531 CLRCXROM = $C006 ;use ROM on cards (WR-only)
532 SETCXROM = $C007 ;use internal ROM (WR-only)
533
534 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
535 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
536
537 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
538 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
539
540 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
541 SET80VID = $C00D ;enable 80-column display mode (WR-only)
542
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)
545 */
546         if (addr == 0xC00E)
547         {
548                 alternateCharset = false;
549         }
550         else if (addr == 0xC00F)
551         {
552                 alternateCharset = true;
553         }
554         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
555         {
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 //              keyDown = false;
559         }
560         else if (addr == 0xC050)
561         {
562                 textMode = false;
563         }
564         else if (addr == 0xC051)
565         {
566                 textMode = true;
567         }
568         else if (addr == 0xC052)
569         {
570                 mixedMode = false;
571         }
572         else if (addr == 0xC053)
573         {
574                 mixedMode = true;
575         }
576         else if (addr == 0xC054)
577         {
578                 displayPage2 = false;
579         }
580         else if (addr == 0xC055)
581         {
582                 displayPage2 = true;
583         }
584         else if (addr == 0xC056)
585         {
586                 hiRes = false;
587         }
588         else if (addr == 0xC057)
589         {
590                 hiRes = true;
591         }
592         else if ((addr & 0xFFFB) == 0xC080)
593         {
594 #ifdef DEBUG_LC
595 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
596 #endif
597 //$C080 49280              OECG  R   Read RAM bank 2; no write
598                 visibleBank = LC_BANK_2;
599                 readRAM = true;
600                 writeRAM = false;
601         }
602         else if ((addr & 0xFFFB) == 0xC081)
603         {
604 #ifdef DEBUG_LC
605 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
606 #endif
607 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
608                 visibleBank = LC_BANK_2;
609                 readRAM = false;
610                 writeRAM = true;
611         }
612         else if ((addr & 0xFFFB) == 0xC082)
613         {
614 #ifdef DEBUG_LC
615 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
616 #endif
617 //$C082 49282              OECG  R   Read ROM; no write
618                 visibleBank = LC_BANK_2;
619                 readRAM = false;
620                 writeRAM = false;
621         }
622         else if ((addr & 0xFFFB) == 0xC083)
623         {
624 #ifdef DEBUG_LC
625 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
626 #endif
627 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
628                 visibleBank = LC_BANK_2;
629                 readRAM = true;
630                 writeRAM = true;
631         }
632         else if ((addr & 0xFFFB) == 0xC088)
633         {
634 #ifdef DEBUG_LC
635 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
636 #endif
637 //$C088 49288              OECG  R   Read RAM bank 1; no write
638                 visibleBank = LC_BANK_1;
639                 readRAM = true;
640                 writeRAM = false;
641         }
642         else if ((addr & 0xFFFB) == 0xC089)
643         {
644 #ifdef DEBUG_LC
645 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
646 #endif
647 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
648                 visibleBank = LC_BANK_1;
649                 readRAM = false;
650                 writeRAM = true;
651         }
652         else if ((addr & 0xFFFB) == 0xC08A)
653         {
654 #ifdef DEBUG_LC
655 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
656 #endif
657 //$C08A 49290              OECG  R   Read ROM; no write
658                 visibleBank = LC_BANK_1;
659                 readRAM = false;
660                 writeRAM = false;
661         }
662         else if ((addr & 0xFFFB) == 0xC08B)
663         {
664 #ifdef DEBUG_LC
665 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
666 #endif
667 //$C08B 49291              OECG  RR  Read/write RAM bank 1
668                 visibleBank = LC_BANK_1;
669                 readRAM = true;
670                 writeRAM = true;
671         }
672 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
673         else if ((addr & 0xFFF8) == 0xC0E0)
674         {
675                 floppyDrive.ControlStepper(addr & 0x07);
676         }
677         else if ((addr & 0xFFFE) == 0xC0E8)
678         {
679                 floppyDrive.ControlMotor(addr & 0x01);
680         }
681         else if ((addr & 0xFFFE) == 0xC0EA)
682         {
683                 floppyDrive.DriveEnable(addr & 0x01);
684         }
685         else if (addr == 0xC0EC)
686         {
687 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
688                 floppyDrive.ReadWrite();
689         }
690         else if (addr == 0xC0ED)
691         {
692                 floppyDrive.SetLatchValue(b);
693         }
694         else if (addr == 0xC0EE)
695         {
696                 floppyDrive.SetReadMode();
697         }
698         else if (addr == 0xC0EF)
699         {
700                 floppyDrive.SetWriteMode();
701         }
702 //Still need to add missing I/O switches here...
703
704 //DEEE: BD 10 BF       LDA   $BF10,X    [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
705 #if 0
706 if (addr >= 0xD000 && addr <= 0xD00F)
707 {
708         WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
709 }
710 #endif
711         if (addr >= 0xC000 && addr <= 0xCFFF)
712                 return; // Protect LC bank #1 from being written to!
713
714         if (addr >= 0xD000)
715         {
716                 if (writeRAM)
717                 {
718                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
719                                 ram[addr - 0x1000] = b;
720                         else
721                                 ram[addr] = b;
722                 }
723
724                 return;
725         }
726
727         ram[addr] = b;
728 }
729
730 //
731 // Load a file into RAM/ROM image space
732 //
733 bool LoadImg(char * filename, uint8 * ram, int size)
734 {
735         FILE * fp = fopen(filename, "rb");
736
737         if (fp == NULL)
738                 return false;
739
740         fread(ram, 1, size, fp);
741         fclose(fp);
742
743         return true;
744 }
745
746 static void SaveApple2State(const char * filename)
747 {
748 }
749
750 static bool LoadApple2State(const char * filename)
751 {
752         return false;
753 }
754
755 //
756 // Main loop
757 //
758 int main(int /*argc*/, char * /*argv*/[])
759 {
760         InitLog("./apple2.log");
761         LoadSettings();
762         srand(time(NULL));                                                                      // Initialize RNG
763
764         // Zero out memory
765 //Need to bankify this stuff for the IIe emulation...
766         memset(ram, 0, 0x10000);
767         memset(rom, 0, 0x10000);
768
769         // Set up V65C02 execution context
770         memset(&mainCPU, 0, sizeof(V65C02REGS));
771         mainCPU.RdMem = RdMem;
772         mainCPU.WrMem = WrMem;
773         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
774
775         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
776         {
777                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
778                 return -1;
779         }
780
781 //This is now included...
782 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
783         {
784                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
785 //              return -1;
786         }//*/
787
788 //Load up disk image from config file (for now)...
789         floppyDrive.LoadImage(settings.diskImagePath1, 0);
790 //      floppyDrive.LoadImage(settings.diskImagePath2, 1);
791 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
792
793 //Kill the DOS ROM in slot 6 for now...
794 //not
795         memcpy(rom + 0xC600, diskROM, 0x100);
796
797         WriteLog("About to initialize video...\n");
798         if (!InitVideo())
799         {
800                 std::cout << "Aborting!" << std::endl;
801                 return -1;
802         }
803
804         // Have to do this *after* video init but *before* sound init...!
805 //Shouldn't be necessary since we're not doing emulation in the ISR...
806         if (settings.autoStateSaving)
807         {
808                 // Load last state from file...
809                 if (!LoadApple2State(settings.autoStatePath))
810                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
811         }
812
813
814 #if 0
815 // State loading!
816 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
817 {
818         cout << "Couldn't load state file!" << endl;
819         cout << "Aborting!!" << endl;
820         return -1;
821 }
822
823 //A  P  Y  X  S     PC
824 //-- -- -- -- ----- -----
825 //00 75 3B 53 FD 01 41 44
826
827 mainCPU.cpuFlags = 0;
828 mainCPU.a = 0x00;
829 mainCPU.x = 0x53;
830 mainCPU.y = 0x3B;
831 mainCPU.cc = 0x75;
832 mainCPU.sp = 0xFD;
833 mainCPU.pc = 0x4441;
834
835 textMode = false;
836 mixedMode = false;
837 displayPage2 = false;
838 hiRes = true;
839
840 //kludge...
841 readHiRam = true;
842 //dumpDis=true;
843 //kludge II...
844 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
845 #endif
846
847         WriteLog("About to initialize audio...\n");
848         SoundInit();
849         SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
850
851         gui = new GUI(surface);                                         // Set up the GUI system object...
852         gui->AddMenuTitle("Apple2");
853         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
854         gui->AddMenuItem("");
855         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
856         gui->CommitItemsToMenu();
857
858         SetupBlurTable();                                                       // Set up the color TV emulation blur table
859         running = true;                                                         // Set running status...
860
861         InitializeEventList();                                          // Clear the event list before we use it...
862         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
863         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
864         startTicks = SDL_GetTicks();
865
866         WriteLog("Entering main loop...\n");
867         while (running)
868         {
869                 double timeToNextEvent = GetTimeToNextEvent();
870                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
871 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
872 //(Fix so that this is not a requirement!)
873 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
874 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
875                 // Handle CPU time delta for sound...
876                 AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
877                 HandleNextEvent();
878         }
879
880         if (settings.autoStateSaving)
881         {
882                 // Save state here...
883                 SaveApple2State(settings.autoStatePath);
884         }
885 floppyDrive.SaveImage();
886
887         SoundDone();
888         VideoDone();
889         SaveSettings();
890         LogDone();
891
892         return 0;
893 }
894
895 /*
896 Apple II keycodes
897 -----------------
898
899 Key     Aln CTL SHF BTH
900 -----------------------
901 space   $A0     $A0     $A0 $A0         No xlation
902 RETURN  $8D     $8D     $8D     $8D             No xlation
903 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
904 1!              $B1 $B1 $A1 $A1         No xlation
905 2"              $B2     $B2     $A2     $A2             No xlation
906 3#              $B3     $B3     $A3     $A3             No xlation
907 4$              $B4     $B4     $A4     $A4             No xlation
908 5%              $B5     $B5     $A5     $A5             No xlation
909 6&              $B6     $B6     $A6     $A6             No xlation
910 7'              $B7     $B7     $A7     $A7             No xlation
911 8(              $B8     $B8     $A8     $A8             No xlation
912 9)              $B9     $B9     $A9     $A9             No xlation
913 :*              $BA     $BA     $AA     $AA             No xlation
914 ;+              $BB     $BB     $AB     $AB             No xlation
915 ,<              $AC     $AC     $BC     $BC             No xlation
916 -=              $AD     $AD     $BD     $BD             No xlation
917 .>              $AE     $AE     $BE     $BE             No xlation
918 /?              $AF     $AF     $BF     $BF             No xlation
919 A               $C1     $81     $C1     $81
920 B               $C2     $82     $C2     $82
921 C               $C3     $83     $C3     $83
922 D               $C4     $84     $C4     $84
923 E               $C5     $85     $C5     $85
924 F               $C6     $86     $C6     $86
925 G               $C7     $87     $C7     $87
926 H               $C8     $88     $C8     $88
927 I               $C9     $89     $C9     $89
928 J               $CA     $8A     $CA     $8A
929 K               $CB     $8B     $CB     $8B
930 L               $CC     $8C     $CC     $8C
931 M               $CD     $8D     $DD     $9D             -> ODD
932 N^              $CE     $8E     $DE     $9E             -> ODD
933 O               $CF     $8F     $CF     $8F
934 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
935 Q               $D1     $91     $D1     $91
936 R               $D2     $92     $D2     $92
937 S               $D3     $93     $D3     $93
938 T               $D4     $94     $D4     $94
939 U               $D5     $95     $D5     $95
940 V               $D6     $96     $D6     $96
941 W               $D7     $97     $D7     $97
942 X               $D8     $98     $D8     $98
943 Y               $D9     $99     $D9     $99
944 Z               $DA     $9A     $DA     $9A
945 <-              $88     $88     $88     $88
946 ->              $95     $95     $95     $95
947 ESC             $9B     $9B     $9B     $9B             No xlation
948
949 */
950 static void FrameCallback(void)
951 {
952         SDL_Event event;
953
954         while (SDL_PollEvent(&event))
955         {
956                 switch (event.type)
957                 {
958                 case SDL_KEYDOWN:
959                         if (event.key.keysym.unicode != 0)
960                         {
961 //Need to do some key translation here, and screen out non-apple keys as well...
962                                 if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
963                                         break;
964
965                                 lastKeyPressed = event.key.keysym.unicode;
966                                 keyDown = true;
967                                 //kludge: should have a caps lock thingy here...
968                                 //or all uppercase for ][+...
969                                 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
970                                         lastKeyPressed &= 0xDF;         // Convert to upper case...
971                         }
972
973                         // CTRL+RESET key emulation (mapped to CTRL+`)
974 // This doesn't work...
975 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
976 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
977                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
978 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
979 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
980                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
981
982                         if (event.key.keysym.sym == SDLK_RIGHT)
983                                 lastKeyPressed = 0x15, keyDown = true;
984                         else if (event.key.keysym.sym == SDLK_LEFT)
985                                 lastKeyPressed = 0x08, keyDown = true;
986
987                         // Use ALT+Q to exit, as well as the usual window decoration method
988                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
989                                 running = false;
990
991                         if (event.key.keysym.sym == SDLK_F12)
992                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
993                         else if (event.key.keysym.sym == SDLK_F11)
994                                 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
995 else if (event.key.keysym.sym == SDLK_F9)
996 {
997         floppyDrive.CreateBlankImage();
998 //      SpawnMessage("Image cleared...");
999 }//*/
1000 else if (event.key.keysym.sym == SDLK_F10)
1001 {
1002         floppyDrive.SwapImages();
1003 //      SpawnMessage("Image swapped...");
1004 }//*/
1005
1006                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1007                                 TogglePalette();
1008                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1009                                 CycleScreenTypes();
1010
1011 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1012                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1013 //NOTE: Should parse the output to determine whether or not the user requested
1014 //      to quit completely... !!! FIX !!!
1015                                 gui->Run();
1016
1017                         break;
1018                 case SDL_QUIT:
1019                         running = false;
1020                 }
1021         }
1022
1023 //ick.  HandleSoundAtFrameEdge();                                       // Sound stuff... (ick)
1024         RenderVideoFrame();
1025         SetCallbackTime(FrameCallback, 16666.66666667);
1026
1027 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
1028 //lessee...
1029 //nope. SDL_Delay(10);
1030         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1031         startTicks = SDL_GetTicks();
1032 }
1033
1034 static void BlinkTimer(void)
1035 {
1036         flash = !flash;
1037         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1038 }