2 // Thunder: A Rolling Thunder Emulator
5 // (C) 2004, 2023 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
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
19 #define THUNDER_VERSION "1.2.2"
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"
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
72 // CPU execution contexts
76 // Bank switch addresses
77 uint32_t banksw1, banksw2;
80 Byte input1, input2, input3, input4, input5;
82 // Miscellaneous stuff
83 bool copySprites = false;
84 const uint8_t stateHeader[20] = "THUNDERSAVESTATE1.0";
86 // Function prototypes
87 uint8_t MCUReadMemory(uint16_t address);
88 void MCUWriteMemory(uint16_t address, uint8_t data);
91 // Read a byte from memory
93 uint8_t MainReadMemory(uint16_t addr)
99 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
100 if ((addr >= 0x4000) && (addr <= 0x43FF))
101 return mcuMem[addr - 0x3000];
104 return data_rom[banksw1 + (addr - 0x6000)]; // Get char data
113 // Write a byte to memory
115 void MainWriteMemory(uint16_t address, uint8_t data)
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
124 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
125 if ((address >= 0x4000) && (address <= 0x43FF))
126 mcuMem[address - 0x3000] = data;
128 gram1[address] = data;
130 if (address == 0x5FF2)
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
141 // Read a byte from memory (2nd processor)
143 uint8_t SubReadMemory(uint16_t address)
145 if (address < 0x8000)
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)];
155 return grom2[address];
159 // Write a byte to memory (2nd processor)
161 void SubWriteMemory(uint16_t address, uint8_t data)
163 // Set sprite data bank switch
164 if (address == 0xD803)
165 banksw2 = (uint32_t)(data & 0x03) << 13;
167 if (address < 0x2000)
168 gram1[address + 0x4000] = data;
170 if ((address >= 0x2000) && (address < 0x6000))
171 gram1[address - 0x2000] = data;
173 if (address == 0x1FF2)
176 if (address == 0x8800)
177 ClearLineOfCurrentV6809(V6809_LINE_IRQ); // IRQ Ack
180 uint8_t MCUReadMemory(uint16_t address)
183 return InternalRegisterRead(address);
184 else if ((address >= 0x1000) && (address <= 0x113F))
185 return ReadPSG(address - 0x1000);
186 else if ((address >= 0x2000) && (address <= 0x2001))
188 // Various joystick + buttons; all are active low.
189 else if (address == 0x2020)
191 else if (address == 0x2021)
193 // This is DSW1 & 2. All switch settings are active low.
194 else if (address == 0x2030)
196 else if (address == 0x2031)
199 return mcuMem[address];
202 void MCUWriteMemory(uint16_t address, uint8_t data)
204 static uint8_t ymRegister;
208 InternalRegisterWrite(address, data);
211 else if (((address >= 0x4000) && (address <= 0xBFFF))
212 || (address >= 0xF000))
214 else if ((address >= 0x1000) && (address <= 0x113F))
216 WritePSG(address - 0x1000, data);
219 else if (address == 0x2000)
224 else if (address == 0x2001)
226 YMWriteReg(0, ymRegister, data);
230 // RAM is from $0 - $3FFF, $C000 - $EFFF
231 mcuMem[address] = data;
234 uint8_t V63701ReadPort1(void)
236 // printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
240 uint8_t V63701ReadPort2(void)
245 void V63701WritePort1(uint8_t data)
247 // printf("V63701WritePort1: Wrote $%02X...\n", data);
250 void V63701WritePort2(uint8_t data)
252 // printf("V63701WritePort2: Wrote $%02X...\n", data);
256 // Generic Load file into image space
257 // (No error checking performed! Responsibility of caller!)
259 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
263 strcpy(path, "./ROMs/");
264 strcat(path, filename);
265 FILE * file = fopen(path, "rb");
269 printf("Could not open file \"%s\"!\n", filename);
273 size_t ignored = fread(&mem[address], 1, length, file);
282 bool ReadColorPROMs(void)
284 FILE * file1 = fopen("./ROMs/" PROM3, "rb");
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);
295 file1 = fopen("./ROMs/" PROM4, "rb");
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);
306 file1 = fopen("./ROMs/" PROM1, "rb");
307 FILE * file2 = fopen("./ROMs/" PROM2, "rb");
309 // If open was successful...
312 // Palette is 12-bit RGB, we stretch it to 24-bit
313 for(int i=0; i<256; i++)
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;
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
333 printf("Could not open PROM files!\n");
343 bool UnpackFonts(void)
346 FILE * file1 = fopen("./ROMs/" ROM7, "rb");
347 FILE * file2 = fopen("./ROMs/" ROM8, "rb");
349 if (!file1 || !file2)
351 printf("Could not open either " ROM7 " or " ROM8 "!\n");
355 for(long i=0; i<0x40000; i+=64)
357 for(int j=0; j<64; j+=8)
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);
376 file1 = fopen("./ROMs/" ROM5, "rb");
377 file2 = fopen("./ROMs/" ROM6, "rb");
379 if (!file1 || !file2)
381 printf("Could not open either " ROM5 " or " ROM6 "!\n");
385 for(long i=0x40000; i<0x60000; i+=64)
387 for(int j=0; j<64; j+=8)
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);
409 void SaveThunderState(const char * filename)
411 WriteLog("Main: Saving Thunder state...\n");
412 FILE * file = fopen(filename, "wb");
416 WriteLog("Could not open file \"%s\" for writing!\n", filename);
421 size_t ignored = fwrite(stateHeader, 1, 19, file);
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);
428 // Write out main memory
429 ignored = fwrite(gram1, 1, 0x10000, file);
430 ignored = fwrite(mcuMem, 1, 0x10000, file);
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);
448 bool LoadThunderState(const char * filename)
450 WriteLog("Main: Loading Thunder state...\n");
451 FILE * file = fopen(filename, "rb");
455 WriteLog("Could not open file \"%s\" for reading!\n", filename);
460 size_t ignored = fread(buffer, 1, 19, file);
463 if (memcmp(buffer, stateHeader, 19) != 0)
466 WriteLog("File \"%s\" is not a valid Thunder save state file!\n", filename);
471 ignored = fread(&cpu1, 1, sizeof(cpu1), file);
472 ignored = fread(&cpu2, 1, sizeof(cpu2), file);
473 ignored = fread(&mcu, 1, sizeof(mcu), file);
476 ignored = fread(gram1, 1, 0x10000, file);
477 ignored = fread(mcuMem, 1, 0x10000, file);
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);
494 // Make sure things are in a sane state before execution :-P
495 cpu1.RdMem = MainReadMemory;
496 cpu1.WrMem = MainWriteMemory;
498 cpu2.RdMem = SubReadMemory;
499 cpu2.WrMem = SubWriteMemory;
501 mcu.RdMem = MCUReadMemory;
502 mcu.WrMem = MCUWriteMemory;
510 int main(int argc, char * argv[])
512 InitLog("thunder.log");
514 bool running; // CPU running state flag...
515 SDL_Event event; // SDL "event"
516 uint32_t ticks, oldTicks;
517 uint8_t frameTickCount = 0;
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");
524 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
526 printf("Loading ROMs...\n");
528 if (!ReadColorPROMs())
531 // Load $8000-$FFFF 1st ROM
532 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000))
535 // Load $8000-$FFFF 2nd ROM
536 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000))
539 // Load 3rd ROM into its own space
540 if (!LoadImg(ROM3, grom3, 0, 0x8000))
543 if (!LoadImg(ROM17, data_rom, 0, 0x10000))
546 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000))
549 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000))
552 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000))
555 if (!LoadImg(ROM9, spr_rom, 0, 0x10000))
558 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000))
561 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000))
564 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000))
567 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000))
570 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000))
573 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000))
576 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000))
579 if (!LoadImg(ROM21, voice_rom, 0, 0x10000))
582 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000))
585 // Load 5, 6, 7, 8th ROMs
589 // Load MCU program + data
590 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000))
593 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000))
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;
602 memset(&cpu2, 0, sizeof(V6809REGS));
603 cpu2.RdMem = SubReadMemory;
604 cpu2.WrMem = SubWriteMemory;
605 cpu2.cpuFlags |= V6809_LINE_RESET;
607 memset(&mcu, 0, sizeof(V63701REGS));
608 mcu.RdMem = MCUReadMemory;
609 mcu.WrMem = MCUWriteMemory;
610 mcu.cpuFlags |= V63701_LINE_RESET;
614 // Set all inputs to inactive...
615 input1.byte = input2.byte = input3.byte = 0xFF;
618 InitGUI(); // Reset # of coins
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
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
639 WriteLog("About to set up screen...\n");
642 oldTicks = SDL_GetTicks();
644 WriteLog("About to set up audio...\n");
647 WriteLog("About to enter main loop...\n");
650 // HandleGUIDebounce(); // Debounce GUI keys
652 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
653 // $423D - $425B by the MCU
655 // SDL key handling...
659 while (SDL_PollEvent(&event))
664 if (event.key.keysym.sym == SDLK_ESCAPE)
666 // Do PCX snapshot (F4)
667 else if (event.key.keysym.sym == SDLK_F4)
669 // SpawnSound(USERSOUND, SCAMERA);
673 else if (event.key.keysym.sym == SDLK_5)
675 else if (event.key.keysym.sym == SDLK_1)
677 else if (event.key.keysym.sym == SDLK_2)
679 else if (event.key.keysym.sym == SDLK_c) // Left
681 else if (event.key.keysym.sym == SDLK_z) // Right
683 else if (event.key.keysym.sym == SDLK_s) // Up
685 else if (event.key.keysym.sym == SDLK_x) // Down
687 else if (event.key.keysym.sym == SDLK_k) // Jump
689 else if (event.key.keysym.sym == SDLK_l) // Fire
692 else if (event.key.keysym.sym == SDLK_F1) // Service mode
693 input5.bit.b7 = !input5.bit.b7;
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;
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");
719 else if (event.key.keysym.sym == SDLK_1)
721 else if (event.key.keysym.sym == SDLK_2)
723 else if (event.key.keysym.sym == SDLK_3)
725 else if (event.key.keysym.sym == SDLK_4)
727 else if (event.key.keysym.sym == SDLK_5)
729 else if (event.key.keysym.sym == SDLK_6)
731 else if (event.key.keysym.sym == SDLK_7)
733 else if (event.key.keysym.sym == SDLK_8)
736 else if (event.key.keysym.sym == SDLK_q)
738 else if (event.key.keysym.sym == SDLK_w)
740 else if (event.key.keysym.sym == SDLK_e)
742 else if (event.key.keysym.sym == SDLK_r)
744 else if (event.key.keysym.sym == SDLK_t)
746 else if (event.key.keysym.sym == SDLK_y)
748 else if (event.key.keysym.sym == SDLK_u)
750 else if (event.key.keysym.sym == SDLK_i)
753 else if (event.key.keysym.sym == SDLK_a)
755 else if (event.key.keysym.sym == SDLK_s)
757 else if (event.key.keysym.sym == SDLK_d)
759 else if (event.key.keysym.sym == SDLK_f)
761 else if (event.key.keysym.sym == SDLK_g)
768 if (event.key.keysym.sym == SDLK_5)
770 else if (event.key.keysym.sym == SDLK_1)
772 else if (event.key.keysym.sym == SDLK_2)
774 else if (event.key.keysym.sym == SDLK_c)
776 else if (event.key.keysym.sym == SDLK_z)
778 else if (event.key.keysym.sym == SDLK_s)
780 else if (event.key.keysym.sym == SDLK_x)
782 else if (event.key.keysym.sym == SDLK_k) // (Q) Jump
784 else if (event.key.keysym.sym == SDLK_l) // (E) Fire
787 if (event.key.keysym.sym == SDLK_1)
789 else if (event.key.keysym.sym == SDLK_2)
791 else if (event.key.keysym.sym == SDLK_3)
793 else if (event.key.keysym.sym == SDLK_4)
795 else if (event.key.keysym.sym == SDLK_5)
797 else if (event.key.keysym.sym == SDLK_6)
799 else if (event.key.keysym.sym == SDLK_7)
801 else if (event.key.keysym.sym == SDLK_8)
804 else if (event.key.keysym.sym == SDLK_q)
806 else if (event.key.keysym.sym == SDLK_w)
808 else if (event.key.keysym.sym == SDLK_e)
810 else if (event.key.keysym.sym == SDLK_r)
812 else if (event.key.keysym.sym == SDLK_t)
814 else if (event.key.keysym.sym == SDLK_y)
816 else if (event.key.keysym.sym == SDLK_u)
818 else if (event.key.keysym.sym == SDLK_i)
821 else if (event.key.keysym.sym == SDLK_a)
823 else if (event.key.keysym.sym == SDLK_s)
825 else if (event.key.keysym.sym == SDLK_d)
827 else if (event.key.keysym.sym == SDLK_f)
829 else if (event.key.keysym.sym == SDLK_g)
838 if (keys[SDLK_ESCAPE])
839 running = false; // ESC to exit...
842 debounce--; // Debounce toggle keys...
847 self_test = !self_test; // Self-test (F1-toggle)
848 debounce = 10; // Key debounce value...
852 gram1[0x4268] = 1; // Video test (F2)
853 debounce = 10; // Key debounce value...
857 scr_type = !scr_type; // Toggle screen (F12)
858 debounce = 10; // Key debounce value...
862 show_scr = !show_scr; // Toggle bkgrnd (F3)
867 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
872 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
873 SetRefreshRate(refresh2); // Inform GUI of refresh
878 debounce = 10; // Key debounce value...
880 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
882 SpawnSound(USERSOUND, SCAMERA);
886 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
896 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
897 if (keys[SDLK_RIGHT]) // Right arrow
900 SelectRight(); // If GUI active...
903 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
904 gram1[0x427F] = 1; // Stick right
910 SelectLeft(); // If GUI active...
913 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
914 gram1[0x4281] = 1; // Left arrow
920 SelectUp(); // If GUI active...
923 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
924 gram1[0x427B] = 1; // Up arrow
930 SelectDown(); // If GUI active...
933 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
934 gram1[0x427D] = 1; // Down arrow
937 if (keys[SDLK_RETURN]) // Return
939 uint8_t retval = UserSelectedSomething();
944 if (retval == REFRESH)
946 refresh2 = !refresh2;
947 SetRefreshRate(refresh2);
952 gram1[0x427A] = 1; // (1)
955 gram1[0x427C] = 1; // (2)
958 gram1[0x427E] = 1; // (3)
961 gram1[0x4280] = 1; // (5)
963 if (keys[SDLK_q] | keys[29])
964 gram1[0x4276] = 1; // (Q) Jump
967 gram1[0x426A] = 1; // (W)
972 if (keys[SDLK_e] | keys[56]) // (E) Fire
978 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
986 gram1[0x426C] = 1; // (R)
989 gram1[0x4262] = 1; // (T)
992 gram1[0x4260] = 1; // (Y)
995 gram1[0x41A5]++; // Coin? (F10)
998 gram1[0x4189]++; // ? (Z) credits l dig
1001 gram1[0x418A]++; // ? (X) credits r dig
1004 gram1[0x418C]++; // ? (C) Start
1007 gram1[0x418D]++; // ? (V)
1010 SpawnSound(USERSOUND, 0); // Do user sound (F7)
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
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++)
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);
1037 // MCU runs at 1,536,000 Hz
1038 // 1536000 / 60 / 640 == 40
1039 // Execute63701(&mcu, 40);
1040 Execute63701(&mcu, 20);
1043 BlitChar(charROM, gram1);
1045 // Make sure that the sprite RAM lags by one frame... :-)
1049 copySprites = false;
1054 if (frameTickCount == 3)
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...
1062 oldTicks = SDL_GetTicks();