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");
351 printf("Could not open file \"%s\"!\n", filename);
355 fread(&mem[address], 1, length, file);
365 bool ReadColorPROMs(void)
370 extern uint32_t palette[256]; // Screen physical palette
371 extern uint8_t ccolor[256][8]; // Character color PROM values
372 extern uint8_t scolor[128][16]; // Sprite color PROM values
374 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
378 for(int i=0; i<256; i++) // Load char pallete with PROM values
380 for(int j=0; j<8; j++)
383 ccolor[i][j] = (uint8_t)ch;
390 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
394 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
396 for(int j=0; j<16; j++)
399 scolor[i][j] = (uint8_t)ch;
406 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
407 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
409 // If open was successful...
412 // Palette is 12-bit RGB, we stretch it to 24-bit
413 for(int i=0; i<256; i++)
418 uint8_t r = (uint8_t)c1 & 0x0F;
419 uint8_t g = (uint8_t)c1 >> 4;
420 uint8_t b = (uint8_t)c2;
421 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
428 // PROM5 has the following in it (tile address decoder):
429 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
430 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
433 printf("Could not open PROM files!\n");
442 bool UnpackFonts(void)
444 // uint8_t b1, b2, b3;
448 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
449 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
452 return false; // Return if not found...
454 for(long i=0; i<0x40000; i+=64)
456 for(int j=0; j<64; j+=8)
458 f1.get(b1); f1.get(b2); f2.get(b3);
459 b3 ^= 0xFF; // Invert top data...
460 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
461 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
462 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
463 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
464 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
465 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
466 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
467 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
474 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
475 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
477 for(long i=0x40000; i<0x60000; i+=64)
479 for(int j=0; j<64; j+=8)
481 f1.get(b1); f1.get(b2); f2.get(b3);
482 b3 ^= 0xFF; // Invert top data
483 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
484 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
485 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
486 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
487 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
488 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
489 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
490 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
504 int main(int argc, char * argv[])
506 InitLog("thunder.log");
508 extern bool disasm; // From 'V6809.CPP'
509 extern bool charbase; // From 'SCREEN.CPP'
512 bool running; // CPU running state flag...
513 SDL_Event event; // SDL "event"
514 extern uint8_t palette[768]; // Screen physical palette
515 uint32_t ticks, oldTicks;
517 printf("THUNDER v"THUNDER_VERSION" by James Hammons\n");
518 printf("Serial #20149417 / Prerelease\n");
519 printf("© 2003, 2014 Underground Software\n\n");
520 printf("This emulator is free software. If you paid for it you were RIPPED OFF\n\n");
522 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
524 // Needed only for debugger
525 gram = gram1; grom = grom1;
527 memset(gram, 0, 0x10000);
528 memset(grom, 0, 0x10000);
529 memset(gram2, 0, 0x10000);
530 memset(grom2, 0, 0x10000);
532 cout << "Loading ROMs..." << endl;
533 if (!ReadColorPROMs())
536 // Load $8000-$FFFF 1st ROM
537 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000))
540 // Load $8000-$FFFF 2nd ROM
541 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000))
544 // Load 3rd ROM into its own space
545 if (!LoadImg(ROM3, grom3, 0, 0x8000))
548 if (!LoadImg(ROM17, data_rom, 0, 0x10000))
551 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000))
554 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000))
557 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000))
560 if (!LoadImg(ROM9, spr_rom, 0, 0x10000))
563 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000))
566 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000))
569 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000))
572 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000))
575 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000))
578 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000))
581 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000))
584 if (!LoadImg(ROM21, voice_rom, 0, 0x10000))
587 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000))
590 // Load 5, 6, 7, 8th ROMs
593 cout << "Could not open font files!" << endl;
597 // Load MCU program + data
598 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000))
601 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000))
604 // Set up V6809, V63701 execution contexts
606 memset(&cpu1, 0, sizeof(V6809REGS));
609 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
611 memset(&cpu2, 0, sizeof(V6809REGS));
614 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
616 memset(&mcu, 0, sizeof(V63701REGS));
617 mcu.RdMem = MCUReadMemory;
618 mcu.WrMem = MCUWriteMemory;
619 mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
621 running = true; // Set running status...
623 // Set all inputs to inactive...
624 input1.byte = input2.byte = input3.byte = input4.byte = input5.byte = 0xFF;
625 banksw1 = 0; // Will this work?
627 InitGUI(); // Reset # of coins
629 WriteLog("About to set up screen...\n");
632 oldTicks = SDL_GetTicks();
634 WriteLog("About to set up audio...\n");
637 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
638 RenderScreenBuffer();
640 WriteLog("About to enter main loop...\n");
643 HandleGUIDebounce(); // Debounce GUI keys
645 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
646 // $423D - $425B by the MCU
650 //gram1[0x423D] = self_test; // Reset DSW1-1
651 // gram1[0x4268] = 0; // Reset Video test
653 // SDL key handling...
657 while (SDL_PollEvent(&event))
662 if (event.key.keysym.sym == SDLK_ESCAPE)
664 // Do PCX snapshot (F4)
665 else if (event.key.keysym.sym == SDLK_F4)
667 // SpawnSound(USERSOUND, SCAMERA);
671 else if (event.key.keysym.sym == SDLK_5)
673 else if (event.key.keysym.sym == SDLK_1)
675 else if (event.key.keysym.sym == SDLK_RIGHT)
677 else if (event.key.keysym.sym == SDLK_LEFT)
679 else if (event.key.keysym.sym == SDLK_UP)
681 else if (event.key.keysym.sym == SDLK_DOWN)
683 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
685 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
688 else if (event.key.keysym.sym == SDLK_1)
690 else if (event.key.keysym.sym == SDLK_2)
692 else if (event.key.keysym.sym == SDLK_3)
694 else if (event.key.keysym.sym == SDLK_4)
696 else if (event.key.keysym.sym == SDLK_5)
698 else if (event.key.keysym.sym == SDLK_6)
700 else if (event.key.keysym.sym == SDLK_7)
702 else if (event.key.keysym.sym == SDLK_8)
705 else if (event.key.keysym.sym == SDLK_q)
707 else if (event.key.keysym.sym == SDLK_w)
709 else if (event.key.keysym.sym == SDLK_e)
711 else if (event.key.keysym.sym == SDLK_r)
713 else if (event.key.keysym.sym == SDLK_t)
715 else if (event.key.keysym.sym == SDLK_y)
717 else if (event.key.keysym.sym == SDLK_u)
719 else if (event.key.keysym.sym == SDLK_i)
722 else if (event.key.keysym.sym == SDLK_a)
724 else if (event.key.keysym.sym == SDLK_s)
726 else if (event.key.keysym.sym == SDLK_d)
728 else if (event.key.keysym.sym == SDLK_f)
730 else if (event.key.keysym.sym == SDLK_g)
737 if (event.key.keysym.sym == SDLK_5)
739 else if (event.key.keysym.sym == SDLK_1)
741 else if (event.key.keysym.sym == SDLK_RIGHT)
743 else if (event.key.keysym.sym == SDLK_LEFT)
745 else if (event.key.keysym.sym == SDLK_UP)
747 else if (event.key.keysym.sym == SDLK_DOWN)
749 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
751 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
754 if (event.key.keysym.sym == SDLK_1)
756 else if (event.key.keysym.sym == SDLK_2)
758 else if (event.key.keysym.sym == SDLK_3)
760 else if (event.key.keysym.sym == SDLK_4)
762 else if (event.key.keysym.sym == SDLK_5)
764 else if (event.key.keysym.sym == SDLK_6)
766 else if (event.key.keysym.sym == SDLK_7)
768 else if (event.key.keysym.sym == SDLK_8)
771 else if (event.key.keysym.sym == SDLK_q)
773 else if (event.key.keysym.sym == SDLK_w)
775 else if (event.key.keysym.sym == SDLK_e)
777 else if (event.key.keysym.sym == SDLK_r)
779 else if (event.key.keysym.sym == SDLK_t)
781 else if (event.key.keysym.sym == SDLK_y)
783 else if (event.key.keysym.sym == SDLK_u)
785 else if (event.key.keysym.sym == SDLK_i)
788 else if (event.key.keysym.sym == SDLK_a)
790 else if (event.key.keysym.sym == SDLK_s)
792 else if (event.key.keysym.sym == SDLK_d)
794 else if (event.key.keysym.sym == SDLK_f)
796 else if (event.key.keysym.sym == SDLK_g)
805 if (keys[SDLK_ESCAPE])
806 running = false; // ESC to exit...
809 debounce--; // Debounce toggle keys...
814 self_test = !self_test; // Self-test (F1-toggle)
815 debounce = 10; // Key debounce value...
819 gram1[0x4268] = 1; // Video test (F2)
820 debounce = 10; // Key debounce value...
824 scr_type = !scr_type; // Toggle screen (F12)
825 debounce = 10; // Key debounce value...
829 show_scr = !show_scr; // Toggle bkgrnd (F3)
834 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
839 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
840 SetRefreshRate(refresh2); // Inform GUI of refresh
845 debounce = 10; // Key debounce value...
847 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
849 SpawnSound(USERSOUND, SCAMERA);
853 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
863 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
864 if (keys[SDLK_RIGHT]) // Right arrow
867 SelectRight(); // If GUI active...
870 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
871 gram1[0x427F] = 1; // Stick right
877 SelectLeft(); // If GUI active...
880 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
881 gram1[0x4281] = 1; // Left arrow
887 SelectUp(); // If GUI active...
890 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
891 gram1[0x427B] = 1; // Up arrow
897 SelectDown(); // If GUI active...
900 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
901 gram1[0x427D] = 1; // Down arrow
904 if (keys[SDLK_RETURN]) // Return
906 uint8_t retval = UserSelectedSomething();
911 if (retval == REFRESH)
913 refresh2 = !refresh2;
914 SetRefreshRate(refresh2);
919 gram1[0x427A] = 1; // (1)
922 gram1[0x427C] = 1; // (2)
925 gram1[0x427E] = 1; // (3)
928 gram1[0x4280] = 1; // (5)
930 if (keys[SDLK_q] | keys[29])
931 gram1[0x4276] = 1; // (Q) Jump
934 gram1[0x426A] = 1; // (W)
939 if (keys[SDLK_e] | keys[56]) // (E) Fire
945 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
953 gram1[0x426C] = 1; // (R)
956 gram1[0x4262] = 1; // (T)
959 gram1[0x4260] = 1; // (Y)
962 gram1[0x41A5]++; // Coin? (F10)
965 gram1[0x4189]++; // ? (Z) credits l dig
968 gram1[0x418A]++; // ? (X) credits r dig
971 gram1[0x418C]++; // ? (C) Start
974 gram1[0x418D]++; // ? (V)
977 SpawnSound(USERSOUND, 0); // Do user sound (F7)
980 // We can do this here because we're not executing the cores yet.
981 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
982 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
983 mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
984 // while (cpu1.clock < 25000)
985 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
986 // 25600 cycles/frame
987 // Setting interleave to 25 and below causes the V6809 core to hang...
988 // 32 gets to the title screen before hanging...
989 // 40 works, until it doesn't... :-P
992 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
993 for(int i=0; i<640; i++)
995 // Gay, but what are ya gonna do?
996 // There's better ways, such as keeping track of when slave writes to master, etc...
997 Execute6809(&cpu1, 40);
998 Execute6809(&cpu2, 40);
1000 // MCU runs at 1,536,000 Hz
1001 // 1536000 / 60 / 640 == 40
1002 Execute63701(&mcu, 40);
1005 // Speed throttling happens here...
1006 // Actually, it's 16.66... Need to account for that somehow
1007 while (SDL_GetTicks() - oldTicks < 16)
1008 SDL_Delay(1); // Release our timeslice...
1010 oldTicks = SDL_GetTicks();
1021 Hitachi uC runs at 6.144 MHz
1022 YM2151 runs at 3.579580 MHz
1025 Rolling Thunder Memory map
1026 --------------------------
1027 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1028 map is inferred by program behaviour. The customs also handle internally irq
1031 The main CPU memory map is the same in all games because CUS47 is used by all
1032 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1033 replaced by other chips.
1035 All RAM is shared between main and sub CPU, except for sound RAM which is
1036 shared between main and sound CPU; the portion of object RAM that is overlapped
1037 by sound RAM is used exclusively by the sub CPU.
1041 Address Dir Data Name Description
1042 ------------------- --- -------- --------- -----------------------
1043 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1044 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1045 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1046 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1047 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1048 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1049 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1050 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1051 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1052 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1053 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1054 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1055 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1056 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1057 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1058 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1059 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1060 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1061 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1063 [1] Note that this is partially overlapped by sound RAM
1064 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1069 Address Dir Data Name Description
1070 ------------------- --- -------- --------- -----------------------
1071 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1072 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1073 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1074 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1075 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1076 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1077 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1078 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1079 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1080 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1081 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1082 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1083 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1084 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1086 [1] Only used by Rolling Thunder
1091 Address Dir Data Name Description
1092 ------------------- --- -------- --------- -----------------------
1093 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1094 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1095 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1096 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1097 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1098 0010 0--- --01 ---- n.c.
1099 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1100 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1101 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1102 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1103 1011 0--- ---- ---- W unknown (CUS41)
1104 1011 1--- ---- ---- W unknown (CUS41)
1105 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1110 - There are two watchdogs, one per CPU (or maybe three). Handling them
1111 separately is necessary to allow entering service mode without manually
1112 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1115 - The sprite hardware buffers spriteram: the program writes the sprite list to
1116 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2
1117 of sprite RAM to signal the chip that the list is complete. The chip will
1118 copy the list from 4-9 to 10-15 and use it from there. This has not been
1119 verified on the real hardware, but it is the most logical way of doing it.
1120 Emulating this behaviour and not using an external buffer is important in
1121 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1122 is not written to. If we buffered spriteram to an external buffer, this would
1123 cause dangling sprites because the buffer would not be updated.
1125 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1126 entering a door. The *closed* door is made of tiles, but the *moving* door is
1127 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1128 there is one frame where neither the tile-based closed door nor the
1129 sprite-based moving door is shown, so it flickers. This behavior has been
1130 confirmed on a real PCB.
1134 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1135 but they don't seem to work as expected. During the first few frames they are
1136 written out of order and hooking them up in the usual way causes the MCU to
1137 stop receiving interrupts.