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"
40 #define ROM1 "rt3-1b.9c"
41 #define ROM2 "rt3-2b.12c"
42 #define ROM3 "rt3-3.12d"
43 #define ROM4 "rt1-4.6b"
44 #define ROM5 "rt1-5.4r"
45 #define ROM6 "rt1-6.4s"
46 #define ROM7 "rt1-7.7r"
47 #define ROM8 "rt1-8.7s"
48 #define ROM9 "rt1-9.12h"
49 #define ROM10 "rt1-10.12k"
50 #define ROM11 "rt1-11.12l"
51 #define ROM12 "rt1-12.12m"
52 #define ROM13 "rt1-13.12p"
53 #define ROM14 "rt1-14.12r"
54 #define ROM15 "rt1-15.12t"
55 #define ROM16 "rt1-16.12u"
56 #define ROM17 "rt1-17.f1"
57 #define ROM18 "rt1-18.h1"
58 #define ROM19 "rt1-19.k1"
59 #define ROM20 "rt1-20.m1"
60 #define ROM21 "rt1-21.f3"
61 #define ROM22 "rt1-22.h3"
62 #define PROM1 "mb7124e.3r"
63 #define PROM2 "mb7116e.3s"
64 #define PROM3 "mb7138h.4v"
65 #define PROM4 "mb7138h.6v"
66 #define PROM5 "mb7112e.6u"
67 #define MCUROM "rt1-mcu.bin"
74 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
75 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
76 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
77 uint8_t chr_rom[0x60000]; // Character ROM pointer
78 uint8_t mcuMem[0x10000]; // 64K for MCU
83 bool trace1 = false; // ditto...
84 bool looking_at_rom = true; // true = R1, false = R2
85 uint32_t banksw1, banksw2; // Bank switch addresses
86 uint16_t game_over_switch; // Game over delay
87 uint16_t dpc; // Debug pc reg...
88 bool show_scr = true; // Whether or not to show background
89 bool enable_cpu = true; // Whether or not to enable CPUs
90 bool irqGoA = true; // IRQ switch for CPU #1
91 bool irqGoB = true; // IRQ switch for CPU #2
93 uint16_t refresh_ = 0; // Crappy global screen stuff...
96 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
97 uint8_t * psg_adrs[16];
98 //uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
100 //uint8_t * voc_adrs[32];
101 uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
102 uint8_t * fm_adrs[14];
104 fstream tr; // Tracelog hook
105 uint16_t pcx; // Where we at?
109 // Read a byte from memory (without touching PC. Not a Fetch!)
111 uint8_t RdMem(uint16_t addr)
115 // $4000-4300 is RAM shared with the microcontroller...
119 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
120 if ((addr >= 0x4000) && (addr <= 0x43FF))
121 return mcuMem[addr - 0x3000];
124 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
136 // Write a byte to memory
138 void WrMem(uint16_t addr, uint8_t b)
141 extern bool charbase; // Needed for screen. Extern it in it??
145 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
149 if (((addr >= 0x4180) && (addr <= 0x4191)) || (addr == 0x4380))
150 printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, addr);
154 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
156 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
158 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
159 if (addr > 0x4284 && addr < 0x42A5 && b)
160 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
164 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
166 game_over_switch = 240; // Set game over delay...
169 // if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
171 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
172 if ((addr >= 0x4000) && (addr <= 0x43FF))
173 mcuMem[addr - 0x3000] = b;
178 charbase = false; // Char banksw1
180 charbase = true; // Char banksw2
181 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
183 if (refresh_++ == 1) // 30 Hz...
185 BlitChar(screen, chr_rom, gram1);
186 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
189 // IRQ Ack (may also be frame go...
190 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
193 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
200 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
202 uint8_t RdMemB(uint16_t addr)
209 b = gram1[addr + 0x4000];
210 if (addr > 0x1FFF && addr < 0x6000)
211 b = gram1[addr - 0x2000];
213 b = grom3[banksw2 + (addr - 0x6000)];
223 // Write a byte to memory (2nd processor)
225 void WrMemB(uint16_t addr, uint8_t b)
228 extern bool charbase;
233 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
237 if (((addr >= 0x0180) && (addr <= 0x0191)) || (addr == 0x0380))
238 printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
243 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
245 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
246 if (addr > 0x0284 && addr < 0x02A5 && b)
247 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
250 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
254 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
256 game_over_switch = 240; // Set game over delay...
259 // if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
262 gram1[addr + 0x4000] = b;
263 if (addr > 0x1FFF && addr < 0x6000)
264 gram1[addr - 0x2000] = b;
265 // if (addr > 0x5FFF)
270 // IRQ Ack (may also be frame go...)
271 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
274 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
280 uint8_t MCUReadMemory(uint16_t address)
285 // printf("V63701 read $%02X from $%02X...\n", memory[address], address);
286 return InternalRegisterRead(address);
291 // Translate local reads @ $1000-$13FF to $4000-$43FF in shared RAM
292 if ((address >= 0x1000) && (address <= 0x13FF))
293 return gram1[0x3000 + address];
296 if ((address >= 0x2000) && (address <= 0x2001))
299 // return YMReadReg(0);
301 // else if (address == 0x2020)
302 // return input_port_0_r(0);
303 // else if (address == 0x2021)
304 // return input_port_1_r(0);
305 // This is DSW1 & 2. All switch settings are active low.
306 else if (address == 0x2030)
308 else if (address == 0x2031)
311 return mcuMem[address];
315 void MCUWriteMemory(uint16_t address, uint8_t data)
317 static uint8_t ymRegister;
322 // printf("V63701 wrote $%02X to $%02X...\n", data, address);
323 InternalRegisterWrite(address, data);
329 // Translate local reads @ $1000-$13FF to $4000-$43FF in shared RAM
330 if ((address >= 0x1000) && (address <= 0x13FF))
332 gram1[0x3000 + address] = data;
337 if (((address >= 0x4000) && (address <= 0xBFFF))
338 || (address >= 0xF000))
340 else if (address == 0x2000)
345 else if (address == 0x2001)
347 //printf("Writing $%02X to YM2151 register $%02X...\n", data, ymRegister);
348 // YMWriteReg(0, ymRegister, data);
352 // RAM is from $0 - $3FFF, $C000 - $EFFF
353 mcuMem[address] = data;
358 // Generic Load file into image space
359 // (No error checking performed! Responsibility of caller!)
361 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
365 strcpy(path, "./ROMs/");
366 strcat(path, filename);
367 FILE * file = fopen(path, "rb");
372 fread(&mem[address], 1, length, file);
382 bool ReadColorPROMs(void)
387 extern uint32_t palette[256]; // Screen physical palette
388 extern uint8_t ccolor[256][8]; // Character color PROM values
389 extern uint8_t scolor[128][16]; // Sprite color PROM values
391 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
395 for(int i=0; i<256; i++) // Load char pallete with PROM values
397 for(int j=0; j<8; j++)
400 ccolor[i][j] = (uint8_t)ch;
407 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
411 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
413 for(int j=0; j<16; j++)
416 scolor[i][j] = (uint8_t)ch;
423 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
424 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
426 // If open was successful...
429 // Palette is 12-bit RGB, we stretch it to 24-bit
430 for(int i=0; i<256; i++)
435 uint8_t r = (uint8_t)c1 & 0x0F;
436 uint8_t g = (uint8_t)c1 >> 4;
437 uint8_t b = (uint8_t)c2;
438 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
445 // PROM5 has the following in it (tile address decoder):
446 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
447 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
456 bool UnpackFonts(void)
458 // uint8_t b1, b2, b3;
462 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
463 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
466 return false; // Return if not found...
468 for(long i=0; i<0x40000; i+=64)
470 for(int j=0; j<64; j+=8)
472 f1.get(b1); f1.get(b2); f2.get(b3);
473 b3 ^= 0xFF; // Invert top data...
474 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
475 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
476 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
477 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
478 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
479 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
480 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
481 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
488 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
489 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
491 for(long i=0x40000; i<0x60000; i+=64)
493 for(int j=0; j<64; j+=8)
495 f1.get(b1); f1.get(b2); f2.get(b3);
496 b3 ^= 0xFF; // Invert top data
497 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
498 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
499 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
500 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
501 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
502 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
503 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
504 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
511 return true; // Made it!
516 // Get length of sample from WAV format
518 uint32_t GetWAVLength(fstream & file)
523 file.ignore(16); // Skip header BS
525 for(int i=0; i<2; i++)
527 file.get(ch); len = (int)(uint8_t)ch;
528 file.get(ch); len |= (int)(uint8_t)ch << 8;
529 file.get(ch); len |= (int)(uint8_t)ch << 16;
530 file.get(ch); len |= (int)(uint8_t)ch << 24;
532 // Skip intermediate data
533 file.ignore(len + 4);
536 // & finally get length of data
537 file.get(ch); len = (int)(uint8_t)ch;
538 file.get(ch); len |= (int)(uint8_t)ch << 8;
539 file.get(ch); len |= (int)(uint8_t)ch << 16;
540 file.get(ch); len |= (int)(uint8_t)ch << 24;
547 // Load PSG samples from disk
555 for(int i=0; i<16; i++)
559 psg_adrs[i] = NULL; // Zero out pointer
560 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
562 fp.open(file, ios::binary | ios::in); // Attempt to open it...
566 len = GetWAVLength(fp); // Get WAV data length...
567 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
569 if (psg_adrs[i] != NULL)
571 for(int j=0; j<(signed)len; j++)
574 psg_adrs[i][j] = ch; // & load it in...
578 printf("Found sample file: %s\t[Length: %u]\n", file, len);
588 // Load FM samples from disk
596 for(int i=0; i<14; i++)
600 fm_adrs[i] = NULL; // Zero out pointer
601 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
602 fp.open(file, ios::binary | ios::in); // Attempt to open it...
607 len = GetWAVLength(fp); // Get WAV length...
608 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
610 if (fm_adrs[i] != NULL)
612 for(int j=0; j<(signed)len; j++)
615 fm_adrs[i][j] = ch; // & load it in...
619 printf("Found sample file: %s\t[Length: %u]\n", file, len);
630 int main(int argc, char * argv[])
632 InitLog("thunder.log");
634 extern bool disasm; // From 'V6809.CPP'
635 extern bool charbase; // From 'SCREEN.CPP'
639 fstream ff; // Declare fstream without file hooks...
640 bool brk = false, brk2 = false; // Breakpoint set flag
641 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
642 bool running; // CPU running state flag...
643 bool self_test = false; // Self-test switch
644 bool scr_type = false; // false=chars, true=pixels
645 uint16_t debounce = 0; // Key de-bounce counter
646 uint16_t fire_debounce = 0; // Fire button debounce counter
647 uint8_t x; // General placeholder...
648 bool active = true; // Program running flag
650 SDL_Event event; // SDL "event"
651 extern uint8_t palette[768]; // Screen physical palette
652 uint32_t ticks, oldTicks;
654 cout << endl << "THUNDER v"THUNDER_VERSION" ";
655 cout << "by James Hammons" << endl;
656 cout << "Serial #20149417 / Prerelease" << endl;
657 cout << "© 2003, 2014 Underground Software" << endl << endl;
659 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
662 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
664 gram = gram1; grom = grom1; // Needed only for debugger
666 memset(gram, 0, 0x10000);
667 memset(grom, 0, 0x10000);
668 memset(gram2, 0, 0x10000);
669 memset(grom2, 0, 0x10000);
671 game_over_switch = 0; // Init game over delay
673 cout << "Loading ROMs..." << endl;
674 if (!ReadColorPROMs()) // Load virtual PROMs
675 { cout << "Could not open PROM files!" << endl; return -1; }
677 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
678 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
680 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
681 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
683 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
684 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
686 // if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
687 // { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
689 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
690 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
692 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
693 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
695 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
696 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
698 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
699 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
701 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
702 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
704 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
705 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
707 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
708 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
710 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
711 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
713 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
714 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
716 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
717 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
719 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
720 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
722 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
723 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
725 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
726 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
728 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
729 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
731 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
733 cout << "Could not open font files!" << endl;
737 // Load MCU program + data
738 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000)) // Load MCU ROM
739 { cout << "Could not open file '" << MCUROM << "'!" << endl; return -1; }
741 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000)) // Load 4th ROM
742 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
744 // Load samples if they're there...
748 // Set up V6809 execution contexts
750 memset(&cpu1, 0, sizeof(V6809REGS));
753 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
755 memset(&cpu2, 0, sizeof(V6809REGS));
758 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
760 memset(&mcu, 0, sizeof(V63701REGS));
761 mcu.RdMem = MCUReadMemory;
762 mcu.WrMem = MCUWriteMemory;
763 mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
765 uint32_t my_clock = 0;
766 running = true; // Set running status...
768 SetRefreshRate(refresh2); // Tell GUI our refresh rate
771 // This is data that is supposed to come from the MCU... So that's why it hangs
772 gram1[0x4182] = 0xA6; // Temp kludge
773 gram1[0x4184] = 0xA6;
774 gram1[0x4183] = 0x00; // More of the same
775 gram1[0x4185] = 0x00;
777 banksw1 = 0; // Will this work?
779 // iclock = 0; // Reset instr clock #1...
780 InitGUI(); // Reset # of coins
782 WriteLog("About to set up screen...\n");
785 oldTicks = SDL_GetTicks();
787 WriteLog("About to set up audio...\n");
789 // This crap SHOULD be in sound.cpp (not yet created)...
790 SDL_AudioSpec desired, obtained;
791 desired.freq = 22050;
792 desired.format = AUDIO_U8;
793 desired.channels = 1;
794 desired.samples = 600;
795 desired.callback = SoundFunc;
796 desired.userdata = NULL;
797 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
798 if (SDL_OpenAudio(&desired, &obtained) < 0)
800 cout << "Couldn't open audio: " << SDL_GetError() << endl;
804 SDL_PauseAudio(0); // Get that audio going!
809 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
810 RenderScreenBuffer();
812 WriteLog("About to enter main loop...\n");
815 HandleGUIDebounce(); // Debounce GUI keys
818 if (game_over_switch)
820 game_over_switch--; // Countdown...
822 if (game_over_switch == 0)
823 gram1[0x4380] = 0; // Kill music!
827 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
828 // $423D - $425B by the MCU
832 //gram1[0x423D] = self_test; // Reset DSW1-1
833 // gram1[0x4268] = 0; // Reset Video test
834 // gram1[0x427A] = 0; gram1[0x427C] = 0;
835 //gram1[0x427B] = 0; gram1[0x427D] = 0;
836 // gram1[0x427E] = 0;// gram1[0x427F] = 0;
837 // gram1[0x4280] = 0;// gram1[0x4281] = 0;
839 // gram1[0x426A] = 0;
841 // gram1[0x426C] = 0;
842 // gram1[0x4262] = 0; gram1[0x4260] = 0;
845 // SDL key handling...
849 while (SDL_PollEvent(&event))
854 if (event.key.keysym.sym == SDLK_ESCAPE)
856 // Do PCX snapshot (F4)
857 else if (event.key.keysym.sym == SDLK_F4)
859 // SpawnSound(USERSOUND, SCAMERA);
863 else if (event.key.keysym.sym == SDLK_F10)
864 gram1[0x41A5]++; // Coin? (F10)
865 else if (event.key.keysym.sym == SDLK_c)
866 gram1[0x418C]++; // ? (C) Start
867 else if (event.key.keysym.sym == SDLK_RIGHT)
869 // Disallow opposite directions @ same time
870 if (gram1[0x4281] == 0)
871 gram1[0x427F] = 1; // Stick right
873 else if (event.key.keysym.sym == SDLK_LEFT)
875 // Disallow opposite directions@same time
876 if (gram1[0x427F] == 0)
877 gram1[0x4281] = 1; // Stick left
879 else if (event.key.keysym.sym == SDLK_UP)
881 // Disallow opposite directions@same time
882 if (gram1[0x427D] == 0)
883 gram1[0x427B] = 1; // Stick up
885 else if (event.key.keysym.sym == SDLK_DOWN)
887 // Disallow opposite directions@same time
888 if (gram1[0x427B] == 0)
889 gram1[0x427D] = 1; // Stick down
891 else if (event.key.keysym.sym == SDLK_q)
892 gram1[0x4276] = 1; // (Q) Jump
893 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
898 if (event.key.keysym.sym == SDLK_RIGHT)
900 else if (event.key.keysym.sym == SDLK_LEFT)
902 else if (event.key.keysym.sym == SDLK_UP)
904 else if (event.key.keysym.sym == SDLK_DOWN)
906 else if (event.key.keysym.sym == SDLK_q)
907 gram1[0x4276] = 0; // (Q) Jump
908 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
916 if (keys[SDLK_ESCAPE])
917 running = false; // ESC to exit...
920 debounce--; // Debounce toggle keys...
925 self_test = !self_test; // Self-test (F1-toggle)
926 debounce = 10; // Key debounce value...
930 gram1[0x4268] = 1; // Video test (F2)
931 debounce = 10; // Key debounce value...
935 scr_type = !scr_type; // Toggle screen (F12)
936 debounce = 10; // Key debounce value...
940 show_scr = !show_scr; // Toggle bkgrnd (F3)
945 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
950 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
951 SetRefreshRate(refresh2); // Inform GUI of refresh
956 debounce = 10; // Key debounce value...
958 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
960 SpawnSound(USERSOUND, SCAMERA);
964 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
974 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
975 if (keys[SDLK_RIGHT]) // Right arrow
978 SelectRight(); // If GUI active...
981 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
982 gram1[0x427F] = 1; // Stick right
988 SelectLeft(); // If GUI active...
991 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
992 gram1[0x4281] = 1; // Left arrow
998 SelectUp(); // If GUI active...
1001 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1002 gram1[0x427B] = 1; // Up arrow
1005 if (keys[SDLK_DOWN])
1008 SelectDown(); // If GUI active...
1011 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1012 gram1[0x427D] = 1; // Down arrow
1015 if (keys[SDLK_RETURN]) // Return
1017 uint8_t retval = UserSelectedSomething();
1022 if (retval == REFRESH)
1024 refresh2 = !refresh2;
1025 SetRefreshRate(refresh2);
1030 gram1[0x427A] = 1; // (1)
1033 gram1[0x427C] = 1; // (2)
1036 gram1[0x427E] = 1; // (3)
1039 gram1[0x4280] = 1; // (5)
1041 if (keys[SDLK_q] | keys[29])
1042 gram1[0x4276] = 1; // (Q) Jump
1045 gram1[0x426A] = 1; // (W)
1050 if (keys[SDLK_e] | keys[56]) // (E) Fire
1056 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1064 gram1[0x426C] = 1; // (R)
1067 gram1[0x4262] = 1; // (T)
1070 gram1[0x4260] = 1; // (Y)
1073 gram1[0x41A5]++; // Coin? (F10)
1076 gram1[0x4189]++; // ? (Z) credits l dig
1079 gram1[0x418A]++; // ? (X) credits r dig
1082 gram1[0x418C]++; // ? (C) Start
1085 gram1[0x418D]++; // ? (V)
1088 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1090 // if (keys[SDLK_F8])
1092 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1093 // charbase = false; // Switch chars out...
1095 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1097 if (keys[SDLK_F11]) // (F11)
1099 Execute6809(&cpu1, 10);
1100 Execute6809(&cpu2, 10);
1103 //F12 is used above, but the values are ignored. So we'll do it here too.
1106 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1107 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1110 if (keys[SDLK_d]) // (D) start disassembly
1114 gram1[0x5606] = 0x00;
1117 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1118 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1122 gram1[0x5FF3] = 0x02;
1123 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1131 // We can do this here because we're not executing the cores yet.
1132 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1133 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1134 // while (cpu1.clock < 25000)
1135 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1136 // 25600 cycles/frame
1137 // Setting interleave to 25 and below causes the V6809 core to hang...
1138 // 32 gets to the title screen before hanging...
1139 // 40 works, until it doesn't... :-P
1142 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1143 for(uint32_t i=0; i<640; i++)
1144 // for(uint32_t i=0; i<1280; i++)
1146 // Gay, but what are ya gonna do?
1147 // There's better ways, such as keeping track of when slave writes to master, etc...
1148 Execute6809(&cpu1, 40);
1149 Execute6809(&cpu2, 40);
1151 // MCU runs at 1,536,000 Hz
1152 // 1536000 / 60 / 640 == 40
1153 Execute63701(&mcu, 40);
1155 } // END: enable_cpu
1157 // Speed throttling happens here...
1158 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1159 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1160 SDL_Delay(1); // Release our timeslice...
1162 oldTicks = SDL_GetTicks();
1163 //cout << "Finished frame..." << endl;
1168 // Deallocate sounds if they were loaded
1169 for(int i=0; i<16; i++)
1171 delete[] psg_adrs[i];
1173 for(int i=0; i<14; i++)
1175 delete[] fm_adrs[i];
1183 Hitachi uC runs at 6.144 MHz
1184 YM2151 runs at 3.579580 MHz
1187 Rolling Thunder Memory map
1188 --------------------------
1189 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1190 map is inferred by program behaviour. The customs also handle internally irq
1193 The main CPU memory map is the same in all games because CUS47 is used by all
1194 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1195 replaced by other chips.
1197 All RAM is shared between main and sub CPU, except for sound RAM which is
1198 shared between main and sound CPU; the portion of object RAM that is overlapped
1199 by sound RAM is used exclusively by the sub CPU.
1203 Address Dir Data Name Description
1204 ------------------- --- -------- --------- -----------------------
1205 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1206 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1207 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1208 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1209 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1210 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1211 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1212 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1213 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1214 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1215 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1216 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1217 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1218 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1219 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1220 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1221 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1222 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1223 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1225 [1] Note that this is partially overlapped by sound RAM
1226 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1231 Address Dir Data Name Description
1232 ------------------- --- -------- --------- -----------------------
1233 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1234 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1235 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1236 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1237 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1238 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1239 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1240 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1241 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1242 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1243 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1244 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1245 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1246 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1248 [1] Only used by Rolling Thunder
1253 Address Dir Data Name Description
1254 ------------------- --- -------- --------- -----------------------
1255 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1256 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1257 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1258 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1259 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1260 0010 0--- --01 ---- n.c.
1261 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1262 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1263 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1264 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1265 1011 0--- ---- ---- W unknown (CUS41)
1266 1011 1--- ---- ---- W unknown (CUS41)
1267 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1272 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1273 in rthunder. The two 6809 in this game synchronize using a semaphore at
1274 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1275 and then increments 5606. While it does its quick things (which require
1276 about 40 clock cycles) it expects CPU2 to clear 5607.
1277 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1278 mode. I haven't investigated on the cause.
1280 - There are two watchdogs, one per CPU (or maybe three). Handling them
1281 separately is necessary to allow entering service mode without manually
1282 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1285 - The sprite hardware buffers spriteram: the program writes the sprite list to
1286 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1287 sprite RAM to signal the chip that the list is complete. The chip will copy
1288 the list from 4-9 to 10-15 and use it from there. This has not been verified
1289 on the real hardware, but it is the most logical way of doing it.
1290 Emulating this behaviour and not using an external buffer is important in
1291 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1292 is not written to. If we buffered spriteram to an external buffer, this would
1293 cause dangling sprites because the buffer would not be updated.
1295 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1296 entering a door. The *closed* door is made of tiles, but the *moving* door is
1297 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1298 there is one frame where neither the tile-based closed door nor the
1299 sprite-based moving door is shown, so it flickers. This behavior has been
1300 confirmed on a real PCB.
1304 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1305 but they don't seem to work as expected. During the first few frames they are
1306 written out of order and hooking them up in the usual way causes the MCU to
1307 stop receiving interrupts.