]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Beginnings of new GUI
[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 //But leaving this out seems to fuck up the key handling of some games...
559                 keyDown = false;
560         }
561         else if (addr == 0xC050)
562         {
563                 textMode = false;
564         }
565         else if (addr == 0xC051)
566         {
567                 textMode = true;
568         }
569         else if (addr == 0xC052)
570         {
571                 mixedMode = false;
572         }
573         else if (addr == 0xC053)
574         {
575                 mixedMode = true;
576         }
577         else if (addr == 0xC054)
578         {
579                 displayPage2 = false;
580         }
581         else if (addr == 0xC055)
582         {
583                 displayPage2 = true;
584         }
585         else if (addr == 0xC056)
586         {
587                 hiRes = false;
588         }
589         else if (addr == 0xC057)
590         {
591                 hiRes = true;
592         }
593         else if ((addr & 0xFFFB) == 0xC080)
594         {
595 #ifdef DEBUG_LC
596 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
597 #endif
598 //$C080 49280              OECG  R   Read RAM bank 2; no write
599                 visibleBank = LC_BANK_2;
600                 readRAM = true;
601                 writeRAM = false;
602         }
603         else if ((addr & 0xFFFB) == 0xC081)
604         {
605 #ifdef DEBUG_LC
606 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
607 #endif
608 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
609                 visibleBank = LC_BANK_2;
610                 readRAM = false;
611                 writeRAM = true;
612         }
613         else if ((addr & 0xFFFB) == 0xC082)
614         {
615 #ifdef DEBUG_LC
616 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
617 #endif
618 //$C082 49282              OECG  R   Read ROM; no write
619                 visibleBank = LC_BANK_2;
620                 readRAM = false;
621                 writeRAM = false;
622         }
623         else if ((addr & 0xFFFB) == 0xC083)
624         {
625 #ifdef DEBUG_LC
626 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
627 #endif
628 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
629                 visibleBank = LC_BANK_2;
630                 readRAM = true;
631                 writeRAM = true;
632         }
633         else if ((addr & 0xFFFB) == 0xC088)
634         {
635 #ifdef DEBUG_LC
636 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
637 #endif
638 //$C088 49288              OECG  R   Read RAM bank 1; no write
639                 visibleBank = LC_BANK_1;
640                 readRAM = true;
641                 writeRAM = false;
642         }
643         else if ((addr & 0xFFFB) == 0xC089)
644         {
645 #ifdef DEBUG_LC
646 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
647 #endif
648 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
649                 visibleBank = LC_BANK_1;
650                 readRAM = false;
651                 writeRAM = true;
652         }
653         else if ((addr & 0xFFFB) == 0xC08A)
654         {
655 #ifdef DEBUG_LC
656 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
657 #endif
658 //$C08A 49290              OECG  R   Read ROM; no write
659                 visibleBank = LC_BANK_1;
660                 readRAM = false;
661                 writeRAM = false;
662         }
663         else if ((addr & 0xFFFB) == 0xC08B)
664         {
665 #ifdef DEBUG_LC
666 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
667 #endif
668 //$C08B 49291              OECG  RR  Read/write RAM bank 1
669                 visibleBank = LC_BANK_1;
670                 readRAM = true;
671                 writeRAM = true;
672         }
673 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
674         else if ((addr & 0xFFF8) == 0xC0E0)
675         {
676                 floppyDrive.ControlStepper(addr & 0x07);
677         }
678         else if ((addr & 0xFFFE) == 0xC0E8)
679         {
680                 floppyDrive.ControlMotor(addr & 0x01);
681         }
682         else if ((addr & 0xFFFE) == 0xC0EA)
683         {
684                 floppyDrive.DriveEnable(addr & 0x01);
685         }
686         else if (addr == 0xC0EC)
687         {
688 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
689                 floppyDrive.ReadWrite();
690         }
691         else if (addr == 0xC0ED)
692         {
693                 floppyDrive.SetLatchValue(b);
694         }
695         else if (addr == 0xC0EE)
696         {
697                 floppyDrive.SetReadMode();
698         }
699         else if (addr == 0xC0EF)
700         {
701                 floppyDrive.SetWriteMode();
702         }
703 //Still need to add missing I/O switches here...
704
705 //DEEE: BD 10 BF       LDA   $BF10,X    [PC=DEF1, SP=01F4, CC=--.B-IZ-, A=00, X=0C, Y=07]
706 #if 0
707 if (addr >= 0xD000 && addr <= 0xD00F)
708 {
709         WriteLog("*** Write to $%04X: $%02X (writeRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (writeRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
710 }
711 #endif
712         if (addr >= 0xC000 && addr <= 0xCFFF)
713                 return; // Protect LC bank #1 from being written to!
714
715         if (addr >= 0xD000)
716         {
717                 if (writeRAM)
718                 {
719                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
720                                 ram[addr - 0x1000] = b;
721                         else
722                                 ram[addr] = b;
723                 }
724
725                 return;
726         }
727
728         ram[addr] = b;
729 }
730
731 //
732 // Load a file into RAM/ROM image space
733 //
734 bool LoadImg(char * filename, uint8 * ram, int size)
735 {
736         FILE * fp = fopen(filename, "rb");
737
738         if (fp == NULL)
739                 return false;
740
741         fread(ram, 1, size, fp);
742         fclose(fp);
743
744         return true;
745 }
746
747 static void SaveApple2State(const char * filename)
748 {
749 }
750
751 static bool LoadApple2State(const char * filename)
752 {
753         return false;
754 }
755
756 //
757 // Main loop
758 //
759 int main(int /*argc*/, char * /*argv*/[])
760 {
761         InitLog("./apple2.log");
762         LoadSettings();
763         srand(time(NULL));                                                                      // Initialize RNG
764
765         // Zero out memory
766 //Need to bankify this stuff for the IIe emulation...
767         memset(ram, 0, 0x10000);
768         memset(rom, 0, 0x10000);
769
770         // Set up V65C02 execution context
771         memset(&mainCPU, 0, sizeof(V65C02REGS));
772         mainCPU.RdMem = RdMem;
773         mainCPU.WrMem = WrMem;
774         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
775
776         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
777         {
778                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
779                 return -1;
780         }
781
782 //This is now included...
783 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
784         {
785                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
786 //              return -1;
787         }//*/
788
789 //Load up disk image from config file (for now)...
790         floppyDrive.LoadImage(settings.diskImagePath1, 0);
791         floppyDrive.LoadImage(settings.diskImagePath2, 1);
792 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
793
794 //Kill the DOS ROM in slot 6 for now...
795 //not
796         memcpy(rom + 0xC600, diskROM, 0x100);
797
798         WriteLog("About to initialize video...\n");
799         if (!InitVideo())
800         {
801                 std::cout << "Aborting!" << std::endl;
802                 return -1;
803         }
804
805         // Have to do this *after* video init but *before* sound init...!
806 //Shouldn't be necessary since we're not doing emulation in the ISR...
807         if (settings.autoStateSaving)
808         {
809                 // Load last state from file...
810                 if (!LoadApple2State(settings.autoStatePath))
811                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
812         }
813
814
815 #if 0
816 // State loading!
817 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
818 {
819         cout << "Couldn't load state file!" << endl;
820         cout << "Aborting!!" << endl;
821         return -1;
822 }
823
824 //A  P  Y  X  S     PC
825 //-- -- -- -- ----- -----
826 //00 75 3B 53 FD 01 41 44
827
828 mainCPU.cpuFlags = 0;
829 mainCPU.a = 0x00;
830 mainCPU.x = 0x53;
831 mainCPU.y = 0x3B;
832 mainCPU.cc = 0x75;
833 mainCPU.sp = 0xFD;
834 mainCPU.pc = 0x4441;
835
836 textMode = false;
837 mixedMode = false;
838 displayPage2 = false;
839 hiRes = true;
840
841 //kludge...
842 readHiRam = true;
843 //dumpDis=true;
844 //kludge II...
845 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
846 #endif
847
848         WriteLog("About to initialize audio...\n");
849         SoundInit();
850         SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
851
852 //      gui = new GUI(surface);                                         // Set up the GUI system object...
853         gui = new GUI(mainSurface);                                     // Set up the GUI system object...
854         gui->AddMenuTitle("Apple2");
855         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
856         gui->AddMenuItem("");
857         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
858         gui->CommitItemsToMenu();
859
860         SetupBlurTable();                                                       // Set up the color TV emulation blur table
861         running = true;                                                         // Set running status...
862
863         InitializeEventList();                                          // Clear the event list before we use it...
864         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
865         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
866         startTicks = SDL_GetTicks();
867
868         WriteLog("Entering main loop...\n");
869         while (running)
870         {
871                 double timeToNextEvent = GetTimeToNextEvent();
872                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
873 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
874 //(Fix so that this is not a requirement!)
875 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
876 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
877                 // Handle CPU time delta for sound...
878                 AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
879                 HandleNextEvent();
880         }
881
882         if (settings.autoStateSaving)
883         {
884                 // Save state here...
885                 SaveApple2State(settings.autoStatePath);
886         }
887 floppyDrive.SaveImage();
888
889         SoundDone();
890         VideoDone();
891         SaveSettings();
892         LogDone();
893
894         return 0;
895 }
896
897 /*
898 Apple II keycodes
899 -----------------
900
901 Key     Aln CTL SHF BTH
902 -----------------------
903 space   $A0     $A0     $A0 $A0         No xlation
904 RETURN  $8D     $8D     $8D     $8D             No xlation
905 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
906 1!              $B1 $B1 $A1 $A1         No xlation
907 2"              $B2     $B2     $A2     $A2             No xlation
908 3#              $B3     $B3     $A3     $A3             No xlation
909 4$              $B4     $B4     $A4     $A4             No xlation
910 5%              $B5     $B5     $A5     $A5             No xlation
911 6&              $B6     $B6     $A6     $A6             No xlation
912 7'              $B7     $B7     $A7     $A7             No xlation
913 8(              $B8     $B8     $A8     $A8             No xlation
914 9)              $B9     $B9     $A9     $A9             No xlation
915 :*              $BA     $BA     $AA     $AA             No xlation
916 ;+              $BB     $BB     $AB     $AB             No xlation
917 ,<              $AC     $AC     $BC     $BC             No xlation
918 -=              $AD     $AD     $BD     $BD             No xlation
919 .>              $AE     $AE     $BE     $BE             No xlation
920 /?              $AF     $AF     $BF     $BF             No xlation
921 A               $C1     $81     $C1     $81
922 B               $C2     $82     $C2     $82
923 C               $C3     $83     $C3     $83
924 D               $C4     $84     $C4     $84
925 E               $C5     $85     $C5     $85
926 F               $C6     $86     $C6     $86
927 G               $C7     $87     $C7     $87
928 H               $C8     $88     $C8     $88
929 I               $C9     $89     $C9     $89
930 J               $CA     $8A     $CA     $8A
931 K               $CB     $8B     $CB     $8B
932 L               $CC     $8C     $CC     $8C
933 M               $CD     $8D     $DD     $9D             -> ODD
934 N^              $CE     $8E     $DE     $9E             -> ODD
935 O               $CF     $8F     $CF     $8F
936 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
937 Q               $D1     $91     $D1     $91
938 R               $D2     $92     $D2     $92
939 S               $D3     $93     $D3     $93
940 T               $D4     $94     $D4     $94
941 U               $D5     $95     $D5     $95
942 V               $D6     $96     $D6     $96
943 W               $D7     $97     $D7     $97
944 X               $D8     $98     $D8     $98
945 Y               $D9     $99     $D9     $99
946 Z               $DA     $9A     $DA     $9A
947 <-              $88     $88     $88     $88
948 ->              $95     $95     $95     $95
949 ESC             $9B     $9B     $9B     $9B             No xlation
950
951 */
952 static void FrameCallback(void)
953 {
954         SDL_Event event;
955
956         while (SDL_PollEvent(&event))
957         {
958                 switch (event.type)
959                 {
960                 case SDL_KEYDOWN:
961                         if (event.key.keysym.unicode != 0)
962                         {
963 //Need to do some key translation here, and screen out non-apple keys as well...
964                                 if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
965                                         break;
966
967                                 lastKeyPressed = event.key.keysym.unicode;
968                                 keyDown = true;
969                                 //kludge: should have a caps lock thingy here...
970                                 //or all uppercase for ][+...
971                                 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
972                                         lastKeyPressed &= 0xDF;         // Convert to upper case...
973                         }
974
975                         // CTRL+RESET key emulation (mapped to CTRL+`)
976 // This doesn't work...
977 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
978 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
979                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
980 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
981 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
982                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
983
984                         if (event.key.keysym.sym == SDLK_RIGHT)
985                                 lastKeyPressed = 0x15, keyDown = true;
986                         else if (event.key.keysym.sym == SDLK_LEFT)
987                                 lastKeyPressed = 0x08, keyDown = true;
988
989                         // Use ALT+Q to exit, as well as the usual window decoration method
990                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
991                                 running = false;
992
993                         if (event.key.keysym.sym == SDLK_F12)
994                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
995                         else if (event.key.keysym.sym == SDLK_F11)
996                                 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
997 else if (event.key.keysym.sym == SDLK_F9)
998 {
999         floppyDrive.CreateBlankImage();
1000 //      SpawnMessage("Image cleared...");
1001 }//*/
1002 else if (event.key.keysym.sym == SDLK_F10)
1003 {
1004         floppyDrive.SwapImages();
1005 //      SpawnMessage("Image swapped...");
1006 }//*/
1007
1008                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1009                                 TogglePalette();
1010                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1011                                 CycleScreenTypes();
1012
1013 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1014                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1015 //NOTE: Should parse the output to determine whether or not the user requested
1016 //      to quit completely... !!! FIX !!!
1017                                 gui->Run();
1018
1019                         break;
1020                 case SDL_QUIT:
1021                         running = false;
1022                 }
1023         }
1024
1025 //ick.  HandleSoundAtFrameEdge();                                       // Sound stuff... (ick)
1026         RenderVideoFrame();
1027         SetCallbackTime(FrameCallback, 16666.66666667);
1028
1029 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
1030 //lessee...
1031 //nope. SDL_Delay(10);
1032         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1033         startTicks = SDL_GetTicks();
1034 }
1035
1036 static void BlinkTimer(void)
1037 {
1038         flash = !flash;
1039         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1040 }