]> Shamusworld >> Repos - thunder/blob - src/thunder.cpp
Added save states; updated application icon.
[thunder] / src / thunder.cpp
1 //
2 // Thunder: A Rolling Thunder Emulator
3 //
4 // by James Hammons
5 // (C) 2004, 2023 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  -----------------------------------------------------------
11 // JLH  07/23/2009  Added changelog ;-)
12 // JLH  08/12/2009  Stabilized emulation so that it works
13 // JLH  04/04/2014  Converted to SDL 2
14 // JLH  04/17/2014  Removed a metric fuck-tonne of cruft, added YM2151 & MCU
15 // JLH  01/13/2023  Finally fixed the sprite lag problem  :-D
16 // JLH  01/13/2023  Added save states
17 //
18
19 #define THUNDER_VERSION         "1.2.2"
20
21 #include <SDL2/SDL.h>
22 #include <string>
23 #include <stdlib.h>
24 #include <time.h>
25 #include "fileio.h"
26 #include "gui.h"
27 #include "log.h"
28 #include "psg.h"
29 #include "screen.h"
30 #include "sound.h"
31 #include "v63701.h"
32 #include "v6809.h"
33 #include "video.h"
34 #include "ym2151.h"
35
36 #define ROM1   "rt3-1b.9c"
37 #define ROM2   "rt3-2b.12c"
38 #define ROM3   "rt3-3.12d"
39 #define ROM4   "rt1-4.6b"
40 #define ROM5   "rt1-5.4r"
41 #define ROM6   "rt1-6.4s"
42 #define ROM7   "rt1-7.7r"
43 #define ROM8   "rt1-8.7s"
44 #define ROM9   "rt1-9.12h"
45 #define ROM10  "rt1-10.12k"
46 #define ROM11  "rt1-11.12l"
47 #define ROM12  "rt1-12.12m"
48 #define ROM13  "rt1-13.12p"
49 #define ROM14  "rt1-14.12r"
50 #define ROM15  "rt1-15.12t"
51 #define ROM16  "rt1-16.12u"
52 #define ROM17  "rt1-17.f1"
53 #define ROM18  "rt1-18.h1"
54 #define ROM19  "rt1-19.k1"
55 #define ROM20  "rt1-20.m1"
56 #define ROM21  "rt1-21.f3"
57 #define ROM22  "rt1-22.h3"
58 #define PROM1  "mb7124e.3r"
59 #define PROM2  "mb7116e.3s"
60 #define PROM3  "mb7138h.4v"
61 #define PROM4  "mb7138h.6v"
62 #define PROM5  "mb7112e.6u"
63 #define MCUROM "rt1-mcu.bin"
64
65 // Global defines
66
67 uint8_t gram1[0x10000], grom1[0x10000], grom2[0x10000];
68 uint8_t grom3[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
69 uint8_t charROM[0x60000];               // Character ROM
70 uint8_t mcuMem[0x10000];                // 64K for MCU
71
72 // CPU execution contexts
73 V6809REGS cpu1, cpu2;
74 V63701REGS mcu;
75
76 // Bank switch addresses
77 uint32_t banksw1, banksw2;
78
79 // MCU inputs
80 Byte input1, input2, input3, input4, input5;
81
82 // Miscellaneous stuff
83 bool copySprites = false;
84 const uint8_t stateHeader[20] = "THUNDERSAVESTATE1.0";
85
86 // Function prototypes
87 uint8_t MCUReadMemory(uint16_t address);
88 void MCUWriteMemory(uint16_t address, uint8_t data);
89
90 //
91 // Read a byte from memory
92 //
93 uint8_t MainReadMemory(uint16_t addr)
94 {
95         uint8_t b;
96
97         if (addr < 0x8000)
98         {
99                 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
100                 if ((addr >= 0x4000) && (addr <= 0x43FF))
101                         return mcuMem[addr - 0x3000];
102
103                 if (addr >= 0x6000)
104                         return data_rom[banksw1 + (addr - 0x6000)];     // Get char data
105
106                 return gram1[addr];
107         }
108
109         return grom1[addr];
110 }
111
112 //
113 // Write a byte to memory
114 //
115 void MainWriteMemory(uint16_t address, uint8_t data)
116 {
117         if (address == 0x6000)
118                 SpawnSound(GAMESOUND, gram1[0x6200], 0);        // Do voice chan 1
119         if (address == 0x6400)
120                 SpawnSound(GAMESOUND, gram1[0x6600], 1);        // Do voice chan 2
121         if (address == 0x6800)
122                 banksw1 = (uint32_t)data << 13;                         // Set char data bankswitch base address
123
124         // Memory shared with MCU (CPU #1 only! CPU #2 does not)
125         if ((address >= 0x4000) && (address <= 0x43FF))
126                 mcuMem[address - 0x3000] = data;
127         else
128                 gram1[address] = data;
129
130         if (address == 0x5FF2)
131                 copySprites = true;
132         if (address == 0x8800)
133                 charBankSwitch = false;         // Char banksw1
134         if (address == 0x8C00)
135                 charBankSwitch = true;          // Char banksw2
136         if (address == 0x8400)                  // Frame go strobe? VBlank acknowledge?
137                 ClearLineOfCurrentV6809(V6809_LINE_IRQ); // IRQ Ack
138 }
139
140 //
141 // Read a byte from memory (2nd processor)
142 //
143 uint8_t SubReadMemory(uint16_t address)
144 {
145         if (address < 0x8000)
146         {
147                 if (address < 0x2000)
148                         return gram1[address + 0x4000];
149                 else if ((address >= 0x2000) && (address < 0x6000))
150                         return gram1[address - 0x2000];
151                 else if (address >= 0x6000)
152                         return grom3[banksw2 + (address - 0x6000)];
153         }
154
155         return grom2[address];
156 }
157
158 //
159 // Write a byte to memory (2nd processor)
160 //
161 void SubWriteMemory(uint16_t address, uint8_t data)
162 {
163         // Set sprite data bank switch
164         if (address == 0xD803)
165                 banksw2 = (uint32_t)(data & 0x03) << 13;
166
167         if (address < 0x2000)
168                 gram1[address + 0x4000] = data;
169
170         if ((address >= 0x2000) && (address < 0x6000))
171                 gram1[address - 0x2000] = data;
172
173         if (address == 0x1FF2)
174                 copySprites = true;
175
176         if (address == 0x8800)
177                 ClearLineOfCurrentV6809(V6809_LINE_IRQ); // IRQ Ack
178 }
179
180 uint8_t MCUReadMemory(uint16_t address)
181 {
182         if (address < 0x20)
183                 return InternalRegisterRead(address);
184         else if ((address >= 0x1000) && (address <= 0x113F))
185                 return ReadPSG(address - 0x1000);
186         else if ((address >= 0x2000) && (address <= 0x2001))
187                 return YMReadReg(0);
188         // Various joystick + buttons; all are active low.
189         else if (address == 0x2020)
190                 return input1.byte;
191         else if (address == 0x2021)
192                 return input2.byte;
193         // This is DSW1 & 2. All switch settings are active low.
194         else if (address == 0x2030)
195                 return input4.byte;
196         else if (address == 0x2031)
197                 return input5.byte;
198
199         return mcuMem[address];
200 }
201
202 void MCUWriteMemory(uint16_t address, uint8_t data)
203 {
204         static uint8_t ymRegister;
205
206         if (address < 0x20)
207         {
208                 InternalRegisterWrite(address, data);
209                 return;
210         }
211         else if (((address >= 0x4000) && (address <= 0xBFFF))
212                 || (address >= 0xF000))
213                 return;
214         else if ((address >= 0x1000) && (address <= 0x113F))
215         {
216                 WritePSG(address - 0x1000, data);
217                 return;
218         }
219         else if (address == 0x2000)
220         {
221                 ymRegister = data;
222                 return;
223         }
224         else if (address == 0x2001)
225         {
226                 YMWriteReg(0, ymRegister, data);
227                 return;
228         }
229
230         // RAM is from $0 - $3FFF, $C000 - $EFFF
231         mcuMem[address] = data;
232 }
233
234 uint8_t V63701ReadPort1(void)
235 {
236 //      printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
237         return input3.byte;
238 }
239
240 uint8_t V63701ReadPort2(void)
241 {
242         return 0xFF;
243 }
244
245 void V63701WritePort1(uint8_t data)
246 {
247 //      printf("V63701WritePort1: Wrote $%02X...\n", data);
248 }
249
250 void V63701WritePort2(uint8_t data)
251 {
252 //      printf("V63701WritePort2: Wrote $%02X...\n", data);
253 }
254
255 //
256 // Generic Load file into image space
257 // (No error checking performed!  Responsibility of caller!)
258 //
259 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
260 {
261         char path[128];
262
263         strcpy(path, "./ROMs/");
264         strcat(path, filename);
265         FILE * file = fopen(path, "rb");
266
267         if (!file)
268         {
269                 printf("Could not open file \"%s\"!\n", filename);
270                 return false;
271         }
272
273         size_t ignored = fread(&mem[address], 1, length, file);
274         fclose(file);
275
276         return true;
277 }
278
279 //
280 // Read color PROMs
281 //
282 bool ReadColorPROMs(void)
283 {
284         FILE * file1 = fopen("./ROMs/" PROM3, "rb");
285
286         if (file1)
287         {
288                 for(int i=0; i<256; i++) // Load char pallete with PROM values
289                         for(int j=0; j<8; j++)
290                                 ccolor[i][j] = (uint8_t)fgetc(file1);
291
292                 fclose(file1);
293         }
294
295         file1 = fopen("./ROMs/" PROM4, "rb");
296
297         if (file1)
298         {
299                 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
300                         for(int j=0; j<16; j++)
301                                 scolor[i][j] = (uint8_t)fgetc(file1);
302
303                 fclose(file1);
304         }
305
306         file1 = fopen("./ROMs/" PROM1, "rb");
307         FILE * file2 = fopen("./ROMs/" PROM2, "rb");
308
309         // If open was successful...
310         if (file1 && file2)
311         {
312                 // Palette is 12-bit RGB, we stretch it to 24-bit
313                 for(int i=0; i<256; i++)
314                 {
315                         uint8_t c1 = fgetc(file1);
316                         uint8_t c2 = fgetc(file2);
317                         uint8_t r = (uint8_t)c1 & 0x0F;
318                         uint8_t g = (uint8_t)c1 >> 4;
319                         uint8_t b = (uint8_t)c2;
320                         palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
321                 }
322
323                 fclose(file1);
324                 fclose(file2);
325         }
326
327         // PROM5 has the following in it (tile address decoder):
328         // 00:  00 20 40 60  02 22 42 62  04 24 44 64  06 26 46 66
329         // 10:  88 A8 C8 E8  8A AA CA EA  8C AC CC EC  8E AE CE EE
330
331         if (!file1)
332         {
333                 printf("Could not open PROM files!\n");
334                 return false;
335         }
336
337         return true;
338 }
339
340 //
341 // Unpack font data
342 //
343 bool UnpackFonts(void)
344 {
345         // 0x4000 $800 chars
346         FILE * file1 = fopen("./ROMs/" ROM7, "rb");
347         FILE * file2 = fopen("./ROMs/" ROM8, "rb");
348
349         if (!file1 || !file2)
350         {
351                 printf("Could not open either " ROM7 " or " ROM8 "!\n");
352                 return false;
353         }
354
355         for(long i=0; i<0x40000; i+=64)
356         {
357                 for(int j=0; j<64; j+=8)
358                 {
359                         uint8_t b1 = (uint8_t)fgetc(file1);
360                         uint8_t b2 = (uint8_t)fgetc(file1);
361                         uint8_t b3 = (uint8_t)fgetc(file2) ^ 0xFF;
362                         charROM[i + j + 0] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
363                         charROM[i + j + 1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
364                         charROM[i + j + 2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
365                         charROM[i + j + 3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
366                         charROM[i + j + 4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
367                         charROM[i + j + 5] = (b3 & 0x04)        | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
368                         charROM[i + j + 6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
369                         charROM[i + j + 7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
370                 }
371         }
372
373         fclose(file1);
374         fclose(file2);
375
376         file1 = fopen("./ROMs/" ROM5, "rb");
377         file2 = fopen("./ROMs/" ROM6, "rb");
378
379         if (!file1 || !file2)
380         {
381                 printf("Could not open either " ROM5 " or " ROM6 "!\n");
382                 return false;
383         }
384
385         for(long i=0x40000; i<0x60000; i+=64)
386         {
387                 for(int j=0; j<64; j+=8)
388                 {
389                         uint8_t b1 = (uint8_t)fgetc(file1);
390                         uint8_t b2 = (uint8_t)fgetc(file1);
391                         uint8_t b3 = (uint8_t)fgetc(file2) ^ 0xFF;
392                         charROM[i + j + 0] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
393                         charROM[i + j + 1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
394                         charROM[i + j + 2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
395                         charROM[i + j + 3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
396                         charROM[i + j + 4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
397                         charROM[i + j + 5] = (b3 & 0x04)        | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
398                         charROM[i + j + 6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
399                         charROM[i + j + 7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
400                 }
401         }
402
403         fclose(file1);
404         fclose(file2);
405
406         return true;
407 }
408
409 void SaveThunderState(const char * filename)
410 {
411         WriteLog("Main: Saving Thunder state...\n");
412         FILE * file = fopen(filename, "wb");
413
414         if (!file)
415         {
416                 WriteLog("Could not open file \"%s\" for writing!\n", filename);
417                 return;
418         }
419
420         // Write out header
421         size_t ignored = fwrite(stateHeader, 1, 19, file);
422
423         // Write out CPUs' state
424         ignored = fwrite(&cpu1, 1, sizeof(cpu1), file);
425         ignored = fwrite(&cpu2, 1, sizeof(cpu2), file);
426         ignored = fwrite(&mcu, 1, sizeof(mcu), file);
427
428         // Write out main memory
429         ignored = fwrite(gram1, 1, 0x10000, file);
430         ignored = fwrite(mcuMem, 1, 0x10000, file);
431
432         // Write out state variables
433         WriteLong(file, banksw1);
434         WriteLong(file, banksw2);
435         fputc((uint8_t)copySprites, file);
436         fputc(input1.byte, file);
437         fputc(input2.byte, file);
438         fputc(input3.byte, file);
439         fputc(input4.byte, file);
440         fputc(input5.byte, file);
441
442         PSGSaveState(file);
443         YMSaveState(file);
444
445         fclose(file);
446 }
447
448 bool LoadThunderState(const char * filename)
449 {
450         WriteLog("Main: Loading Thunder state...\n");
451         FILE * file = fopen(filename, "rb");
452
453         if (!file)
454         {
455                 WriteLog("Could not open file \"%s\" for reading!\n", filename);
456                 return false;
457         }
458
459         uint8_t buffer[19];
460         size_t ignored = fread(buffer, 1, 19, file);
461
462         // Sanity check...
463         if (memcmp(buffer, stateHeader, 19) != 0)
464         {
465                 fclose(file);
466                 WriteLog("File \"%s\" is not a valid Thunder save state file!\n", filename);
467                 return false;
468         }
469
470         // Read CPUs' state
471         ignored = fread(&cpu1, 1, sizeof(cpu1), file);
472         ignored = fread(&cpu2, 1, sizeof(cpu2), file);
473         ignored = fread(&mcu, 1, sizeof(mcu), file);
474
475         // Read main memory
476         ignored = fread(gram1, 1, 0x10000, file);
477         ignored = fread(mcuMem, 1, 0x10000, file);
478
479         // Read in state variables
480         banksw1 = ReadLong(file);
481         banksw2 = ReadLong(file);
482         copySprites = (bool)fgetc(file);
483         input1.byte = fgetc(file);
484         input2.byte = fgetc(file);
485         input3.byte = fgetc(file);
486         input4.byte = fgetc(file);
487         input5.byte = fgetc(file);
488
489         PSGLoadState(file);
490         YMLoadState(file);
491
492         fclose(file);
493
494         // Make sure things are in a sane state before execution :-P
495         cpu1.RdMem = MainReadMemory;
496         cpu1.WrMem = MainWriteMemory;
497
498         cpu2.RdMem = SubReadMemory;
499         cpu2.WrMem = SubWriteMemory;
500
501         mcu.RdMem = MCUReadMemory;
502         mcu.WrMem = MCUWriteMemory;
503
504         return true;
505 }
506
507 //
508 // Main loop
509 //
510 int main(int argc, char * argv[])
511 {
512         InitLog("thunder.log");
513
514         bool running;                                   // CPU running state flag...
515         SDL_Event event;                                // SDL "event"
516         uint32_t ticks, oldTicks;
517         uint8_t frameTickCount = 0;
518
519         printf("THUNDER v" THUNDER_VERSION " by James Hammons\n");
520         printf("Serial #20230113 / Prerelease\n");
521         printf("© 2003, 2023 Underground Software\n\n");
522         printf("This emulator is free software. If you paid for it you were RIPPED OFF\n\n");
523
524 //      SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
525
526         printf("Loading ROMs...\n");
527
528         if (!ReadColorPROMs())
529                 return -1;
530
531         // Load $8000-$FFFF 1st ROM
532         if (!LoadImg(ROM1, grom1, 0x8000, 0x8000))
533                 return -1;
534
535          // Load $8000-$FFFF 2nd ROM
536         if (!LoadImg(ROM2, grom2, 0x8000, 0x8000))
537                 return -1;
538
539         // Load 3rd ROM into its own space
540         if (!LoadImg(ROM3, grom3, 0, 0x8000))
541                 return -1;
542
543         if (!LoadImg(ROM17, data_rom, 0,       0x10000))
544                 return -1;
545
546         if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000))
547                 return -1;
548
549         if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000))
550                 return -1;
551
552         if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000))
553                 return -1;
554
555         if (!LoadImg(ROM9,  spr_rom, 0,       0x10000))
556                 return -1;
557
558         if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000))
559                 return -1;
560
561         if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000))
562                 return -1;
563
564         if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000))
565                 return -1;
566
567         if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000))
568                 return -1;
569
570         if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000))
571                 return -1;
572
573         if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000))
574                 return -1;
575
576         if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000))
577                 return -1;
578
579         if (!LoadImg(ROM21, voice_rom, 0, 0x10000))
580                 return -1;
581
582         if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000))
583                 return -1;
584
585         // Load 5, 6, 7, 8th ROMs
586         if (!UnpackFonts())
587                 return -1;
588
589         // Load MCU program + data
590         if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000))
591                 return -1;
592
593         if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000))
594                 return -1;
595
596         // Set up V6809, V63701 execution contexts
597         memset(&cpu1, 0, sizeof(V6809REGS));
598         cpu1.RdMem = MainReadMemory;
599         cpu1.WrMem = MainWriteMemory;
600         cpu1.cpuFlags |= V6809_LINE_RESET;
601
602         memset(&cpu2, 0, sizeof(V6809REGS));
603         cpu2.RdMem = SubReadMemory;
604         cpu2.WrMem = SubWriteMemory;
605         cpu2.cpuFlags |= V6809_LINE_RESET;
606
607         memset(&mcu, 0, sizeof(V63701REGS));
608         mcu.RdMem = MCUReadMemory;
609         mcu.WrMem = MCUWriteMemory;
610         mcu.cpuFlags |= V63701_LINE_RESET;
611
612         running = true;
613
614         // Set all inputs to inactive...
615         input1.byte = input2.byte = input3.byte = 0xFF;
616         banksw1 = 0;
617         banksw2 = 0;
618         InitGUI();                 // Reset # of coins
619
620         // Set up DIP switches...
621         input4.bit.b0 = 1;              // DSW B-0: Continues (1 = 6, 0 = 3)
622         input4.bit.b1 = 1;              // DSW B-2: ???
623         input4.bit.b2 = 1;              // DSW B-4: Difficulty (1 = normal, 0 = easy)
624         input4.bit.b3 = 1;              // DSW B-6: Bonus lives (70K/200K = 1, 100K/300K = 0)
625         input4.bit.b4 = 1;              // DSW A-0: Coin #2 credit
626         input4.bit.b5 = 1;              // DSW A-2: Freeze mode (0 = freeze)
627         input4.bit.b6 = 1;              // DSW A-4: Attract mode sound (1 = on)
628         input4.bit.b7 = 1;              // DSW A-6: Coin #1 credit
629
630         input5.bit.b0 = 1;              // DSW B-1: Cabinet type (1 = A, 0 = B)
631         input5.bit.b1 = 0;              // DSW B-3: Level select (0 = on)
632         input5.bit.b2 = 0;              // DSW B-5: Time (1 = 120, 0 = 150)
633         input5.bit.b3 = 0;              // DSW B-7: Lives (1 = 3, 0 = 5)
634         input5.bit.b4 = 1;              // DSW A-1: Coin #2 credit
635         input5.bit.b5 = 1;              // DSW A-3: Invulnerability (0 = on)
636         input5.bit.b6 = 1;              // DSW A-5: Coin #1 credit
637         input5.bit.b7 = 1;              // DSW A-7: Service mode
638
639 WriteLog("About to set up screen...\n");
640         InitVideo();
641
642         oldTicks = SDL_GetTicks();
643
644 WriteLog("About to set up audio...\n");
645         InitSound();
646
647 WriteLog("About to enter main loop...\n");
648         while (running)
649         {
650 //              HandleGUIDebounce();                                    // Debounce GUI keys
651
652 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
653 // $423D - $425B by the MCU
654
655                 // SDL key handling...
656
657                 SDL_Event event;
658
659                 while (SDL_PollEvent(&event))
660                 {
661                         switch (event.type)
662                         {
663                         case SDL_KEYDOWN:
664                                 if (event.key.keysym.sym == SDLK_ESCAPE)
665                                         running = false;
666                                 // Do PCX snapshot (F4)
667                                 else if (event.key.keysym.sym == SDLK_F4)
668                                 {
669 //                                      SpawnSound(USERSOUND, SCAMERA);
670                                         SavePCXSnapshot();
671                                 }
672 #if 1
673                                 else if (event.key.keysym.sym == SDLK_5)
674                                         input1.bit.b5 = 0;
675                                 else if (event.key.keysym.sym == SDLK_1)
676                                         input1.bit.b6 = 0;
677                                 else if (event.key.keysym.sym == SDLK_2)
678                                         input2.bit.b6 = 0;
679                                 else if (event.key.keysym.sym == SDLK_c)        // Left
680                                         input3.bit.b5 = 0;
681                                 else if (event.key.keysym.sym == SDLK_z)        // Right
682                                         input3.bit.b4 = 0;
683                                 else if (event.key.keysym.sym == SDLK_s)        // Up
684                                         input2.bit.b2 = 0;
685                                 else if (event.key.keysym.sym == SDLK_x)        // Down
686                                         input1.bit.b2 = 0;
687                                 else if (event.key.keysym.sym == SDLK_k)        // Jump
688                                         input1.bit.b1 = 0;
689                                 else if (event.key.keysym.sym == SDLK_l)        // Fire
690                                         input3.bit.b3 = 0;
691
692                                 else if (event.key.keysym.sym == SDLK_F1)       // Service mode
693                                         input5.bit.b7 = !input5.bit.b7;
694
695                                 else if (event.key.keysym.sym == SDLK_q)
696                                         input4.bit.b7 = !input4.bit.b7;
697                                 else if (event.key.keysym.sym == SDLK_w)
698                                         input5.bit.b6 = !input5.bit.b6;
699                                 else if (event.key.keysym.sym == SDLK_e)
700                                         input5.bit.b5 = !input5.bit.b5;
701                                 else if (event.key.keysym.sym == SDLK_r)
702                                         input5.bit.b4 = !input5.bit.b4;
703                                 else if (event.key.keysym.sym == SDLK_t)
704                                         input5.bit.b3 = !input5.bit.b3;
705                                 else if (event.key.keysym.sym == SDLK_y)
706                                         input5.bit.b2 = !input5.bit.b2;
707                                 else if (event.key.keysym.sym == SDLK_u)
708                                         input5.bit.b1 = !input5.bit.b1;
709                                 else if (event.key.keysym.sym == SDLK_i)
710                                         input5.bit.b0 = !input5.bit.b0;
711
712                                 // Quick'n'Dirty save state handling...  :-P
713                                 else if (event.key.keysym.sym == SDLK_F8)
714                                         SaveThunderState("thun0001.state");
715                                 else if (event.key.keysym.sym == SDLK_F9)
716                                         LoadThunderState("thun0001.state");
717
718 #else
719                                 else if (event.key.keysym.sym == SDLK_1)
720                                         input1.bit.b0 = 0;
721                                 else if (event.key.keysym.sym == SDLK_2)
722                                         input1.bit.b1 = 0;
723                                 else if (event.key.keysym.sym == SDLK_3)
724                                         input1.bit.b2 = 0;
725                                 else if (event.key.keysym.sym == SDLK_4)
726                                         input1.bit.b3 = 0;
727                                 else if (event.key.keysym.sym == SDLK_5)
728                                         input1.bit.b4 = 0;
729                                 else if (event.key.keysym.sym == SDLK_6)
730                                         input1.bit.b5 = 0;
731                                 else if (event.key.keysym.sym == SDLK_7)
732                                         input1.bit.b6 = 0;
733                                 else if (event.key.keysym.sym == SDLK_8)
734                                         input1.bit.b7 = 0;
735
736                                 else if (event.key.keysym.sym == SDLK_q)
737                                         input2.bit.b0 = 0;
738                                 else if (event.key.keysym.sym == SDLK_w)
739                                         input2.bit.b1 = 0;
740                                 else if (event.key.keysym.sym == SDLK_e)
741                                         input2.bit.b2 = 0;
742                                 else if (event.key.keysym.sym == SDLK_r)
743                                         input2.bit.b3 = 0;
744                                 else if (event.key.keysym.sym == SDLK_t)
745                                         input2.bit.b4 = 0;
746                                 else if (event.key.keysym.sym == SDLK_y)
747                                         input2.bit.b5 = 0;
748                                 else if (event.key.keysym.sym == SDLK_u)
749                                         input2.bit.b6 = 0;
750                                 else if (event.key.keysym.sym == SDLK_i)
751                                         input2.bit.b7 = 0;
752
753                                 else if (event.key.keysym.sym == SDLK_a)
754                                         input3.bit.b0 = 0;
755                                 else if (event.key.keysym.sym == SDLK_s)
756                                         input3.bit.b1 = 0;
757                                 else if (event.key.keysym.sym == SDLK_d)
758                                         input3.bit.b2 = 0;
759                                 else if (event.key.keysym.sym == SDLK_f)
760                                         input3.bit.b3 = 0;
761                                 else if (event.key.keysym.sym == SDLK_g)
762                                         input3.bit.b4 = 0;
763 #endif
764
765                                 break;
766                         case SDL_KEYUP:
767 #if 1
768                                 if (event.key.keysym.sym == SDLK_5)
769                                         input1.bit.b5 = 1;
770                                 else if (event.key.keysym.sym == SDLK_1)
771                                         input1.bit.b6 = 1;
772                                 else if (event.key.keysym.sym == SDLK_2)
773                                         input2.bit.b6 = 1;
774                                 else if (event.key.keysym.sym == SDLK_c)
775                                         input3.bit.b5 = 1;
776                                 else if (event.key.keysym.sym == SDLK_z)
777                                         input3.bit.b4 = 1;
778                                 else if (event.key.keysym.sym == SDLK_s)
779                                         input2.bit.b2 = 1;
780                                 else if (event.key.keysym.sym == SDLK_x)
781                                         input1.bit.b2 = 1;
782                                 else if (event.key.keysym.sym == SDLK_k)        // (Q)  Jump
783                                         input1.bit.b1 = 1;
784                                 else if (event.key.keysym.sym == SDLK_l)        // (E) Fire
785                                         input3.bit.b3 = 1;
786 #else
787                                 if (event.key.keysym.sym == SDLK_1)
788                                         input1.bit.b0 = 1;
789                                 else if (event.key.keysym.sym == SDLK_2)
790                                         input1.bit.b1 = 1;
791                                 else if (event.key.keysym.sym == SDLK_3)
792                                         input1.bit.b2 = 1;
793                                 else if (event.key.keysym.sym == SDLK_4)
794                                         input1.bit.b3 = 1;
795                                 else if (event.key.keysym.sym == SDLK_5)
796                                         input1.bit.b4 = 1;
797                                 else if (event.key.keysym.sym == SDLK_6)
798                                         input1.bit.b5 = 1;
799                                 else if (event.key.keysym.sym == SDLK_7)
800                                         input1.bit.b6 = 1;
801                                 else if (event.key.keysym.sym == SDLK_8)
802                                         input1.bit.b7 = 1;
803
804                                 else if (event.key.keysym.sym == SDLK_q)
805                                         input2.bit.b0 = 1;
806                                 else if (event.key.keysym.sym == SDLK_w)
807                                         input2.bit.b1 = 1;
808                                 else if (event.key.keysym.sym == SDLK_e)
809                                         input2.bit.b2 = 1;
810                                 else if (event.key.keysym.sym == SDLK_r)
811                                         input2.bit.b3 = 1;
812                                 else if (event.key.keysym.sym == SDLK_t)
813                                         input2.bit.b4 = 1;
814                                 else if (event.key.keysym.sym == SDLK_y)
815                                         input2.bit.b5 = 1;
816                                 else if (event.key.keysym.sym == SDLK_u)
817                                         input2.bit.b6 = 1;
818                                 else if (event.key.keysym.sym == SDLK_i)
819                                         input2.bit.b7 = 1;
820
821                                 else if (event.key.keysym.sym == SDLK_a)
822                                         input3.bit.b0 = 1;
823                                 else if (event.key.keysym.sym == SDLK_s)
824                                         input3.bit.b1 = 1;
825                                 else if (event.key.keysym.sym == SDLK_d)
826                                         input3.bit.b2 = 1;
827                                 else if (event.key.keysym.sym == SDLK_f)
828                                         input3.bit.b3 = 1;
829                                 else if (event.key.keysym.sym == SDLK_g)
830                                         input3.bit.b4 = 1;
831 #endif
832
833                                 break;
834                         }
835                 }
836
837 #if 0
838                                 if (keys[SDLK_ESCAPE])
839                                         running = false;                     // ESC to exit...
840
841                                 if (debounce)
842                                         debounce--;                          // Debounce toggle keys...
843                                 else
844                                 {
845                                         if (keys[SDLK_F1])
846                                         {
847                                                 self_test = !self_test;            // Self-test (F1-toggle)
848                                                 debounce = 10;                     // Key debounce value...
849                                         }
850                                         if (keys[SDLK_F2])
851                                         {
852                                                 gram1[0x4268] = 1;                 // Video test (F2)
853                                                 debounce = 10;                     // Key debounce value...
854                                         }
855                                         if (keys[SDLK_F12])
856                                         {
857                                                 scr_type = !scr_type;              // Toggle screen (F12)
858                                                 debounce = 10;                     // Key debounce value...
859                                         }
860                                         if (keys[SDLK_F3])
861                                         {
862                                                 show_scr = !show_scr;              // Toggle bkgrnd (F3)
863                                                 debounce = 10;
864                                         }
865                                         if (keys[SDLK_F6])
866                                         {
867                                                 enable_cpu = !enable_cpu;          // Toggle CPUs (F6)
868                                                 debounce = 10;
869                                         }
870                                         if (keys[SDLK_F5])
871                                         {
872                                                 refresh2 = !refresh2;             // Toggle 30/60Hz (F5)
873                                                 SetRefreshRate(refresh2);         // Inform GUI of refresh
874                                                 if (refresh2)
875                                                         SpawnMsg(M60FPS);
876                                                 else
877                                                         SpawnMsg(M30FPS);
878                                                 debounce = 10;                    // Key debounce value...
879                                         }
880                                         if (keys[SDLK_F4])                      // Do PCX snapshot (F4)
881                                         {
882                                                 SpawnSound(USERSOUND, SCAMERA);
883                                                 SnapPCX(screen);
884                                                 debounce = 10;
885                                         }
886                                         if (keys[SDLK_TAB])                      // Tab active/deactivate GUI
887                                         {
888                                                 if (ShowGUI())
889                                                         DeactivateGUI();
890                                                 else
891                                                         ActivateGUI();
892
893                                                 debounce = 10;
894                                         }
895                                 }
896                                 //if (keys[0x3E])  gram1[0x4247] = 1;  // Screen hold DS (F4)
897                                 if (keys[SDLK_RIGHT])                                           // Right arrow
898                                 {
899                                         if (ShowGUI())
900                                                 SelectRight();                     // If GUI active...
901                                         else
902                                         {
903                                                 if (!keys[SDLK_LEFT])                     // Disallow opposite directions @ same time
904                                                         gram1[0x427F] = 1;               // Stick right
905                                         }
906                                 }
907                                 if (keys[SDLK_LEFT])
908                                 {
909                                         if (ShowGUI())
910                                                 SelectLeft();                      // If GUI active...
911                                         else
912                                         {
913                                                 if (!keys[SDLK_RIGHT])                     // Disallow opposite directions@same time
914                                                 gram1[0x4281] = 1;               // Left arrow
915                                         }
916                                 }
917                                 if (keys[SDLK_UP])
918                                 {
919                                         if (ShowGUI())
920                                                 SelectUp();                        // If GUI active...
921                                         else
922                                         {
923                                                 if (!keys[SDLK_DOWN])                     // Disallow opposite directions@same time
924                                                         gram1[0x427B] = 1;               // Up arrow
925                                         }
926                                 }
927                                 if (keys[SDLK_DOWN])
928                                 {
929                                         if (ShowGUI())
930                                                 SelectDown();                                   // If GUI active...
931                                         else
932                                         {
933                                                 if (!keys[SDLK_UP])                             // Disallow opposite directions@same time
934                                                         gram1[0x427D] = 1;                      // Down arrow
935                                         }
936                                 }
937                                 if (keys[SDLK_RETURN])                                                  // Return
938                                 {
939                                         uint8_t retval = UserSelectedSomething();
940
941                                         if (retval == EXIT)
942                                                 running = false;
943
944                                         if (retval == REFRESH)
945                                         {
946                                                 refresh2 = !refresh2;
947                                                 SetRefreshRate(refresh2);
948                                         }
949                                 }
950
951                                 if (keys[SDLK_1])
952                                         gram1[0x427A] = 1;                      // (1)
953
954                                 if (keys[SDLK_2])
955                                         gram1[0x427C] = 1;                      // (2)
956
957                                 if (keys[SDLK_3])
958                                         gram1[0x427E] = 1;                      // (3)
959
960                                 if (keys[SDLK_5])
961                                         gram1[0x4280] = 1;                      // (5)
962
963                                 if (keys[SDLK_q] | keys[29])
964                                         gram1[0x4276] = 1;                      // (Q)  Jump
965
966                                 if (keys[SDLK_w])
967                                         gram1[0x426A] = 1;                      // (W)
968
969                                 if (fire_debounce)
970                                         fire_debounce--;
971
972                                 if (keys[SDLK_e] | keys[56])    // (E) Fire
973                                 {
974                                         if (!fire_debounce)
975                                         {
976                                                 gram1[0x4278] = 1;
977
978                                                 if (gram1[0x3F08] == 0xFF)              // Ugly kludge for debouncing gun
979                                                         fire_debounce = 8;
980                                                 else
981                                                         fire_debounce = 2;
982                                         }
983                                 }
984
985                                 if (keys[SDLK_r])
986                                         gram1[0x426C] = 1;                      // (R)
987
988                                 if (keys[SDLK_t])
989                                         gram1[0x4262] = 1;                      // (T)
990
991                                 if (keys[SDLK_y])
992                                         gram1[0x4260] = 1;                      // (Y)
993
994                                 if (keys[SDLK_F10])
995                                         gram1[0x41A5]++;                        // Coin? (F10)
996
997                                 if (keys[SDLK_z])
998                                         gram1[0x4189]++;                        // ? (Z) credits l dig
999
1000                                 if (keys[SDLK_x])
1001                                         gram1[0x418A]++;                        // ? (X) credits r dig
1002
1003                                 if (keys[SDLK_c])
1004                                         gram1[0x418C]++;                        // ? (C) Start
1005
1006                                 if (keys[SDLK_v])
1007                                         gram1[0x418D]++;                        // ? (V)
1008
1009                                 if (keys[SDLK_F7])
1010                                         SpawnSound(USERSOUND, 0);       // Do user sound (F7)
1011 #endif
1012
1013                 // We can do this here because we're not executing the cores yet.
1014                 cpu1.cpuFlags |= V6809_LINE_IRQ;
1015                 cpu2.cpuFlags |= V6809_LINE_IRQ;
1016                 mcu.cpuFlags |= V63701_LINE_IRQ;
1017 //                                      while (cpu1.clock < 25000)
1018 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1019 // 25600 cycles/frame
1020 // Setting interleave to 25 and below causes the V6809 core to hang...
1021 // 32 gets to the title screen before hanging...
1022 // 40 works, until it doesn't... :-P
1023 // 640 * 40
1024 // 800 * 32
1025 // 20 seems to work...  :-)
1026 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1027 //              for(int i=0; i<640; i++)
1028                 for(int i=0; i<1280; i++)
1029                 {
1030                         // Gay, but what are ya gonna do?
1031                         // There's better ways, such as keeping track of when slave writes to master, etc...
1032 //                      Execute6809(&cpu1, 40);
1033 //                      Execute6809(&cpu2, 40);
1034                         Execute6809(&cpu1, 20);
1035                         Execute6809(&cpu2, 20);
1036
1037                         // MCU runs at 1,536,000 Hz
1038                         // 1536000 / 60 / 640 == 40
1039 //                      Execute63701(&mcu, 40);
1040                         Execute63701(&mcu, 20);
1041                 }
1042
1043                 BlitChar(charROM, gram1);
1044
1045                 // Make sure that the sprite RAM lags by one frame...  :-)
1046                 if (copySprites)
1047                 {
1048                         CopySprites();
1049                         copySprites = false;
1050                 }
1051
1052                 frameTickCount++;
1053
1054                 if (frameTickCount == 3)
1055                         frameTickCount = 0;
1056
1057                 // Speed throttling happens here...
1058                 // Actually, it's 16.66... Need to account for that somehow [DONE]
1059                 while (SDL_GetTicks() - oldTicks < (16 + (frameTickCount == 2 ? 1 : 0)))
1060                         SDL_Delay(1);                           // Release our timeslice...
1061
1062                 oldTicks = SDL_GetTicks();
1063         }
1064
1065         SDL_Quit();
1066         LogDone();
1067
1068         return 1;
1069 }