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