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