2 // Thunder: A Rolling Thunder Emulator w/6809 debugger
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"
27 //#include <curses.h> // For getch()
39 #define ROM1 "rt3-1b.9c"
40 #define ROM2 "rt3-2b.12c"
41 #define ROM3 "rt3-3.12d"
42 #define ROM4 "rt1-4.6b"
43 #define ROM5 "rt1-5.4r"
44 #define ROM6 "rt1-6.4s"
45 #define ROM7 "rt1-7.7r"
46 #define ROM8 "rt1-8.7s"
47 #define ROM9 "rt1-9.12h"
48 #define ROM10 "rt1-10.12k"
49 #define ROM11 "rt1-11.12l"
50 #define ROM12 "rt1-12.12m"
51 #define ROM13 "rt1-13.12p"
52 #define ROM14 "rt1-14.12r"
53 #define ROM15 "rt1-15.12t"
54 #define ROM16 "rt1-16.12u"
55 #define ROM17 "rt1-17.f1"
56 #define ROM18 "rt1-18.h1"
57 #define ROM19 "rt1-19.k1"
58 #define ROM20 "rt1-20.m1"
59 #define ROM21 "rt1-21.f3"
60 #define ROM22 "rt1-22.h3"
61 #define PROM1 "mb7124e.3r"
62 #define PROM2 "mb7116e.3s"
63 #define PROM3 "mb7138h.4v"
64 #define PROM4 "mb7138h.6v"
65 #define PROM5 "mb7112e.6u"
66 #define MCUROM "rt1-mcu.bin"
73 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
74 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
75 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
76 uint8_t chr_rom[0x60000]; // Character ROM pointer
80 bool trace1 = false; // ditto...
81 bool looking_at_rom = true; // true = R1, false = R2
82 uint32_t banksw1, banksw2; // Bank switch addresses
83 uint16_t game_over_switch; // Game over delay
84 uint16_t dpc; // Debug pc reg...
85 bool show_scr = true; // Whether or not to show background
86 bool enable_cpu = true; // Whether or not to enable CPUs
87 bool irqGoA = true; // IRQ switch for CPU #1
88 bool irqGoB = true; // IRQ switch for CPU #2
90 uint16_t refresh_ = 0; // Crappy global screen stuff...
93 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
94 uint8_t * psg_adrs[16];
95 uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
97 uint8_t * voc_adrs[32];
98 uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
99 uint8_t * fm_adrs[14];
101 fstream tr; // Tracelog hook
102 uint16_t pcx; // Where we at?
106 // Read a byte from memory (without touching PC. Not a Fetch!)
108 uint8_t RdMem(uint16_t addr)
112 // $4000-4300 is RAM shared with the microcontroller...
117 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
129 // Write a byte to memory
131 void WrMem(uint16_t addr, uint8_t b)
134 extern bool charbase; // Needed for screen. Extern it in it??
138 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
142 if (((addr >= 0x4180) && (addr <= 0x4191)) || (addr == 0x4380))
143 printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, addr);
147 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
149 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
151 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
152 if (addr > 0x4284 && addr < 0x42A5 && b)
153 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
156 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
158 game_over_switch = 240; // Set game over delay...
160 if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
163 charbase = false; // Char banksw1
165 charbase = true; // Char banksw2
166 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
168 if (refresh_++ == 1) // 30 Hz...
170 BlitChar(screen, chr_rom, gram1);
171 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
174 // IRQ Ack (may also be frame go...
175 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
178 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
185 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
187 uint8_t RdMemB(uint16_t addr)
194 b = gram1[addr + 0x4000];
195 if (addr > 0x1FFF && addr < 0x6000)
196 b = gram1[addr - 0x2000];
198 b = grom3[banksw2 + (addr - 0x6000)]; // Correct?
208 // Write a byte to memory (2nd processor)
210 void WrMemB(uint16_t addr, uint8_t b)
213 extern bool charbase;
218 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
222 if (((addr >= 0x0180) && (addr <= 0x0191)) || (addr == 0x0380))
223 printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
227 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
229 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
230 if (addr > 0x0284 && addr < 0x02A5 && b)
231 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
233 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
236 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
238 game_over_switch = 240; // Set game over delay...
240 if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
243 gram1[addr + 0x4000] = b;
244 if (addr > 0x1FFF && addr < 0x6000)
245 gram1[addr - 0x2000] = b;
251 // IRQ Ack (may also be frame go...)
252 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
255 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
262 // Generic Load file into image space
263 // (No error checking performed! Responsibility of caller!)
265 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
269 strcpy(path, "./ROMs/");
270 strcat(path, filename);
271 FILE * file = fopen(path, "rb");
276 fread(&mem[address], 1, length, file);
286 bool ReadColorPROMs(void)
291 extern uint32_t palette[256]; // Screen physical palette
292 extern uint8_t ccolor[256][8]; // Character color PROM values
293 extern uint8_t scolor[128][16]; // Sprite color PROM values
295 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
299 for(int i=0; i<256; i++) // Load char pallete with PROM values
301 for(int j=0; j<8; j++)
304 ccolor[i][j] = (uint8_t)ch;
311 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
315 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
317 for(int j=0; j<16; j++)
320 scolor[i][j] = (uint8_t)ch;
327 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
328 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
330 // If open was successful...
333 // Palette is 12-bit RGB, we stretch it to 24-bit
334 for(int i=0; i<256; i++)
340 r = (uint8_t)c1 & 0x0F;
341 g = (uint8_t)c1 >> 4;
343 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
350 // PROM5 has the following in it (tile address decoder):
351 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
352 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
361 bool UnpackFonts(void)
363 // uint8_t b1, b2, b3;
367 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
368 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
371 return false; // Return if not found...
373 for(long i=0; i<0x40000; i+=64)
375 for(int j=0; j<64; j+=8)
377 f1.get(b1); f1.get(b2); f2.get(b3);
378 b3 ^= 0xFF; // Invert top data...
379 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
380 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
381 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
382 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
383 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
384 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
385 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
386 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
393 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
394 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
396 for(long i=0x40000; i<0x60000; i+=64)
398 for(int j=0; j<64; j+=8)
400 f1.get(b1); f1.get(b2); f2.get(b3);
401 b3 ^= 0xFF; // Invert top data
402 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
403 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
404 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
405 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
406 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
407 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
408 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
409 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
416 return true; // Made it!
421 // Get length of sample from WAV format
423 uint32_t GetWAVLength(fstream & file)
428 file.ignore(16); // Skip header BS
430 for(int i=0; i<2; i++)
432 file.get(ch); len = (int)(uint8_t)ch;
433 file.get(ch); len |= (int)(uint8_t)ch << 8;
434 file.get(ch); len |= (int)(uint8_t)ch << 16;
435 file.get(ch); len |= (int)(uint8_t)ch << 24;
437 file.ignore(len + 4); // Skip intermediate data
440 file.get(ch); len = (int)(uint8_t)ch; // & finally get length of data
441 file.get(ch); len |= (int)(uint8_t)ch << 8;
442 file.get(ch); len |= (int)(uint8_t)ch << 16;
443 file.get(ch); len |= (int)(uint8_t)ch << 24;
450 // Load PSG samples from disk
458 for(int i=0; i<16; i++)
462 psg_adrs[i] = NULL; // Zero out pointer
463 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
465 fp.open(file, ios::binary | ios::in); // Attempt to open it...
469 len = GetWAVLength(fp); // Get WAV data length...
470 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
472 if (psg_adrs[i] != NULL)
474 for(int j=0; j<(signed)len; j++)
477 psg_adrs[i][j] = ch; // & load it in...
481 printf("Found sample file: %s\t[Length: %u]\n", file, len);
491 // Load FM samples from disk
499 for(int i=0; i<14; i++)
503 fm_adrs[i] = NULL; // Zero out pointer
504 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
505 fp.open(file, ios::binary | ios::in); // Attempt to open it...
510 len = GetWAVLength(fp); // Get WAV length...
511 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
513 if (fm_adrs[i] != NULL)
515 for(int j=0; j<(signed)len; j++)
518 fm_adrs[i][j] = ch; // & load it in...
522 printf("Found sample file: %s\t[Length: %u]\n", file, len);
533 int main(int argc, char * argv[])
535 InitLog("thunder.log");
537 extern bool disasm; // From 'V6809.CPP'
538 extern bool charbase; // From 'SCREEN.CPP'
542 fstream ff; // Declare fstream without file hooks...
543 bool brk = false, brk2 = false; // Breakpoint set flag
544 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
545 bool running; // CPU running state flag...
546 bool self_test = false; // Self-test switch
547 bool scr_type = false; // false=chars, true=pixels
548 uint16_t debounce = 0; // Key de-bounce counter
549 uint16_t fire_debounce = 0; // Fire button debounce counter
550 uint8_t x; // General placeholder...
551 bool active = true; // Program running flag
553 SDL_Event event; // SDL "event"
554 extern uint8_t palette[768]; // Screen physical palette
555 uint32_t ticks, oldTicks;
557 cout << endl << "THUNDER v"THUNDER_VERSION" ";
558 cout << "by James Hammons" << endl;
559 cout << "Serial #20149417 / Prerelease" << endl;
560 cout << "© 2003, 2014 Underground Software" << endl << endl;
562 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
565 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
567 gram = gram1; grom = grom1; // Needed only for debugger
569 memset(gram, 0, 0x10000);
570 memset(grom, 0, 0x10000);
571 memset(gram2, 0, 0x10000);
572 memset(grom2, 0, 0x10000);
574 game_over_switch = 0; // Init game over delay
576 cout << "Loading ROMs..." << endl;
577 if (!ReadColorPROMs()) // Load virtual PROMs
578 { cout << "Could not open PROM files!" << endl; return -1; }
580 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
581 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
583 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
584 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
586 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
587 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
589 if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
590 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
592 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
593 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
595 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
596 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
598 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
599 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
601 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
602 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
604 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
605 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
607 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
608 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
610 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
611 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
613 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
614 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
616 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
617 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
619 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
620 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
622 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
623 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
625 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
626 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
628 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
629 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
631 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
632 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
634 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
636 cout << "Could not open font files!" << endl;
640 // Load samples if they're there...
644 // Set up V6809 execution contexts
646 memset(&cpu1, 0, sizeof(V6809REGS));
649 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
651 memset(&cpu2, 0, sizeof(V6809REGS));
654 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
656 uint32_t my_clock = 0;
657 running = true; // Set running status...
659 SetRefreshRate(refresh2); // Tell GUI our refresh rate
662 // This is data that is supposed to come from the MCU... So that's why it hangs
663 gram1[0x4182] = 0xA6; // Temp kludge
664 gram1[0x4184] = 0xA6;
665 gram1[0x4183] = 0x00; // More of the same
666 gram1[0x4185] = 0x00;
668 banksw1 = 0; // Will this work?
670 // iclock = 0; // Reset instr clock #1...
671 InitGUI(); // Reset # of coins
673 WriteLog("About to set up screen...\n");
676 oldTicks = SDL_GetTicks();
678 WriteLog("About to set up audio...\n");
680 // This crap SHOULD be in sound.cpp (not yet created)...
681 SDL_AudioSpec desired, obtained;
682 desired.freq = 22050;
683 desired.format = AUDIO_U8;
684 desired.channels = 1;
685 desired.samples = 600;
686 desired.callback = SoundFunc;
687 desired.userdata = NULL;
688 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
689 if (SDL_OpenAudio(&desired, &obtained) < 0)
691 cout << "Couldn't open audio: " << SDL_GetError() << endl;
695 SDL_PauseAudio(0); // Get that audio going!
698 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
699 RenderScreenBuffer();
701 WriteLog("About to enter main loop...\n");
704 HandleGUIDebounce(); // Debounce GUI keys
706 if (game_over_switch)
708 game_over_switch--; // Countdown...
710 if (game_over_switch == 0)
711 gram1[0x4380] = 0; // Kill music!
716 //gram1[0x423D] = self_test; // Reset DSW1-1
717 gram1[0x4268] = 0; // Reset Video test
718 gram1[0x427A] = 0; gram1[0x427C] = 0;
719 //gram1[0x427B] = 0; gram1[0x427D] = 0;
720 gram1[0x427E] = 0;// gram1[0x427F] = 0;
721 gram1[0x4280] = 0;// gram1[0x4281] = 0;
726 gram1[0x4262] = 0; gram1[0x4260] = 0;
729 // SDL key handling...
733 while (SDL_PollEvent(&event))
738 if (event.key.keysym.sym == SDLK_ESCAPE)
740 else if (event.key.keysym.sym == SDLK_F10)
741 gram1[0x41A5]++; // Coin? (F10)
742 else if (event.key.keysym.sym == SDLK_c)
743 gram1[0x418C]++; // ? (C) Start
744 else if (event.key.keysym.sym == SDLK_RIGHT)
746 // Disallow opposite directions @ same time
747 if (gram1[0x4281] == 0)
748 gram1[0x427F] = 1; // Stick right
750 else if (event.key.keysym.sym == SDLK_LEFT)
752 // Disallow opposite directions@same time
753 if (gram1[0x427F] == 0)
754 gram1[0x4281] = 1; // Stick left
756 else if (event.key.keysym.sym == SDLK_UP)
758 // Disallow opposite directions@same time
759 if (gram1[0x427D] == 0)
760 gram1[0x427B] = 1; // Stick up
762 else if (event.key.keysym.sym == SDLK_DOWN)
764 // Disallow opposite directions@same time
765 if (gram1[0x427B] == 0)
766 gram1[0x427D] = 1; // Stick down
768 else if (event.key.keysym.sym == SDLK_q)
769 gram1[0x4276] = 1; // (Q) Jump
770 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
775 if (event.key.keysym.sym == SDLK_RIGHT)
777 else if (event.key.keysym.sym == SDLK_LEFT)
779 else if (event.key.keysym.sym == SDLK_UP)
781 else if (event.key.keysym.sym == SDLK_DOWN)
783 else if (event.key.keysym.sym == SDLK_q)
784 gram1[0x4276] = 0; // (Q) Jump
785 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
793 if (keys[SDLK_ESCAPE])
794 running = false; // ESC to exit...
797 debounce--; // Debounce toggle keys...
802 self_test = !self_test; // Self-test (F1-toggle)
803 debounce = 10; // Key debounce value...
807 gram1[0x4268] = 1; // Video test (F2)
808 debounce = 10; // Key debounce value...
812 scr_type = !scr_type; // Toggle screen (F12)
813 debounce = 10; // Key debounce value...
817 show_scr = !show_scr; // Toggle bkgrnd (F3)
822 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
827 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
828 SetRefreshRate(refresh2); // Inform GUI of refresh
833 debounce = 10; // Key debounce value...
835 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
837 SpawnSound(USERSOUND, SCAMERA);
841 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
851 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
852 if (keys[SDLK_RIGHT]) // Right arrow
855 SelectRight(); // If GUI active...
858 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
859 gram1[0x427F] = 1; // Stick right
865 SelectLeft(); // If GUI active...
868 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
869 gram1[0x4281] = 1; // Left arrow
875 SelectUp(); // If GUI active...
878 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
879 gram1[0x427B] = 1; // Up arrow
885 SelectDown(); // If GUI active...
888 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
889 gram1[0x427D] = 1; // Down arrow
892 if (keys[SDLK_RETURN]) // Return
894 uint8_t retval = UserSelectedSomething();
899 if (retval == REFRESH)
901 refresh2 = !refresh2;
902 SetRefreshRate(refresh2);
907 gram1[0x427A] = 1; // (1)
910 gram1[0x427C] = 1; // (2)
913 gram1[0x427E] = 1; // (3)
916 gram1[0x4280] = 1; // (5)
918 if (keys[SDLK_q] | keys[29])
919 gram1[0x4276] = 1; // (Q) Jump
922 gram1[0x426A] = 1; // (W)
927 if (keys[SDLK_e] | keys[56]) // (E) Fire
933 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
941 gram1[0x426C] = 1; // (R)
944 gram1[0x4262] = 1; // (T)
947 gram1[0x4260] = 1; // (Y)
950 gram1[0x41A5]++; // Coin? (F10)
953 gram1[0x4189]++; // ? (Z) credits l dig
956 gram1[0x418A]++; // ? (X) credits r dig
959 gram1[0x418C]++; // ? (C) Start
962 gram1[0x418D]++; // ? (V)
965 SpawnSound(USERSOUND, 0); // Do user sound (F7)
967 // if (keys[SDLK_F8])
969 // gram1[0x4380] = 0; // (F8) kill music (this worx)
970 // charbase = false; // Switch chars out...
972 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
974 if (keys[SDLK_F11]) // (F11)
976 Execute6809(&cpu1, 10);
977 Execute6809(&cpu2, 10);
980 //F12 is used above, but the values are ignored. So we'll do it here too.
983 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
984 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
987 if (keys[SDLK_d]) // (D) start disassembly
991 gram1[0x5606] = 0x00;
994 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
995 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
999 gram1[0x5FF3] = 0x02;
1000 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1008 // We can do this here because we're not executing the cores yet.
1009 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1010 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1011 // while (cpu1.clock < 25000)
1012 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1013 // 25600 cycles/frame
1014 // Setting interleave to 25 and below causes the V6809 core to hang...
1015 // 32 gets to the title screen before hanging...
1016 // 40 works, until it doesn't... :-P
1019 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1020 for(uint32_t i=0; i<640; i++)
1021 // for(uint32_t i=0; i<1280; i++)
1023 // Gay, but what are ya gonna do?
1024 // There's better ways, such as keeping track of when slave writes to master, etc...
1025 Execute6809(&cpu1, 40);
1026 Execute6809(&cpu2, 40);
1028 } // END: enable_cpu
1030 // Speed throttling happens here...
1031 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1032 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1033 SDL_Delay(1); // Release our timeslice...
1035 oldTicks = SDL_GetTicks();
1036 //cout << "Finished frame..." << endl;
1041 // Deallocate sounds if they were loaded
1042 for(int i=0; i<16; i++)
1044 delete[] psg_adrs[i];
1046 for(int i=0; i<14; i++)
1048 delete[] fm_adrs[i];
1056 Hitachi uC runs at 6.144 MHz
1057 YM2151 runs at 3.579580 MHz
1060 Rolling Thunder Memory map
1061 --------------------------
1062 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1063 map is inferred by program behaviour. The customs also handle internally irq
1066 The main CPU memory map is the same in all games because CUS47 is used by all
1067 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1068 replaced by other chips.
1070 All RAM is shared between main and sub CPU, except for sound RAM which is
1071 shared between main and sound CPU; the portion of object RAM that is overlapped
1072 by sound RAM is used exclusively by the sub CPU.
1076 Address Dir Data Name Description
1077 ------------------- --- -------- --------- -----------------------
1078 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1079 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1080 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1081 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1082 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1083 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1084 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1085 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1086 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1087 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1088 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1089 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1090 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1091 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1092 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1093 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1094 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1095 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1096 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1098 [1] Note that this is partially overlapped by sound RAM
1099 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1104 Address Dir Data Name Description
1105 ------------------- --- -------- --------- -----------------------
1106 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1107 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1108 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1109 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1110 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1111 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1112 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1113 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1114 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1115 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1116 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1117 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1118 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1119 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1121 [1] Only used by Rolling Thunder
1126 Address Dir Data Name Description
1127 ------------------- --- -------- --------- -----------------------
1128 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1129 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1130 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1131 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1132 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1133 0010 0--- --01 ---- n.c.
1134 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1135 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1136 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1137 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1138 1011 0--- ---- ---- W unknown (CUS41)
1139 1011 1--- ---- ---- W unknown (CUS41)
1140 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1145 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1146 in rthunder. The two 6809 in this game synchronize using a semaphore at
1147 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1148 and then increments 5606. While it does its quick things (which require
1149 about 40 clock cycles) it expects CPU2 to clear 5607.
1150 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1151 mode. I haven't investigated on the cause.
1153 - There are two watchdogs, one per CPU (or maybe three). Handling them
1154 separately is necessary to allow entering service mode without manually
1155 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1158 - The sprite hardware buffers spriteram: the program writes the sprite list to
1159 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1160 sprite RAM to signal the chip that the list is complete. The chip will copy
1161 the list from 4-9 to 10-15 and use it from there. This has not been verified
1162 on the real hardware, but it is the most logical way of doing it.
1163 Emulating this behaviour and not using an external buffer is important in
1164 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1165 is not written to. If we buffered spriteram to an external buffer, this would
1166 cause dangling sprites because the buffer would not be updated.
1168 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1169 entering a door. The *closed* door is made of tiles, but the *moving* door is
1170 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1171 there is one frame where neither the tile-based closed door nor the
1172 sprite-based moving door is shown, so it flickers. This behavior has been
1173 confirmed on a real PCB.
1177 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1178 but they don't seem to work as expected. During the first few frames they are
1179 written out of order and hooking them up in the usual way causes the MCU to
1180 stop receiving interrupts.