]> Shamusworld >> Repos - apple2/blob - src/apple2.cpp
Created new MMU based code for R/W handling. Should be much faster now.
[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();
1535
1536         SoundDone();
1537         VideoDone();
1538         SaveSettings();
1539         LogDone();
1540
1541         return 0;
1542 }
1543
1544
1545 /*
1546 Apple II keycodes
1547 -----------------
1548
1549 Key     Aln CTL SHF BTH
1550 -----------------------
1551 space   $A0     $A0     $A0 $A0         No xlation
1552 RETURN  $8D     $8D     $8D     $8D             No xlation
1553 0               $B0     $B0     $B0     $B0             Need to screen shift+0 (?)
1554 1!              $B1 $B1 $A1 $A1         No xlation
1555 2"              $B2     $B2     $A2     $A2             No xlation
1556 3#              $B3     $B3     $A3     $A3             No xlation
1557 4$              $B4     $B4     $A4     $A4             No xlation
1558 5%              $B5     $B5     $A5     $A5             No xlation
1559 6&              $B6     $B6     $A6     $A6             No xlation
1560 7'              $B7     $B7     $A7     $A7             No xlation
1561 8(              $B8     $B8     $A8     $A8             No xlation
1562 9)              $B9     $B9     $A9     $A9             No xlation
1563 :*              $BA     $BA     $AA     $AA             No xlation
1564 ;+              $BB     $BB     $AB     $AB             No xlation
1565 ,<              $AC     $AC     $BC     $BC             No xlation
1566 -=              $AD     $AD     $BD     $BD             No xlation
1567 .>              $AE     $AE     $BE     $BE             No xlation
1568 /?              $AF     $AF     $BF     $BF             No xlation
1569 A               $C1     $81     $C1     $81
1570 B               $C2     $82     $C2     $82
1571 C               $C3     $83     $C3     $83
1572 D               $C4     $84     $C4     $84
1573 E               $C5     $85     $C5     $85
1574 F               $C6     $86     $C6     $86
1575 G               $C7     $87     $C7     $87
1576 H               $C8     $88     $C8     $88
1577 I               $C9     $89     $C9     $89
1578 J               $CA     $8A     $CA     $8A
1579 K               $CB     $8B     $CB     $8B
1580 L               $CC     $8C     $CC     $8C
1581 M               $CD     $8D     $DD     $9D             -> ODD
1582 N^              $CE     $8E     $DE     $9E             -> ODD
1583 O               $CF     $8F     $CF     $8F
1584 P@              $D0     $90     $C0     $80             Need to xlate CTL+SHFT+P & SHFT+P (?)
1585 Q               $D1     $91     $D1     $91
1586 R               $D2     $92     $D2     $92
1587 S               $D3     $93     $D3     $93
1588 T               $D4     $94     $D4     $94
1589 U               $D5     $95     $D5     $95
1590 V               $D6     $96     $D6     $96
1591 W               $D7     $97     $D7     $97
1592 X               $D8     $98     $D8     $98
1593 Y               $D9     $99     $D9     $99
1594 Z               $DA     $9A     $DA     $9A
1595 <-              $88     $88     $88     $88
1596 ->              $95     $95     $95     $95
1597 ESC             $9B     $9B     $9B     $9B             No xlation
1598
1599 */
1600 //static uint64_t lastCPUCycles = 0;
1601 static uint32_t frameCount = 0;
1602 static void FrameCallback(void)
1603 {
1604         SDL_Event event;
1605
1606         while (SDL_PollEvent(&event))
1607         {
1608                 switch (event.type)
1609                 {
1610 // Problem with using SDL_TEXTINPUT is that it causes key delay. :-/
1611                 case SDL_TEXTINPUT:
1612 //Need to do some key translation here, and screen out non-apple keys as well...
1613 //(really, could do it all in SDL_KEYDOWN, would just have to get symbols &
1614 // everything else done separately. this is slightly easier. :-P)
1615 //                      if (event.key.keysym.sym == SDLK_TAB)   // Prelim key screening...
1616                         if (event.edit.text[0] == '\t') // Prelim key screening...
1617                                 break;
1618
1619                         lastKeyPressed = event.edit.text[0];
1620                         keyDown = true;
1621
1622                         //kludge: should have a caps lock thingy here...
1623                         //or all uppercase for ][+...
1624 //                      if (lastKeyPressed >= 'a' && lastKeyPressed <='z')
1625 //                              lastKeyPressed &= 0xDF;         // Convert to upper case...
1626
1627                         break;
1628                 case SDL_KEYDOWN:
1629                         // CTRL+RESET key emulation (mapped to CTRL+`)
1630 // This doesn't work...
1631 //                      if (event.key.keysym.sym == SDLK_BREAK && (event.key.keysym.mod & KMOD_CTRL))
1632 //                      if (event.key.keysym.sym == SDLK_PAUSE && (event.key.keysym.mod & KMOD_CTRL))
1633                         if (event.key.keysym.sym == SDLK_BACKQUOTE && (event.key.keysym.mod & KMOD_CTRL))
1634 //NOTE that this shouldn't take place until the key is lifted... !!! FIX !!!
1635 //ALSO it seems to leave the machine in an inconsistent state vis-a-vis the language card...
1636                                 mainCPU.cpuFlags |= V65C02_ASSERT_LINE_RESET;
1637
1638                         if (event.key.keysym.sym == SDLK_RIGHT)
1639                                 lastKeyPressed = 0x15, keyDown = true;
1640                         else if (event.key.keysym.sym == SDLK_LEFT)
1641                                 lastKeyPressed = 0x08, keyDown = true;
1642                         else if (event.key.keysym.sym == SDLK_UP)
1643                                 lastKeyPressed = 0x0B, keyDown = true;
1644                         else if (event.key.keysym.sym == SDLK_DOWN)
1645                                 lastKeyPressed = 0x0A, keyDown = true;
1646                         else if (event.key.keysym.sym == SDLK_RETURN)
1647                                 lastKeyPressed = 0x0D, keyDown = true;
1648                         else if (event.key.keysym.sym == SDLK_ESCAPE)
1649                                 lastKeyPressed = 0x1B, keyDown = true;
1650                         else if (event.key.keysym.sym == SDLK_BACKSPACE)
1651                                 lastKeyPressed = 0x7F, keyDown = true;
1652
1653                         // Fix CTRL+key combo...
1654                         if (event.key.keysym.mod & KMOD_CTRL)
1655                         {
1656                                 if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1657                                 {
1658                                         lastKeyPressed = (event.key.keysym.sym - SDLK_a) + 1;
1659                                         keyDown = true;
1660 //printf("Key combo pressed: CTRL+%c\n", lastKeyPressed + 0x40);
1661                                 }
1662                         }
1663
1664                         // Use ALT+Q to exit, as well as the usual window decoration method
1665                         if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod & KMOD_ALT))
1666                                 running = false;
1667
1668                         if (event.key.keysym.sym == SDLK_PAUSE)
1669                         {
1670                                 pauseMode = !pauseMode;
1671
1672                                 if (pauseMode)
1673                                 {
1674                                         SoundPause();
1675                                         SpawnMessage("*** PAUSED ***");
1676                                 }
1677                                 else
1678                                 {
1679                                         SoundResume();
1680                                         SpawnMessage("*** RESUME ***");
1681                                 }
1682                         }
1683
1684                         // Paddle buttons 0 & 1
1685                         if (event.key.keysym.sym == SDLK_INSERT)
1686                                 openAppleDown = true;
1687                         if (event.key.keysym.sym == SDLK_PAGEUP)
1688                                 closedAppleDown = true;
1689
1690                         if (event.key.keysym.sym == SDLK_F11)
1691                                 dumpDis = !dumpDis;                             // Toggle the disassembly process
1692 //                      else if (event.key.keysym.sym == SDLK_F11)
1693 //                              floppyDrive.LoadImage("./disks/bt1_char.dsk");//Kludge to load char disk...
1694 else if (event.key.keysym.sym == SDLK_F9)
1695 {
1696         floppyDrive.CreateBlankImage();
1697 //      SpawnMessage("Image cleared...");
1698 }//*/
1699 else if (event.key.keysym.sym == SDLK_F10)
1700 {
1701         floppyDrive.SwapImages();
1702 //      SpawnMessage("Image swapped...");
1703 }//*/
1704
1705                         if (event.key.keysym.sym == SDLK_F2)// Toggle the palette
1706                                 TogglePalette();
1707                         else if (event.key.keysym.sym == SDLK_F3)// Cycle through screen types
1708                                 CycleScreenTypes();
1709
1710 //                      if (event.key.keysym.sym == SDLK_F5)    // Temp GUI launch key
1711                         if (event.key.keysym.sym == SDLK_F1)    // GUI launch key
1712 //NOTE: Should parse the output to determine whether or not the user requested
1713 //      to quit completely... !!! FIX !!!
1714                                 gui->Run();
1715
1716                         if (event.key.keysym.sym == SDLK_F5)
1717                         {
1718                                 VolumeDown();
1719                                 char volStr[19] = "[****************]";
1720 //                              volStr[GetVolume()] = 0;
1721                                 for(int i=GetVolume(); i<16; i++)
1722                                         volStr[1 + i] = '-';
1723                                 SpawnMessage("Volume: %s", volStr);
1724                         }
1725                         else if (event.key.keysym.sym == SDLK_F6)
1726                         {
1727                                 VolumeUp();
1728                                 char volStr[19] = "[****************]";
1729 //                              volStr[GetVolume()] = 0;
1730                                 for(int i=GetVolume(); i<16; i++)
1731                                         volStr[1 + i] = '-';
1732                                 SpawnMessage("Volume: %s", volStr);
1733                         }
1734
1735                         static bool fullscreenDebounce = false;
1736
1737                         if (event.key.keysym.sym == SDLK_F12)
1738                         {
1739                                 if (!fullscreenDebounce)
1740                                 {
1741                                         ToggleFullScreen();
1742                                         fullscreenDebounce = true;
1743                                 }
1744                         }
1745 //                      else
1746
1747                         break;
1748                 case SDL_KEYUP:
1749                         if (event.key.keysym.sym == SDLK_F12)
1750                                 fullscreenDebounce = false;
1751
1752                         // Paddle buttons 0 & 1
1753                         if (event.key.keysym.sym == SDLK_INSERT)
1754                                 openAppleDown = false;
1755                         if (event.key.keysym.sym == SDLK_PAGEUP)
1756                                 closedAppleDown = false;
1757
1758 //                      if (event.key.keysym.sym >= SDLK_a && event.key.keysym.sym <= SDLK_z)
1759 //                              keyDown = false;
1760
1761                         break;
1762                 case SDL_QUIT:
1763                         running = false;
1764                 }
1765         }
1766
1767 //#warning "!!! Taking MAJOR time hit with the video frame rendering !!!"
1768         RenderVideoFrame();
1769         SetCallbackTime(FrameCallback, 16666.66666667);
1770
1771 #ifdef CPU_CLOCK_CHECKING
1772 //We know it's stopped, so we can get away with this...
1773 counter++;
1774 if (counter == 60)
1775 {
1776         uint64_t clock = GetCurrentV65C02Clock();
1777 //totalCPU += (uint32_t)(clock - lastClock);
1778
1779         printf("Executed %u cycles...\n", (uint32_t)(clock - lastClock));
1780         lastClock = clock;
1781 //      totalCPU = 0;
1782         counter = 0;
1783 }
1784 #endif
1785 //Instead of this, we should yield remaining time to other processes... !!! FIX !!! [DONE]
1786 //lessee...
1787 //nope.
1788 //Actually, slows things down too much...
1789 //SDL_Delay(10);
1790 //      while (SDL_GetTicks() - startTicks < 16);       // Wait for next frame...
1791
1792 // This is the problem: If you set the interval to 16, it runs faster than
1793 // 1/60s per frame. If you set it to 17, it runs slower. What we need is to
1794 // have it do 16 for one frame, then 17 for two others. Then it should average
1795 // out to 1/60s per frame every 3 frames.
1796         frameCount = (frameCount + 1) % 3;
1797
1798         uint32_t waitFrameTime = 17 - (frameCount == 0 ? 1 : 0);
1799
1800         while (SDL_GetTicks() - startTicks < waitFrameTime)
1801                 SDL_Delay(1);                                                   // Wait for next frame...
1802
1803         startTicks = SDL_GetTicks();
1804 #if 0
1805         uint64_t cpuCycles = GetCurrentV65C02Clock();
1806         uint32_t cyclesBurned = (uint32_t)(cpuCycles - lastCPUCycles);
1807         WriteLog("FrameCallback: used %i cycles\n", cyclesBurned);
1808         lastCPUCycles = cpuCycles;
1809 #endif
1810
1811 //let's wait, then signal...
1812 //works longer, but then still falls behind...
1813 #ifdef THREADED_65C02
1814         if (!pauseMode)
1815                 SDL_CondSignal(cpuCond);//OK, let the CPU go another frame...
1816 #endif
1817 }
1818
1819
1820 static void BlinkTimer(void)
1821 {
1822         flash = !flash;
1823         SetCallbackTime(BlinkTimer, 250000);            // Set up blinking at 1/4 sec intervals
1824 }
1825
1826
1827 /*
1828 Next problem is this: How to have events occur and synchronize with the rest
1829 of the threads?
1830
1831   o Have the CPU thread manage the timer mechanism? (need to have a method of carrying
1832     remainder CPU cycles over...)
1833
1834 One way would be to use a fractional accumulator, then subtract 1 every
1835 time it overflows. Like so:
1836
1837 double overflow = 0;
1838 uint32_t time = 20;
1839 while (!done)
1840 {
1841         Execute6808(&soundCPU, time);
1842         overflow += 0.289115646;
1843         if (overflow > 1.0)
1844         {
1845                 overflow -= 1.0;
1846                 time = 21;
1847         }
1848         else
1849                 time = 20;
1850 }
1851 */