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.2.0"
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"
68 uint8_t gram1[0x10000], grom1[0x10000], grom2[0x10000];
69 uint8_t grom3[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
70 uint8_t charROM[0x60000]; // Character ROM
71 uint8_t mcuMem[0x10000]; // 64K for MCU
73 // CPU execution contexts
77 // Bank switch addresses
78 uint32_t banksw1, banksw2;
81 Byte input1, input2, input3, input4, input5;
83 // Function prototypes
84 uint8_t MCUReadMemory(uint16_t address);
85 void MCUWriteMemory(uint16_t address, uint8_t data);
89 // Read a byte from memory
91 uint8_t MainReadMemory(uint16_t addr)
97 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
98 if ((addr >= 0x4000) && (addr <= 0x43FF))
99 return mcuMem[addr - 0x3000];
102 return data_rom[banksw1 + (addr - 0x6000)]; // Get char data
112 // Write a byte to memory
114 void MainWriteMemory(uint16_t address, uint8_t data)
118 if (address == 0x6000)
119 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
120 if (address == 0x6400)
121 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
122 if (address == 0x6800)
123 banksw1 = (uint32_t)data << 13; // Set char data bankswitch base address
125 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
126 if ((address >= 0x4000) && (address <= 0x43FF))
127 mcuMem[address - 0x3000] = data;
129 gram1[address] = data;
131 if (address == 0x5FF2)
133 if (address == 0x8800)
134 charBankSwitch = false; // Char banksw1
135 if (address == 0x8C00)
136 charBankSwitch = true; // Char banksw2
137 if (address == 0x8400) // Frame go strobe? VBlank acknowledge?
139 // BlitChar(charROM, gram1);
141 // IRQ Ack (may also be frame go...)
142 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
145 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", data);
152 // Read a byte from memory (2nd processor)
154 uint8_t SubReadMemory(uint16_t address)
156 if (address < 0x8000)
158 if (address < 0x2000)
159 return gram1[address + 0x4000];
160 else if ((address >= 0x2000) && (address < 0x6000))
161 return gram1[address - 0x2000];
162 else if (address >= 0x6000)
163 return grom3[banksw2 + (address - 0x6000)];
166 return grom2[address];
171 // Write a byte to memory (2nd processor)
173 void SubWriteMemory(uint16_t address, uint8_t data)
177 // Set sprite data bank switch
178 if (address == 0xD803)
179 banksw2 = (uint32_t)(data & 0x03) << 13;
181 if (address < 0x2000)
182 gram1[address + 0x4000] = data;
184 if ((address >= 0x2000) && (address < 0x6000))
185 gram1[address - 0x2000] = data;
187 if (address == 0x1FF2)
190 if (address == 0x8800)
192 // IRQ Ack (may also be frame go...)
193 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
196 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", data);
202 uint8_t MCUReadMemory(uint16_t address)
205 return InternalRegisterRead(address);
206 else if ((address >= 0x1000) && (address <= 0x113F))
207 return ReadPSG(address - 0x1000);
208 else if ((address >= 0x2000) && (address <= 0x2001))
210 // Various joystick + buttons; all are active low.
211 else if (address == 0x2020)
213 else if (address == 0x2021)
215 // This is DSW1 & 2. All switch settings are active low.
216 else if (address == 0x2030)
218 else if (address == 0x2031)
221 return mcuMem[address];
225 void MCUWriteMemory(uint16_t address, uint8_t data)
227 static uint8_t ymRegister;
231 InternalRegisterWrite(address, data);
234 else if (((address >= 0x4000) && (address <= 0xBFFF))
235 || (address >= 0xF000))
237 else if ((address >= 0x1000) && (address <= 0x113F))
239 WritePSG(address - 0x1000, data);
242 else if (address == 0x2000)
247 else if (address == 0x2001)
249 YMWriteReg(0, ymRegister, data);
253 // RAM is from $0 - $3FFF, $C000 - $EFFF
254 mcuMem[address] = data;
258 uint8_t V63701ReadPort1(void)
260 // printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
265 uint8_t V63701ReadPort2(void)
271 void V63701WritePort1(uint8_t data)
273 // printf("V63701WritePort1: Wrote $%02X...\n", data);
277 void V63701WritePort2(uint8_t data)
279 // printf("V63701WritePort2: Wrote $%02X...\n", data);
284 // Generic Load file into image space
285 // (No error checking performed! Responsibility of caller!)
287 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
291 strcpy(path, "./ROMs/");
292 strcat(path, filename);
293 FILE * file = fopen(path, "rb");
297 printf("Could not open file \"%s\"!\n", filename);
301 fread(&mem[address], 1, length, file);
311 bool ReadColorPROMs(void)
313 FILE * file1 = fopen("./ROMs/"PROM3, "rb");
317 for(int i=0; i<256; i++) // Load char pallete with PROM values
318 for(int j=0; j<8; j++)
319 ccolor[i][j] = (uint8_t)fgetc(file1);
324 file1 = fopen("./ROMs/"PROM4, "rb");
328 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
329 for(int j=0; j<16; j++)
330 scolor[i][j] = (uint8_t)fgetc(file1);
335 file1 = fopen("./ROMs/"PROM1, "rb");
336 FILE * file2 = fopen("./ROMs/"PROM2, "rb");
338 // If open was successful...
341 // Palette is 12-bit RGB, we stretch it to 24-bit
342 for(int i=0; i<256; i++)
344 uint8_t c1 = fgetc(file1);
345 uint8_t c2 = fgetc(file2);
346 uint8_t r = (uint8_t)c1 & 0x0F;
347 uint8_t g = (uint8_t)c1 >> 4;
348 uint8_t b = (uint8_t)c2;
349 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
356 // PROM5 has the following in it (tile address decoder):
357 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
358 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
362 printf("Could not open PROM files!\n");
373 bool UnpackFonts(void)
376 FILE * file1 = fopen("./ROMs/"ROM7, "rb");
377 FILE * file2 = fopen("./ROMs/"ROM8, "rb");
379 if (!file1 || !file2)
381 printf("Could not open either "ROM7" or "ROM8"!\n");
385 for(long i=0; i<0x40000; 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);
406 file1 = fopen("./ROMs/"ROM5, "rb");
407 file2 = fopen("./ROMs/"ROM6, "rb");
409 if (!file1 || !file2)
411 printf("Could not open either "ROM5" or "ROM6"!\n");
415 for(long i=0x40000; i<0x60000; i+=64)
417 for(int j=0; j<64; j+=8)
419 uint8_t b1 = (uint8_t)fgetc(file1);
420 uint8_t b2 = (uint8_t)fgetc(file1);
421 uint8_t b3 = (uint8_t)fgetc(file2) ^ 0xFF;
422 charROM[i + j + 0] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
423 charROM[i + j + 1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
424 charROM[i + j + 2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
425 charROM[i + j + 3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
426 charROM[i + j + 4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
427 charROM[i + j + 5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
428 charROM[i + j + 6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
429 charROM[i + j + 7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
443 int main(int argc, char * argv[])
445 InitLog("thunder.log");
447 extern bool disasm; // From 'V6809.CPP'
449 bool running; // CPU running state flag...
450 SDL_Event event; // SDL "event"
451 uint32_t ticks, oldTicks;
452 uint8_t frameTickCount = 0;
454 printf("THUNDER v"THUNDER_VERSION" by James Hammons\n");
455 printf("Serial #20149417 / Prerelease\n");
456 printf("© 2003, 2014 Underground Software\n\n");
457 printf("This emulator is free software. If you paid for it you were RIPPED OFF\n\n");
459 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
461 printf("Loading ROMs...\n");
463 if (!ReadColorPROMs())
466 // Load $8000-$FFFF 1st ROM
467 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000))
470 // Load $8000-$FFFF 2nd ROM
471 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000))
474 // Load 3rd ROM into its own space
475 if (!LoadImg(ROM3, grom3, 0, 0x8000))
478 if (!LoadImg(ROM17, data_rom, 0, 0x10000))
481 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000))
484 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000))
487 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000))
490 if (!LoadImg(ROM9, spr_rom, 0, 0x10000))
493 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000))
496 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000))
499 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000))
502 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000))
505 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000))
508 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000))
511 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000))
514 if (!LoadImg(ROM21, voice_rom, 0, 0x10000))
517 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000))
520 // Load 5, 6, 7, 8th ROMs
524 // Load MCU program + data
525 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000))
528 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000))
531 // Set up V6809, V63701 execution contexts
532 memset(&cpu1, 0, sizeof(V6809REGS));
533 cpu1.RdMem = MainReadMemory;
534 cpu1.WrMem = MainWriteMemory;
535 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
537 memset(&cpu2, 0, sizeof(V6809REGS));
538 cpu2.RdMem = SubReadMemory;
539 cpu2.WrMem = SubWriteMemory;
540 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
542 memset(&mcu, 0, sizeof(V63701REGS));
543 mcu.RdMem = MCUReadMemory;
544 mcu.WrMem = MCUWriteMemory;
545 mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
549 // Set all inputs to inactive...
550 input1.byte = input2.byte = input3.byte = 0xFF;
553 InitGUI(); // Reset # of coins
555 // Set up DIP switches...
556 input4.bit.b0 = 1; // DSW B-0: Contiues (1 = 6, 0 = 3)
557 input4.bit.b1 = 1; // DSW B-2: ???
558 input4.bit.b2 = 1; // DSW B-4: Difficulty (1 = normal, 0 = easy)
559 input4.bit.b3 = 1; // DSW B-6: Bonus lives (70K/200K = 1, 100K/300K = 0)
560 input4.bit.b4 = 1; // DSW A-0: Coin #2 credit
561 input4.bit.b5 = 1; // DSW A-2: Freeze mode (0 = freeze)
562 input4.bit.b6 = 1; // DSW A-4: Attract mode sound (1 = on)
563 input4.bit.b7 = 1; // DSW A-6: Coin #1 credit
565 input5.bit.b0 = 1; // DSW B-1: Cabinet type (1 = A, 0 = B)
566 input5.bit.b1 = 0; // DSW B-3: Level select (0 = on)
567 input5.bit.b2 = 0; // DSW B-5: Time (1 = 120, 0 = 150)
568 input5.bit.b3 = 0; // DSW B-7: Lives (1 = 3, 0 = 5)
569 input5.bit.b4 = 1; // DSW A-1: Coin #2 credit
570 input5.bit.b5 = 1; // DSW A-3: Invulnerability (0 = on)
571 input5.bit.b6 = 1; // DSW A-5: Coin #1 credit
572 input5.bit.b7 = 1; // DSW A-7: Service mode
574 WriteLog("About to set up screen...\n");
577 oldTicks = SDL_GetTicks();
579 WriteLog("About to set up audio...\n");
582 //memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
583 //RenderScreenBuffer();
585 WriteLog("About to enter main loop...\n");
588 // HandleGUIDebounce(); // Debounce GUI keys
590 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
591 // $423D - $425B by the MCU
595 //gram1[0x423D] = self_test; // Reset DSW1-1
596 // gram1[0x4268] = 0; // Reset Video test
598 // SDL key handling...
602 while (SDL_PollEvent(&event))
607 if (event.key.keysym.sym == SDLK_ESCAPE)
609 // Do PCX snapshot (F4)
610 else if (event.key.keysym.sym == SDLK_F4)
612 // SpawnSound(USERSOUND, SCAMERA);
616 else if (event.key.keysym.sym == SDLK_5)
618 else if (event.key.keysym.sym == SDLK_1)
620 else if (event.key.keysym.sym == SDLK_2)
622 else if (event.key.keysym.sym == SDLK_c) // Left
624 else if (event.key.keysym.sym == SDLK_z) // Right
626 else if (event.key.keysym.sym == SDLK_s) // Up
628 else if (event.key.keysym.sym == SDLK_x) // Down
630 else if (event.key.keysym.sym == SDLK_k) // Jump
632 else if (event.key.keysym.sym == SDLK_l) // Fire
635 else if (event.key.keysym.sym == SDLK_F1) // Service mode
636 input5.bit.b7 = !input5.bit.b7;
638 else if (event.key.keysym.sym == SDLK_q)
639 input4.bit.b7 = !input4.bit.b7;
640 else if (event.key.keysym.sym == SDLK_w)
641 input5.bit.b6 = !input5.bit.b6;
642 else if (event.key.keysym.sym == SDLK_e)
643 input5.bit.b5 = !input5.bit.b5;
644 else if (event.key.keysym.sym == SDLK_r)
645 input5.bit.b4 = !input5.bit.b4;
646 else if (event.key.keysym.sym == SDLK_t)
647 input5.bit.b3 = !input5.bit.b3;
648 else if (event.key.keysym.sym == SDLK_y)
649 input5.bit.b2 = !input5.bit.b2;
650 else if (event.key.keysym.sym == SDLK_u)
651 input5.bit.b1 = !input5.bit.b1;
652 else if (event.key.keysym.sym == SDLK_i)
653 input5.bit.b0 = !input5.bit.b0;
656 else if (event.key.keysym.sym == SDLK_1)
658 else if (event.key.keysym.sym == SDLK_2)
660 else if (event.key.keysym.sym == SDLK_3)
662 else if (event.key.keysym.sym == SDLK_4)
664 else if (event.key.keysym.sym == SDLK_5)
666 else if (event.key.keysym.sym == SDLK_6)
668 else if (event.key.keysym.sym == SDLK_7)
670 else if (event.key.keysym.sym == SDLK_8)
673 else if (event.key.keysym.sym == SDLK_q)
675 else if (event.key.keysym.sym == SDLK_w)
677 else if (event.key.keysym.sym == SDLK_e)
679 else if (event.key.keysym.sym == SDLK_r)
681 else if (event.key.keysym.sym == SDLK_t)
683 else if (event.key.keysym.sym == SDLK_y)
685 else if (event.key.keysym.sym == SDLK_u)
687 else if (event.key.keysym.sym == SDLK_i)
690 else if (event.key.keysym.sym == SDLK_a)
692 else if (event.key.keysym.sym == SDLK_s)
694 else if (event.key.keysym.sym == SDLK_d)
696 else if (event.key.keysym.sym == SDLK_f)
698 else if (event.key.keysym.sym == SDLK_g)
705 if (event.key.keysym.sym == SDLK_5)
707 else if (event.key.keysym.sym == SDLK_1)
709 else if (event.key.keysym.sym == SDLK_2)
711 else if (event.key.keysym.sym == SDLK_c)
713 else if (event.key.keysym.sym == SDLK_z)
715 else if (event.key.keysym.sym == SDLK_s)
717 else if (event.key.keysym.sym == SDLK_x)
719 else if (event.key.keysym.sym == SDLK_k) // (Q) Jump
721 else if (event.key.keysym.sym == SDLK_l) // (E) Fire
724 if (event.key.keysym.sym == SDLK_1)
726 else if (event.key.keysym.sym == SDLK_2)
728 else if (event.key.keysym.sym == SDLK_3)
730 else if (event.key.keysym.sym == SDLK_4)
732 else if (event.key.keysym.sym == SDLK_5)
734 else if (event.key.keysym.sym == SDLK_6)
736 else if (event.key.keysym.sym == SDLK_7)
738 else if (event.key.keysym.sym == SDLK_8)
741 else if (event.key.keysym.sym == SDLK_q)
743 else if (event.key.keysym.sym == SDLK_w)
745 else if (event.key.keysym.sym == SDLK_e)
747 else if (event.key.keysym.sym == SDLK_r)
749 else if (event.key.keysym.sym == SDLK_t)
751 else if (event.key.keysym.sym == SDLK_y)
753 else if (event.key.keysym.sym == SDLK_u)
755 else if (event.key.keysym.sym == SDLK_i)
758 else if (event.key.keysym.sym == SDLK_a)
760 else if (event.key.keysym.sym == SDLK_s)
762 else if (event.key.keysym.sym == SDLK_d)
764 else if (event.key.keysym.sym == SDLK_f)
766 else if (event.key.keysym.sym == SDLK_g)
775 if (keys[SDLK_ESCAPE])
776 running = false; // ESC to exit...
779 debounce--; // Debounce toggle keys...
784 self_test = !self_test; // Self-test (F1-toggle)
785 debounce = 10; // Key debounce value...
789 gram1[0x4268] = 1; // Video test (F2)
790 debounce = 10; // Key debounce value...
794 scr_type = !scr_type; // Toggle screen (F12)
795 debounce = 10; // Key debounce value...
799 show_scr = !show_scr; // Toggle bkgrnd (F3)
804 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
809 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
810 SetRefreshRate(refresh2); // Inform GUI of refresh
815 debounce = 10; // Key debounce value...
817 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
819 SpawnSound(USERSOUND, SCAMERA);
823 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
833 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
834 if (keys[SDLK_RIGHT]) // Right arrow
837 SelectRight(); // If GUI active...
840 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
841 gram1[0x427F] = 1; // Stick right
847 SelectLeft(); // If GUI active...
850 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
851 gram1[0x4281] = 1; // Left arrow
857 SelectUp(); // If GUI active...
860 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
861 gram1[0x427B] = 1; // Up arrow
867 SelectDown(); // If GUI active...
870 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
871 gram1[0x427D] = 1; // Down arrow
874 if (keys[SDLK_RETURN]) // Return
876 uint8_t retval = UserSelectedSomething();
881 if (retval == REFRESH)
883 refresh2 = !refresh2;
884 SetRefreshRate(refresh2);
889 gram1[0x427A] = 1; // (1)
892 gram1[0x427C] = 1; // (2)
895 gram1[0x427E] = 1; // (3)
898 gram1[0x4280] = 1; // (5)
900 if (keys[SDLK_q] | keys[29])
901 gram1[0x4276] = 1; // (Q) Jump
904 gram1[0x426A] = 1; // (W)
909 if (keys[SDLK_e] | keys[56]) // (E) Fire
915 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
923 gram1[0x426C] = 1; // (R)
926 gram1[0x4262] = 1; // (T)
929 gram1[0x4260] = 1; // (Y)
932 gram1[0x41A5]++; // Coin? (F10)
935 gram1[0x4189]++; // ? (Z) credits l dig
938 gram1[0x418A]++; // ? (X) credits r dig
941 gram1[0x418C]++; // ? (C) Start
944 gram1[0x418D]++; // ? (V)
947 SpawnSound(USERSOUND, 0); // Do user sound (F7)
950 // We can do this here because we're not executing the cores yet.
951 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
952 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
953 mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
954 // while (cpu1.clock < 25000)
955 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
956 // 25600 cycles/frame
957 // Setting interleave to 25 and below causes the V6809 core to hang...
958 // 32 gets to the title screen before hanging...
959 // 40 works, until it doesn't... :-P
962 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
963 for(int i=0; i<640; i++)
965 // Gay, but what are ya gonna do?
966 // There's better ways, such as keeping track of when slave writes to master, etc...
967 Execute6809(&cpu1, 40);
968 Execute6809(&cpu2, 40);
970 // MCU runs at 1,536,000 Hz
971 // 1536000 / 60 / 640 == 40
972 Execute63701(&mcu, 40);
975 BlitChar(charROM, gram1);
979 if (frameTickCount == 3)
982 // Speed throttling happens here...
983 // Actually, it's 16.66... Need to account for that somehow [DONE]
984 while (SDL_GetTicks() - oldTicks < (16 + (frameTickCount == 2 ? 1 : 0)))
985 SDL_Delay(1); // Release our timeslice...
987 oldTicks = SDL_GetTicks();
998 Hitachi uC runs at 6.144 MHz
999 YM2151 runs at 3.579580 MHz
1002 Rolling Thunder Memory map
1003 --------------------------
1004 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1005 map is inferred by program behaviour. The customs also handle internally irq
1008 The main CPU memory map is the same in all games because CUS47 is used by all
1009 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1010 replaced by other chips.
1012 All RAM is shared between main and sub CPU, except for sound RAM which is
1013 shared between main and sound CPU; the portion of object RAM that is overlapped
1014 by sound RAM is used exclusively by the sub CPU.
1018 Address Dir Data Name Description
1019 ------------------- --- -------- --------- -----------------------
1020 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1021 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1022 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1023 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1024 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1025 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1026 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1027 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1028 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1029 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1030 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1031 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1032 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1033 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1034 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1035 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1036 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1037 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1038 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1040 [1] Note that this is partially overlapped by sound RAM
1041 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1046 Address Dir Data Name Description
1047 ------------------- --- -------- --------- -----------------------
1048 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1049 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1050 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1051 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1052 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1053 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1054 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1055 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1056 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1057 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1058 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1059 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1060 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1061 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1063 [1] Only used by Rolling Thunder
1068 Address Dir Data Name Description
1069 ------------------- --- -------- --------- -----------------------
1070 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1071 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1072 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1073 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1074 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1075 0010 0--- --01 ---- n.c.
1076 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1077 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1078 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1079 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1080 1011 0--- ---- ---- W unknown (CUS41)
1081 1011 1--- ---- ---- W unknown (CUS41)
1082 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1087 - There are two watchdogs, one per CPU (or maybe three). Handling them
1088 separately is necessary to allow entering service mode without manually
1089 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1092 - The sprite hardware buffers spriteram: the program writes the sprite list to
1093 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2
1094 of sprite RAM to signal the chip that the list is complete. The chip will
1095 copy the list from 4-9 to 10-15 and use it from there. This has not been
1096 verified on the real hardware, but it is the most logical way of doing it.
1097 Emulating this behaviour and not using an external buffer is important in
1098 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1099 is not written to. If we buffered spriteram to an external buffer, this would
1100 cause dangling sprites because the buffer would not be updated.
1102 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1103 entering a door. The *closed* door is made of tiles, but the *moving* door is
1104 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1105 there is one frame where neither the tile-based closed door nor the
1106 sprite-based moving door is shown, so it flickers. This behavior has been
1107 confirmed on a real PCB.
1111 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1112 but they don't seem to work as expected. During the first few frames they are
1113 written out of order and hooking them up in the usual way causes the MCU to
1114 stop receiving interrupts.