]> Shamusworld >> Repos - thunder/blob - src/thunder.cpp
18cf8df7b3b212602b1fbc5e44c958949f8cd0ac
[thunder] / src / thunder.cpp
1 //
2 // Thunder: A Rolling Thunder Emulator
3 //
4 // by James Hammons
5 // (C) 2004, 2014 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
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
15 //
16
17 #define THUNDER_VERSION         "1.1.0"
18
19 #include <SDL2/SDL.h>
20 #include <iostream>
21 #include <iomanip>
22 #include <fstream>
23 #include <string>
24 #include <new>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdint.h>
28 #include <time.h>
29 #include "gui.h"
30 #include "log.h"
31 #include "psg.h"
32 #include "screen.h"
33 #include "sound.h"
34 #include "v63701.h"
35 #include "v6809.h"
36 #include "video.h"
37 #include "ym2151.h"
38
39
40 using namespace std;
41
42
43 #define ROM1   "rt3-1b.9c"
44 #define ROM2   "rt3-2b.12c"
45 #define ROM3   "rt3-3.12d"
46 #define ROM4   "rt1-4.6b"
47 #define ROM5   "rt1-5.4r"
48 #define ROM6   "rt1-6.4s"
49 #define ROM7   "rt1-7.7r"
50 #define ROM8   "rt1-8.7s"
51 #define ROM9   "rt1-9.12h"
52 #define ROM10  "rt1-10.12k"
53 #define ROM11  "rt1-11.12l"
54 #define ROM12  "rt1-12.12m"
55 #define ROM13  "rt1-13.12p"
56 #define ROM14  "rt1-14.12r"
57 #define ROM15  "rt1-15.12t"
58 #define ROM16  "rt1-16.12u"
59 #define ROM17  "rt1-17.f1"
60 #define ROM18  "rt1-18.h1"
61 #define ROM19  "rt1-19.k1"
62 #define ROM20  "rt1-20.m1"
63 #define ROM21  "rt1-21.f3"
64 #define ROM22  "rt1-22.h3"
65 #define PROM1  "mb7124e.3r"
66 #define PROM2  "mb7116e.3s"
67 #define PROM3  "mb7138h.4v"
68 #define PROM4  "mb7138h.6v"
69 #define PROM5  "mb7112e.6u"
70 #define MCUROM "rt1-mcu.bin"
71
72
73 // Global defines
74
75 SDL_Surface * screen;
76
77 uint8_t * gram, * grom;                 // Allocate RAM & ROM pointers
78 // Actual memory
79 uint8_t gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000];
80 uint8_t grom3[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
81 uint8_t chr_rom[0x60000];               // Character ROM pointer
82 uint8_t mcuMem[0x10000];                // 64K for MCU
83
84 V6809REGS cpu1, cpu2;                   // CPU execution contexts
85 V63701REGS mcu;
86
87 bool trace1 = false;                    // ditto...
88 bool looking_at_rom = true;             // true = R1, false = R2
89 uint32_t banksw1, banksw2;              // Bank switch addresses
90 uint16_t game_over_switch;              // Game over delay
91 uint16_t dpc;                                   // Debug pc reg...
92 bool show_scr = true;                   // Whether or not to show background
93 bool enable_cpu = true;                 // Whether or not to enable CPUs
94 bool irqGoA = true;                             // IRQ switch for CPU #1
95 bool irqGoB = true;                             // IRQ switch for CPU #2
96
97 uint16_t refresh_ = 0;                  // Crappy global screen stuff...
98 bool refresh2 = true;
99
100 uint32_t psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
101 uint8_t * psg_adrs[16];
102
103 Byte input1, input2, input3, input4, input5;
104
105 fstream tr;                                                     // Tracelog hook
106 uint16_t pcx;                                           // Where we at?
107
108 // Function prototypes
109 uint8_t MCUReadMemory(uint16_t address);
110 void MCUWriteMemory(uint16_t address, uint8_t data);
111
112
113 //
114 // Read a byte from memory
115 //
116 uint8_t RdMem(uint16_t addr)
117 {
118         uint8_t b;
119
120         if (addr < 0x8000)
121         {
122                 // Memory shared with MCU (CPU #1 only! CPU #2 does not)
123                 if ((addr >= 0x4000) && (addr <= 0x43FF))
124                         return mcuMem[addr - 0x3000];
125 //              if ((addr >= 0x4000) && (addr <= 0x41FF))
126 //                      return MCUReadMemory(addr - 0x3000);
127 //              if ((addr >= 0x4200) && (addr <= 0x43FF))
128 //                      return mcuMem[addr - 0x3000];
129
130                 if (addr >= 0x6000)
131                         return data_rom[banksw1 + (addr - 0x6000)];     // Get char data
132                 else
133                         return gram1[addr];
134         }
135
136         return grom1[addr];
137 }
138
139
140 //
141 // Write a byte to memory
142 //
143 void WrMem(uint16_t address, uint8_t data)
144 {
145         extern bool disasm;
146         extern bool charbase;   // Needed for screen. Extern it in it??
147 #if 0
148         if (address == 0x4182)
149         {
150                 WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
151         }
152 #endif
153 #if 0
154 if (((address >= 0x4180) && (address <= 0x4191)) || (address == 0x4380))
155         printf("WriteMem: CPU #1 writing $%02X to $%04X...\n", b, address);
156 #endif
157
158         if (address == 0x6000)
159 {
160 //printf("Spawning VOICE channel #1: $6000=$%02X, $6200=$%02X\n", b, gram1[0x6200]);
161                 SpawnSound(GAMESOUND, gram1[0x6200], 0);        // Do voice chan 1
162 }
163         if (address == 0x6400)
164 {
165 //printf("Spawning VOICE channel #2: $6400=$%02X, $6600=$%02X\n", b, gram1[0x6600]);
166                 SpawnSound(GAMESOUND, gram1[0x6600], 1);        // Do voice chan 2
167 }
168         if (address == 0x6800)
169                 banksw1 = (uint32_t)data << 13;                         // Set char data bankswitch base address
170         if (address > 0x4284 && address < 0x42A5 && data)
171                 SpawnSound(PSGSOUND, address - 0x4285);         // Do PSG sound on chans 2, 3
172
173 //if ((address >= 0x4284) && (address < 0x42A5))
174 //{
175 //      printf("WrMem: Wrote $%02X to PSG address $%04X...\n", data, address);
176 //}
177         // Memory shared with MCU (CPU #1 only! CPU #2 does not)
178         if ((address >= 0x4000) && (address <= 0x43FF))
179                 mcuMem[address - 0x3000] = data;
180 //      if ((address >= 0x4000) && (address <= 0x41FF))
181 //              MCUWriteMemory(address - 0x3000, data);
182 //      if ((address >= 0x4200) && (address <= 0x43FF))
183 //              mcuMem[address - 0x3000] = data;
184         else
185                 gram1[address] = data;
186
187         if (address == 0x8800)
188                 charbase = false;                                                       // Char banksw1
189         if (address == 0x8C00)
190                 charbase = true;                                                        // Char banksw2
191         if (address == 0x8400)                                                          // Frame go strobe? VBlank acknowledge?
192         {
193                 BlitChar(screen, chr_rom, gram1);
194
195                 // IRQ Ack (may also be frame go...
196                 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
197 #if 1
198         if (disasm)
199                 WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", data);
200 #endif
201         }
202 }
203
204
205 //
206 // Read a byte from memory (2nd processor)
207 //
208 uint8_t RdMemB(uint16_t address)
209 {
210         if (address < 0x8000)
211         {
212                 if (address < 0x2000)
213                         return gram1[address + 0x4000];
214                 else if ((address >= 0x2000) && (address < 0x6000))
215                         return gram1[address - 0x2000];
216                 else if (address >= 0x6000)
217                         return grom3[banksw2 + (address - 0x6000)];
218         }
219
220         return grom2[address];
221 }
222
223
224 //
225 // Write a byte to memory (2nd processor)
226 //
227 void WrMemB(uint16_t address, uint8_t data)
228 {
229         extern bool disasm;
230         extern bool charbase;
231
232         // Set sprite data bank switch
233         if (address == 0xD803)
234                 banksw2 = (uint32_t)(data & 0x03) << 13;
235
236         if (address < 0x2000)
237                 gram1[address + 0x4000] = data;
238
239         if ((address >= 0x2000) && (address < 0x6000))
240                 gram1[address - 0x2000] = data;
241
242         if (address == 0x8800)
243         {
244                 // IRQ Ack (may also be frame go...)
245                 ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
246 #if 1
247         if (disasm)
248                 WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", data);
249 #endif
250         }
251 }
252
253
254 uint8_t MCUReadMemory(uint16_t address)
255 {
256         if (address < 0x20)
257                 return InternalRegisterRead(address);
258         else if ((address >= 0x1000) && (address <= 0x113F))
259 //      else if ((address >= 0x1000) && (address <= 0x11FF))
260                 return ReadPSG(address - 0x1000);
261         else if ((address >= 0x2000) && (address <= 0x2001))
262                 return YMReadReg(0);
263         // Various joystick + buttons; all are active low.
264         else if (address == 0x2020)
265                 return input1.byte;
266         else if (address == 0x2021)
267                 return input2.byte;
268         // This is DSW1 & 2. All switch settings are active low.
269         else if (address == 0x2030)
270                 return input4.byte;
271         else if (address == 0x2031)
272                 return input5.byte;
273
274         return mcuMem[address];
275 }
276
277
278 void MCUWriteMemory(uint16_t address, uint8_t data)
279 {
280         static uint8_t ymRegister;
281
282         if (address < 0x20)
283         {
284                 InternalRegisterWrite(address, data);
285                 return;
286         }
287         else if (((address >= 0x4000) && (address <= 0xBFFF))
288                 || (address >= 0xF000))
289                 return;
290         else if ((address >= 0x1000) && (address <= 0x113F))
291 //      else if ((address >= 0x1000) && (address <= 0x11FF))
292         {
293                 WritePSG(address - 0x1000, data);
294                 return;
295         }
296         else if (address == 0x2000)
297         {
298                 ymRegister = data;
299                 return;
300         }
301         else if (address == 0x2001)
302         {
303                 YMWriteReg(0, ymRegister, data);
304                 return;
305         }
306
307         // RAM is from $0 - $3FFF, $C000 - $EFFF
308         mcuMem[address] = data;
309 }
310
311
312 uint8_t V63701ReadPort1(void)
313 {
314 //      printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
315         return input3.byte;
316 }
317
318
319 uint8_t V63701ReadPort2(void)
320 {
321         return 0xFF;
322 }
323
324
325 void V63701WritePort1(uint8_t data)
326 {
327 //      printf("V63701WritePort1: Wrote $%02X...\n", data);
328 }
329
330
331 void V63701WritePort2(uint8_t data)
332 {
333 //      printf("V63701WritePort2: Wrote $%02X...\n", data);
334 }
335
336
337 //
338 // Generic Load file into image space
339 // (No error checking performed!  Responsibility of caller!)
340 //
341 bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t length)
342 {
343         char path[128];
344
345         strcpy(path, "./ROMs/");
346         strcat(path, filename);
347         FILE * file = fopen(path, "rb");
348
349         if (!file)
350                 return false;
351
352         fread(&mem[address], 1, length, file);
353         fclose(file);
354
355         return true;
356 }
357
358
359 //
360 // Read color PROMs
361 //
362 bool ReadColorPROMs(void)
363 {
364         fstream ff1, ff2;
365         //  uint8_t ch;
366         char ch;
367         extern uint32_t palette[256];     // Screen physical palette
368         extern uint8_t ccolor[256][8];   // Character color PROM values
369         extern uint8_t scolor[128][16];  // Sprite color PROM values
370
371         ff1.open("./ROMs/"PROM3, ios::binary | ios::in);
372
373         if (ff1)
374         {
375                 for(int i=0; i<256; i++) // Load char pallete with PROM values
376                 {
377                         for(int j=0; j<8; j++)
378                         {
379                                 ff1.get(ch);
380                                 ccolor[i][j] = (uint8_t)ch;
381                         }
382                 }
383
384                 ff1.close();
385         }
386
387         ff1.open("./ROMs/"PROM4, ios::binary | ios::in);
388
389         if (ff1)
390         {
391                 for(int i=0; i<128; i++) // Load sprite pallete with PROM values
392                 {
393                         for(int j=0; j<16; j++)
394                         {
395                                 ff1.get(ch);
396                                 scolor[i][j] = (uint8_t)ch;
397                         }
398                 }
399
400                 ff1.close();
401         }
402
403         ff1.open("./ROMs/"PROM1, ios::binary | ios::in);
404         ff2.open("./ROMs/"PROM2, ios::binary | ios::in);
405
406         // If open was successful...
407         if (ff1 && ff2)
408         {
409                 // Palette is 12-bit RGB, we stretch it to 24-bit
410                 for(int i=0; i<256; i++)
411                 {
412                         char c1, c2;
413                         ff1.get(c1);
414                         ff2.get(c2);
415                         uint8_t r = (uint8_t)c1 & 0x0F;
416                         uint8_t g = (uint8_t)c1 >> 4;
417                         uint8_t b = (uint8_t)c2;
418                         palette[i] = 0xFF000000 | (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r;
419                 }
420
421                 ff1.close();
422                 ff2.close();
423         }
424
425         // PROM5 has the following in it (tile address decoder):
426         // 00:  00 20 40 60  02 22 42 62  04 24 44 64  06 26 46 66
427         // 10:  88 A8 C8 E8  8A AA CA EA  8C AC CC EC  8E AE CE EE 
428
429         return ff1;
430 }
431
432
433 //
434 // Unpack font data
435 //
436 bool UnpackFonts(void)
437 {
438 //  uint8_t b1, b2, b3;
439         char b1, b2, b3;
440         fstream f1, f2;
441         //0x4000 $800 chars
442         f1.open("./ROMs/"ROM7, ios::binary | ios::in);
443         f2.open("./ROMs/"ROM8, ios::binary | ios::in);
444
445         if ((!f1) || (!f2))
446                 return false;  // Return if not found...
447
448         for(long i=0; i<0x40000; i+=64)
449         {
450                 for(int j=0; j<64; j+=8)
451                 {
452                         f1.get(b1);  f1.get(b2);  f2.get(b3);
453                         b3 ^= 0xFF; // Invert top data...
454                         chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
455                         chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
456                         chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
457                         chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
458                         chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
459                         chr_rom[i+j+5] = (b3 & 0x04)        | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
460                         chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
461                         chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
462                 }
463         }
464
465         f1.close();
466         f2.close();
467
468         f1.open("./ROMs/"ROM5, ios::binary | ios::in);
469         f2.open("./ROMs/"ROM6, ios::binary | ios::in);
470
471         for(long i=0x40000; i<0x60000; i+=64)
472         {
473                 for(int j=0; j<64; j+=8)
474                 {
475                         f1.get(b1);  f1.get(b2);  f2.get(b3);
476                         b3 ^= 0xFF;                             // Invert top data
477                         chr_rom[i+j] = ((b3 & 0x80) >> 5) | ((b1 & 0x80) >> 6) | ((b1 & 0x08) >> 3);
478                         chr_rom[i+j+1] = ((b3 & 0x40) >> 4) | ((b1 & 0x40) >> 5) | ((b1 & 0x04) >> 2);
479                         chr_rom[i+j+2] = ((b3 & 0x20) >> 3) | ((b1 & 0x20) >> 4) | ((b1 & 0x02) >> 1);
480                         chr_rom[i+j+3] = ((b3 & 0x10) >> 2) | ((b1 & 0x10) >> 3) | (b1 & 0x01);
481                         chr_rom[i+j+4] = ((b3 & 0x08) >> 1) | ((b2 & 0x80) >> 6) | ((b2 & 0x08) >> 3);
482                         chr_rom[i+j+5] = (b3 & 0x04)        | ((b2 & 0x40) >> 5) | ((b2 & 0x04) >> 2);
483                         chr_rom[i+j+6] = ((b3 & 0x02) << 1) | ((b2 & 0x20) >> 4) | ((b2 & 0x02) >> 1);
484                         chr_rom[i+j+7] = ((b3 & 0x01) << 2) | ((b2 & 0x10) >> 3) | (b2 & 0x01);
485                 }
486         }
487
488         f1.close();
489         f2.close();
490
491         return true;                                // Made it!
492 }
493
494
495 //
496 // Get length of sample from WAV format
497 //
498 uint32_t GetWAVLength(fstream & file)
499 {
500         char ch;
501         uint32_t len;
502
503         file.ignore(16);                                                                        // Skip header BS
504
505         for(int i=0; i<2; i++)
506         {
507                 file.get(ch);  len = (int)(uint8_t)ch;
508                 file.get(ch);  len |= (int)(uint8_t)ch << 8;
509                 file.get(ch);  len |= (int)(uint8_t)ch << 16;
510                 file.get(ch);  len |= (int)(uint8_t)ch << 24;
511
512                 // Skip intermediate data
513                 file.ignore(len + 4);
514         }
515
516         // & finally get length of data
517         file.get(ch);  len = (int)(uint8_t)ch;
518         file.get(ch);  len |= (int)(uint8_t)ch << 8;
519         file.get(ch);  len |= (int)(uint8_t)ch << 16;
520         file.get(ch);  len |= (int)(uint8_t)ch << 24;
521
522         return len;
523 }
524
525
526 #if 0
527 //
528 // Load PSG samples from disk
529 //
530 void LoadPSGs(void)
531 {
532         char file[40];
533         char ch;
534         uint32_t len;
535
536         for(int i=0; i<16; i++)
537         {
538                 fstream fp;
539
540                 psg_adrs[i] = NULL;                                                             // Zero out pointer
541                 sprintf(file, "./sounds/psg%i.wav", i);                 // Create filename
542
543                 fp.open(file, ios::binary | ios::in);                   // Attempt to open it...
544
545                 if (fp)
546                 {
547                         len = GetWAVLength(fp);                                         // Get WAV data length...
548                         psg_adrs[i] = new uint8_t[len];                         // Attempt to allocate space...
549
550                         if (psg_adrs[i] != NULL)
551                         {
552                                 for(int j=0; j<(signed)len; j++)
553                                 {
554                                         fp.get(ch);
555                                         psg_adrs[i][j] = ch;                            // & load it in...
556                                 }
557
558                                 psg_lens[i] = len;
559                                 printf("Found sample file: %s\t[Length: %u]\n", file, len);
560                         }
561
562                         fp.close();
563                 }
564         }
565 }
566 #endif
567 FILE * psg1, * psg2;
568
569
570 //
571 // Main loop
572 //
573 int main(int argc, char * argv[])
574 {
575         InitLog("thunder.log");
576
577 extern bool disasm;     // From 'V6809.CPP'
578         extern bool charbase;                                           // From 'SCREEN.CPP'
579         charbase = false;
580
581         char lbuff[80];
582         fstream ff;                       // Declare fstream without file hooks...
583         bool brk = false, brk2 = false;   // Breakpoint set flag
584         uint16_t brkpnt, brkpnt2;             // Where the breakpoint is...
585         bool running;                     // CPU running state flag...
586         bool self_test = false;           // Self-test switch
587         bool scr_type = false;            // false=chars, true=pixels
588         uint16_t debounce = 0;                // Key de-bounce counter
589         uint16_t fire_debounce = 0;           // Fire button debounce counter
590         uint8_t x;                           // General placeholder...
591 //      bool active = true;                                             // Program running flag
592
593         SDL_Event event;                                                                // SDL "event"
594         extern uint8_t palette[768];                                    // Screen physical palette
595         uint32_t ticks, oldTicks;
596
597         cout << endl << "THUNDER v"THUNDER_VERSION" ";
598         cout << "by James Hammons" << endl;
599         cout << "Serial #20149417 / Prerelease" << endl;
600         cout << "© 2003, 2014 Underground Software" << endl << endl;
601
602         cout << "This emulator is free software. If you paid for it you were RIPPED OFF"
603                 << endl << endl;
604
605 //      SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
606
607         gram = gram1;  grom = grom1;           // Needed only for debugger
608
609         memset(gram, 0, 0x10000);
610         memset(grom, 0, 0x10000);
611         memset(gram2, 0, 0x10000);
612         memset(grom2, 0, 0x10000);
613
614         game_over_switch = 0;   // Init game over delay
615
616         cout << "Loading ROMs..." << endl;
617         if (!ReadColorPROMs())                   // Load virtual PROMs
618         { cout << "Could not open PROM files!" << endl;  return -1; }
619
620         if (!LoadImg(ROM1, grom1, 0x8000, 0x8000)) // Load $8000-$FFFF 1st ROM
621         { cout << "Could not open file '" << ROM1 << "'!" << endl;  return -1; }
622
623         if (!LoadImg(ROM2, grom2, 0x8000, 0x8000)) // Load $8000-$FFFF 2nd ROM
624         { cout << "Could not open file '" << ROM2 << "'!" << endl;  return -1; }
625
626         if (!LoadImg(ROM3, grom3, 0, 0x8000))      // Load 3rd ROM into its own space
627         { cout << "Could not open file '" << ROM3 << "'!" << endl;  return -1; }
628
629         if (!LoadImg(ROM17, data_rom, 0,       0x10000))  // Load 17th ROM
630         { cout << "Could not open file '" << ROM17 << "'!" << endl;  return -1; }
631
632         if (!LoadImg(ROM18, data_rom, 0x10000, 0x10000))  // Load 18th ROM
633         { cout << "Could not open file '" << ROM18 << "'!" << endl;  return -1; }
634
635         if (!LoadImg(ROM19, data_rom, 0x20000, 0x10000))  // Load 19th ROM
636         { cout << "Could not open file '" << ROM19 << "'!" << endl;  return -1; }
637
638         if (!LoadImg(ROM20, data_rom, 0x30000, 0x10000))  // Load 20th ROM
639         { cout << "Could not open file '" << ROM20 << "'!" << endl;  return -1; }
640
641         if (!LoadImg(ROM9,  spr_rom, 0,       0x10000))   // Load 9th ROM
642         { cout << "Could not open file '" << ROM9 << "'!" << endl;  return -1; }
643
644         if (!LoadImg(ROM10, spr_rom, 0x10000, 0x10000))   // Load 10th ROM
645         { cout << "Could not open file '" << ROM10 << "'!" << endl;  return -1; }
646
647         if (!LoadImg(ROM11, spr_rom, 0x20000, 0x10000))   // Load 11th ROM
648         { cout << "Could not open file '" << ROM11 << "'!" << endl;  return -1; }
649
650         if (!LoadImg(ROM12, spr_rom, 0x30000, 0x10000))   // Load 12th ROM
651         { cout << "Could not open file '" << ROM12 << "'!" << endl;  return -1; }
652
653         if (!LoadImg(ROM13, spr_rom, 0x40000, 0x10000))   // Load 13th ROM
654         { cout << "Could not open file '" << ROM13 << "'!" << endl;  return -1; }
655
656         if (!LoadImg(ROM14, spr_rom, 0x50000, 0x10000))   // Load 14th ROM
657         { cout << "Could not open file '" << ROM14 << "'!" << endl;  return -1; }
658
659         if (!LoadImg(ROM15, spr_rom, 0x60000, 0x10000))   // Load 15th ROM
660         { cout << "Could not open file '" << ROM15 << "'!" << endl;  return -1; }
661
662         if (!LoadImg(ROM16, spr_rom, 0x70000, 0x10000))   // Load 16th ROM
663         { cout << "Could not open file '" << ROM16 << "'!" << endl;  return -1; }
664
665         if (!LoadImg(ROM21, voice_rom, 0, 0x10000))  // Load 21st ROM
666         { cout << "Could not open file '" << ROM21 << "'!" << endl;  return -1; }
667
668         if (!LoadImg(ROM22, voice_rom, 0x10000, 0x10000))  // Load 22nd ROM
669         { cout << "Could not open file '" << ROM22 << "'!" << endl;  return -1; }
670
671         if (!UnpackFonts())                         // Load 5, 6, 7, 8th ROMs
672         {
673                 cout << "Could not open font files!" << endl;
674                 return -1;
675         }
676
677         // Load MCU program + data
678         if (!LoadImg(MCUROM, mcuMem, 0xF000, 0x1000))   // Load MCU ROM
679         { cout << "Could not open file '" << MCUROM << "'!" << endl;  return -1; }
680
681         if (!LoadImg(ROM4, mcuMem, 0x4000, 0x8000))             // Load 4th ROM
682         { cout << "Could not open file '" << ROM4 << "'!" << endl;  return -1; }
683
684 // Now emulated! :-D
685         // Load PSG samples if they're there...
686 //      LoadPSGs();
687
688         // Set up V6809 execution contexts
689
690         memset(&cpu1, 0, sizeof(V6809REGS));
691         cpu1.RdMem = RdMem;
692         cpu1.WrMem = WrMem;
693         cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
694
695         memset(&cpu2, 0, sizeof(V6809REGS));
696         cpu2.RdMem = RdMemB;
697         cpu2.WrMem = WrMemB;
698         cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
699
700         memset(&mcu, 0, sizeof(V63701REGS));
701         mcu.RdMem = MCUReadMemory;
702         mcu.WrMem = MCUWriteMemory;
703         mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
704
705 //      uint32_t my_clock = 0;
706         running = true;                                                         // Set running status...
707 //      trace1 = false;
708 //      SetRefreshRate(refresh2);                                       // Tell GUI our refresh rate
709
710         // Set all inputs to inactive...
711         input1.byte = input2.byte = input3.byte = input4.byte = input5.byte = 0xFF;
712 //      mcu.port1 = 0xFF;
713 //      mcu.port2 = 0xFF;
714 #if 0
715         // This is data that is supposed to come from the MCU... So that's why it hangs
716         gram1[0x4182] = 0xA6;          // Temp kludge
717         gram1[0x4184] = 0xA6;
718         gram1[0x4183] = 0x00;          // More of the same
719         gram1[0x4185] = 0x00;
720 #endif
721         banksw1 = 0;                   // Will this work?
722         banksw2 = 0;
723         InitGUI();                 // Reset # of coins
724
725 WriteLog("About to set up screen...\n");
726         InitVideo();
727
728         oldTicks = SDL_GetTicks();
729
730 WriteLog("About to set up audio...\n");
731         InitSound();
732
733 memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
734 RenderScreenBuffer();
735
736 #if 1
737 psg1 = fopen("psgchan1.raw", "wb");
738 psg2 = fopen("psgchan3.raw", "wb");
739 #endif
740
741 WriteLog("About to enter main loop...\n");
742         while (running)
743         {
744                 HandleGUIDebounce();                                    // Debounce GUI keys
745
746 #if 0
747                 if (game_over_switch)
748                 {
749                         game_over_switch--;  // Countdown...
750
751                         if (game_over_switch == 0)
752                                 gram1[0x4380] = 0; // Kill music!
753                 }
754 #endif
755
756 // Dipswitches are presented to the main CPUs as 0 or 1 at locations
757 // $423D - $425B by the MCU
758
759 //testing... (works)
760 //gram1[0x423D] = 1;
761                 //gram1[0x423D] = self_test;                    // Reset DSW1-1
762 //              gram1[0x4268] = 0;                                              // Reset Video test
763
764                 // SDL key handling...
765
766                 SDL_Event event;
767
768                 while (SDL_PollEvent(&event))
769                 {
770                         switch (event.type)
771                         {
772                         case SDL_KEYDOWN:
773                                 if (event.key.keysym.sym == SDLK_ESCAPE)
774                                         running = false;
775                                 // Do PCX snapshot (F4)
776                                 else if (event.key.keysym.sym == SDLK_F4)
777                                 {
778 //                                      SpawnSound(USERSOUND, SCAMERA);
779                                         SavePCXSnapshot();
780 //                                      debounce = 10;
781                                 }
782 #if 1
783                                 else if (event.key.keysym.sym == SDLK_5)
784                                         input1.bit.b5 = 0;
785                                 else if (event.key.keysym.sym == SDLK_1)
786                                         input1.bit.b6 = 0;
787                                 else if (event.key.keysym.sym == SDLK_RIGHT)
788                                         input3.bit.b5 = 0;
789                                 else if (event.key.keysym.sym == SDLK_LEFT)
790                                         input3.bit.b4 = 0;
791                                 else if (event.key.keysym.sym == SDLK_UP)
792                                         input2.bit.b2 = 0;
793                                 else if (event.key.keysym.sym == SDLK_DOWN)
794                                         input1.bit.b2 = 0;
795                                 else if (event.key.keysym.sym == SDLK_q)        // (Q)  Jump
796                                         input1.bit.b1 = 0;
797                                 else if (event.key.keysym.sym == SDLK_e)        // (E) Fire
798                                         input3.bit.b3 = 0;
799 #else
800                                 else if (event.key.keysym.sym == SDLK_1)
801                                         input1.bit.b0 = 0;
802                                 else if (event.key.keysym.sym == SDLK_2)
803                                         input1.bit.b1 = 0;
804                                 else if (event.key.keysym.sym == SDLK_3)
805                                         input1.bit.b2 = 0;
806                                 else if (event.key.keysym.sym == SDLK_4)
807                                         input1.bit.b3 = 0;
808                                 else if (event.key.keysym.sym == SDLK_5)
809                                         input1.bit.b4 = 0;
810                                 else if (event.key.keysym.sym == SDLK_6)
811                                         input1.bit.b5 = 0;
812                                 else if (event.key.keysym.sym == SDLK_7)
813                                         input1.bit.b6 = 0;
814                                 else if (event.key.keysym.sym == SDLK_8)
815                                         input1.bit.b7 = 0;
816
817                                 else if (event.key.keysym.sym == SDLK_q)
818                                         input2.bit.b0 = 0;
819                                 else if (event.key.keysym.sym == SDLK_w)
820                                         input2.bit.b1 = 0;
821                                 else if (event.key.keysym.sym == SDLK_e)
822                                         input2.bit.b2 = 0;
823                                 else if (event.key.keysym.sym == SDLK_r)
824                                         input2.bit.b3 = 0;
825                                 else if (event.key.keysym.sym == SDLK_t)
826                                         input2.bit.b4 = 0;
827                                 else if (event.key.keysym.sym == SDLK_y)
828                                         input2.bit.b5 = 0;
829                                 else if (event.key.keysym.sym == SDLK_u)
830                                         input2.bit.b6 = 0;
831                                 else if (event.key.keysym.sym == SDLK_i)
832                                         input2.bit.b7 = 0;
833
834                                 else if (event.key.keysym.sym == SDLK_a)
835                                         input3.bit.b0 = 0;
836                                 else if (event.key.keysym.sym == SDLK_s)
837                                         input3.bit.b1 = 0;
838                                 else if (event.key.keysym.sym == SDLK_d)
839                                         input3.bit.b2 = 0;
840                                 else if (event.key.keysym.sym == SDLK_f)
841                                         input3.bit.b3 = 0;
842                                 else if (event.key.keysym.sym == SDLK_g)
843                                         input3.bit.b4 = 0;
844 #endif
845
846                                 break;
847                         case SDL_KEYUP:
848 #if 1
849                                 if (event.key.keysym.sym == SDLK_5)
850                                         input1.bit.b5 = 1;
851                                 else if (event.key.keysym.sym == SDLK_1)
852                                         input1.bit.b6 = 1;
853                                 else if (event.key.keysym.sym == SDLK_RIGHT)
854                                         input3.bit.b5 = 1;
855                                 else if (event.key.keysym.sym == SDLK_LEFT)
856                                         input3.bit.b4 = 1;
857                                 else if (event.key.keysym.sym == SDLK_UP)
858                                         input2.bit.b2 = 1;
859                                 else if (event.key.keysym.sym == SDLK_DOWN)
860                                         input1.bit.b2 = 1;
861                                 else if (event.key.keysym.sym == SDLK_q)        // (Q)  Jump
862                                         input1.bit.b1 = 1;
863                                 else if (event.key.keysym.sym == SDLK_e)        // (E) Fire
864                                         input3.bit.b3 = 1;
865 #else
866                                 if (event.key.keysym.sym == SDLK_1)
867                                         input1.bit.b0 = 1;
868                                 else if (event.key.keysym.sym == SDLK_2)
869                                         input1.bit.b1 = 1;
870                                 else if (event.key.keysym.sym == SDLK_3)
871                                         input1.bit.b2 = 1;
872                                 else if (event.key.keysym.sym == SDLK_4)
873                                         input1.bit.b3 = 1;
874                                 else if (event.key.keysym.sym == SDLK_5)
875                                         input1.bit.b4 = 1;
876                                 else if (event.key.keysym.sym == SDLK_6)
877                                         input1.bit.b5 = 1;
878                                 else if (event.key.keysym.sym == SDLK_7)
879                                         input1.bit.b6 = 1;
880                                 else if (event.key.keysym.sym == SDLK_8)
881                                         input1.bit.b7 = 1;
882
883                                 else if (event.key.keysym.sym == SDLK_q)
884                                         input2.bit.b0 = 1;
885                                 else if (event.key.keysym.sym == SDLK_w)
886                                         input2.bit.b1 = 1;
887                                 else if (event.key.keysym.sym == SDLK_e)
888                                         input2.bit.b2 = 1;
889                                 else if (event.key.keysym.sym == SDLK_r)
890                                         input2.bit.b3 = 1;
891                                 else if (event.key.keysym.sym == SDLK_t)
892                                         input2.bit.b4 = 1;
893                                 else if (event.key.keysym.sym == SDLK_y)
894                                         input2.bit.b5 = 1;
895                                 else if (event.key.keysym.sym == SDLK_u)
896                                         input2.bit.b6 = 1;
897                                 else if (event.key.keysym.sym == SDLK_i)
898                                         input2.bit.b7 = 1;
899
900                                 else if (event.key.keysym.sym == SDLK_a)
901                                         input3.bit.b0 = 1;
902                                 else if (event.key.keysym.sym == SDLK_s)
903                                         input3.bit.b1 = 1;
904                                 else if (event.key.keysym.sym == SDLK_d)
905                                         input3.bit.b2 = 1;
906                                 else if (event.key.keysym.sym == SDLK_f)
907                                         input3.bit.b3 = 1;
908                                 else if (event.key.keysym.sym == SDLK_g)
909                                         input3.bit.b4 = 1;
910 #endif
911
912                                 break;
913                         }
914                 }
915
916 #if 0
917                                 if (keys[SDLK_ESCAPE])
918                                         running = false;                     // ESC to exit...
919
920                                 if (debounce)
921                                         debounce--;                          // Debounce toggle keys...
922                                 else
923                                 {
924                                         if (keys[SDLK_F1])
925                                         {
926                                                 self_test = !self_test;            // Self-test (F1-toggle)
927                                                 debounce = 10;                     // Key debounce value...
928                                         }
929                                         if (keys[SDLK_F2])
930                                         {
931                                                 gram1[0x4268] = 1;                 // Video test (F2)
932                                                 debounce = 10;                     // Key debounce value...
933                                         }
934                                         if (keys[SDLK_F12])
935                                         {
936                                                 scr_type = !scr_type;              // Toggle screen (F12)
937                                                 debounce = 10;                     // Key debounce value...
938                                         }
939                                         if (keys[SDLK_F3])
940                                         {
941                                                 show_scr = !show_scr;              // Toggle bkgrnd (F3)
942                                                 debounce = 10;
943                                         }
944                                         if (keys[SDLK_F6])
945                                         {
946                                                 enable_cpu = !enable_cpu;          // Toggle CPUs (F6)
947                                                 debounce = 10;
948                                         }
949                                         if (keys[SDLK_F5])
950                                         {
951                                                 refresh2 = !refresh2;             // Toggle 30/60Hz (F5)
952                                                 SetRefreshRate(refresh2);         // Inform GUI of refresh
953                                                 if (refresh2)
954                                                         SpawnMsg(M60FPS);
955                                                 else
956                                                         SpawnMsg(M30FPS);
957                                                 debounce = 10;                    // Key debounce value...
958                                         }
959                                         if (keys[SDLK_F4])                      // Do PCX snapshot (F4)
960                                         {
961                                                 SpawnSound(USERSOUND, SCAMERA);
962                                                 SnapPCX(screen);
963                                                 debounce = 10;
964                                         }
965                                         if (keys[SDLK_TAB])                      // Tab active/deactivate GUI
966                                         {
967                                                 if (ShowGUI())
968                                                         DeactivateGUI();
969                                                 else
970                                                         ActivateGUI();
971
972                                                 debounce = 10;
973                                         }
974                                 }
975                                 //if (keys[0x3E])  gram1[0x4247] = 1;  // Screen hold DS (F4)
976                                 if (keys[SDLK_RIGHT])                                           // Right arrow
977                                 {
978                                         if (ShowGUI())
979                                                 SelectRight();                     // If GUI active...
980                                         else
981                                         {
982                                                 if (!keys[SDLK_LEFT])                     // Disallow opposite directions @ same time
983                                                         gram1[0x427F] = 1;               // Stick right
984                                         }
985                                 }
986                                 if (keys[SDLK_LEFT])
987                                 {
988                                         if (ShowGUI())
989                                                 SelectLeft();                      // If GUI active...
990                                         else
991                                         {
992                                                 if (!keys[SDLK_RIGHT])                     // Disallow opposite directions@same time
993                                                 gram1[0x4281] = 1;               // Left arrow
994                                         }
995                                 }
996                                 if (keys[SDLK_UP])
997                                 {
998                                         if (ShowGUI())
999                                                 SelectUp();                        // If GUI active...
1000                                         else
1001                                         {
1002                                                 if (!keys[SDLK_DOWN])                     // Disallow opposite directions@same time
1003                                                         gram1[0x427B] = 1;               // Up arrow
1004                                         }
1005                                 }
1006                                 if (keys[SDLK_DOWN])
1007                                 {
1008                                         if (ShowGUI())
1009                                                 SelectDown();                                   // If GUI active...
1010                                         else
1011                                         {
1012                                                 if (!keys[SDLK_UP])                             // Disallow opposite directions@same time
1013                                                         gram1[0x427D] = 1;                      // Down arrow
1014                                         }
1015                                 }
1016                                 if (keys[SDLK_RETURN])                                                  // Return
1017                                 {
1018                                         uint8_t retval = UserSelectedSomething();
1019
1020                                         if (retval == EXIT)
1021                                                 running = false;
1022
1023                                         if (retval == REFRESH)
1024                                         {
1025                                                 refresh2 = !refresh2;
1026                                                 SetRefreshRate(refresh2);
1027                                         }
1028                                 }
1029
1030                                 if (keys[SDLK_1])
1031                                         gram1[0x427A] = 1;                      // (1)
1032
1033                                 if (keys[SDLK_2])
1034                                         gram1[0x427C] = 1;                      // (2)
1035
1036                                 if (keys[SDLK_3])
1037                                         gram1[0x427E] = 1;                      // (3)
1038
1039                                 if (keys[SDLK_5])
1040                                         gram1[0x4280] = 1;                      // (5)
1041
1042                                 if (keys[SDLK_q] | keys[29])
1043                                         gram1[0x4276] = 1;                      // (Q)  Jump
1044
1045                                 if (keys[SDLK_w])
1046                                         gram1[0x426A] = 1;                      // (W)
1047
1048                                 if (fire_debounce)
1049                                         fire_debounce--;
1050
1051                                 if (keys[SDLK_e] | keys[56])    // (E) Fire
1052                                 {
1053                                         if (!fire_debounce)
1054                                         {
1055                                                 gram1[0x4278] = 1;
1056
1057                                                 if (gram1[0x3F08] == 0xFF)              // Ugly kludge for debouncing gun
1058                                                         fire_debounce = 8;
1059                                                 else
1060                                                         fire_debounce = 2;
1061                                         }
1062                                 }
1063
1064                                 if (keys[SDLK_r])
1065                                         gram1[0x426C] = 1;                      // (R)
1066
1067                                 if (keys[SDLK_t])
1068                                         gram1[0x4262] = 1;                      // (T)
1069
1070                                 if (keys[SDLK_y])
1071                                         gram1[0x4260] = 1;                      // (Y)
1072
1073                                 if (keys[SDLK_F10])
1074                                         gram1[0x41A5]++;                        // Coin? (F10)
1075
1076                                 if (keys[SDLK_z])
1077                                         gram1[0x4189]++;                        // ? (Z) credits l dig
1078
1079                                 if (keys[SDLK_x])
1080                                         gram1[0x418A]++;                        // ? (X) credits r dig
1081
1082                                 if (keys[SDLK_c])
1083                                         gram1[0x418C]++;                        // ? (C) Start
1084
1085                                 if (keys[SDLK_v])
1086                                         gram1[0x418D]++;                        // ? (V)
1087
1088                                 if (keys[SDLK_F7])
1089                                         SpawnSound(USERSOUND, 0);       // Do user sound (F7)
1090
1091 //                              if (keys[SDLK_F8])
1092 //                              {
1093 //                                      gram1[0x4380] = 0;                      // (F8) kill music (this worx)
1094 //                                      charbase = false;                       // Switch chars out...
1095 //                              }
1096 //                              if (keys[SDLK_F9])  gram1[0x4285] = 1;          // (F9) strobe unknown loc
1097
1098                                 if (keys[SDLK_F11])                             // (F11)
1099                                 {
1100                                         Execute6809(&cpu1, 10);
1101                                         Execute6809(&cpu2, 10);
1102                                 }
1103 //                      }
1104 //F12 is used above, but the values are ignored. So we'll do it here too.
1105                                 if (keys[SDLK_F12])
1106                                 {
1107                                         cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
1108                                         cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
1109                                 }
1110
1111                                 if (keys[SDLK_d])                               // (D) start disassembly
1112                                         disasm = true;
1113 #if 0
1114         if (keys[SDLK_k])
1115                 gram1[0x5606] = 0x00;
1116         if (keys[SDLK_l])
1117         {
1118                 gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
1119                 WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
1120         }
1121         if (keys[SDLK_o])
1122         {
1123                 gram1[0x5FF3] = 0x02;
1124                 WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
1125         }
1126 #endif
1127 #endif
1128
1129                 if (enable_cpu)
1130 //                              if (true)
1131                 {
1132                         // We can do this here because we're not executing the cores yet.
1133                         cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1134                         cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
1135                         mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
1136 //                                      while (cpu1.clock < 25000)
1137 // 1.538 MHz = 25633.333... cycles per frame (1/60 s)
1138 // 25600 cycles/frame
1139 // Setting interleave to 25 and below causes the V6809 core to hang...
1140 // 32 gets to the title screen before hanging...
1141 // 40 works, until it doesn't... :-P
1142 // 640 * 40
1143 // 800 * 32
1144 // Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
1145                         for(uint32_t i=0; i<640; i++)
1146 //                                      for(uint32_t i=0; i<1280; i++)
1147                         {
1148                                 // Gay, but what are ya gonna do?
1149                                 // There's better ways, such as keeping track of when slave writes to master, etc...
1150                                 Execute6809(&cpu1, 40);
1151                                 Execute6809(&cpu2, 40);
1152
1153                                 // MCU runs at 1,536,000 Hz
1154                                 // 1536000 / 60 / 640 == 40
1155                                 Execute63701(&mcu, 40);
1156                         }
1157                 } // END: enable_cpu
1158
1159                 // Speed throttling happens here...
1160                 while (SDL_GetTicks() - oldTicks < 16)  // Actually, it's 16.66... Need to account for that somehow
1161 //                              while (SDL_GetTicks() - oldTicks < 32)  // Actually, it's 16.66... Need to account for that somehow
1162                         SDL_Delay(1);                           // Release our timeslice...
1163
1164                 oldTicks = SDL_GetTicks();
1165 //cout << "Finished frame..." << endl;
1166         }
1167
1168         SDL_Quit();
1169
1170 #if 1
1171 fclose(psg1);
1172 fclose(psg2);
1173 #endif
1174 #if 0
1175         // Deallocate sounds if they were loaded
1176         for(int i=0; i<16; i++)
1177                 if (psg_adrs[i])
1178                         delete[] psg_adrs[i];
1179 #endif
1180 #if 0
1181         for(int i=0; i<14; i++)
1182                 if (fm_adrs[i])
1183                         delete[] fm_adrs[i];
1184 #endif
1185
1186         LogDone();
1187
1188         return 1;
1189 }
1190
1191 #if 0
1192 Hitachi uC runs at 6.144 MHz
1193 YM2151 runs at 3.579580 MHz
1194
1195
1196 Rolling Thunder Memory map
1197 --------------------------
1198 Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
1199 map is inferred by program behaviour. The customs also handle internally irq
1200 and watchdog.
1201
1202 The main CPU memory map is the same in all games because CUS47 is used by all
1203 games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
1204 replaced by other chips.
1205
1206 All RAM is shared between main and sub CPU, except for sound RAM which is
1207 shared between main and sound CPU; the portion of object RAM that is overlapped
1208 by sound RAM is used exclusively by the sub CPU.
1209
1210 MAIN CPU:
1211
1212 Address             Dir Data     Name      Description
1213 ------------------- --- -------- --------- -----------------------
1214 000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0   tilemap 0/1 RAM (shared with sub CPU)
1215 001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1   tilemap 2/3 RAM (shared with sub CPU)
1216 0100 00xx xxxx xxxx R/W xxxxxxxx SOUND     sound RAM (through CUS30, shared with MCU)
1217 0100 0000 xxxx xxxx R/W xxxxxxxx           portion holding the sound wave data
1218 0100 0001 00xx xxxx R/W xxxxxxxx           portion holding the sound registers
1219 010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT    work RAM (shared with sub CPU) [1]
1220 0101 1xxx xxxx xxxx R/W xxxxxxxx           portion holding sprite registers
1221 011x xxxx xxxx xxxx R   xxxxxxxx ROM 9D    program ROM (banked) [2]
1222 1xxx xxxx xxxx xxxx R   xxxxxxxx ROM 9C    program ROM
1223 1000 00-- ---- ----   W --------           watchdog reset (RES generated by CUS47)
1224 1000 01-- ---- ----   W --------           main CPU irq acknowledge (IRQ generated by CUS47)
1225 1000 1x-- ---- ----   W -------- BANK      tile gfx bank select (data is in A10) (latch in CUS47)
1226 1001 00-- ---- -x0x   W xxxxxxxx LATCH0    tilemap 0/1 X scroll + priority
1227 1001 00-- ---- -x10   W xxxxxxxx LATCH0    tilemap 0/1 Y scroll
1228 1001 00-- ---- --11   W ------xx BAMNKM    ROM 9D bank select
1229 1001 01-- ---- -x0x   W xxxxxxxx LATCH1    tilemap 2/3 X scroll + priority
1230 1001 01-- ---- -x10   W xxxxxxxx LATCH1    tilemap 2/3 Y scroll
1231 1001 01-- ---- --11   W ------xx BAMNKS    ROM 12D bank select
1232 1100 00-- ---- ----   W xxxxxxxx BACKCOLOR background color
1233
1234 [1] Note that this is partially overlapped by sound RAM
1235 [2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
1236
1237
1238 SUB CPU:
1239
1240 Address             Dir Data     Name      Description
1241 ------------------- --- -------- --------- -----------------------
1242 000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ    work RAM (shared with main CPU)
1243 0001 1xxx xxxx xxxx R/W xxxxxxxx           portion holding sprite registers
1244 001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0   tilemap 0/1 RAM (shared with main CPU)
1245 010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1   tilemap 2/3 RAM (shared with main CPU)
1246 011x xxxx xxxx xxxx R   xxxxxxxx ROM 12D   program ROM (banked) [1]
1247 1xxx xxxx xxxx xxxx R   xxxxxxxx ROM 12C   program ROM
1248 1000 0--- ---- ----   W --------           watchdog reset (MRESET generated by CUS41)
1249 1000 1--- ---- ----   W --------           main CPU irq acknowledge (generated by CUS41)
1250 1101 0--- ---- -x0x   W xxxxxxxx LATCH0    tilemap 0/1 X scroll + priority
1251 1101 0--- ---- -x10   W xxxxxxxx LATCH0    tilemap 0/1 Y scroll
1252 1101 0--- ---- --11   W ------xx BAMNKM    ROM 9D bank select
1253 1101 1--- ---- -x0x   W xxxxxxxx LATCH1    tilemap 2/3 X scroll + priority
1254 1101 1--- ---- -x10   W xxxxxxxx LATCH1    tilemap 2/3 Y scroll
1255 1101 1--- ---- --11   W ------xx BAMNKS    ROM 12D bank select
1256
1257 [1] Only used by Rolling Thunder
1258
1259
1260 MCU:
1261
1262 Address             Dir Data     Name      Description
1263 ------------------- --- -------- --------- -----------------------
1264 0000 0000 xxxx xxxx                        MCU internal registers, timers, ports and RAM
1265 0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F    sound RAM (through CUS30, partially shared with main CPU)
1266 0001 0000 xxxx xxxx R/W xxxxxxxx           portion holding the sound wave data
1267 0001 0001 00xx xxxx R/W xxxxxxxx           portion holding the sound registers
1268 0010 0--- --00 ---x R/W xxxxxxxx YMCS      YM2151
1269 0010 0--- --01 ----                        n.c.
1270 0010 0--- --10 ---- R   xxxxxxxx PORTA     switch inputs
1271 0010 0--- --11 ---- R   xxxxxxxx PORTB     dip switches
1272 01xx xxxx xxxx xxxx R   xxxxxxxx ROM 6B    program ROM (lower half)
1273 10xx xxxx xxxx xxxx R   xxxxxxxx ROM 6B    program ROM (upper half)
1274 1011 0--- ---- ----   W                    unknown (CUS41)
1275 1011 1--- ---- ----   W                    unknown (CUS41)
1276 1111 xxxx xxxx xxxx R   xxxxxxxx           MCU internal ROM
1277
1278
1279 Notes:
1280 -----
1281 - we are using an unusually high CPU interleave factor (800) to avoid hangs
1282   in rthunder. The two 6809 in this game synchronize using a semaphore at
1283   5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
1284   and then increments 5606. While it does its quick things (which require
1285   about 40 clock cycles) it expects CPU2 to clear 5607.
1286   Raising the interleave factor to 1000 makes wndrmomo crash during attract
1287   mode. I haven't investigated on the cause.
1288
1289 - There are two watchdogs, one per CPU (or maybe three). Handling them
1290   separately is necessary to allow entering service mode without manually
1291   resetting in rthunder and genpeitd: only one of the CPUs stops writing to
1292   the watchdog.
1293
1294 - The sprite hardware buffers spriteram: the program writes the sprite list to
1295   offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
1296   sprite RAM to signal the chip that the list is complete. The chip will copy
1297   the list from 4-9 to 10-15 and use it from there. This has not been verified
1298   on the real hardware, but it is the most logical way of doing it.
1299   Emulating this behaviour and not using an external buffer is important in
1300   rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
1301   is not written to. If we buffered spriteram to an external buffer, this would
1302   cause dangling sprites because the buffer would not be updated.
1303
1304 - spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
1305   entering a door. The *closed* door is made of tiles, but the *moving* door is
1306   made of sprites. Since sprites are delayed by 1 frame, when you enter a door
1307   there is one frame where neither the tile-based closed door nor the
1308   sprite-based moving door is shown, so it flickers. This behavior has been
1309   confirmed on a real PCB.
1310
1311 TODO:
1312 ----
1313 - The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
1314   but they don't seem to work as expected. During the first few frames they are
1315   written out of order and hooking them up in the usual way causes the MCU to
1316   stop receiving interrupts.
1317
1318 #endif
1319