]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Miscellaneous sound engine fixes (still needs fixing!), floppy diagnostics
[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 //This sux...
375         if (addr >= 0xC100 && addr <= 0xCFFF)   // The $C000-$CFFF block is *never* RAM
376                 b = rom[addr];
377         else if (addr >= 0xD000)
378         {
379                 if (readRAM)
380                 {
381                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
382                                 b = ram[addr - 0x1000];
383                         else
384                                 b = ram[addr];
385                 }
386                 else
387                         b = rom[addr];
388         }
389         else
390                 b = ram[addr];
391
392         return b;
393 }
394
395 /*
396 A-9 (Mockingboard)
397 APPENDIX F Assembly Language Program Listings
398
399         1       *PRIMARY ROUTINES
400         2       *FOR SLOT 4
401         3       *
402         4                       ORG     $9000
403         5       *                               ;ADDRESSES FOR FIRST 6522
404         6       ORB             EQU     $C400           ;PORT B
405         7       ORA             EQU     $C401           ;PORT A
406         8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
407         9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
408         10      *                                       ;ADDRESSES FOR SECOND 6522
409         11      ORB2            EQU     $C480           ;PORT B
410         12      ORA2            EQU     $C481           ;PORT A
411         13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
412         14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
413 */
414 void WrMem(uint16 addr, uint8 b)
415 {
416 //temp...
417 //extern V6809REGS regs;
418 //if (addr >= 0xC800 && addr <= 0xCBFE)
419 //if (addr == 0xC80F || addr == 0xC80D)
420 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
421
422 #if 0
423 if (addr >= 0xC000 && addr <= 0xC0FF)
424         WriteLog("\n*** Write at I/O address %04X\n", addr);
425 #endif
426 /*
427 Check the BIKO version on Asimov to see if it's been cracked or not...
428
429 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
430 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
431 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
432 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
433 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
434
435 ; INX here *ensures* 1 - 6!!! BUG!!!
436 ; Or is it? Could this be part of a braindead copy protection scheme? It's
437 ; awfully close to NOP ($EA)...
438 ; Nothing else touches it once it's been written... Hmm...
439
440 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
441 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
442 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
443 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
444
445 ; 4E13: 03 00
446 ; 4E15: 25 25 15 15 10 20
447 ; 4E1B: 03 41 99 99 01 00 12
448 ; 4E22: 99 70
449
450 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
451 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
452 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
453 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
454
455 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
456
457 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
458 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
459
460 ; Print "ALAKAZAM!" and so on...
461
462 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
463 */
464 #if 0
465 if (addr == 0x7F47)
466         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
467 #endif
468 /*
469 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
470 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
471
472 CLRAUXRD = $C002 ;read from main 48K (WR-only)
473 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
474
475 CLRAUXWR = $C004 ;write to main 48K (WR-only)
476 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
477
478 CLRCXROM = $C006 ;use ROM on cards (WR-only)
479 SETCXROM = $C007 ;use internal ROM (WR-only)
480
481 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
482 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
483
484 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
485 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
486
487 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
488 SET80VID = $C00D ;enable 80-column display mode (WR-only)
489
490 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
491 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
492 */
493         if (addr == 0xC00E)
494         {
495                 alternateCharset = false;
496         }
497         else if (addr == 0xC00F)
498         {
499                 alternateCharset = true;
500         }
501         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
502         {
503 //Actually, according to the A2 ref, this should do nothing since a write
504 //is immediately preceded by a read leaving it in the same state it was...
505 //              keyDown = false;
506         }
507         else if (addr == 0xC050)
508         {
509                 textMode = false;
510         }
511         else if (addr == 0xC051)
512         {
513                 textMode = true;
514         }
515         else if (addr == 0xC052)
516         {
517                 mixedMode = false;
518         }
519         else if (addr == 0xC053)
520         {
521                 mixedMode = true;
522         }
523         else if (addr == 0xC054)
524         {
525                 displayPage2 = false;
526         }
527         else if (addr == 0xC055)
528         {
529                 displayPage2 = true;
530         }
531         else if (addr == 0xC056)
532         {
533                 hiRes = false;
534         }
535         else if (addr == 0xC057)
536         {
537                 hiRes = true;
538         }
539         else if ((addr & 0xFFFB) == 0xC080)
540         {
541 #ifdef DEBUG_LC
542 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
543 #endif
544 //$C080 49280              OECG  R   Read RAM bank 2; no write
545                 visibleBank = LC_BANK_2;
546                 readRAM = true;
547                 writeRAM = false;
548         }
549         else if ((addr & 0xFFFB) == 0xC081)
550         {
551 #ifdef DEBUG_LC
552 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
553 #endif
554 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
555                 visibleBank = LC_BANK_2;
556                 readRAM = false;
557                 writeRAM = true;
558         }
559         else if ((addr & 0xFFFB) == 0xC082)
560         {
561 #ifdef DEBUG_LC
562 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
563 #endif
564 //$C082 49282              OECG  R   Read ROM; no write
565                 visibleBank = LC_BANK_2;
566                 readRAM = false;
567                 writeRAM = false;
568         }
569         else if ((addr & 0xFFFB) == 0xC083)
570         {
571 #ifdef DEBUG_LC
572 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
573 #endif
574 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
575                 visibleBank = LC_BANK_2;
576                 readRAM = true;
577                 writeRAM = true;
578         }
579         else if ((addr & 0xFFFB) == 0xC088)
580         {
581 #ifdef DEBUG_LC
582 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
583 #endif
584 //$C088 49288              OECG  R   Read RAM bank 1; no write
585                 visibleBank = LC_BANK_1;
586                 readRAM = true;
587                 writeRAM = false;
588         }
589         else if ((addr & 0xFFFB) == 0xC089)
590         {
591 #ifdef DEBUG_LC
592 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
593 #endif
594 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
595                 visibleBank = LC_BANK_1;
596                 readRAM = false;
597                 writeRAM = true;
598         }
599         else if ((addr & 0xFFFB) == 0xC08A)
600         {
601 #ifdef DEBUG_LC
602 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
603 #endif
604 //$C08A 49290              OECG  R   Read ROM; no write
605                 visibleBank = LC_BANK_1;
606                 readRAM = false;
607                 writeRAM = false;
608         }
609         else if ((addr & 0xFFFB) == 0xC08B)
610         {
611 #ifdef DEBUG_LC
612 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
613 #endif
614 //$C08B 49291              OECG  RR  Read/write RAM bank 1
615                 visibleBank = LC_BANK_1;
616                 readRAM = true;
617                 writeRAM = true;
618         }
619 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
620         else if ((addr & 0xFFF8) == 0xC0E0)
621         {
622                 floppyDrive.ControlStepper(addr & 0x07);
623         }
624         else if ((addr & 0xFFFE) == 0xC0E8)
625         {
626                 floppyDrive.ControlMotor(addr & 0x01);
627         }
628         else if ((addr & 0xFFFE) == 0xC0EA)
629         {
630                 floppyDrive.DriveEnable(addr & 0x01);
631         }
632         else if (addr == 0xC0EC)
633         {
634 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
635                 floppyDrive.ReadWrite();
636         }
637         else if (addr == 0xC0ED)
638         {
639                 floppyDrive.SetLatchValue(b);
640         }
641         else if (addr == 0xC0EE)
642         {
643                 floppyDrive.SetReadMode();
644         }
645         else if (addr == 0xC0EF)
646         {
647                 floppyDrive.SetWriteMode();
648         }
649 //Still need to add missing I/O switches here...
650
651         if (addr >= 0xD000)
652         {
653                 if (writeRAM)
654                 {
655                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
656                                 ram[addr - 0x1000] = b;
657                         else
658                                 ram[addr] = b;
659                 }
660
661                 return;
662         }
663
664         ram[addr] = b;
665 }
666
667 //
668 // Load a file into RAM/ROM image space
669 //
670 bool LoadImg(char * filename, uint8 * ram, int size)
671 {
672         FILE * fp = fopen(filename, "rb");
673
674         if (fp == NULL)
675                 return false;
676
677         fread(ram, 1, size, fp);
678         fclose(fp);
679
680         return true;
681 }
682
683 static void SaveApple2State(const char * filename)
684 {
685 }
686
687 static bool LoadApple2State(const char * filename)
688 {
689         return false;
690 }
691
692 //
693 // Main loop
694 //
695 int main(int /*argc*/, char * /*argv*/[])
696 {
697         InitLog("./apple2.log");
698         LoadSettings();
699         srand(time(NULL));                                                                      // Initialize RNG
700
701         // Zero out memory
702 //Need to bankify this stuff for the IIe emulation...
703         memset(ram, 0, 0x10000);
704         memset(rom, 0, 0x10000);
705
706         // Set up V65C02 execution context
707         memset(&mainCPU, 0, sizeof(V65C02REGS));
708         mainCPU.RdMem = RdMem;
709         mainCPU.WrMem = WrMem;
710         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
711
712         if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
713         {
714                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
715                 return -1;
716         }
717
718 //This is now included...
719 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
720         {
721                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
722 //              return -1;
723         }//*/
724
725 //Load up disk image from config file (for now)...
726         floppyDrive.LoadImage(settings.diskImagePath1, 0);
727 //      floppyDrive.LoadImage(settings.diskImagePath2, 1);
728 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
729
730 //Kill the DOS ROM in slot 6 for now...
731 //not
732         memcpy(rom + 0xC600, diskROM, 0x100);
733
734         WriteLog("About to initialize video...\n");
735         if (!InitVideo())
736         {
737                 std::cout << "Aborting!" << std::endl;
738                 return -1;
739         }
740
741         // Have to do this *after* video init but *before* sound init...!
742 //Shouldn't be necessary since we're not doing emulation in the ISR...
743         if (settings.autoStateSaving)
744         {
745                 // Load last state from file...
746                 if (!LoadApple2State(settings.autoStatePath))
747                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
748         }
749
750
751 #if 0
752 // State loading!
753 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
754 {
755         cout << "Couldn't load state file!" << endl;
756         cout << "Aborting!!" << endl;
757         return -1;
758 }
759
760 //A  P  Y  X  S     PC
761 //-- -- -- -- ----- -----
762 //00 75 3B 53 FD 01 41 44
763
764 mainCPU.cpuFlags = 0;
765 mainCPU.a = 0x00;
766 mainCPU.x = 0x53;
767 mainCPU.y = 0x3B;
768 mainCPU.cc = 0x75;
769 mainCPU.sp = 0xFD;
770 mainCPU.pc = 0x4441;
771
772 textMode = false;
773 mixedMode = false;
774 displayPage2 = false;
775 hiRes = true;
776
777 //kludge...
778 readHiRam = true;
779 //dumpDis=true;
780 //kludge II...
781 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
782 #endif
783
784         WriteLog("About to initialize audio...\n");
785         SoundInit();
786         SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
787
788         gui = new GUI(surface);                                         // Set up the GUI system object...
789         gui->AddMenuTitle("Apple2");
790         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
791         gui->AddMenuItem("");
792         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
793         gui->CommitItemsToMenu();
794
795         SetupBlurTable();                                                       // Set up the color TV emulation blur table
796         running = true;                                                         // Set running status...
797
798         InitializeEventList();                                          // Clear the event list before we use it...
799         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
800         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
801         startTicks = SDL_GetTicks();
802
803         WriteLog("Entering main loop...\n");
804         while (running)
805         {
806                 double timeToNextEvent = GetTimeToNextEvent();
807                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
808 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
809 //(Fix so that this is not a requirement!)
810 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
811 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
812                 // Handle CPU time delta for sound...
813                 AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
814                 HandleNextEvent();
815         }
816
817         if (settings.autoStateSaving)
818         {
819                 // Save state here...
820                 SaveApple2State(settings.autoStatePath);
821         }
822 floppyDrive.SaveImage();
823
824         SoundDone();
825         VideoDone();
826         SaveSettings();
827         LogDone();
828
829         return 0;
830 }
831
832 /*
833 Apple II keycodes
834 -----------------
835
836 Key     Aln CTL SHF BTH
837 -----------------------
838 space   $A0     $A0     $A0 $A0         No xlation
839 RETURN  $8D     $8D     $8D     $8D             No xlation
840 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
841 1!              $B1 $B1 $A1 $A1         No xlation
842 2"              $B2     $B2     $A2     $A2             No xlation
843 3#              $B3     $B3     $A3     $A3             No xlation
844 4$              $B4     $B4     $A4     $A4             No xlation
845 5%              $B5     $B5     $A5     $A5             No xlation
846 6&              $B6     $B6     $A6     $A6             No xlation
847 7'              $B7     $B7     $A7     $A7             No xlation
848 8(              $B8     $B8     $A8     $A8             No xlation
849 9)              $B9     $B9     $A9     $A9             No xlation
850 :*              $BA     $BA     $AA     $AA             No xlation
851 ;+              $BB     $BB     $AB     $AB             No xlation
852 ,<              $AC     $AC     $BC     $BC             No xlation
853 -=              $AD     $AD     $BD     $BD             No xlation
854 .>              $AE     $AE     $BE     $BE             No xlation
855 /?              $AF     $AF     $BF     $BF             No xlation
856 A               $C1     $81     $C1     $81
857 B               $C2     $82     $C2     $82
858 C               $C3     $83     $C3     $83
859 D               $C4     $84     $C4     $84
860 E               $C5     $85     $C5     $85
861 F               $C6     $86     $C6     $86
862 G               $C7     $87     $C7     $87
863 H               $C8     $88     $C8     $88
864 I               $C9     $89     $C9     $89
865 J               $CA     $8A     $CA     $8A
866 K               $CB     $8B     $CB     $8B
867 L               $CC     $8C     $CC     $8C
868 M               $CD     $8D     $DD     $9D             -> ODD
869 N^              $CE     $8E     $DE     $9E             -> ODD
870 O               $CF     $8F     $CF     $8F
871 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
872 Q               $D1     $91     $D1     $91
873 R               $D2     $92     $D2     $92
874 S               $D3     $93     $D3     $93
875 T               $D4     $94     $D4     $94
876 U               $D5     $95     $D5     $95
877 V               $D6     $96     $D6     $96
878 W               $D7     $97     $D7     $97
879 X               $D8     $98     $D8     $98
880 Y               $D9     $99     $D9     $99
881 Z               $DA     $9A     $DA     $9A
882 <-              $88     $88     $88     $88
883 ->              $95     $95     $95     $95
884 ESC             $9B     $9B     $9B     $9B             No xlation
885
886 */
887 static void FrameCallback(void)
888 {
889         SDL_Event event;
890
891         while (SDL_PollEvent(&event))
892         {
893                 switch (event.type)
894                 {
895                 case SDL_KEYDOWN:
896                         if (event.key.keysym.unicode != 0)
897                         {
898 //Need to do some key translation here, and screen out non-apple keys as well...
899                                 if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
900                                         break;
901
902                                 lastKeyPressed = event.key.keysym.unicode;
903                                 keyDown = true;
904                                 //kludge: should have a caps lock thingy here...
905                                 //or all uppercase for ][+...
906                                 if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
907                                         lastKeyPressed &= 0xDF;         // Convert to upper case...
908                         }
909
910                         // CTRL+RESET key emulation (mapped to CTRL+`)
911 // This doesn't work...
912 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
913 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
914                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
915 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
916 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
917                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
918
919                         if (event.key.keysym.sym == SDLK_RIGHT)
920                                 lastKeyPressed = 0x15, keyDown = true;
921                         else if (event.key.keysym.sym == SDLK_LEFT)
922                                 lastKeyPressed = 0x08, keyDown = true;
923
924                         // Use ALT+Q to exit, as well as the usual window decoration method
925                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
926                                 running = false;
927
928                         if (event.key.keysym.sym == SDLK_F12)
929                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
930                         else if (event.key.keysym.sym == SDLK_F11)
931                                 floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
932 else if (event.key.keysym.sym == SDLK_F9)
933 {
934         floppyDrive.CreateBlankImage();
935 //      SpawnMessage("Image cleared...");
936 }//*/
937 else if (event.key.keysym.sym == SDLK_F10)
938 {
939         floppyDrive.SwapImages();
940 //      SpawnMessage("Image swapped...");
941 }//*/
942
943                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
944                                 TogglePalette();
945                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
946                                 CycleScreenTypes();
947
948 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
949                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
950 //NOTE: Should parse the output to determine whether or not the user requested
951 //      to quit completely... !!! FIX !!!
952                                 gui->Run();
953
954                         break;
955                 case SDL_QUIT:
956                         running = false;
957                 }
958         }
959
960 //ick.  HandleSoundAtFrameEdge();                                       // Sound stuff... (ick)
961         RenderVideoFrame();
962         SetCallbackTime(FrameCallback, 16666.66666667);
963
964 //Instead of this, we should yield remaining time to other processes... !!! FIX !!!
965 //lessee...
966 //nope. SDL_Delay(10);
967         while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
968         startTicks = SDL_GetTicks();
969 }
970
971 static void BlinkTimer(void)
972 {
973         flash = !flash;
974         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
975 }