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