]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
a8c9e680b8196db228243a3c3665819f928ff9a5
[apple2] / src / apple2.cpp
1 //
2 // Apple 2 SDL Portable Apple Emulator
3 //
4 // by James 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 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 // JLH  09/24/2013  Added //e support
21 //
22
23 // STILL TO DO:
24 //
25 // - Port to SDL [DONE]
26 // - GUI goodies
27 // - Weed out unneeded functions [DONE]
28 // - Disk I/O [DONE]
29 // - 128K IIe related stuff [DONE]
30 // - State loading/saving
31 //
32
33 #include "apple2.h"
34
35 #include <SDL2/SDL.h>
36 #include <fstream>
37 #include <string>
38 #include <iomanip>
39 #include <iostream>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include "log.h"
44 #include "video.h"
45 #include "sound.h"
46 #include "settings.h"
47 #include "v65c02.h"
48 #include "applevideo.h"
49 #include "timing.h"
50 #include "floppy.h"
51 #include "firmware.h"
52 #include "mmu.h"
53
54 #include "gui/gui.h"
55 #include "gui/window.h"
56 #include "gui/draggablewindow2.h"
57 #include "gui/textedit.h"
58
59 // Debug and misc. defines
60
61 #define THREADED_65C02
62 #define CPU_THREAD_OVERFLOW_COMPENSATION
63 #define DEBUG_LC
64 //#define CPU_CLOCK_CHECKING
65 //#define THREAD_DEBUGGING
66 #define SOFT_SWITCH_DEBUGGING
67
68 // Global variables
69
70 uint8_t ram[0x10000], rom[0x10000];                             // RAM & ROM spaces
71 uint8_t ram2[0x10000];                                                  // Auxillary RAM
72 //uint8_t diskRom[0x100];                                                       // Disk ROM space
73 V65C02REGS mainCPU;                                                             // v65C02 execution context
74 uint8_t appleType = APPLE_TYPE_IIE;
75 FloppyDrive floppyDrive;
76
77 // Local variables
78
79 uint8_t lastKeyPressed = 0;
80 bool keyDown = false;
81 bool openAppleDown = false;
82 bool closedAppleDown = false;
83 bool store80Mode = false;
84 bool vbl = false;
85 bool slotCXROM = false;
86 bool slotC3ROM = false;
87 bool ramrd = false;
88 bool ramwrt = false;
89 bool altzp = false;
90 bool ioudis = true;
91 bool dhires = false;
92
93 //static FloppyDrive floppyDrive;
94
95 enum { LC_BANK_1, LC_BANK_2 };
96
97 static uint8_t visibleBank = LC_BANK_1;
98 static bool readRAM = false;
99 static bool writeRAM = false;
100
101 static bool running = true;                                             // Machine running state flag...
102 static uint32_t startTicks;
103 static bool pauseMode = false;
104
105 static GUI * gui = NULL;
106
107 // Local functions (technically, they're global...)
108
109 bool LoadImg(char * filename, uint8_t * ram, int size);
110 uint8_t RdMem(uint16_t addr);
111 void WrMem(uint16_t addr, uint8_t b);
112 static void SaveApple2State(const char * filename);
113 static bool LoadApple2State(const char * filename);
114
115 // Local timer callback functions
116
117 static void FrameCallback(void);
118 static void BlinkTimer(void);
119
120 #ifdef THREADED_65C02
121 // Test of threaded execution of 6502
122 static SDL_Thread * cpuThread = NULL;
123 //static SDL_mutex * cpuMutex = NULL;
124 static SDL_cond * cpuCond = NULL;
125 static SDL_sem * mainSem = NULL;
126 static bool cpuFinished = false;
127 static bool cpuSleep = false;
128
129 // NB: Apple //e Manual sez 6502 is running @ 1,022,727 Hz
130
131 // Let's try a thread...
132 /*
133 Here's how it works: Execute 1 frame's worth, then sleep.
134 Other stuff wakes it up
135 */
136 int CPUThreadFunc(void * data)
137 {
138         // Mutex must be locked for conditional to work...
139         // Also, must be created in the thread that uses it...
140         SDL_mutex * cpuMutex = SDL_CreateMutex();
141
142 // decrement mainSem...
143 //SDL_SemWait(mainSem);
144 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
145         float overflow = 0.0;
146 #endif
147
148         do
149         {
150                 if (cpuSleep)
151                         SDL_CondWait(cpuCond, cpuMutex);
152
153 // decrement mainSem...
154 #ifdef THREAD_DEBUGGING
155 WriteLog("CPU: SDL_SemWait(mainSem);\n");
156 #endif
157 SDL_SemWait(mainSem);
158
159 // There are exactly 800 slices of 21.333 cycles per frame, so it works out
160 // evenly.
161 #if 0
162                 uint32_t cycles = 17066;
163 #ifdef CPU_THREAD_OVERFLOW_COMPENSATION
164 // ODD! It's closer *without* this overflow compensation. ??? WHY ???
165                 overflow += 0.666666667;
166
167                 if (overflow > 1.0)
168                 {
169                         overflow -= 1.0;
170                         cycles++;
171                 }
172 #endif
173
174 #ifdef THREAD_DEBUGGING
175 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
176 #endif
177                 Execute65C02(&mainCPU, cycles); // how much? 1 frame (after 1 s, off by 40 cycles) not any more--it's off by as much as 240 now!
178
179                 // Adjust the sound routine's last cycle toggled time base
180                 // Also, since we're finished executing, .clock is now valid
181 #ifdef THREAD_DEBUGGING
182 WriteLog("CPU: AdjustLastToggleCycles(mainCPU.clock);\n");
183 #endif
184                 AdjustLastToggleCycles(mainCPU.clock);
185 #else
186 #ifdef THREAD_DEBUGGING
187 WriteLog("CPU: Execute65C02(&mainCPU, cycles);\n");
188 #endif
189                 for(int i=0; i<800; i++)
190                 {
191                         uint32_t cycles = 21;
192                         overflow += 0.333333334;
193
194                         if (overflow > 1.0)
195                         {
196                                 cycles++;
197                                 overflow -= 1.0;
198                         }
199
200                         Execute65C02(&mainCPU, cycles);
201                         WriteSampleToBuffer();
202
203                         // Dunno if this is correct (seems to be close enough)...
204                         vbl = (i < 670 ? true : false);
205                 }
206 #endif
207
208 #ifdef THREAD_DEBUGGING
209 WriteLog("CPU: SDL_mutexP(cpuMutex);\n");
210 #endif
211                 SDL_mutexP(cpuMutex);
212 // increment mainSem...
213 #ifdef THREAD_DEBUGGING
214 WriteLog("CPU: SDL_SemPost(mainSem);\n");
215 #endif
216                 SDL_SemPost(mainSem);
217 #ifdef THREAD_DEBUGGING
218 WriteLog("CPU: SDL_CondWait(cpuCond, cpuMutex);\n");
219 #endif
220                 SDL_CondWait(cpuCond, cpuMutex);
221
222 #ifdef THREAD_DEBUGGING
223 WriteLog("CPU: SDL_mutexV(cpuMutex);\n");
224 #endif
225                 SDL_mutexV(cpuMutex);
226         }
227         while (!cpuFinished);
228
229         SDL_DestroyMutex(cpuMutex);
230
231         return 0;
232 }
233 #endif
234
235
236 // Test GUI function
237
238 Element * TestWindow(void)
239 {
240         Element * win = new DraggableWindow2(10, 10, 128, 128);
241 //      ((DraggableWindow *)win)->AddElement(new TextEdit(4, 16, 92, 0, "u2prog.dsk", win));
242
243         return win;
244 }
245
246
247 Element * QuitEmulator(void)
248 {
249         gui->Stop();
250         running = false;
251
252         return NULL;
253 }
254
255
256 /*
257  Small Apple II memory map:
258
259 $C010 - Clear bit 7 of keyboard data ($C000)
260 $C030 - Toggle speaker diaphragm
261 $C051 - Display text
262 $C054 - Select page 1
263 $C056 - Select lo-res
264 $C058 - Set annuciator-0 output to 0
265 $C05A - Set annuciator-0 output to 0
266 $C05D - Set annuciator-0 output to 1
267 $C05F - Set annuciator-0 output to 1
268 $C0E0 - Disk control stepper ($C0E0-7)
269 $C0E9 - Disk control motor (on)
270 $C0EA - Disk enable (drive 1)
271 $C0EC - Disk R/W
272 $C0EE - Disk set read mode
273 */
274
275 //
276 // V65C02 read/write memory functions
277 //
278
279 uint8_t RdMem(uint16_t addr)
280 {
281         uint8_t b;
282
283 #if 0
284 if (addr >= 0xC000 && addr <= 0xC0FF)
285         WriteLog("\n*** Read at I/O address %04X\n", addr);
286 #endif
287 #if 0
288 if (addr >= 0xC080 && addr <= 0xC08F)
289         WriteLog("\n*** Read at I/O address %04X\n", addr);
290 #endif
291
292         if ((addr & 0xFFF0) == 0xC000)                  // Read $C000-$C00F
293         {
294                 return lastKeyPressed | (keyDown ? 0x80 : 0x00);
295         }
296 //      else if ((addr & 0xFFF8) == 0xC010)             // Read $C010-$C01F
297         else if (addr == 0xC010)
298         {
299 //This is bogus: keyDown is set to false, so return val NEVER is set...
300 //Fixed...
301 //Also, this is IIe/IIc only...!
302                 uint8_t retVal = lastKeyPressed | (keyDown ? 0x80 : 0x00);
303                 keyDown = false;
304                 return retVal;
305         }
306         // These are //e locations
307         else if (addr == 0xC011)
308         {
309 #ifdef SOFT_SWITCH_DEBUGGING
310 WriteLog("RDBANK2 (read)\n");
311 #endif
312                 return (visibleBank == LC_BANK_2 ? 0x80 : 0x00);
313         }
314         else if (addr == 0xC012)
315         {
316 #ifdef SOFT_SWITCH_DEBUGGING
317 WriteLog("RDLCRAM (read)\n");
318 #endif
319                 return (readRAM ? 0x80 : 0x00);
320         }
321         else if (addr == 0xC013)
322         {
323 #ifdef SOFT_SWITCH_DEBUGGING
324 WriteLog("RAMRD (read)\n");
325 #endif
326                 return (ramrd ? 0x80 : 0x00);
327         }
328         else if (addr == 0xC014)
329         {
330 #ifdef SOFT_SWITCH_DEBUGGING
331 WriteLog("RAMWRT (read)\n");
332 #endif
333                 return (ramwrt ? 0x80 : 0x00);
334         }
335         else if (addr == 0xC015)
336         {
337 #ifdef SOFT_SWITCH_DEBUGGING
338 WriteLog("SLOTCXROM (read)\n");
339 #endif
340                 return (slotCXROM ? 0x80 : 0x00);
341         }
342         else if (addr == 0xC016)
343         {
344 #ifdef SOFT_SWITCH_DEBUGGING
345 WriteLog("ALTZP (read)\n");
346 #endif
347                 return (altzp ? 0x80 : 0x00);
348         }
349         else if (addr == 0xC017)
350         {
351 #ifdef SOFT_SWITCH_DEBUGGING
352 WriteLog("SLOTC3ROM (read)\n");
353 #endif
354                 return (slotC3ROM ? 0x80 : 0x00);
355         }
356         else if (addr == 0xC018)
357         {
358 #ifdef SOFT_SWITCH_DEBUGGING
359 WriteLog("80STORE (read)\n");
360 #endif
361                 return (store80Mode ? 0x80 : 0x00);
362         }
363         else if (addr == 0xC019)
364         {
365 #ifdef SOFT_SWITCH_DEBUGGING
366 WriteLog("VBL (read)\n");
367 #endif
368 // NB: The doco suggests that this signal goes LOW when in the VBI.
369 // Which means that we need to control this by counting lines somewhere.
370                 return (vbl ? 0x80 : 0x00);
371         }
372         else if (addr == 0xC01A)
373         {
374 #ifdef SOFT_SWITCH_DEBUGGING
375 WriteLog("TEXT (read)\n");
376 #endif
377                 return (textMode ? 0x80 : 0x00);
378         }
379         else if (addr == 0xC01B)
380         {
381 #ifdef SOFT_SWITCH_DEBUGGING
382 WriteLog("MIXED (read)\n");
383 #endif
384                 return (mixedMode ? 0x80 : 0x00);
385         }
386         else if (addr == 0xC01C)
387         {
388 #ifdef SOFT_SWITCH_DEBUGGING
389 WriteLog("PAGE2 (read)\n");
390 #endif
391                 return (displayPage2 ? 0x80 : 0x00);
392         }
393         else if (addr == 0xC01D)
394         {
395 #ifdef SOFT_SWITCH_DEBUGGING
396 WriteLog("HIRES (read)\n");
397 #endif
398                 return (hiRes ? 0x80 : 0x00);
399         }
400         else if (addr == 0xC01E)
401         {
402 #ifdef SOFT_SWITCH_DEBUGGING
403 WriteLog("ALTCHARSET (read)\n");
404 #endif
405                 return (alternateCharset ? 0x80 : 0x00);
406         }
407         else if (addr == 0xC01F)
408         {
409 #ifdef SOFT_SWITCH_DEBUGGING
410 WriteLog("80COL (read)\n");
411 #endif
412                 return (col80Mode ? 0x80 : 0x00);
413         }
414         else if ((addr & 0xFFF0) == 0xC030)             // Read $C030-$C03F
415         {
416                 ToggleSpeaker();
417 //should it return something else here???
418                 return 0x00;
419         }
420         else if (addr == 0xC050)                                // Read $C050
421         {
422 #ifdef SOFT_SWITCH_DEBUGGING
423 WriteLog("TEXT off (read)\n");
424 #endif
425                 textMode = false;
426         }
427         else if (addr == 0xC051)                                // Read $C051
428         {
429 #ifdef SOFT_SWITCH_DEBUGGING
430 WriteLog("TEXT on (read)\n");
431 #endif
432                 textMode = true;
433         }
434         else if (addr == 0xC052)                                // Read $C052
435         {
436 #ifdef SOFT_SWITCH_DEBUGGING
437 WriteLog("MIXED off (read)\n");
438 #endif
439                 mixedMode = false;
440         }
441         else if (addr == 0xC053)                                // Read $C053
442         {
443 #ifdef SOFT_SWITCH_DEBUGGING
444 WriteLog("MIXED on (read)\n");
445 #endif
446                 mixedMode = true;
447         }
448         else if (addr == 0xC054)                                // Read $C054
449         {
450 #ifdef SOFT_SWITCH_DEBUGGING
451 WriteLog("PAGE2 off (read)\n");
452 #endif
453                 displayPage2 = false;
454         }
455         else if (addr == 0xC055)                                // Read $C055
456         {
457 #ifdef SOFT_SWITCH_DEBUGGING
458 WriteLog("PAGE2 on (read)\n");
459 #endif
460                 displayPage2 = true;
461         }
462         else if (addr == 0xC056)                                // Read $C056
463         {
464 #ifdef SOFT_SWITCH_DEBUGGING
465 WriteLog("HIRES off (read)\n");
466 #endif
467                 hiRes = false;
468         }
469         else if (addr == 0xC057)                                // Read $C057
470         {
471 #ifdef SOFT_SWITCH_DEBUGGING
472 WriteLog("HIRES on (read)\n");
473 #endif
474                 hiRes = true;
475         }
476         else if (addr == 0xC05E)
477         {
478 #ifdef SOFT_SWITCH_DEBUGGING
479 WriteLog("DHIRES on (read)\n");
480 #endif
481                 if (ioudis)
482                         dhires = true;
483         }
484         else if (addr == 0xC05F)
485         {
486 #ifdef SOFT_SWITCH_DEBUGGING
487 WriteLog("DHIRES off (read)\n");
488 #endif
489                 if (ioudis)
490                         dhires = false;
491         }
492         else if (addr == 0xC061)                                // Read $C061
493         {
494                 // Open Apple key (or push button 0)
495                 return (openAppleDown ? 0x80 : 0x00);
496         }
497         else if (addr == 0xC062)                                // Read $C062
498         {
499                 // Open Apple key (or push button 0)
500                 return (closedAppleDown ? 0x80 : 0x00);
501         }
502         // The way the paddles work is that a strobe is written (or read) to $C070,
503         // then software counts down the time that it takes for the paddle outputs
504         // to have bit 7 return to 0. If there are no paddles connected, bit 7
505         // stays at 1.
506         else if (addr == 0xC064)        // Paddles 0-3
507         {
508                 return 0xFF;
509         }
510         else if (addr == 0xC065)
511         {
512                 return 0xFF;
513         }
514         else if (addr == 0xC066)
515         {
516                 return 0xFF;
517         }
518         else if (addr == 0xC067)
519         {
520                 return 0xFF;
521         }
522         else if (addr == 0xC07E)
523         {
524 #ifdef SOFT_SWITCH_DEBUGGING
525 WriteLog("IOUDIS (read)\n");
526 #endif
527                 return (ioudis ? 0x80 : 0x00);
528         }
529         else if (addr == 0xC07F)
530         {
531 #ifdef SOFT_SWITCH_DEBUGGING
532 WriteLog("DHIRES (read)\n");
533 #endif
534                 return (dhires ? 0x80 : 0x00);
535         }
536
537 //Note that this is a kludge: The $D000-$DFFF 4K space is shared (since $C000-$CFFF is
538 //memory mapped) between TWO banks, and that that $E000-$FFFF RAM space is a single bank.
539 //[SHOULD BE FIXED NOW]
540 //OK! This switch selects bank 2 of the 4K bank at $D000-$DFFF. One access makes it
541 //visible, two makes it R/W.
542
543 /*
544 301  LDA $E000
545 304  PHA
546 305  LDA $C081
547 308  PLA
548 309  PHA
549 30A  CMP $E000
550 30D  BNE $332
551 30F  LDA $C083
552 312  LDA $C083
553 315  LDA #$A5
554 317  STA $D000
555 31A  CMP $D000
556 31D  BNE $332
557 31F  LSR A
558 320  STA $D000
559 323  CMP $D000
560 326  BNE $332
561 328  LDA $C081
562 32B  LDA $C081
563 32E  LDA #$01
564 330  BNE $334
565 332  LDA #$00
566 334  STA $300
567 337  PLA
568 338  CMP $E000
569 33B  BEQ $340
570 33D  LDA $C080
571 340  RTS
572
573 A = PEEK($C082)
574 */
575
576         else if ((addr & 0xFFFB) == 0xC080)
577         {
578 #ifdef DEBUG_LC
579 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
580 #endif
581 //$C080 49280              OECG  R   Read RAM bank 2; no write
582                 visibleBank = LC_BANK_2;
583                 readRAM = true;
584                 writeRAM = false;
585         }
586         else if ((addr & 0xFFFB) == 0xC081)
587         {
588 #ifdef DEBUG_LC
589 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
590 #endif
591 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
592                 visibleBank = LC_BANK_2;
593                 readRAM = false;
594                 writeRAM = true;
595         }
596         else if ((addr & 0xFFFB) == 0xC082)
597         {
598 #ifdef DEBUG_LC
599 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
600 #endif
601 //$C082 49282              OECG  R   Read ROM; no write
602                 visibleBank = LC_BANK_2;
603                 readRAM = false;
604                 writeRAM = false;
605         }
606         else if ((addr & 0xFFFB) == 0xC083)
607         {
608 #ifdef DEBUG_LC
609 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
610 #endif
611 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
612                 visibleBank = LC_BANK_2;
613                 readRAM = true;
614                 writeRAM = true;
615         }
616         else if ((addr & 0xFFFB) == 0xC088)
617         {
618 #ifdef DEBUG_LC
619 WriteLog("LC(R): $%04X 49288 OECG R Read RAM bank 1; no write\n", addr);
620 #endif
621 //$C088 49288              OECG  R   Read RAM bank 1; no write
622                 visibleBank = LC_BANK_1;
623                 readRAM = true;
624                 writeRAM = false;
625 //Hm. Some stuff seems to want this.
626 //nope, was looking at $C0E8... return 0xFF;
627         }
628         else if ((addr & 0xFFFB) == 0xC089)
629         {
630 #ifdef DEBUG_LC
631 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
632 #endif
633 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
634                 visibleBank = LC_BANK_1;
635                 readRAM = false;
636                 writeRAM = true;
637         }
638         else if ((addr & 0xFFFB) == 0xC08A)
639         {
640 #ifdef DEBUG_LC
641 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
642 #endif
643 //$C08A 49290              OECG  R   Read ROM; no write
644                 visibleBank = LC_BANK_1;
645                 readRAM = false;
646                 writeRAM = false;
647         }
648         else if ((addr & 0xFFFB) == 0xC08B)
649         {
650 #ifdef DEBUG_LC
651 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
652 #endif
653 //$C08B 49291              OECG  RR  Read/write RAM bank 1
654                 visibleBank = LC_BANK_1;
655                 readRAM = true;
656                 writeRAM = true;
657         }
658         else if ((addr & 0xFFF8) == 0xC0E0)
659         {
660                 floppyDrive.ControlStepper(addr & 0x07);
661         }
662         else if ((addr & 0xFFFE) == 0xC0E8)
663         {
664                 floppyDrive.ControlMotor(addr & 0x01);
665         }
666         else if ((addr & 0xFFFE) == 0xC0EA)
667         {
668                 floppyDrive.DriveEnable(addr & 0x01);
669         }
670         else if (addr == 0xC0EC)
671         {
672                 return floppyDrive.ReadWrite();
673         }
674         else if (addr == 0xC0ED)
675         {
676                 return floppyDrive.GetLatchValue();
677         }
678         else if (addr == 0xC0EE)
679         {
680                 floppyDrive.SetReadMode();
681         }
682         else if (addr == 0xC0EF)
683         {
684                 floppyDrive.SetWriteMode();
685         }
686
687 //#define LC_DEBUGGING
688 #ifdef LC_DEBUGGING
689 bool showpath = false;
690 if (addr >= 0xD000 && addr <= 0xD00F)
691         showpath = true;
692 #endif
693 //This sux...
694         if (addr >= 0xC100 && addr <= 0xC7FF)   // The $C000-$CFFF block is *never* RAM
695         {
696 // Looks like the ][e ref manual got this one wrong: slotCXROM set should mean
697 // use internal ROM, NOT slot ROM. :-/
698 // (fixed now, by setting the switch correctly in the write mem section :-P)
699                 if (!slotCXROM)
700 //              if (slotCXROM)
701                         b = rom[addr];
702                 else
703                 {
704                         if (addr >= 0xC100 && addr <= 0xC1FF)
705                                 b = parallelROM[addr & 0xFF];
706                         else if (addr >= 0xC600 && addr <= 0xC6FF)
707                                 b = diskROM[addr & 0xFF];
708                         else if (addr >= 0xC300 && addr <= 0xC3FF && !slotC3ROM)
709                                 b = rom[addr];
710                         else
711                                 b = 0xFF;
712 //                              b = rom[addr];
713                 }
714 #ifdef LC_DEBUGGING
715 if (showpath)
716         WriteLog("b is from $C100-$CFFF block...\n");
717 #endif
718         }
719         else if (addr >= 0xC800 && addr <= 0xCFFF)      // 2K peripheral or OS ROM
720         {
721                 b = rom[addr];
722         }
723         else if (addr >= 0xD000)
724         {
725                 if (readRAM)
726                 {
727                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
728 #ifdef LC_DEBUGGING
729                         {
730 #endif
731 //                              b = ram[addr - 0x1000];
732                                 b = (altzp ? ram2[addr - 0x1000] : ram[addr - 0x1000]);
733 #ifdef LC_DEBUGGING
734 if (showpath)
735         WriteLog("b is from LC bank #1 (ram[addr - 0x1000])...\n");
736                         }
737 #endif
738                         else
739 #ifdef LC_DEBUGGING
740                         {
741 #endif
742 //                              b = ram[addr];
743                                 b = (altzp ? ram2[addr] : ram[addr]);
744 #ifdef LC_DEBUGGING
745 if (showpath)
746         WriteLog("b is from LC bank #2 (ram[addr])...\n");
747                         }
748 #endif
749                 }
750                 else
751 #ifdef LC_DEBUGGING
752                 {
753 #endif
754                         b = rom[addr];
755 #ifdef LC_DEBUGGING
756 if (showpath)
757         WriteLog("b is from LC ROM (rom[addr])...\n");
758                 }
759 #endif
760         }
761         else
762         {
763 // 80STORE only works for WRITING, not READING!
764 #if 0
765                 // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
766                 if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
767                 {
768                         if (displayPage2)
769                                 b = ram2[addr];
770                         else
771                                 b = ram[addr];
772
773                         return b;
774                 }
775 #endif
776
777                 // Finally, check for auxillary/altzp write switches
778                 if (addr < 0x0200)
779                         b = (altzp ? ram2[addr] : ram[addr]);
780                 else
781                         b = (ramrd ? ram2[addr] : ram[addr]);
782 #ifdef LC_DEBUGGING
783 if (showpath)
784         WriteLog("b is from ram[addr]...\n");
785 #endif
786         }
787
788 #ifdef LC_DEBUGGING
789 if (addr >= 0xD000 && addr <= 0xD00F)
790 {
791         WriteLog("*** Read from $%04X: $%02X (readRAM=%s, PC=%04X, ram$D000=%02X)\n", addr, b, (readRAM ? "T" : "F"), mainCPU.pc, ram[0xC000]);
792 }
793 #endif
794
795         return b;
796 }
797
798 /*
799 A-9 (Mockingboard)
800 APPENDIX F Assembly Language Program Listings
801
802         1       *PRIMARY ROUTINES
803         2       *FOR SLOT 4
804         3       *
805         4                       ORG     $9000
806         5       *                               ;ADDRESSES FOR FIRST 6522
807         6       ORB             EQU     $C400           ;PORT B
808         7       ORA             EQU     $C401           ;PORT A
809         8       DDRB            EQU     $C402           ;DATA DIRECTION REGISTER (A)
810         9       DDRA            EQU     $C403           ;DATA DIRECTION REGISTER (B)
811         10      *                                       ;ADDRESSES FOR SECOND 6522
812         11      ORB2            EQU     $C480           ;PORT B
813         12      ORA2            EQU     $C481           ;PORT A
814         13      DDRB2   EQU     $C482           ;DATA DIRECTION REGISTER (B)
815         14      DDRA2   EQU     $C483           ;DATA DIRECTION REGISTER (A)
816 */
817 void WrMem(uint16_t addr, uint8_t b)
818 {
819 //temp...
820 //extern V6809REGS regs;
821 //if (addr >= 0xC800 && addr <= 0xCBFE)
822 //if (addr == 0xC80F || addr == 0xC80D)
823 //      WriteLog("WrMem: Writing address %04X with %02X [PC=%04X, $CB00=%02X]\n", addr, b, regs.pc, gram[0xCB00]);//*/
824
825 #if 0
826 if (addr >= 0xC000 && addr <= 0xC0FF)
827         WriteLog("\n*** Write at I/O address %04X\n", addr);
828 #endif
829 /*
830 Check the BIKO version on Asimov to see if it's been cracked or not...
831
832 7F3D: 29 07          AND   #$07       [PC=7F3F, SP=01EA, CC=---B-I--, A=01, X=4B, Y=00]
833 7F3F: C9 06          CMP   #$06       [PC=7F41, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
834 7F41: 90 03          BCC   $7F46      [PC=7F46, SP=01EA, CC=N--B-I--, A=01, X=4B, Y=00]
835 [7F43: 4C 83 7E      JMP   $7E83] <- Skipped over... (Prints "THANK YOU VERY MUCH!")
836 7F46: AA             TAX              [PC=7F47, SP=01EA, CC=---B-I--, A=01, X=01, Y=00]
837
838 ; INX here *ensures* 1 - 6!!! BUG!!!
839 ; Or is it? Could this be part of a braindead copy protection scheme? It's
840 ; awfully close to NOP ($EA)...
841 ; Nothing else touches it once it's been written... Hmm...
842
843 7F47: E8             INX              [PC=7F48, SP=01EA, CC=---B-I--, A=01, X=02, Y=00]
844 7F48: F8             SED              [PC=7F49, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
845 7F49: 18             CLC              [PC=7F4A, SP=01EA, CC=---BDI--, A=01, X=02, Y=00]
846 7F4A: BD 15 4E       LDA   $4E15,X    [PC=7F4D, SP=01EA, CC=---BDI--, A=15, X=02, Y=00]
847
848 ; 4E13: 03 00
849 ; 4E15: 25 25 15 15 10 20
850 ; 4E1B: 03 41 99 99 01 00 12
851 ; 4E22: 99 70
852
853 7F4D: 65 FC          ADC   $FC        [PC=7F4F, SP=01EA, CC=---BDI--, A=16, X=02, Y=00]
854 7F4F: 65 FC          ADC   $FC        [PC=7F51, SP=01EA, CC=---BDI--, A=17, X=02, Y=00]
855 7F51: 65 FC          ADC   $FC        [PC=7F53, SP=01EA, CC=---BDI--, A=18, X=02, Y=00]
856 7F53: 65 FC          ADC   $FC        [PC=7F55, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
857
858 ; NO checking is done on the raised stat! Aarrrgggghhhhh!
859
860 7F55: 9D 15 4E       STA   $4E15,X    [PC=7F58, SP=01EA, CC=---BDI--, A=19, X=02, Y=00]
861 7F58: D8             CLD              [PC=7F59, SP=01EA, CC=---B-I--, A=19, X=02, Y=00]
862
863 ; Print "ALAKAZAM!" and so on...
864
865 7F59: 20 2C 40       JSR   $402C      [PC=402C, SP=01E8, CC=---B-I--, A=19, X=02, Y=00]
866 */
867 #if 0
868 if (addr == 0x7F47)
869         WriteLog("\n*** Byte %02X written at address %04X\n", b, addr);
870 #endif
871 /*
872 I think this is IIc/IIe only...
873
874 CLR80STORE=$C000 ;80STORE Off- disable 80-column memory mapping (Write)
875 SET80STORE=$C001 ;80STORE On- enable 80-column memory mapping (WR-only)
876
877 CLRAUXRD = $C002 ;read from main 48K (WR-only)
878 SETAUXRD = $C003 ;read from aux/alt 48K (WR-only)
879
880 CLRAUXWR = $C004 ;write to main 48K (WR-only)
881 SETAUXWR = $C005 ;write to aux/alt 48K (WR-only)
882
883 CLRCXROM = $C006 ;use ROM on cards (WR-only)
884 SETCXROM = $C007 ;use internal ROM (WR-only)
885
886 CLRAUXZP = $C008 ;use main zero page, stack, & LC (WR-only)
887 SETAUXZP = $C009 ;use alt zero page, stack, & LC (WR-only)
888
889 CLRC3ROM = $C00A ;use internal Slot 3 ROM (WR-only)
890 SETC3ROM = $C00B ;use external Slot 3 ROM (WR-only)
891
892 CLR80VID = $C00C ;disable 80-column display mode (WR-only)
893 SET80VID = $C00D ;enable 80-column display mode (WR-only)
894
895 CLRALTCH = $C00E ;use main char set- norm LC, Flash UC (WR-only)
896 SETALTCH = $C00F ;use alt char set- norm inverse, LC; no Flash (WR-only)
897 */
898         if (addr == 0xC000)
899         {
900 #ifdef SOFT_SWITCH_DEBUGGING
901 WriteLog("80STORE off (write)\n");
902 #endif
903                 store80Mode = false;
904         }
905         else if (addr == 0xC001)
906         {
907 #ifdef SOFT_SWITCH_DEBUGGING
908 WriteLog("80STORE on (write)\n");
909 #endif
910                 store80Mode = true;
911         }
912         else if (addr == 0xC002)
913         {
914 #ifdef SOFT_SWITCH_DEBUGGING
915 WriteLog("RAMRD off (write)\n");
916 #endif
917                 ramrd = false;
918         }
919         else if (addr == 0xC003)
920         {
921 #ifdef SOFT_SWITCH_DEBUGGING
922 WriteLog("RAMRD on (write)\n");
923 #endif
924                 ramrd = true;
925         }
926         else if (addr == 0xC004)
927         {
928 #ifdef SOFT_SWITCH_DEBUGGING
929 WriteLog("RAMWRT off (write)\n");
930 #endif
931                 ramwrt = false;
932         }
933         else if (addr == 0xC005)
934         {
935 #ifdef SOFT_SWITCH_DEBUGGING
936 WriteLog("RAMWRT on (write)\n");
937 #endif
938                 ramwrt = true;
939         }
940         else if (addr == 0xC006)
941         {
942                 // This is the only soft switch that breaks the usual convention.
943 #ifdef SOFT_SWITCH_DEBUGGING
944 WriteLog("SLOTCXROM on (write)\n");
945 #endif
946                 slotCXROM = true;
947         }
948         else if (addr == 0xC007)
949         {
950 #ifdef SOFT_SWITCH_DEBUGGING
951 WriteLog("SLOTCXROM off (write)\n");
952 #endif
953                 slotCXROM = false;
954         }
955         else if (addr == 0xC008)
956         {
957 #ifdef SOFT_SWITCH_DEBUGGING
958 WriteLog("ALTZP off (write)\n");
959 #endif
960                 altzp = false;
961         }
962         else if (addr == 0xC009)
963         {
964 #ifdef SOFT_SWITCH_DEBUGGING
965 WriteLog("ALTZP on (write)\n");
966 #endif
967                 altzp = true;
968         }
969         else if (addr == 0xC00A)
970         {
971 #ifdef SOFT_SWITCH_DEBUGGING
972 WriteLog("SLOTC3ROM off (write)\n");
973 #endif
974                 slotC3ROM = false;
975         }
976         else if (addr == 0xC00B)
977         {
978 #ifdef SOFT_SWITCH_DEBUGGING
979 WriteLog("SLOTC3ROM on (write)\n");
980 #endif
981                 slotC3ROM = true;
982         }
983         else if (addr == 0xC00C)
984         {
985 #ifdef SOFT_SWITCH_DEBUGGING
986 WriteLog("80COL off (write)\n");
987 #endif
988                 col80Mode = false;
989         }
990         else if (addr == 0xC00D)
991         {
992 #ifdef SOFT_SWITCH_DEBUGGING
993 WriteLog("80COL on (write)\n");
994 #endif
995                 col80Mode = true;
996         }
997         else if (addr == 0xC00E)
998         {
999 #ifdef SOFT_SWITCH_DEBUGGING
1000 WriteLog("ALTCHARSET off (write)\n");
1001 #endif
1002                 alternateCharset = false;
1003         }
1004         else if (addr == 0xC00F)
1005         {
1006 #ifdef SOFT_SWITCH_DEBUGGING
1007 WriteLog("ALTCHARSET on (write)\n");
1008 #endif
1009                 alternateCharset = true;
1010         }
1011         else if ((addr & 0xFFF0) == 0xC010)             // Keyboard strobe
1012         {
1013 //Actually, according to the A2 ref, this should do nothing since a write
1014 //is immediately preceded by a read leaving it in the same state it was...
1015 //But leaving this out seems to fuck up the key handling of some games...
1016                 keyDown = false;
1017         }
1018         else if ((addr & 0xFFF0) == 0xC030)             // Read $C030-$C03F
1019         {
1020 //Likewise, the speaker is supposed to do nothing if you write to it, and
1021 //for the same reason. But without this, you get no sound in David's
1022 //Midnight Magic...
1023                 ToggleSpeaker();
1024         }
1025         else if (addr == 0xC050)
1026         {
1027 #ifdef SOFT_SWITCH_DEBUGGING
1028 WriteLog("TEXT off (write)\n");
1029 #endif
1030                 textMode = false;
1031         }
1032         else if (addr == 0xC051)
1033         {
1034 #ifdef SOFT_SWITCH_DEBUGGING
1035 WriteLog("TEXT on (write)\n");
1036 #endif
1037                 textMode = true;
1038         }
1039         else if (addr == 0xC052)
1040         {
1041 #ifdef SOFT_SWITCH_DEBUGGING
1042 WriteLog("MIXED off (write)\n");
1043 #endif
1044                 mixedMode = false;
1045         }
1046         else if (addr == 0xC053)
1047         {
1048 #ifdef SOFT_SWITCH_DEBUGGING
1049 WriteLog("MIXED on (write)\n");
1050 #endif
1051                 mixedMode = true;
1052         }
1053         else if (addr == 0xC054)
1054         {
1055 #ifdef SOFT_SWITCH_DEBUGGING
1056 WriteLog("PAGE2 off (write)\n");
1057 #endif
1058                 displayPage2 = false;
1059         }
1060         else if (addr == 0xC055)
1061         {
1062 #ifdef SOFT_SWITCH_DEBUGGING
1063 WriteLog("PAGE2 on (write)\n");
1064 #endif
1065                 displayPage2 = true;
1066         }
1067         else if (addr == 0xC056)
1068         {
1069 #ifdef SOFT_SWITCH_DEBUGGING
1070 WriteLog("HIRES off (write)\n");
1071 #endif
1072                 hiRes = false;
1073         }
1074         else if (addr == 0xC057)
1075         {
1076 #ifdef SOFT_SWITCH_DEBUGGING
1077 WriteLog("HIRES on (write)\n");
1078 #endif
1079                 hiRes = true;
1080         }
1081         else if (addr == 0xC05E)
1082         {
1083 #ifdef SOFT_SWITCH_DEBUGGING
1084 WriteLog("DHIRES on (write)\n");
1085 #endif
1086                 if (ioudis)
1087                         dhires = true;
1088
1089 //static int goDumpDis = 0;
1090 //goDumpDis++;
1091 //if (goDumpDis == 2)
1092 //      dumpDis = true;
1093         }
1094         else if (addr == 0xC05F)
1095         {
1096 #ifdef SOFT_SWITCH_DEBUGGING
1097 WriteLog("DHIRES off (write)\n");
1098 #endif
1099                 if (ioudis)
1100                         dhires = false;
1101         }
1102         else if (addr == 0xC07E)
1103         {
1104 #ifdef SOFT_SWITCH_DEBUGGING
1105 WriteLog("IOUDIS on (write)\n");
1106 #endif
1107                 ioudis = true;
1108         }
1109         else if (addr == 0xC07F)
1110         {
1111 #ifdef SOFT_SWITCH_DEBUGGING
1112 WriteLog("IOUDIS off (write)\n");
1113 #endif
1114                 ioudis = false;
1115         }
1116         else if ((addr & 0xFFFB) == 0xC080)
1117         {
1118 #ifdef DEBUG_LC
1119 WriteLog("LC(R): $C080 49280 OECG R Read RAM bank 2; no write\n");
1120 #endif
1121 //$C080 49280              OECG  R   Read RAM bank 2; no write
1122                 visibleBank = LC_BANK_2;
1123                 readRAM = true;
1124                 writeRAM = false;
1125         }
1126         else if ((addr & 0xFFFB) == 0xC081)
1127         {
1128 #ifdef DEBUG_LC
1129 WriteLog("LC(R): $C081 49281 OECG RR Read ROM; write RAM bank 2\n");
1130 #endif
1131 //$C081 49281 ROMIN        OECG  RR  Read ROM; write RAM bank 2
1132                 visibleBank = LC_BANK_2;
1133                 readRAM = false;
1134                 writeRAM = true;
1135         }
1136         else if ((addr & 0xFFFB) == 0xC082)
1137         {
1138 #ifdef DEBUG_LC
1139 WriteLog("LC(R): $C082 49282 OECG R Read ROM; no write\n");
1140 #endif
1141 //$C082 49282              OECG  R   Read ROM; no write
1142                 visibleBank = LC_BANK_2;
1143                 readRAM = false;
1144                 writeRAM = false;
1145         }
1146         else if ((addr & 0xFFFB) == 0xC083)
1147         {
1148 #ifdef DEBUG_LC
1149 WriteLog("LC(R): $C083 49283 OECG RR Read/Write RAM bank 2\n");
1150 #endif
1151 //$C083 49283 LCBANK2      OECG  RR  Read/write RAM bank 2
1152                 visibleBank = LC_BANK_2;
1153                 readRAM = true;
1154                 writeRAM = true;
1155         }
1156         else if ((addr & 0xFFFB) == 0xC088)
1157         {
1158 #ifdef DEBUG_LC
1159 WriteLog("LC(R): $C088 49288 OECG R Read RAM bank 1; no write\n");
1160 #endif
1161 //$C088 49288              OECG  R   Read RAM bank 1; no write
1162                 visibleBank = LC_BANK_1;
1163                 readRAM = true;
1164                 writeRAM = false;
1165         }
1166         else if ((addr & 0xFFFB) == 0xC089)
1167         {
1168 #ifdef DEBUG_LC
1169 WriteLog("LC(R): $C089 49289 OECG RR Read ROM; write RAM bank 1\n");
1170 #endif
1171 //$C089 49289              OECG  RR  Read ROM; write RAM bank 1
1172                 visibleBank = LC_BANK_1;
1173                 readRAM = false;
1174                 writeRAM = true;
1175         }
1176         else if ((addr & 0xFFFB) == 0xC08A)
1177         {
1178 #ifdef DEBUG_LC
1179 WriteLog("LC(R): $C08A 49290 OECG R Read ROM; no write\n");
1180 #endif
1181 //$C08A 49290              OECG  R   Read ROM; no write
1182                 visibleBank = LC_BANK_1;
1183                 readRAM = false;
1184                 writeRAM = false;
1185         }
1186         else if ((addr & 0xFFFB) == 0xC08B)
1187         {
1188 #ifdef DEBUG_LC
1189 WriteLog("LC(R): $C08B 49291 OECG RR Read/Write RAM bank 1\n");
1190 #endif
1191 //$C08B 49291              OECG  RR  Read/write RAM bank 1
1192                 visibleBank = LC_BANK_1;
1193                 readRAM = true;
1194                 writeRAM = true;
1195         }
1196 //This is determined by which slot it is in--this assumes slot 6. !!! FIX !!!
1197         else if ((addr & 0xFFF8) == 0xC0E0)
1198         {
1199                 floppyDrive.ControlStepper(addr & 0x07);
1200         }
1201         else if ((addr & 0xFFFE) == 0xC0E8)
1202         {
1203                 floppyDrive.ControlMotor(addr & 0x01);
1204         }
1205         else if ((addr & 0xFFFE) == 0xC0EA)
1206         {
1207                 floppyDrive.DriveEnable(addr & 0x01);
1208         }
1209         else if (addr == 0xC0EC)
1210         {
1211 //change this to Write()? (and the other to Read()?) Dunno. Seems to work OK, but still...
1212 //or DoIO
1213                 floppyDrive.ReadWrite();
1214         }
1215         else if (addr == 0xC0ED)
1216         {
1217                 floppyDrive.SetLatchValue(b);
1218         }
1219         else if (addr == 0xC0EE)
1220         {
1221                 floppyDrive.SetReadMode();
1222         }
1223         else if (addr == 0xC0EF)
1224         {
1225                 floppyDrive.SetWriteMode();
1226         }
1227
1228         if (addr >= 0xC000 && addr <= 0xCFFF)
1229                 return; // Protect LC bank #1 from being written to!
1230
1231         if (addr >= 0xD000)
1232         {
1233                 if (writeRAM)
1234                 {
1235 #if 0
1236                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
1237                                 ram[addr - 0x1000] = b;
1238                         else
1239                                 ram[addr] = b;
1240 #else
1241                         if (addr <= 0xDFFF && visibleBank == LC_BANK_1)
1242                         {
1243                                 if (altzp)
1244                                         ram2[addr - 0x1000] = b;
1245                                 else
1246                                         ram[addr - 0x1000] = b;
1247                         }
1248                         else
1249                         {
1250                                 if (altzp)
1251                                         ram2[addr] = b;
1252                                 else
1253                                         ram[addr] = b;
1254                         }
1255 #endif
1256                 }
1257
1258                 return;
1259         }
1260
1261         // Check for 80STORE mode (STORE80 takes precedence over RAMRD/WRT)...
1262         if ((((addr >= 0x0400) && (addr <= 0x07FF)) || ((addr >= 0x2000) && (addr <= 0x3FFF))) && store80Mode)
1263         {
1264                 if (displayPage2)
1265                         ram2[addr] = b;
1266                 else
1267                         ram[addr] = b;
1268
1269                 return;
1270         }
1271
1272         // Finally, check for auxillary/altzp write switches
1273 #if 0
1274         if (ramwrt)
1275                 ram2[addr] = b;
1276         else
1277         {
1278                 if (altzp)
1279                         ram2[addr] = b;
1280                 else
1281                         ram[addr] = b;
1282         }
1283 #else
1284         if (addr < 0x0200)
1285 //      if (addr < 0x0200 || addr >= 0xD000)
1286         {
1287 #if 0
1288 if (addr == 0x38)
1289         WriteLog("Write $38: $%02X\n", b);
1290 else if (addr == 0x39)
1291         WriteLog("Write $39: $%02X\n", b);
1292 #endif
1293                 if (altzp)
1294                         ram2[addr] = b;
1295                 else
1296                         ram[addr] = b;
1297         }
1298         else
1299         {
1300                 if (ramwrt)
1301                         ram2[addr] = b;
1302                 else
1303                         ram[addr] = b;
1304         }
1305 #endif
1306 }
1307
1308
1309 //
1310 // Load a file into RAM/ROM image space
1311 //
1312 bool LoadImg(char * filename, uint8_t * ram, int size)
1313 {
1314         FILE * fp = fopen(filename, "rb");
1315
1316         if (fp == NULL)
1317                 return false;
1318
1319         fread(ram, 1, size, fp);
1320         fclose(fp);
1321
1322         return true;
1323 }
1324
1325
1326 static void SaveApple2State(const char * filename)
1327 {
1328 }
1329
1330
1331 static bool LoadApple2State(const char * filename)
1332 {
1333         return false;
1334 }
1335
1336
1337 #ifdef CPU_CLOCK_CHECKING
1338 uint8_t counter = 0;
1339 uint32_t totalCPU = 0;
1340 uint64_t lastClock = 0;
1341 #endif
1342 //
1343 // Main loop
1344 //
1345 int main(int /*argc*/, char * /*argv*/[])
1346 {
1347         InitLog("./apple2.log");
1348         LoadSettings();
1349         srand(time(NULL));                                                                      // Initialize RNG
1350
1351         // Zero out memory
1352         memset(ram, 0, 0x10000);
1353         memset(rom, 0, 0x10000);
1354         memset(ram2, 0, 0x10000);
1355
1356 #if 1
1357         // Set up MMU
1358         SetupAddressMap();
1359
1360         // Set up V65C02 execution context
1361         memset(&mainCPU, 0, sizeof(V65C02REGS));
1362         mainCPU.RdMem = AppleReadMem;
1363         mainCPU.WrMem = AppleWriteMem;
1364 #else
1365         mainCPU.RdMem = RdMem;
1366         mainCPU.WrMem = WrMem;
1367 #endif
1368         mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1369
1370 //      alternateCharset = true;
1371 //      if (!LoadImg(settings.BIOSPath, rom + 0xD000, 0x3000))
1372         if (!LoadImg(settings.BIOSPath, rom + 0xC000, 0x4000))
1373         {
1374                 WriteLog("Could not open file '%s'!\n", settings.BIOSPath);
1375                 return -1;
1376         }
1377
1378 //This is now included...
1379 /*      if (!LoadImg(settings.diskPath, diskRom, 0x100))
1380         {
1381                 WriteLog("Could not open file '%s'!\nDisk II will be unavailable!\n", settings.diskPath);
1382 //              return -1;
1383         }//*/
1384
1385 //Load up disk image from config file (for now)...
1386         floppyDrive.LoadImage(settings.diskImagePath1, 0);
1387         floppyDrive.LoadImage(settings.diskImagePath2, 1);
1388 //      floppyDrive.LoadImage("./disks/temp.nib", 1);   // Load temp .nib file into second drive...
1389
1390 //Kill the DOS ROM in slot 6 for now...
1391 //not
1392 //      memcpy(rom + 0xC600, diskROM, 0x100);
1393 //      memcpy(rom + 0xC700, diskROM, 0x100);   // Slot 7???
1394
1395         WriteLog("About to initialize video...\n");
1396
1397         if (!InitVideo())
1398         {
1399                 std::cout << "Aborting!" << std::endl;
1400                 return -1;
1401         }
1402
1403         // Have to do this *after* video init but *before* sound init...!
1404 //Shouldn't be necessary since we're not doing emulation in the ISR...
1405         if (settings.autoStateSaving)
1406         {
1407                 // Load last state from file...
1408                 if (!LoadApple2State(settings.autoStatePath))
1409                         WriteLog("Unable to use Apple2 state file \"%s\"!\n", settings.autoStatePath);
1410         }
1411
1412
1413 #if 0
1414 // State loading!
1415 if (!LoadImg("./BT1_6502_RAM_SPACE.bin", ram, 0x10000))
1416 {
1417         cout << "Couldn't load state file!" << endl;
1418         cout << "Aborting!!" << endl;
1419         return -1;
1420 }
1421
1422 //A  P  Y  X  S     PC
1423 //-- -- -- -- ----- -----
1424 //00 75 3B 53 FD 01 41 44
1425
1426 mainCPU.cpuFlags = 0;
1427 mainCPU.a = 0x00;
1428 mainCPU.x = 0x53;
1429 mainCPU.y = 0x3B;
1430 mainCPU.cc = 0x75;
1431 mainCPU.sp = 0xFD;
1432 mainCPU.pc = 0x4441;
1433
1434 textMode = false;
1435 mixedMode = false;
1436 displayPage2 = false;
1437 hiRes = true;
1438
1439 //kludge...
1440 readHiRam = true;
1441 //dumpDis=true;
1442 //kludge II...
1443 memcpy(ram + 0xD000, ram + 0xC000, 0x1000);
1444 #endif
1445
1446         WriteLog("About to initialize audio...\n");
1447         SoundInit();
1448 //nope  SDL_EnableUNICODE(1);                                           // Needed to do key translation shit
1449
1450 //      gui = new GUI(surface);                                         // Set up the GUI system object...
1451 //      gui = new GUI(mainSurface);                                     // Set up the GUI system object...
1452 // SDL 2... this will likely cause Apple 2 to crash
1453 //      gui = new GUI(NULL);                                    // Set up the GUI system object...
1454 #if 0
1455         gui->AddMenuTitle("Apple2");
1456         gui->AddMenuItem("Test!", TestWindow/*, hotkey*/);
1457         gui->AddMenuItem("");
1458         gui->AddMenuItem("Quit", QuitEmulator, SDLK_q);
1459         gui->CommitItemsToMenu();
1460 #endif
1461
1462         SetupBlurTable();                                                       // Set up the color TV emulation blur table
1463         running = true;                                                         // Set running status...
1464
1465         InitializeEventList();                                          // Clear the event list before we use it...
1466         SetCallbackTime(FrameCallback, 16666.66666667); // Set frame to fire at 1/60 s interval
1467         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 s intervals
1468         startTicks = SDL_GetTicks();
1469
1470 #ifdef THREADED_65C02
1471         cpuCond = SDL_CreateCond();
1472         mainSem = SDL_CreateSemaphore(1);
1473         cpuThread = SDL_CreateThread(CPUThreadFunc, NULL, NULL);
1474 //Hmm... CPU does POST (+1), wait, then WAIT (-1)
1475 //      SDL_sem * mainMutex = SDL_CreateMutex();
1476 #endif
1477
1478         WriteLog("Entering main loop...\n");
1479         while (running)
1480         {
1481                 double timeToNextEvent = GetTimeToNextEvent();
1482 #ifndef THREADED_65C02
1483                 Execute65C02(&mainCPU, USEC_TO_M6502_CYCLES(timeToNextEvent));
1484 #endif
1485 //We MUST remove a frame's worth of time in order for the CPU to function... !!! FIX !!!
1486 //(Fix so that this is not a requirement!)
1487 //Fixed, but mainCPU.clock is destroyed in the bargain. Oh well.
1488 //              mainCPU.clock -= USEC_TO_M6502_CYCLES(timeToNextEvent);
1489
1490 #ifdef CPU_CLOCK_CHECKING
1491 #ifndef THREADED_65C02
1492 totalCPU += USEC_TO_M6502_CYCLES(timeToNextEvent);
1493 #endif
1494 #endif
1495                 // Handle CPU time delta for sound...
1496 //Don't need this anymore now that we use absolute time...
1497 //              AddToSoundTimeBase(USEC_TO_M6502_CYCLES(timeToNextEvent));
1498                 HandleNextEvent();
1499         }
1500
1501 #ifdef THREADED_65C02
1502 WriteLog("Main: cpuFinished = true;\n");
1503 cpuFinished = true;
1504 //#warning "If sound thread is behind, CPU thread will never wake up... !!! FIX !!!" [DONE]
1505 //What to do? How do you know when the CPU is sleeping???
1506 //USE A CONDITIONAL!!! OF COURSE!!!!!!11!11!11!!!1!
1507 #if 0
1508 SDL_mutexP(mainMutex);
1509 SDL_CondWait(mainCond, mainMutex);      // Wait for CPU thread to get to signal point...
1510 SDL_mutexV(mainMutex);
1511 #else
1512 //Nope, use a semaphore...
1513 WriteLog("Main: SDL_SemWait(mainSem);\n");
1514 SDL_SemWait(mainSem);//should lock until CPU thread is waiting...
1515 #endif
1516
1517 WriteLog("Main: SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up\n");
1518 SDL_CondSignal(cpuCond);//thread is probably asleep, wake it up
1519 WriteLog("Main: SDL_WaitThread(cpuThread, NULL);\n");
1520 SDL_WaitThread(cpuThread, NULL);
1521 //nowok:SDL_WaitThread(CPUThreadFunc, NULL);
1522 WriteLog("Main: SDL_DestroyCond(cpuCond);\n");
1523 SDL_DestroyCond(cpuCond);
1524
1525 //SDL_DestroyMutex(mainMutex);
1526 SDL_DestroySemaphore(mainSem);
1527 #endif
1528
1529         if (settings.autoStateSaving)
1530         {
1531                 // Save state here...
1532                 SaveApple2State(settings.autoStatePath);
1533         }
1534 floppyDrive.SaveImage(0);
1535 floppyDrive.SaveImage(1);
1536
1537         SoundDone();
1538         VideoDone();
1539         SaveSettings();
1540         LogDone();
1541
1542         return 0;
1543 }
1544
1545
1546 /*
1547 Apple II keycodes
1548 -----------------
1549
1550 Key     Aln CTL SHF BTH
1551 -----------------------
1552 space   $A0     $A0     $A0 $A0         No xlation
1553 RETURN  $8D     $8D     $8D     $8D             No xlation
1554 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
1555 1!              $B1 $B1 $A1 $A1         No xlation
1556 2"              $B2     $B2     $A2     $A2             No xlation
1557 3#              $B3     $B3     $A3     $A3             No xlation
1558 4$              $B4     $B4     $A4     $A4             No xlation
1559 5%              $B5     $B5     $A5     $A5             No xlation
1560 6&              $B6     $B6     $A6     $A6             No xlation
1561 7'              $B7     $B7     $A7     $A7             No xlation
1562 8(              $B8     $B8     $A8     $A8             No xlation
1563 9)              $B9     $B9     $A9     $A9             No xlation
1564 :*              $BA     $BA     $AA     $AA             No xlation
1565 ;+              $BB     $BB     $AB     $AB             No xlation
1566 ,<              $AC     $AC     $BC     $BC             No xlation
1567 -=              $AD     $AD     $BD     $BD             No xlation
1568 .>              $AE     $AE     $BE     $BE             No xlation
1569 /?              $AF     $AF     $BF     $BF             No xlation
1570 A               $C1     $81     $C1     $81
1571 B               $C2     $82     $C2     $82
1572 C               $C3     $83     $C3     $83
1573 D               $C4     $84     $C4     $84
1574 E               $C5     $85     $C5     $85
1575 F               $C6     $86     $C6     $86
1576 G               $C7     $87     $C7     $87
1577 H               $C8     $88     $C8     $88
1578 I               $C9     $89     $C9     $89
1579 J               $CA     $8A     $CA     $8A
1580 K               $CB     $8B     $CB     $8B
1581 L               $CC     $8C     $CC     $8C
1582 M               $CD     $8D     $DD     $9D             -> ODD
1583 N^              $CE     $8E     $DE     $9E             -> ODD
1584 O               $CF     $8F     $CF     $8F
1585 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
1586 Q               $D1     $91     $D1     $91
1587 R               $D2     $92     $D2     $92
1588 S               $D3     $93     $D3     $93
1589 T               $D4     $94     $D4     $94
1590 U               $D5     $95     $D5     $95
1591 V               $D6     $96     $D6     $96
1592 W               $D7     $97     $D7     $97
1593 X               $D8     $98     $D8     $98
1594 Y               $D9     $99     $D9     $99
1595 Z               $DA     $9A     $DA     $9A
1596 <-              $88     $88     $88     $88
1597 ->              $95     $95     $95     $95
1598 ESC             $9B     $9B     $9B     $9B             No xlation
1599
1600 */
1601 //static uint64_t lastCPUCycles = 0;
1602 static uint32_t frameCount = 0;
1603 static void FrameCallback(void)
1604 {
1605         SDL_Event event;
1606
1607         while (SDL_PollEvent(&event))
1608         {
1609                 switch (event.type)
1610                 {
1611 // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
1612                 case SDL_TEXTINPUT:
1613 //Need to do some key translation here, and screen out non-apple keys as well...
1614 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
1615 // everything else done separately. this is slightly easier. :-P)
1616 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
1617                         if (event.edit.text[0] == '\t') // Prelim key screening...
1618                                 break;
1619
1620                         lastKeyPressed = event.edit.text[0];
1621                         keyDown = true;
1622
1623                         //kludge: should have a caps lock thingy here...
1624                         //or all uppercase for ][+...
1625 //                      if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
1626 //                              lastKeyPressed &= 0xDF;         // Convert to upper case...
1627
1628                         break;
1629                 case SDL_KEYDOWN:
1630                         // CTRL+RESET key emulation (mapped to CTRL+`)
1631 // This doesn't work...
1632 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
1633 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
1634                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
1635 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
1636 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
1637                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1638
1639                         if (event.key.keysym.sym == SDLK_RIGHT)
1640                                 lastKeyPressed = 0x15, keyDown = true;
1641                         else if (event.key.keysym.sym == SDLK_LEFT)
1642                                 lastKeyPressed = 0x08, keyDown = true;
1643                         else if (event.key.keysym.sym == SDLK_UP)
1644                                 lastKeyPressed = 0x0B, keyDown = true;
1645                         else if (event.key.keysym.sym == SDLK_DOWN)
1646                                 lastKeyPressed = 0x0A, keyDown = true;
1647                         else if (event.key.keysym.sym == SDLK_RETURN)
1648                                 lastKeyPressed = 0x0D, keyDown = true;
1649                         else if (event.key.keysym.sym == SDLK_ESCAPE)
1650                                 lastKeyPressed = 0x1B, keyDown = true;
1651                         else if (event.key.keysym.sym == SDLK_BACKSPACE)
1652                                 lastKeyPressed = 0x7F, keyDown = true;
1653
1654                         // Fix CTRL+key combo...
1655                         if (event.key.keysym.mod & KMOD_CTRL)
1656                         {
1657                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1658                                 {
1659                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
1660                                         keyDown = true;
1661 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
1662                                 }
1663                         }
1664
1665                         // Use ALT+Q to exit, as well as the usual window decoration method
1666                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1667                                 running = false;
1668
1669                         if (event.key.keysym.sym == SDLK_PAUSE)
1670                         {
1671                                 pauseMode = !pauseMode;
1672
1673                                 if (pauseMode)
1674                                 {
1675                                         SoundPause();
1676                                         SpawnMessage("*** PAUSED ***");
1677                                 }
1678                                 else
1679                                 {
1680                                         SoundResume();
1681                                         SpawnMessage("*** RESUME ***");
1682                                 }
1683                         }
1684
1685                         // Paddle buttons 0 & 1
1686                         if (event.key.keysym.sym == SDLK_INSERT)
1687                                 openAppleDown = true;
1688                         if (event.key.keysym.sym == SDLK_PAGEUP)
1689                                 closedAppleDown = true;
1690
1691                         if (event.key.keysym.sym == SDLK_F11)
1692                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
1693 //                      else if (event.key.keysym.sym == SDLK_F11)
1694 //                              floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1695 else if (event.key.keysym.sym == SDLK_F9)
1696 {
1697         floppyDrive.CreateBlankImage();
1698 //      SpawnMessage("Image cleared...");
1699 }//*/
1700 else if (event.key.keysym.sym == SDLK_F10)
1701 {
1702         floppyDrive.SwapImages();
1703 //      SpawnMessage("Image swapped...");
1704 }//*/
1705
1706                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1707                                 TogglePalette();
1708                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1709                                 CycleScreenTypes();
1710
1711 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1712                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1713 //NOTE: Should parse the output to determine whether or not the user requested
1714 //      to quit completely... !!! FIX !!!
1715                                 gui->Run();
1716
1717                         if (event.key.keysym.sym == SDLK_F5)
1718                         {
1719                                 VolumeDown();
1720                                 char volStr[19] = "[****************]";
1721 //                              volStr[GetVolume()] = 0;
1722                                 for(int i=GetVolume(); i<16; i++)
1723                                         volStr[1 + i] = '-';
1724                                 SpawnMessage("Volume: %s", volStr);
1725                         }
1726                         else if (event.key.keysym.sym == SDLK_F6)
1727                         {
1728                                 VolumeUp();
1729                                 char volStr[19] = "[****************]";
1730 //                              volStr[GetVolume()] = 0;
1731                                 for(int i=GetVolume(); i<16; i++)
1732                                         volStr[1 + i] = '-';
1733                                 SpawnMessage("Volume: %s", volStr);
1734                         }
1735
1736                         static bool fullscreenDebounce = false;
1737
1738                         if (event.key.keysym.sym == SDLK_F12)
1739                         {
1740                                 if (!fullscreenDebounce)
1741                                 {
1742                                         ToggleFullScreen();
1743                                         fullscreenDebounce = true;
1744                                 }
1745                         }
1746 //                      else
1747
1748                         break;
1749                 case SDL_KEYUP:
1750                         if (event.key.keysym.sym == SDLK_F12)
1751                                 fullscreenDebounce = false;
1752
1753                         // Paddle buttons 0 & 1
1754                         if (event.key.keysym.sym == SDLK_INSERT)
1755                                 openAppleDown = false;
1756                         if (event.key.keysym.sym == SDLK_PAGEUP)
1757                                 closedAppleDown = false;
1758
1759 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1760 //                              keyDown = false;
1761
1762                         break;
1763                 case SDL_QUIT:
1764                         running = false;
1765                 }
1766         }
1767
1768 //#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
1769         RenderVideoFrame();
1770         SetCallbackTime(FrameCallback, 16666.66666667);
1771
1772 #ifdef CPU_CLOCK_CHECKING
1773 //We know it's stopped, so we can get away with this...
1774 counter++;
1775 if (counter == 60)
1776 {
1777         uint64_t clock = GetCurrentV65C02Clock();
1778 //totalCPU += (uint32_t)(clock - lastClock);
1779
1780         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
1781         lastClock = clock;
1782 //      totalCPU = 0;
1783         counter = 0;
1784 }
1785 #endif
1786 //Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE]
1787 //lessee...
1788 //nope.
1789 //Actually, slows things down too much...
1790 //SDL_Delay(10);
1791 //      while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1792
1793 // This is the problem: If you set the interval to 16, it runs faster than
1794 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
1795 // have it do 16 for one frame, then 17 for two others. Then it should average
1796 // out to 1/60s per frame every 3 frames.
1797         frameCount = (frameCount + 1) % 3;
1798
1799         uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
1800
1801         while (SDL_GetTicks() - startTicks < waitFrameTime)
1802                 SDL_Delay(1);                                                   // Wait for next frame...
1803
1804         startTicks = SDL_GetTicks();
1805 #if 0
1806         uint64_t cpuCycles = GetCurrentV65C02Clock();
1807         uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
1808         WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
1809         lastCPUCycles = cpuCycles;
1810 #endif
1811
1812 //let's wait, then signal...
1813 //works longer, but then still falls behind...
1814 #ifdef THREADED_65C02
1815         if (!pauseMode)
1816                 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
1817 #endif
1818 }
1819
1820
1821 static void BlinkTimer(void)
1822 {
1823         flash = !flash;
1824         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1825 }
1826
1827
1828 /*
1829 Next problem is this: How to have events occur and synchronize with the rest
1830 of the threads?
1831
1832   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
1833     remainder CPU cycles over...)
1834
1835 One way would be to use a fractional accumulator, then subtract 1 every
1836 time it overflows. Like so:
1837
1838 double overflow = 0;
1839 uint32_t time = 20;
1840 while (!done)
1841 {
1842         Execute6808(&soundCPU, time);
1843         overflow += 0.289115646;
1844         if (overflow > 1.0)
1845         {
1846                 overflow -= 1.0;
1847                 time = 21;
1848         }
1849         else
1850                 time = 20;
1851 }
1852 */