2 // Thunder: A Rolling Thunder Emulator
5 // (C) 2004, 2014 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
17 #define THUNDER_VERSION "1.1.0"
43 #define ROM1 "rt3-1b.9c"
44 #define ROM2 "rt3-2b.12c"
45 #define ROM3 "rt3-3.12d"
46 #define ROM4 "rt1-4.6b"
47 #define ROM5 "rt1-5.4r"
48 #define ROM6 "rt1-6.4s"
49 #define ROM7 "rt1-7.7r"
50 #define ROM8 "rt1-8.7s"
51 #define ROM9 "rt1-9.12h"
52 #define ROM10 "rt1-10.12k"
53 #define ROM11 "rt1-11.12l"
54 #define ROM12 "rt1-12.12m"
55 #define ROM13 "rt1-13.12p"
56 #define ROM14 "rt1-14.12r"
57 #define ROM15 "rt1-15.12t"
58 #define ROM16 "rt1-16.12u"
59 #define ROM17 "rt1-17.f1"
60 #define ROM18 "rt1-18.h1"
61 #define ROM19 "rt1-19.k1"
62 #define ROM20 "rt1-20.m1"
63 #define ROM21 "rt1-21.f3"
64 #define ROM22 "rt1-22.h3"
65 #define PROM1 "mb7124e.3r"
66 #define PROM2 "mb7116e.3s"
67 #define PROM3 "mb7138h.4v"
68 #define PROM4 "mb7138h.6v"
69 #define PROM5 "mb7112e.6u"
70 #define MCUROM "rt1-mcu.bin"
77 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
79 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000];
80 uint8_t grom3[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
81 uint8_t chr_rom[0x60000]; // Character ROM pointer
82 uint8_t mcuMem[0x10000]; // 64K for MCU
84 V6809REGS cpu1, cpu2; // CPU execution contexts
87 bool trace1 = false; // ditto...
88 bool looking_at_rom = true; // true = R1, false = R2
89 uint32_t banksw1, banksw2; // Bank switch addresses
90 uint16_t game_over_switch; // Game over delay
91 uint16_t dpc; // Debug pc reg...
92 bool show_scr = true; // Whether or not to show background
93 bool enable_cpu = true; // Whether or not to enable CPUs
94 bool irqGoA = true; // IRQ switch for CPU #1
95 bool irqGoB = true; // IRQ switch for CPU #2
97 uint16_t refresh_ = 0; // Crappy global screen stuff...
100 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
101 uint8_t * psg_adrs[16];
103 Byte input1, input2, input3, input4, input5;
105 fstream tr; // Tracelog hook
106 uint16_t pcx; // Where we at?
108 // Function prototypes
109 uint8_t MCUReadMemory(uint16_t address);
110 void MCUWriteMemory(uint16_t address, uint8_t data);
114 // Read a byte from memory
116 uint8_t RdMem(uint16_t addr)
122 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
123 if ((addr >= 0x4000) && (addr <= 0x43FF))
124 return mcuMem[addr - 0x3000];
125 // if ((addr >= 0x4000) && (addr <= 0x41FF))
126 // return MCUReadMemory(addr - 0x3000);
127 // if ((addr >= 0x4200) && (addr <= 0x43FF))
128 // return mcuMem[addr - 0x3000];
131 return data_rom[banksw1 + (addr - 0x6000)]; // Get char data
141 // Write a byte to memory
143 void WrMem(uint16_t address, uint8_t data)
146 extern bool charbase; // Needed for screen. Extern it in it??
148 if (address == 0x4182)
150 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
154 if (((address >= 0x4180) && (address <= 0x4191)) || (address == 0x4380))
155 printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, address);
158 if (address == 0x6000)
160 //printf("Spawning VOICE channel #1: $6000=$%02X, $6200=$%02X\n", b, gram1[0x6200]);
161 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
163 if (address == 0x6400)
165 //printf("Spawning VOICE channel #2: $6400=$%02X, $6600=$%02X\n", b, gram1[0x6600]);
166 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
168 if (address == 0x6800)
169 banksw1 = (uint32_t)data << 13; // Set char data bankswitch base address
170 if (address > 0x4284 && address < 0x42A5 && data)
171 SpawnSound(PSGSOUND, address - 0x4285); // Do PSG sound on chans 2, 3
173 //if ((address >= 0x4284) && (address < 0x42A5))
175 // printf("WrMem: Wrote $%02X to PSG address $%04X...\n", data, address);
177 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
178 if ((address >= 0x4000) && (address <= 0x43FF))
179 mcuMem[address - 0x3000] = data;
180 // if ((address >= 0x4000) && (address <= 0x41FF))
181 // MCUWriteMemory(address - 0x3000, data);
182 // if ((address >= 0x4200) && (address <= 0x43FF))
183 // mcuMem[address - 0x3000] = data;
185 gram1[address] = data;
187 if (address == 0x8800)
188 charbase = false; // Char banksw1
189 if (address == 0x8C00)
190 charbase = true; // Char banksw2
191 if (address == 0x8400) // Frame go strobe? VBlank acknowledge?
193 BlitChar(screen, chr_rom, gram1);
195 // IRQ Ack (may also be frame go...
196 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
199 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", data);
206 // Read a byte from memory (2nd processor)
208 uint8_t RdMemB(uint16_t address)
210 if (address < 0x8000)
212 if (address < 0x2000)
213 return gram1[address + 0x4000];
214 else if ((address >= 0x2000) && (address < 0x6000))
215 return gram1[address - 0x2000];
216 else if (address >= 0x6000)
217 return grom3[banksw2 + (address - 0x6000)];
220 return grom2[address];
225 // Write a byte to memory (2nd processor)
227 void WrMemB(uint16_t address, uint8_t data)
230 extern bool charbase;
232 // Set sprite data bank switch
233 if (address == 0xD803)
234 banksw2 = (uint32_t)(data & 0x03) << 13;
236 if (address < 0x2000)
237 gram1[address + 0x4000] = data;
239 if ((address >= 0x2000) && (address < 0x6000))
240 gram1[address - 0x2000] = data;
242 if (address == 0x8800)
244 // IRQ Ack (may also be frame go...)
245 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
248 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", data);
254 uint8_t MCUReadMemory(uint16_t address)
257 return InternalRegisterRead(address);
258 else if ((address >= 0x1000) && (address <= 0x113F))
259 // else if ((address >= 0x1000) && (address <= 0x11FF))
260 return ReadPSG(address - 0x1000);
261 else if ((address >= 0x2000) && (address <= 0x2001))
263 // Various joystick + buttons; all are active low.
264 else if (address == 0x2020)
266 else if (address == 0x2021)
268 // This is DSW1 & 2. All switch settings are active low.
269 else if (address == 0x2030)
271 else if (address == 0x2031)
274 return mcuMem[address];
278 void MCUWriteMemory(uint16_t address, uint8_t data)
280 static uint8_t ymRegister;
284 InternalRegisterWrite(address, data);
287 else if (((address >= 0x4000) && (address <= 0xBFFF))
288 || (address >= 0xF000))
290 else if ((address >= 0x1000) && (address <= 0x113F))
291 // else if ((address >= 0x1000) && (address <= 0x11FF))
293 WritePSG(address - 0x1000, data);
296 else if (address == 0x2000)
301 else if (address == 0x2001)
303 YMWriteReg(0, ymRegister, data);
307 // RAM is from $0 - $3FFF, $C000 - $EFFF
308 mcuMem[address] = data;
312 uint8_t V63701ReadPort1(void)
314 // printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
319 uint8_t V63701ReadPort2(void)
325 void V63701WritePort1(uint8_t data)
327 // printf("V63701WritePort1: Wrote $%02X...\n", data);
331 void V63701WritePort2(uint8_t data)
333 // printf("V63701WritePort2: Wrote $%02X...\n", data);
338 // Generic Load file into image space
339 // (No error checking performed! Responsibility of caller!)
341 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
345 strcpy(path, "./ROMs/");
346 strcat(path, filename);
347 FILE * file = fopen(path, "rb");
352 fread(&mem[address], 1, length, file);
362 bool ReadColorPROMs(void)
367 extern uint32_t palette[256]; // Screen physical palette
368 extern uint8_t ccolor[256][8]; // Character color PROM values
369 extern uint8_t scolor[128][16]; // Sprite color PROM values
371 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
375 for(int i=0; i<256; i++) // Load char pallete with PROM values
377 for(int j=0; j<8; j++)
380 ccolor[i][j] = (uint8_t)ch;
387 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
391 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
393 for(int j=0; j<16; j++)
396 scolor[i][j] = (uint8_t)ch;
403 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
404 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
406 // If open was successful...
409 // Palette is 12-bit RGB, we stretch it to 24-bit
410 for(int i=0; i<256; i++)
415 uint8_t r = (uint8_t)c1 & 0x0F;
416 uint8_t g = (uint8_t)c1 >> 4;
417 uint8_t b = (uint8_t)c2;
418 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
425 // PROM5 has the following in it (tile address decoder):
426 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
427 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
436 bool UnpackFonts(void)
438 // uint8_t b1, b2, b3;
442 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
443 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
446 return false; // Return if not found...
448 for(long i=0; i<0x40000; i+=64)
450 for(int j=0; j<64; j+=8)
452 f1.get(b1); f1.get(b2); f2.get(b3);
453 b3 ^= 0xFF; // Invert top data...
454 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
455 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
456 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
457 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
458 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
459 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
460 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
461 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
468 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
469 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
471 for(long i=0x40000; i<0x60000; i+=64)
473 for(int j=0; j<64; j+=8)
475 f1.get(b1); f1.get(b2); f2.get(b3);
476 b3 ^= 0xFF; // Invert top data
477 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
478 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
479 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
480 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
481 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
482 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
483 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
484 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
491 return true; // Made it!
496 // Get length of sample from WAV format
498 uint32_t GetWAVLength(fstream & file)
503 file.ignore(16); // Skip header BS
505 for(int i=0; i<2; i++)
507 file.get(ch); len = (int)(uint8_t)ch;
508 file.get(ch); len |= (int)(uint8_t)ch << 8;
509 file.get(ch); len |= (int)(uint8_t)ch << 16;
510 file.get(ch); len |= (int)(uint8_t)ch << 24;
512 // Skip intermediate data
513 file.ignore(len + 4);
516 // & finally get length of data
517 file.get(ch); len = (int)(uint8_t)ch;
518 file.get(ch); len |= (int)(uint8_t)ch << 8;
519 file.get(ch); len |= (int)(uint8_t)ch << 16;
520 file.get(ch); len |= (int)(uint8_t)ch << 24;
528 // Load PSG samples from disk
536 for(int i=0; i<16; i++)
540 psg_adrs[i] = NULL; // Zero out pointer
541 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
543 fp.open(file, ios::binary | ios::in); // Attempt to open it...
547 len = GetWAVLength(fp); // Get WAV data length...
548 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
550 if (psg_adrs[i] != NULL)
552 for(int j=0; j<(signed)len; j++)
555 psg_adrs[i][j] = ch; // & load it in...
559 printf("Found sample file: %s\t[Length: %u]\n", file, len);
573 int main(int argc, char * argv[])
575 InitLog("thunder.log");
577 extern bool disasm; // From 'V6809.CPP'
578 extern bool charbase; // From 'SCREEN.CPP'
582 fstream ff; // Declare fstream without file hooks...
583 bool brk = false, brk2 = false; // Breakpoint set flag
584 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
585 bool running; // CPU running state flag...
586 bool self_test = false; // Self-test switch
587 bool scr_type = false; // false=chars, true=pixels
588 uint16_t debounce = 0; // Key de-bounce counter
589 uint16_t fire_debounce = 0; // Fire button debounce counter
590 uint8_t x; // General placeholder...
591 // bool active = true; // Program running flag
593 SDL_Event event; // SDL "event"
594 extern uint8_t palette[768]; // Screen physical palette
595 uint32_t ticks, oldTicks;
597 cout << endl << "THUNDER v"THUNDER_VERSION" ";
598 cout << "by James Hammons" << endl;
599 cout << "Serial #20149417 / Prerelease" << endl;
600 cout << "© 2003, 2014 Underground Software" << endl << endl;
602 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
605 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
607 gram = gram1; grom = grom1; // Needed only for debugger
609 memset(gram, 0, 0x10000);
610 memset(grom, 0, 0x10000);
611 memset(gram2, 0, 0x10000);
612 memset(grom2, 0, 0x10000);
614 game_over_switch = 0; // Init game over delay
616 cout << "Loading ROMs..." << endl;
617 if (!ReadColorPROMs()) // Load virtual PROMs
618 { cout << "Could not open PROM files!" << endl; return -1; }
620 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
621 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
623 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
624 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
626 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
627 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
629 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
630 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
632 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
633 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
635 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
636 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
638 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
639 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
641 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
642 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
644 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
645 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
647 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
648 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
650 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
651 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
653 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
654 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
656 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
657 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
659 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
660 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
662 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
663 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
665 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
666 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
668 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
669 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
671 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
673 cout << "Could not open font files!" << endl;
677 // Load MCU program + data
678 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000)) // Load MCU ROM
679 { cout << "Could not open file '" << MCUROM << "'!" << endl; return -1; }
681 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000)) // Load 4th ROM
682 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
685 // Load PSG samples if they're there...
688 // Set up V6809 execution contexts
690 memset(&cpu1, 0, sizeof(V6809REGS));
693 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
695 memset(&cpu2, 0, sizeof(V6809REGS));
698 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
700 memset(&mcu, 0, sizeof(V63701REGS));
701 mcu.RdMem = MCUReadMemory;
702 mcu.WrMem = MCUWriteMemory;
703 mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
705 // uint32_t my_clock = 0;
706 running = true; // Set running status...
708 // SetRefreshRate(refresh2); // Tell GUI our refresh rate
710 // Set all inputs to inactive...
711 input1.byte = input2.byte = input3.byte = input4.byte = input5.byte = 0xFF;
715 // This is data that is supposed to come from the MCU... So that's why it hangs
716 gram1[0x4182] = 0xA6; // Temp kludge
717 gram1[0x4184] = 0xA6;
718 gram1[0x4183] = 0x00; // More of the same
719 gram1[0x4185] = 0x00;
721 banksw1 = 0; // Will this work?
723 InitGUI(); // Reset # of coins
725 WriteLog("About to set up screen...\n");
728 oldTicks = SDL_GetTicks();
730 WriteLog("About to set up audio...\n");
733 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
734 RenderScreenBuffer();
737 psg1 = fopen("psgchan1.raw", "wb");
738 psg2 = fopen("psgchan3.raw", "wb");
741 WriteLog("About to enter main loop...\n");
744 HandleGUIDebounce(); // Debounce GUI keys
747 if (game_over_switch)
749 game_over_switch--; // Countdown...
751 if (game_over_switch == 0)
752 gram1[0x4380] = 0; // Kill music!
756 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
757 // $423D - $425B by the MCU
761 //gram1[0x423D] = self_test; // Reset DSW1-1
762 // gram1[0x4268] = 0; // Reset Video test
764 // SDL key handling...
768 while (SDL_PollEvent(&event))
773 if (event.key.keysym.sym == SDLK_ESCAPE)
775 // Do PCX snapshot (F4)
776 else if (event.key.keysym.sym == SDLK_F4)
778 // SpawnSound(USERSOUND, SCAMERA);
783 else if (event.key.keysym.sym == SDLK_5)
785 else if (event.key.keysym.sym == SDLK_1)
787 else if (event.key.keysym.sym == SDLK_RIGHT)
789 else if (event.key.keysym.sym == SDLK_LEFT)
791 else if (event.key.keysym.sym == SDLK_UP)
793 else if (event.key.keysym.sym == SDLK_DOWN)
795 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
797 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
800 else if (event.key.keysym.sym == SDLK_1)
802 else if (event.key.keysym.sym == SDLK_2)
804 else if (event.key.keysym.sym == SDLK_3)
806 else if (event.key.keysym.sym == SDLK_4)
808 else if (event.key.keysym.sym == SDLK_5)
810 else if (event.key.keysym.sym == SDLK_6)
812 else if (event.key.keysym.sym == SDLK_7)
814 else if (event.key.keysym.sym == SDLK_8)
817 else if (event.key.keysym.sym == SDLK_q)
819 else if (event.key.keysym.sym == SDLK_w)
821 else if (event.key.keysym.sym == SDLK_e)
823 else if (event.key.keysym.sym == SDLK_r)
825 else if (event.key.keysym.sym == SDLK_t)
827 else if (event.key.keysym.sym == SDLK_y)
829 else if (event.key.keysym.sym == SDLK_u)
831 else if (event.key.keysym.sym == SDLK_i)
834 else if (event.key.keysym.sym == SDLK_a)
836 else if (event.key.keysym.sym == SDLK_s)
838 else if (event.key.keysym.sym == SDLK_d)
840 else if (event.key.keysym.sym == SDLK_f)
842 else if (event.key.keysym.sym == SDLK_g)
849 if (event.key.keysym.sym == SDLK_5)
851 else if (event.key.keysym.sym == SDLK_1)
853 else if (event.key.keysym.sym == SDLK_RIGHT)
855 else if (event.key.keysym.sym == SDLK_LEFT)
857 else if (event.key.keysym.sym == SDLK_UP)
859 else if (event.key.keysym.sym == SDLK_DOWN)
861 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
863 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
866 if (event.key.keysym.sym == SDLK_1)
868 else if (event.key.keysym.sym == SDLK_2)
870 else if (event.key.keysym.sym == SDLK_3)
872 else if (event.key.keysym.sym == SDLK_4)
874 else if (event.key.keysym.sym == SDLK_5)
876 else if (event.key.keysym.sym == SDLK_6)
878 else if (event.key.keysym.sym == SDLK_7)
880 else if (event.key.keysym.sym == SDLK_8)
883 else if (event.key.keysym.sym == SDLK_q)
885 else if (event.key.keysym.sym == SDLK_w)
887 else if (event.key.keysym.sym == SDLK_e)
889 else if (event.key.keysym.sym == SDLK_r)
891 else if (event.key.keysym.sym == SDLK_t)
893 else if (event.key.keysym.sym == SDLK_y)
895 else if (event.key.keysym.sym == SDLK_u)
897 else if (event.key.keysym.sym == SDLK_i)
900 else if (event.key.keysym.sym == SDLK_a)
902 else if (event.key.keysym.sym == SDLK_s)
904 else if (event.key.keysym.sym == SDLK_d)
906 else if (event.key.keysym.sym == SDLK_f)
908 else if (event.key.keysym.sym == SDLK_g)
917 if (keys[SDLK_ESCAPE])
918 running = false; // ESC to exit...
921 debounce--; // Debounce toggle keys...
926 self_test = !self_test; // Self-test (F1-toggle)
927 debounce = 10; // Key debounce value...
931 gram1[0x4268] = 1; // Video test (F2)
932 debounce = 10; // Key debounce value...
936 scr_type = !scr_type; // Toggle screen (F12)
937 debounce = 10; // Key debounce value...
941 show_scr = !show_scr; // Toggle bkgrnd (F3)
946 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
951 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
952 SetRefreshRate(refresh2); // Inform GUI of refresh
957 debounce = 10; // Key debounce value...
959 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
961 SpawnSound(USERSOUND, SCAMERA);
965 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
975 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
976 if (keys[SDLK_RIGHT]) // Right arrow
979 SelectRight(); // If GUI active...
982 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
983 gram1[0x427F] = 1; // Stick right
989 SelectLeft(); // If GUI active...
992 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
993 gram1[0x4281] = 1; // Left arrow
999 SelectUp(); // If GUI active...
1002 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1003 gram1[0x427B] = 1; // Up arrow
1006 if (keys[SDLK_DOWN])
1009 SelectDown(); // If GUI active...
1012 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1013 gram1[0x427D] = 1; // Down arrow
1016 if (keys[SDLK_RETURN]) // Return
1018 uint8_t retval = UserSelectedSomething();
1023 if (retval == REFRESH)
1025 refresh2 = !refresh2;
1026 SetRefreshRate(refresh2);
1031 gram1[0x427A] = 1; // (1)
1034 gram1[0x427C] = 1; // (2)
1037 gram1[0x427E] = 1; // (3)
1040 gram1[0x4280] = 1; // (5)
1042 if (keys[SDLK_q] | keys[29])
1043 gram1[0x4276] = 1; // (Q) Jump
1046 gram1[0x426A] = 1; // (W)
1051 if (keys[SDLK_e] | keys[56]) // (E) Fire
1057 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1065 gram1[0x426C] = 1; // (R)
1068 gram1[0x4262] = 1; // (T)
1071 gram1[0x4260] = 1; // (Y)
1074 gram1[0x41A5]++; // Coin? (F10)
1077 gram1[0x4189]++; // ? (Z) credits l dig
1080 gram1[0x418A]++; // ? (X) credits r dig
1083 gram1[0x418C]++; // ? (C) Start
1086 gram1[0x418D]++; // ? (V)
1089 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1091 // if (keys[SDLK_F8])
1093 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1094 // charbase = false; // Switch chars out...
1096 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1098 if (keys[SDLK_F11]) // (F11)
1100 Execute6809(&cpu1, 10);
1101 Execute6809(&cpu2, 10);
1104 //F12 is used above, but the values are ignored. So we'll do it here too.
1107 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1108 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1111 if (keys[SDLK_d]) // (D) start disassembly
1115 gram1[0x5606] = 0x00;
1118 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1119 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1123 gram1[0x5FF3] = 0x02;
1124 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1132 // We can do this here because we're not executing the cores yet.
1133 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1134 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1135 mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
1136 // while (cpu1.clock < 25000)
1137 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1138 // 25600 cycles/frame
1139 // Setting interleave to 25 and below causes the V6809 core to hang...
1140 // 32 gets to the title screen before hanging...
1141 // 40 works, until it doesn't... :-P
1144 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1145 for(uint32_t i=0; i<640; i++)
1146 // for(uint32_t i=0; i<1280; i++)
1148 // Gay, but what are ya gonna do?
1149 // There's better ways, such as keeping track of when slave writes to master, etc...
1150 Execute6809(&cpu1, 40);
1151 Execute6809(&cpu2, 40);
1153 // MCU runs at 1,536,000 Hz
1154 // 1536000 / 60 / 640 == 40
1155 Execute63701(&mcu, 40);
1157 } // END: enable_cpu
1159 // Speed throttling happens here...
1160 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1161 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1162 SDL_Delay(1); // Release our timeslice...
1164 oldTicks = SDL_GetTicks();
1165 //cout << "Finished frame..." << endl;
1175 // Deallocate sounds if they were loaded
1176 for(int i=0; i<16; i++)
1178 delete[] psg_adrs[i];
1181 for(int i=0; i<14; i++)
1183 delete[] fm_adrs[i];
1192 Hitachi uC runs at 6.144 MHz
1193 YM2151 runs at 3.579580 MHz
1196 Rolling Thunder Memory map
1197 --------------------------
1198 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1199 map is inferred by program behaviour. The customs also handle internally irq
1202 The main CPU memory map is the same in all games because CUS47 is used by all
1203 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1204 replaced by other chips.
1206 All RAM is shared between main and sub CPU, except for sound RAM which is
1207 shared between main and sound CPU; the portion of object RAM that is overlapped
1208 by sound RAM is used exclusively by the sub CPU.
1212 Address Dir Data Name Description
1213 ------------------- --- -------- --------- -----------------------
1214 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1215 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1216 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1217 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1218 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1219 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1220 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1221 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1222 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1223 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1224 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1225 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1226 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1227 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1228 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1229 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1230 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1231 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1232 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1234 [1] Note that this is partially overlapped by sound RAM
1235 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1240 Address Dir Data Name Description
1241 ------------------- --- -------- --------- -----------------------
1242 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1243 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1244 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1245 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1246 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1247 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1248 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1249 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1250 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1251 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1252 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1253 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1254 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1255 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1257 [1] Only used by Rolling Thunder
1262 Address Dir Data Name Description
1263 ------------------- --- -------- --------- -----------------------
1264 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1265 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1266 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1267 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1268 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1269 0010 0--- --01 ---- n.c.
1270 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1271 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1272 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1273 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1274 1011 0--- ---- ---- W unknown (CUS41)
1275 1011 1--- ---- ---- W unknown (CUS41)
1276 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1281 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1282 in rthunder. The two 6809 in this game synchronize using a semaphore at
1283 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1284 and then increments 5606. While it does its quick things (which require
1285 about 40 clock cycles) it expects CPU2 to clear 5607.
1286 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1287 mode. I haven't investigated on the cause.
1289 - There are two watchdogs, one per CPU (or maybe three). Handling them
1290 separately is necessary to allow entering service mode without manually
1291 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1294 - The sprite hardware buffers spriteram: the program writes the sprite list to
1295 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1296 sprite RAM to signal the chip that the list is complete. The chip will copy
1297 the list from 4-9 to 10-15 and use it from there. This has not been verified
1298 on the real hardware, but it is the most logical way of doing it.
1299 Emulating this behaviour and not using an external buffer is important in
1300 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1301 is not written to. If we buffered spriteram to an external buffer, this would
1302 cause dangling sprites because the buffer would not be updated.
1304 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1305 entering a door. The *closed* door is made of tiles, but the *moving* door is
1306 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1307 there is one frame where neither the tile-based closed door nor the
1308 sprite-based moving door is shown, so it flickers. This behavior has been
1309 confirmed on a real PCB.
1313 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1314 but they don't seem to work as expected. During the first few frames they are
1315 written out of order and hooking them up in the usual way causes the MCU to
1316 stop receiving interrupts.