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