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"
42 #define ROM1 "rt3-1b.9c"
43 #define ROM2 "rt3-2b.12c"
44 #define ROM3 "rt3-3.12d"
45 #define ROM4 "rt1-4.6b"
46 #define ROM5 "rt1-5.4r"
47 #define ROM6 "rt1-6.4s"
48 #define ROM7 "rt1-7.7r"
49 #define ROM8 "rt1-8.7s"
50 #define ROM9 "rt1-9.12h"
51 #define ROM10 "rt1-10.12k"
52 #define ROM11 "rt1-11.12l"
53 #define ROM12 "rt1-12.12m"
54 #define ROM13 "rt1-13.12p"
55 #define ROM14 "rt1-14.12r"
56 #define ROM15 "rt1-15.12t"
57 #define ROM16 "rt1-16.12u"
58 #define ROM17 "rt1-17.f1"
59 #define ROM18 "rt1-18.h1"
60 #define ROM19 "rt1-19.k1"
61 #define ROM20 "rt1-20.m1"
62 #define ROM21 "rt1-21.f3"
63 #define ROM22 "rt1-22.h3"
64 #define PROM1 "mb7124e.3r"
65 #define PROM2 "mb7116e.3s"
66 #define PROM3 "mb7138h.4v"
67 #define PROM4 "mb7138h.6v"
68 #define PROM5 "mb7112e.6u"
69 #define MCUROM "rt1-mcu.bin"
76 uint8_t * gram, * grom; // Allocate RAM & ROM pointers
77 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000]; // Actual memory
78 uint8_t grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
79 uint8_t chr_rom[0x60000]; // Character ROM pointer
80 uint8_t mcuMem[0x10000]; // 64K for MCU
85 bool trace1 = false; // ditto...
86 bool looking_at_rom = true; // true = R1, false = R2
87 uint32_t banksw1, banksw2; // Bank switch addresses
88 uint16_t game_over_switch; // Game over delay
89 uint16_t dpc; // Debug pc reg...
90 bool show_scr = true; // Whether or not to show background
91 bool enable_cpu = true; // Whether or not to enable CPUs
92 bool irqGoA = true; // IRQ switch for CPU #1
93 bool irqGoB = true; // IRQ switch for CPU #2
95 uint16_t refresh_ = 0; // Crappy global screen stuff...
98 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
99 uint8_t * psg_adrs[16];
100 //uint32_t voc_lens[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101 // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
102 //uint8_t * voc_adrs[32];
103 //uint32_t fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
104 //uint8_t * fm_adrs[14];
106 Byte input1, input2, input3, input4, input5;
108 fstream tr; // Tracelog hook
109 uint16_t pcx; // Where we at?
113 // Read a byte from memory (without touching PC. Not a Fetch!)
115 uint8_t RdMem(uint16_t addr)
119 // $4000-4300 is RAM shared with the microcontroller...
123 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
124 if ((addr >= 0x4000) && (addr <= 0x43FF))
125 return mcuMem[addr - 0x3000];
128 b = data_rom[banksw1 + (addr - 0x6000)]; // Get char data
140 // Write a byte to memory
142 void WrMem(uint16_t addr, uint8_t b)
145 extern bool charbase; // Needed for screen. Extern it in it??
149 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
153 if (((addr >= 0x4180) && (addr <= 0x4191)) || (addr == 0x4380))
154 printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, addr);
158 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
160 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
162 banksw1 = (uint32_t)b << 13; // Set char data bankswitch base address
163 if (addr > 0x4284 && addr < 0x42A5 && b)
164 SpawnSound(PSGSOUND, addr - 0x4285); // Do PSG sound on chans 2, 3
168 SpawnSound(FMSOUND, b); // Do FM sound on channel 4
170 game_over_switch = 240; // Set game over delay...
173 // if (addr < 0x423D || addr > 0x425C) // Protect writes to DSWs
174 //if (addr == 0x4191)
176 // printf("V6809: Writing %02X to $4191...\n", b);
178 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
179 if ((addr >= 0x4000) && (addr <= 0x43FF))
180 mcuMem[addr - 0x3000] = b;
185 charbase = false; // Char banksw1
187 charbase = true; // Char banksw2
188 if (addr == 0x8400) // Frame go strobe? VBlank acknowledge?
190 if (refresh_++ == 1) // 30 Hz...
192 BlitChar(screen, chr_rom, gram1);
193 refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz...
196 // IRQ Ack (may also be frame go...
197 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
200 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
207 // Read a byte from memory (without touching PC. Not a Fetch!) (2nd processor)
209 uint8_t RdMemB(uint16_t addr)
216 b = gram1[addr + 0x4000];
217 if (addr > 0x1FFF && addr < 0x6000)
218 b = gram1[addr - 0x2000];
220 b = grom3[banksw2 + (addr - 0x6000)];
230 // Write a byte to memory (2nd processor)
232 void WrMemB(uint16_t addr, uint8_t b)
235 extern bool charbase;
240 WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
244 if (((addr >= 0x0180) && (addr <= 0x0191)) || (addr == 0x0380))
245 printf("WriteMem: CPU #2 writing $%02X to $%04X...\n", b, addr);
250 SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
252 SpawnSound(GAMESOUND, gram1[0x6600], 1); // Do voice chan 2
253 if (addr > 0x0284 && addr < 0x02A5 && b)
254 SpawnSound(PSGSOUND, addr - 0x0285); // Do PSG sound on chans 2, 3
257 banksw2 = (uint32_t)(b & 0x03) << 13; // Set sprite data bank switch
261 SpawnSound(FMSOUND, b); // Do FM sound on chan 4
263 game_over_switch = 240; // Set game over delay...
266 // if (addr < 0x023D || addr > 0x025C) // Protect writes against DSWs
269 gram1[addr + 0x4000] = b;
270 if (addr > 0x1FFF && addr < 0x6000)
271 gram1[addr - 0x2000] = b;
272 // if (addr > 0x5FFF)
277 // IRQ Ack (may also be frame go...)
278 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
281 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
287 uint8_t MCUReadMemory(uint16_t address)
291 // printf("V63701 read $%02X from $%02X...\n", memory[address], address);
292 return InternalRegisterRead(address);
294 else if ((address >= 0x2000) && (address <= 0x2001))
296 // return 0; //for now
299 // Various joystick + buttons; all are active low.
300 else if (address == 0x2020)
302 //printf("MCURead: Returning $%02X from $2020...\n", input1.byte);
305 else if (address == 0x2021)
307 //extern bool V63701LogGo;
308 //if (input2.byte == 0xDF)
309 // V63701LogGo = true;
311 //printf("MCURead: Returning $%02X from $2021...\n", input2.byte);
314 // This is DSW1 & 2. All switch settings are active low.
315 else if (address == 0x2030)
317 //printf("MCURead: Returning $%02X from $2030...\n", input4.byte);
320 else if (address == 0x2031)
322 //printf("MCURead: Returning $%02X from $2031...\n", input5.byte);
326 return mcuMem[address];
330 void MCUWriteMemory(uint16_t address, uint8_t data)
332 static uint8_t ymRegister;
337 // printf("V63701 wrote $%02X to $%02X...\n", data, address);
338 InternalRegisterWrite(address, data);
343 //if (address == 0x1191)
345 // printf("V63701: Writing $%02X to $1191...\n", data);
348 // Translate local reads @ $1000-$13FF to $4000-$43FF in shared RAM
349 if ((address >= 0x1000) && (address <= 0x13FF))
351 gram1[0x3000 + address] = data;
356 if (((address >= 0x4000) && (address <= 0xBFFF))
357 || (address >= 0xF000))
359 else if (address == 0x2000)
364 else if (address == 0x2001)
366 //printf("Writing $%02X to YM2151 register $%02X...\n", data, ymRegister);
367 YMWriteReg(0, ymRegister, data);
371 // RAM is from $0 - $3FFF, $C000 - $EFFF
372 mcuMem[address] = data;
376 uint8_t V63701ReadPort1(void)
378 // printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
383 uint8_t V63701ReadPort2(void)
389 void V63701WritePort1(uint8_t data)
391 // printf("V63701WritePort1: Wrote $%02X...\n", data);
395 void V63701WritePort2(uint8_t data)
397 // printf("V63701WritePort2: Wrote $%02X...\n", data);
402 // Generic Load file into image space
403 // (No error checking performed! Responsibility of caller!)
405 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
409 strcpy(path, "./ROMs/");
410 strcat(path, filename);
411 FILE * file = fopen(path, "rb");
416 fread(&mem[address], 1, length, file);
426 bool ReadColorPROMs(void)
431 extern uint32_t palette[256]; // Screen physical palette
432 extern uint8_t ccolor[256][8]; // Character color PROM values
433 extern uint8_t scolor[128][16]; // Sprite color PROM values
435 ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
439 for(int i=0; i<256; i++) // Load char pallete with PROM values
441 for(int j=0; j<8; j++)
444 ccolor[i][j] = (uint8_t)ch;
451 ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
455 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
457 for(int j=0; j<16; j++)
460 scolor[i][j] = (uint8_t)ch;
467 ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
468 ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
470 // If open was successful...
473 // Palette is 12-bit RGB, we stretch it to 24-bit
474 for(int i=0; i<256; i++)
479 uint8_t r = (uint8_t)c1 & 0x0F;
480 uint8_t g = (uint8_t)c1 >> 4;
481 uint8_t b = (uint8_t)c2;
482 palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
489 // PROM5 has the following in it (tile address decoder):
490 // 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
491 // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
500 bool UnpackFonts(void)
502 // uint8_t b1, b2, b3;
506 f1.open("./ROMs/"ROM7, ios::binary | ios::in);
507 f2.open("./ROMs/"ROM8, ios::binary | ios::in);
510 return false; // Return if not found...
512 for(long i=0; i<0x40000; i+=64)
514 for(int j=0; j<64; j+=8)
516 f1.get(b1); f1.get(b2); f2.get(b3);
517 b3 ^= 0xFF; // Invert top data...
518 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
519 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
520 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
521 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
522 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
523 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
524 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
525 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
532 f1.open("./ROMs/"ROM5, ios::binary | ios::in);
533 f2.open("./ROMs/"ROM6, ios::binary | ios::in);
535 for(long i=0x40000; i<0x60000; i+=64)
537 for(int j=0; j<64; j+=8)
539 f1.get(b1); f1.get(b2); f2.get(b3);
540 b3 ^= 0xFF; // Invert top data
541 chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
542 chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
543 chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
544 chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
545 chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
546 chr_rom[i+j+5] = (b3 & 0x04) | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
547 chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
548 chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
555 return true; // Made it!
560 // Get length of sample from WAV format
562 uint32_t GetWAVLength(fstream & file)
567 file.ignore(16); // Skip header BS
569 for(int i=0; i<2; i++)
571 file.get(ch); len = (int)(uint8_t)ch;
572 file.get(ch); len |= (int)(uint8_t)ch << 8;
573 file.get(ch); len |= (int)(uint8_t)ch << 16;
574 file.get(ch); len |= (int)(uint8_t)ch << 24;
576 // Skip intermediate data
577 file.ignore(len + 4);
580 // & finally get length of data
581 file.get(ch); len = (int)(uint8_t)ch;
582 file.get(ch); len |= (int)(uint8_t)ch << 8;
583 file.get(ch); len |= (int)(uint8_t)ch << 16;
584 file.get(ch); len |= (int)(uint8_t)ch << 24;
591 // Load PSG samples from disk
599 for(int i=0; i<16; i++)
603 psg_adrs[i] = NULL; // Zero out pointer
604 sprintf(file, "./sounds/psg%i.wav", i); // Create filename
606 fp.open(file, ios::binary | ios::in); // Attempt to open it...
610 len = GetWAVLength(fp); // Get WAV data length...
611 psg_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
613 if (psg_adrs[i] != NULL)
615 for(int j=0; j<(signed)len; j++)
618 psg_adrs[i][j] = ch; // & load it in...
622 printf("Found sample file: %s\t[Length: %u]\n", file, len);
633 // Load FM samples from disk
641 for(int i=0; i<14; i++)
645 fm_adrs[i] = NULL; // Zero out pointer
646 sprintf(file, "./sounds/fm%i.wav", i); // Create filename
647 fp.open(file, ios::binary | ios::in); // Attempt to open it...
652 len = GetWAVLength(fp); // Get WAV length...
653 fm_adrs[i] = new uint8_t[len]; // Attempt to allocate space...
655 if (fm_adrs[i] != NULL)
657 for(int j=0; j<(signed)len; j++)
660 fm_adrs[i][j] = ch; // & load it in...
664 printf("Found sample file: %s\t[Length: %u]\n", file, len);
676 int main(int argc, char * argv[])
678 InitLog("thunder.log");
680 extern bool disasm; // From 'V6809.CPP'
681 extern bool charbase; // From 'SCREEN.CPP'
685 fstream ff; // Declare fstream without file hooks...
686 bool brk = false, brk2 = false; // Breakpoint set flag
687 uint16_t brkpnt, brkpnt2; // Where the breakpoint is...
688 bool running; // CPU running state flag...
689 bool self_test = false; // Self-test switch
690 bool scr_type = false; // false=chars, true=pixels
691 uint16_t debounce = 0; // Key de-bounce counter
692 uint16_t fire_debounce = 0; // Fire button debounce counter
693 uint8_t x; // General placeholder...
694 bool active = true; // Program running flag
696 SDL_Event event; // SDL "event"
697 extern uint8_t palette[768]; // Screen physical palette
698 uint32_t ticks, oldTicks;
700 cout << endl << "THUNDER v"THUNDER_VERSION" ";
701 cout << "by James Hammons" << endl;
702 cout << "Serial #20149417 / Prerelease" << endl;
703 cout << "© 2003, 2014 Underground Software" << endl << endl;
705 cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
708 // SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
710 gram = gram1; grom = grom1; // Needed only for debugger
712 memset(gram, 0, 0x10000);
713 memset(grom, 0, 0x10000);
714 memset(gram2, 0, 0x10000);
715 memset(grom2, 0, 0x10000);
717 game_over_switch = 0; // Init game over delay
719 cout << "Loading ROMs..." << endl;
720 if (!ReadColorPROMs()) // Load virtual PROMs
721 { cout << "Could not open PROM files!" << endl; return -1; }
723 if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
724 { cout << "Could not open file '" << ROM1 << "'!" << endl; return -1; }
726 if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
727 { cout << "Could not open file '" << ROM2 << "'!" << endl; return -1; }
729 if (!LoadImg(ROM3, grom3, 0, 0x8000)) // Load 3rd ROM into its own space
730 { cout << "Could not open file '" << ROM3 << "'!" << endl; return -1; }
732 // if (!LoadImg(ROM4, grom4, 0, 0x8000)) // Load 4rd ROM into its own space
733 // { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
735 if (!LoadImg(ROM17, data_rom, 0, 0x10000)) // Load 17th ROM
736 { cout << "Could not open file '" << ROM17 << "'!" << endl; return -1; }
738 if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000)) // Load 18th ROM
739 { cout << "Could not open file '" << ROM18 << "'!" << endl; return -1; }
741 if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000)) // Load 19th ROM
742 { cout << "Could not open file '" << ROM19 << "'!" << endl; return -1; }
744 if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000)) // Load 20th ROM
745 { cout << "Could not open file '" << ROM20 << "'!" << endl; return -1; }
747 if (!LoadImg(ROM9, spr_rom, 0, 0x10000)) // Load 9th ROM
748 { cout << "Could not open file '" << ROM9 << "'!" << endl; return -1; }
750 if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000)) // Load 10th ROM
751 { cout << "Could not open file '" << ROM10 << "'!" << endl; return -1; }
753 if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000)) // Load 11th ROM
754 { cout << "Could not open file '" << ROM11 << "'!" << endl; return -1; }
756 if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000)) // Load 12th ROM
757 { cout << "Could not open file '" << ROM12 << "'!" << endl; return -1; }
759 if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000)) // Load 13th ROM
760 { cout << "Could not open file '" << ROM13 << "'!" << endl; return -1; }
762 if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000)) // Load 14th ROM
763 { cout << "Could not open file '" << ROM14 << "'!" << endl; return -1; }
765 if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000)) // Load 15th ROM
766 { cout << "Could not open file '" << ROM15 << "'!" << endl; return -1; }
768 if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000)) // Load 16th ROM
769 { cout << "Could not open file '" << ROM16 << "'!" << endl; return -1; }
771 if (!LoadImg(ROM21, voice_rom, 0, 0x10000)) // Load 21st ROM
772 { cout << "Could not open file '" << ROM21 << "'!" << endl; return -1; }
774 if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000)) // Load 22nd ROM
775 { cout << "Could not open file '" << ROM22 << "'!" << endl; return -1; }
777 if (!UnpackFonts()) // Load 5, 6, 7, 8th ROMs
779 cout << "Could not open font files!" << endl;
783 // Load MCU program + data
784 if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000)) // Load MCU ROM
785 { cout << "Could not open file '" << MCUROM << "'!" << endl; return -1; }
787 if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000)) // Load 4th ROM
788 { cout << "Could not open file '" << ROM4 << "'!" << endl; return -1; }
790 // Load samples if they're there...
794 // Set up V6809 execution contexts
796 memset(&cpu1, 0, sizeof(V6809REGS));
799 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
801 memset(&cpu2, 0, sizeof(V6809REGS));
804 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
806 memset(&mcu, 0, sizeof(V63701REGS));
807 mcu.RdMem = MCUReadMemory;
808 mcu.WrMem = MCUWriteMemory;
809 mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
811 uint32_t my_clock = 0;
812 running = true; // Set running status...
814 SetRefreshRate(refresh2); // Tell GUI our refresh rate
816 // Set all inputs to inactive...
817 input1.byte = input2.byte = input3.byte = input4.byte = input5.byte = 0xFF;
821 // This is data that is supposed to come from the MCU... So that's why it hangs
822 gram1[0x4182] = 0xA6; // Temp kludge
823 gram1[0x4184] = 0xA6;
824 gram1[0x4183] = 0x00; // More of the same
825 gram1[0x4185] = 0x00;
827 banksw1 = 0; // Will this work?
829 // iclock = 0; // Reset instr clock #1...
830 InitGUI(); // Reset # of coins
832 WriteLog("About to set up screen...\n");
835 oldTicks = SDL_GetTicks();
837 WriteLog("About to set up audio...\n");
839 // This crap SHOULD be in sound.cpp (not yet created)...
840 SDL_AudioSpec desired, obtained;
841 desired.freq = 22050;
842 desired.format = AUDIO_U8;
843 desired.channels = 1;
844 desired.samples = 600;
845 desired.callback = SoundFunc;
846 desired.userdata = NULL;
847 // Also, should check to see if it got the hardware it needed, correct sample size, etc.
848 if (SDL_OpenAudio(&desired, &obtained) < 0)
850 cout << "Couldn't open audio: " << SDL_GetError() << endl;
854 SDL_PauseAudio(0); // Get that audio going!
859 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
860 RenderScreenBuffer();
862 WriteLog("About to enter main loop...\n");
865 HandleGUIDebounce(); // Debounce GUI keys
868 if (game_over_switch)
870 game_over_switch--; // Countdown...
872 if (game_over_switch == 0)
873 gram1[0x4380] = 0; // Kill music!
877 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
878 // $423D - $425B by the MCU
882 //gram1[0x423D] = self_test; // Reset DSW1-1
883 // gram1[0x4268] = 0; // Reset Video test
884 // gram1[0x427A] = 0; gram1[0x427C] = 0;
885 //gram1[0x427B] = 0; gram1[0x427D] = 0;
886 // gram1[0x427E] = 0;// gram1[0x427F] = 0;
887 // gram1[0x4280] = 0;// gram1[0x4281] = 0;
889 // gram1[0x426A] = 0;
891 // gram1[0x426C] = 0;
892 // gram1[0x4262] = 0; gram1[0x4260] = 0;
895 // SDL key handling...
899 while (SDL_PollEvent(&event))
904 if (event.key.keysym.sym == SDLK_ESCAPE)
906 // Do PCX snapshot (F4)
907 else if (event.key.keysym.sym == SDLK_F4)
909 // SpawnSound(USERSOUND, SCAMERA);
914 else if (event.key.keysym.sym == SDLK_5)
916 else if (event.key.keysym.sym == SDLK_1)
918 else if (event.key.keysym.sym == SDLK_RIGHT)
920 else if (event.key.keysym.sym == SDLK_LEFT)
922 else if (event.key.keysym.sym == SDLK_UP)
924 else if (event.key.keysym.sym == SDLK_DOWN)
926 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
928 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
931 else if (event.key.keysym.sym == SDLK_1)
933 else if (event.key.keysym.sym == SDLK_2)
935 else if (event.key.keysym.sym == SDLK_3)
937 else if (event.key.keysym.sym == SDLK_4)
939 else if (event.key.keysym.sym == SDLK_5)
941 else if (event.key.keysym.sym == SDLK_6)
943 else if (event.key.keysym.sym == SDLK_7)
945 else if (event.key.keysym.sym == SDLK_8)
948 else if (event.key.keysym.sym == SDLK_q)
950 else if (event.key.keysym.sym == SDLK_w)
952 else if (event.key.keysym.sym == SDLK_e)
954 else if (event.key.keysym.sym == SDLK_r)
956 else if (event.key.keysym.sym == SDLK_t)
958 else if (event.key.keysym.sym == SDLK_y)
960 else if (event.key.keysym.sym == SDLK_u)
962 else if (event.key.keysym.sym == SDLK_i)
965 else if (event.key.keysym.sym == SDLK_a)
967 else if (event.key.keysym.sym == SDLK_s)
969 else if (event.key.keysym.sym == SDLK_d)
971 else if (event.key.keysym.sym == SDLK_f)
973 else if (event.key.keysym.sym == SDLK_g)
980 if (event.key.keysym.sym == SDLK_5)
982 else if (event.key.keysym.sym == SDLK_1)
984 else if (event.key.keysym.sym == SDLK_RIGHT)
986 else if (event.key.keysym.sym == SDLK_LEFT)
988 else if (event.key.keysym.sym == SDLK_UP)
990 else if (event.key.keysym.sym == SDLK_DOWN)
992 else if (event.key.keysym.sym == SDLK_q) // (Q) Jump
994 else if (event.key.keysym.sym == SDLK_e) // (E) Fire
997 if (event.key.keysym.sym == SDLK_1)
999 else if (event.key.keysym.sym == SDLK_2)
1001 else if (event.key.keysym.sym == SDLK_3)
1003 else if (event.key.keysym.sym == SDLK_4)
1005 else if (event.key.keysym.sym == SDLK_5)
1007 else if (event.key.keysym.sym == SDLK_6)
1009 else if (event.key.keysym.sym == SDLK_7)
1011 else if (event.key.keysym.sym == SDLK_8)
1014 else if (event.key.keysym.sym == SDLK_q)
1016 else if (event.key.keysym.sym == SDLK_w)
1018 else if (event.key.keysym.sym == SDLK_e)
1020 else if (event.key.keysym.sym == SDLK_r)
1022 else if (event.key.keysym.sym == SDLK_t)
1024 else if (event.key.keysym.sym == SDLK_y)
1026 else if (event.key.keysym.sym == SDLK_u)
1028 else if (event.key.keysym.sym == SDLK_i)
1031 else if (event.key.keysym.sym == SDLK_a)
1033 else if (event.key.keysym.sym == SDLK_s)
1035 else if (event.key.keysym.sym == SDLK_d)
1037 else if (event.key.keysym.sym == SDLK_f)
1039 else if (event.key.keysym.sym == SDLK_g)
1048 if (keys[SDLK_ESCAPE])
1049 running = false; // ESC to exit...
1052 debounce--; // Debounce toggle keys...
1057 self_test = !self_test; // Self-test (F1-toggle)
1058 debounce = 10; // Key debounce value...
1062 gram1[0x4268] = 1; // Video test (F2)
1063 debounce = 10; // Key debounce value...
1067 scr_type = !scr_type; // Toggle screen (F12)
1068 debounce = 10; // Key debounce value...
1072 show_scr = !show_scr; // Toggle bkgrnd (F3)
1077 enable_cpu = !enable_cpu; // Toggle CPUs (F6)
1082 refresh2 = !refresh2; // Toggle 30/60Hz (F5)
1083 SetRefreshRate(refresh2); // Inform GUI of refresh
1088 debounce = 10; // Key debounce value...
1090 if (keys[SDLK_F4]) // Do PCX snapshot (F4)
1092 SpawnSound(USERSOUND, SCAMERA);
1096 if (keys[SDLK_TAB]) // Tab active/deactivate GUI
1106 //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4)
1107 if (keys[SDLK_RIGHT]) // Right arrow
1110 SelectRight(); // If GUI active...
1113 if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time
1114 gram1[0x427F] = 1; // Stick right
1117 if (keys[SDLK_LEFT])
1120 SelectLeft(); // If GUI active...
1123 if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time
1124 gram1[0x4281] = 1; // Left arrow
1130 SelectUp(); // If GUI active...
1133 if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time
1134 gram1[0x427B] = 1; // Up arrow
1137 if (keys[SDLK_DOWN])
1140 SelectDown(); // If GUI active...
1143 if (!keys[SDLK_UP]) // Disallow opposite directions@same time
1144 gram1[0x427D] = 1; // Down arrow
1147 if (keys[SDLK_RETURN]) // Return
1149 uint8_t retval = UserSelectedSomething();
1154 if (retval == REFRESH)
1156 refresh2 = !refresh2;
1157 SetRefreshRate(refresh2);
1162 gram1[0x427A] = 1; // (1)
1165 gram1[0x427C] = 1; // (2)
1168 gram1[0x427E] = 1; // (3)
1171 gram1[0x4280] = 1; // (5)
1173 if (keys[SDLK_q] | keys[29])
1174 gram1[0x4276] = 1; // (Q) Jump
1177 gram1[0x426A] = 1; // (W)
1182 if (keys[SDLK_e] | keys[56]) // (E) Fire
1188 if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun
1196 gram1[0x426C] = 1; // (R)
1199 gram1[0x4262] = 1; // (T)
1202 gram1[0x4260] = 1; // (Y)
1205 gram1[0x41A5]++; // Coin? (F10)
1208 gram1[0x4189]++; // ? (Z) credits l dig
1211 gram1[0x418A]++; // ? (X) credits r dig
1214 gram1[0x418C]++; // ? (C) Start
1217 gram1[0x418D]++; // ? (V)
1220 SpawnSound(USERSOUND, 0); // Do user sound (F7)
1222 // if (keys[SDLK_F8])
1224 // gram1[0x4380] = 0; // (F8) kill music (this worx)
1225 // charbase = false; // Switch chars out...
1227 // if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc
1229 if (keys[SDLK_F11]) // (F11)
1231 Execute6809(&cpu1, 10);
1232 Execute6809(&cpu2, 10);
1235 //F12 is used above, but the values are ignored. So we'll do it here too.
1238 cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1239 cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1242 if (keys[SDLK_d]) // (D) start disassembly
1246 gram1[0x5606] = 0x00;
1249 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1250 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1254 gram1[0x5FF3] = 0x02;
1255 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1263 // We can do this here because we're not executing the cores yet.
1264 cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1265 cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1266 mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
1267 // while (cpu1.clock < 25000)
1268 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1269 // 25600 cycles/frame
1270 // Setting interleave to 25 and below causes the V6809 core to hang...
1271 // 32 gets to the title screen before hanging...
1272 // 40 works, until it doesn't... :-P
1275 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1276 for(uint32_t i=0; i<640; i++)
1277 // for(uint32_t i=0; i<1280; i++)
1279 // Gay, but what are ya gonna do?
1280 // There's better ways, such as keeping track of when slave writes to master, etc...
1281 Execute6809(&cpu1, 40);
1282 Execute6809(&cpu2, 40);
1284 // MCU runs at 1,536,000 Hz
1285 // 1536000 / 60 / 640 == 40
1286 Execute63701(&mcu, 40);
1288 } // END: enable_cpu
1290 // Speed throttling happens here...
1291 while (SDL_GetTicks() - oldTicks < 16) // Actually, it's 16.66... Need to account for that somehow
1292 // while (SDL_GetTicks() - oldTicks < 32) // Actually, it's 16.66... Need to account for that somehow
1293 SDL_Delay(1); // Release our timeslice...
1295 oldTicks = SDL_GetTicks();
1296 //cout << "Finished frame..." << endl;
1301 // Deallocate sounds if they were loaded
1302 for(int i=0; i<16; i++)
1304 delete[] psg_adrs[i];
1307 for(int i=0; i<14; i++)
1309 delete[] fm_adrs[i];
1318 Hitachi uC runs at 6.144 MHz
1319 YM2151 runs at 3.579580 MHz
1322 Rolling Thunder Memory map
1323 --------------------------
1324 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1325 map is inferred by program behaviour. The customs also handle internally irq
1328 The main CPU memory map is the same in all games because CUS47 is used by all
1329 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1330 replaced by other chips.
1332 All RAM is shared between main and sub CPU, except for sound RAM which is
1333 shared between main and sound CPU; the portion of object RAM that is overlapped
1334 by sound RAM is used exclusively by the sub CPU.
1338 Address Dir Data Name Description
1339 ------------------- --- -------- --------- -----------------------
1340 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0 tilemap 0/1 RAM (shared with sub CPU)
1341 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1 tilemap 2/3 RAM (shared with sub CPU)
1342 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND sound RAM (through CUS30, shared with MCU)
1343 0100 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1344 0100 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1345 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT work RAM (shared with sub CPU) [1]
1346 0101 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1347 011x xxxx xxxx xxxx R xxxxxxxx ROM 9D program ROM (banked) [2]
1348 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 9C program ROM
1349 1000 00-- ---- ---- W -------- watchdog reset (RES generated by CUS47)
1350 1000 01-- ---- ---- W -------- main CPU irq acknowledge (IRQ generated by CUS47)
1351 1000 1x-- ---- ---- W -------- BANK tile gfx bank select (data is in A10) (latch in CUS47)
1352 1001 00-- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1353 1001 00-- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1354 1001 00-- ---- --11 W ------xx BAMNKM ROM 9D bank select
1355 1001 01-- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1356 1001 01-- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1357 1001 01-- ---- --11 W ------xx BAMNKS ROM 12D bank select
1358 1100 00-- ---- ---- W xxxxxxxx BACKCOLOR background color
1360 [1] Note that this is partially overlapped by sound RAM
1361 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1366 Address Dir Data Name Description
1367 ------------------- --- -------- --------- -----------------------
1368 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ work RAM (shared with main CPU)
1369 0001 1xxx xxxx xxxx R/W xxxxxxxx portion holding sprite registers
1370 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0 tilemap 0/1 RAM (shared with main CPU)
1371 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1 tilemap 2/3 RAM (shared with main CPU)
1372 011x xxxx xxxx xxxx R xxxxxxxx ROM 12D program ROM (banked) [1]
1373 1xxx xxxx xxxx xxxx R xxxxxxxx ROM 12C program ROM
1374 1000 0--- ---- ---- W -------- watchdog reset (MRESET generated by CUS41)
1375 1000 1--- ---- ---- W -------- main CPU irq acknowledge (generated by CUS41)
1376 1101 0--- ---- -x0x W xxxxxxxx LATCH0 tilemap 0/1 X scroll + priority
1377 1101 0--- ---- -x10 W xxxxxxxx LATCH0 tilemap 0/1 Y scroll
1378 1101 0--- ---- --11 W ------xx BAMNKM ROM 9D bank select
1379 1101 1--- ---- -x0x W xxxxxxxx LATCH1 tilemap 2/3 X scroll + priority
1380 1101 1--- ---- -x10 W xxxxxxxx LATCH1 tilemap 2/3 Y scroll
1381 1101 1--- ---- --11 W ------xx BAMNKS ROM 12D bank select
1383 [1] Only used by Rolling Thunder
1388 Address Dir Data Name Description
1389 ------------------- --- -------- --------- -----------------------
1390 0000 0000 xxxx xxxx MCU internal registers, timers, ports and RAM
1391 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F sound RAM (through CUS30, partially shared with main CPU)
1392 0001 0000 xxxx xxxx R/W xxxxxxxx portion holding the sound wave data
1393 0001 0001 00xx xxxx R/W xxxxxxxx portion holding the sound registers
1394 0010 0--- --00 ---x R/W xxxxxxxx YMCS YM2151
1395 0010 0--- --01 ---- n.c.
1396 0010 0--- --10 ---- R xxxxxxxx PORTA switch inputs
1397 0010 0--- --11 ---- R xxxxxxxx PORTB dip switches
1398 01xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (lower half)
1399 10xx xxxx xxxx xxxx R xxxxxxxx ROM 6B program ROM (upper half)
1400 1011 0--- ---- ---- W unknown (CUS41)
1401 1011 1--- ---- ---- W unknown (CUS41)
1402 1111 xxxx xxxx xxxx R xxxxxxxx MCU internal ROM
1407 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1408 in rthunder. The two 6809 in this game synchronize using a semaphore at
1409 5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1410 and then increments 5606. While it does its quick things (which require
1411 about 40 clock cycles) it expects CPU2 to clear 5607.
1412 Raising the interleave factor to 1000 makes wndrmomo crash during attract
1413 mode. I haven't investigated on the cause.
1415 - There are two watchdogs, one per CPU (or maybe three). Handling them
1416 separately is necessary to allow entering service mode without manually
1417 resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1420 - The sprite hardware buffers spriteram: the program writes the sprite list to
1421 offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1422 sprite RAM to signal the chip that the list is complete. The chip will copy
1423 the list from 4-9 to 10-15 and use it from there. This has not been verified
1424 on the real hardware, but it is the most logical way of doing it.
1425 Emulating this behaviour and not using an external buffer is important in
1426 rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1427 is not written to. If we buffered spriteram to an external buffer, this would
1428 cause dangling sprites because the buffer would not be updated.
1430 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1431 entering a door. The *closed* door is made of tiles, but the *moving* door is
1432 made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1433 there is one frame where neither the tile-based closed door nor the
1434 sprite-based moving door is shown, so it flickers. This behavior has been
1435 confirmed on a real PCB.
1439 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1440 but they don't seem to work as expected. During the first few frames they are
1441 written out of order and hooking them up in the usual way causes the MCU to
1442 stop receiving interrupts.