]> Shamusworld >> Repos - thunder/blobdiff - src/thunder.cpp
Added missing files... D'oh!
[thunder] / src / thunder.cpp
index 3a9d8ae3ae84a67965ffd918b0552425e3c32164..8ee60f525a32c1fe713f3f9ce2fe5d3ac2710caf 100755 (executable)
@@ -9,9 +9,10 @@
 // WHO  WHEN        WHAT
 // ---  ----------  ------------------------------------------------------------
 // JLH  07/23/2009  Added changelog ;-)
+// JLH  08/12/2009  Stabilized emulation so that it works
 //
 
-#define THUNDER_VERSION       "0.9.9"
+#define THUNDER_VERSION                "0.9.9"
 
 #include <iostream>
 #include <iomanip>
 //#include <conio.h>                   // For getch()
 #include <curses.h>                    // For getch()
 #include <time.h>
-#include "SDL.h"                                                                               // Get yer SDL out...!
+#include "SDL.h"                                                               // Get yer SDL out...!
 #include "types.h"
 #include "v6809.h"
 #include "screen.h"
 #include "gui.h"
 #include "log.h"
 
-using namespace std;                                                                   // Yes!
+using namespace std;                                                   // Yes!
 
 #if 0
 #define ROM1  "RT3-1B.ROM"
@@ -99,32 +100,20 @@ uint8 * gram, * grom;                                                      // Allocate RAM & ROM pointers
 uint8 gram1[0x10000], gram2[0x10000], grom1[0x10000], grom2[0x10000];  // Actual memory
 uint8 grom3[0x8000], grom4[0x8000], data_rom[0x40000], spr_rom[0x80000], voice_rom[0x20000];
 uint8 chr_rom[0x60000];                                                        // Character ROM pointer
-/*
-  gram1 = new uint8[0x10000];
-  grom1 = new uint8[0x10000];
-  gram2 = new uint8[0x10000];
-  grom2 = new uint8[0x10000];
-  chr_rom = new uint8[0x60000];
-  grom3 = new uint8[0x8000];
-  grom4 = new uint8[0x8000];
-  data_rom = new uint8[0x40000];
-  spr_rom = new uint8[0x80000];
-  voice_rom = new uint8[0x20000];
-*/
 
 V6809REGS cpu1, cpu2;
 
-bool trace1 = false;                 // ditto...
-bool looking_at_rom = true;         // true = R1, false = R2
-uint32 banksw1, banksw2;             // Bank switch addresses
-uint16 game_over_switch;              // Game over delay
-uint16 dpc;                           // Debug pc reg...
-bool show_scr = true;            // Whether or not to show background
-bool enable_cpu = true;          // Whether or not to enable CPUs
-bool irqGoA = true;              // IRQ switch for CPU #1
-bool irqGoB = true;              // IRQ switch for CPU #2
-
-uint16 refresh_ = 0;                // Crappy global screen stuff...
+bool trace1 = false;                                                   // ditto...
+bool looking_at_rom = true;                                            // true = R1, false = R2
+uint32 banksw1, banksw2;                                               // Bank switch addresses
+uint16 game_over_switch;                                               // Game over delay
+uint16 dpc;                                                                            // Debug pc reg...
+bool show_scr = true;                                                  // Whether or not to show background
+bool enable_cpu = true;                                                        // Whether or not to enable CPUs
+bool irqGoA = true;                                                            // IRQ switch for CPU #1
+bool irqGoB = true;                                                            // IRQ switch for CPU #2
+
+uint16 refresh_ = 0;                                                   // Crappy global screen stuff...
 bool refresh2 = true;
 
 uint32 psg_lens[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -136,7 +125,7 @@ uint32 fm_lens[14] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 uint8 * fm_adrs[14];
 
 fstream tr;    // Tracelog hook
-uint16    pcx;   // Where we at?
+uint16 pcx;   // Where we at?
 
 static uint8 * keys;                                                   // SDL raw keyboard matrix
 
@@ -302,6 +291,8 @@ uint8 RdMem(uint16 addr)
 {
        uint8 b;
 
+       // $4000-4300 is RAM shared with the microcontroller...
+
        if (addr < 0x8000)
        {
                if (addr > 0x5FFF)
@@ -320,6 +311,7 @@ uint8 RdMem(uint16 addr)
 //
 void WrMem(uint16 addr, uint8 b)
 {
+       extern bool disasm;
        extern bool charbase;                                                           // Needed for screen. Extern it in it??
   //extern uint16 sr, ur, xr, yr;            // Needed for tracelog
   //extern uint16 pcr;
@@ -333,6 +325,12 @@ void WrMem(uint16 addr, uint8 b)
     //}
     tr << endl;
   }//*/
+#if 0
+       if (addr == 0x4182)
+       {
+               WriteLog("\nWriteMem: CPU #1 writing $%02X to $4182!\n\n", b);
+       }
+#endif
 
        if (addr == 0x6000)
                SpawnSound(GAMESOUND, gram1[0x6200], 0);                // Do voice chan 1
@@ -361,8 +359,13 @@ void WrMem(uint16 addr, uint8 b)
                        BlitChar(screen, chr_rom, gram1);
                        refresh_ = (refresh2 ? 1 : 0);                          // 60/30 Hz...
                }
-//             irqGoA = true; // Will this work??? no...
-               cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
+
+               // IRQ Ack (may also be frame go...
+               ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
+#if 1
+       if (disasm)
+               WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b);
+#endif
        }
 }
 
@@ -401,6 +404,7 @@ uint8 RdMemB(uint16 addr)
 //
 void WrMemB(uint16 addr, uint8 b)
 {
+       extern bool disasm;
        extern bool charbase;
   //extern uint16 sr, ur, xr, yr;            // Needed for tracelog
   //extern uint16 pcr;
@@ -414,10 +418,13 @@ void WrMemB(uint16 addr, uint8 b)
     //}
     tr << endl;
   }//*/
+#if 0
+       if (addr == 0x0182)
+       {
+               WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b);
+       }
+#endif
 
-       if (addr == 0x8800)
-//             irqGoB = true;                                                                  // Will it work??? no...
-               cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok???
        if (addr == 0x6000)
                SpawnSound(GAMESOUND, gram1[0x6200], 0);                // Do voice chan 1
        if (addr == 0x6400)
@@ -441,6 +448,15 @@ void WrMemB(uint16 addr, uint8 b)
                if (addr > 0x5FFF)
                        gram1[addr] = b;
        }
+       if (addr == 0x8800)
+       {
+               // IRQ Ack (may also be frame go...)
+               ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
+#if 1
+       if (disasm)
+               WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b);
+#endif
+       }
 }
 
 //
@@ -451,25 +467,25 @@ void DisplayBytes(uint16 src, unsigned long dst)
   uint8 cnt;
   unsigned long i;
 
-  printf("%04X: ", src);
+  WriteLog("%04X: ", src);
   cnt = 0;                               // Init counter...
   if (src > dst)  dst += 0x10000;        // That should fix the FFFF bug...
   for(i=src; i<dst; i++)
   {
-    printf("%02X ", (uint8)(looking_at_rom ? RdMem(i) : RdMemB(i)));
+    WriteLog("%02X ", (uint8)(looking_at_rom ? RdMem(i) : RdMemB(i)));
     cnt++;                               // Bump counter...
   }
   for(i=cnt; i<5; i++)                   // Pad the leftover spaces...
   {
-    cout << "   ";
+    WriteLog("   ");
   }
 }
 
 // temp crap...
-uint8 Fetch(void) { return 0; }
-uint16 FetchW(void) { return 0; }
-uint8 FetchB(void) { return 0; }
-uint16 FetchWB(void) { return 0; }
+uint8 Fetch(void) { return RdMem(dpc); }
+uint16 FetchW(void) { return (uint16)((RdMem(dpc) << 8) | RdMem(dpc+1)); }
+uint8 FetchB(void) { return RdMemB(dpc); }
+uint16 FetchWB(void) { return (uint16)((RdMemB(dpc) << 8) | RdMemB(dpc+1)); }
 
 //
 // Decode a 6809 instruction at 'addr'
@@ -626,7 +642,7 @@ void Decode_6809()
       break; }
   }
   DisplayBytes(dpc, (looking_at_rom ? pcr : pcrB));  // Show bytes
-  cout << outbuf << endl;     // display opcode & addressing, etc
+  WriteLog(outbuf); WriteLog("\n");     // display opcode & addressing, etc
   dpc = (looking_at_rom ? pcr : pcrB);  // Advance debug PC
   pcr = pc_save;  pcrB = pcB_save;  // Restore PCs
 }
@@ -870,7 +886,6 @@ void LoadPSGs(void)
                if (fp)
                {
                        len = GetWAVLength(fp);                                         // Get WAV data length...
-
                        psg_adrs[i] = new uint8[len];                           // Attempt to allocate space...
 
                        if (psg_adrs[i] != NULL)
@@ -882,7 +897,8 @@ void LoadPSGs(void)
                                }
 
                                psg_lens[i] = len;
-                               cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
+//                             cout << "Found sample file: " << file << "\t[Length: " << dec << len << "]" << endl;
+                               printf("Found sample file: %s\t[Length: %u]\n", file, len);
                        }
 
                        fp.close();
@@ -911,7 +927,6 @@ void LoadFMs(void)
                if (fp)
                {
                        len = GetWAVLength(fp);                                         // Get WAV length...
-
                        fm_adrs[i] = new uint8[len];                                    // Attempt to allocate space...
 
                        if (fm_adrs[i] != NULL)
@@ -923,8 +938,8 @@ void LoadFMs(void)
                                }
 
                                fm_lens[i] = len;
-                               cout << "Found sample file: " << file << " [Length: " << dec << len
-                                       << "]" << endl;
+//                             cout << "Found sample file: " << file << " [Length: " << dec << len << "]" << endl;
+                               printf("Found sample file: %s\t[Length: %u]\n", file, len);
                        }
 
                        fp.close();
@@ -939,6 +954,7 @@ int main(int argc, char * argv[])
 {
        InitLog("thunder.log");
 
+extern bool disasm;    // From 'V6809.CPP'
        extern bool charbase;                                           // From 'SCREEN.CPP'
        charbase = false;
 
@@ -979,41 +995,6 @@ int main(int argc, char * argv[])
 
        keys = SDL_GetKeyState(NULL);                           // Get the SDL keyboard matrix
 
-#if 0
-  cout << "Allocating memory..." << endl;
-//Does this anyway...  set_new_handler(0);    // Make 'new' return NULL on failure...
-  gram1 = new uint8[0x10000];
-  if (gram1 == NULL)  { cout << "Could not allocate RAM space #1!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  grom1 = new uint8[0x10000];
-  if (grom1 == NULL)  { cout << "Could not allocate ROM space #1!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  gram2 = new uint8[0x10000];
-  if (gram2 == NULL)  { cout << "Could not allocate RAM space #2!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  grom2 = new uint8[0x10000];
-  if (grom2 == NULL)  { cout << "Could not allocate ROM space #2!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  chr_rom = new uint8[0x60000];
-  if (chr_rom == NULL)  { cout << "Could not allocate character RAM!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  grom3 = new uint8[0x8000];
-  if (grom3 == NULL)  { cout << "Could not allocate ROM space #4!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  grom4 = new uint8[0x8000];
-  if (grom4 == NULL)  { cout << "Could not allocate ROM space #5!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  data_rom = new uint8[0x40000];
-  if (data_rom == NULL)  { cout << "Could not allocate ROM level data!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  spr_rom = new uint8[0x80000];
-  if (spr_rom == NULL)  { cout << "Could not allocate ROM sprite data!" << endl
-                      << "Aborting!" << endl;  return -1; }
-  voice_rom = new uint8[0x20000];
-  if (voice_rom == NULL)  { cout << "Could not allocate ROM voice data!" << endl
-                      << "Aborting!" << endl;  return -1; }
-#endif
-
   gram = gram1;  grom = grom1;           // Needed only for debugger
 
   for(long i=0; i<0x10000; i++)
@@ -1220,35 +1201,42 @@ WriteLog("Executing 'run' command...\n");
                        if (lbuff[0] == 'r')                                            // If run, then reset CPUs
                        {
 WriteLog("Executing secondary 'run' command...\n");
+#if 1
+                               // This is data that is supposed to come from the MCU... So that's why it hangs
                                gram1[0x4182] = 0xA6;          // Temp kludge
                                gram1[0x4184] = 0xA6;
                                gram1[0x4183] = 0x00;          // More of the same
                                gram1[0x4185] = 0x00;
+#endif
                                banksw1 = 0;                   // Will this work?
                                banksw2 = 0;
 //        iclock = 0;                // Reset instr clock #1...
                                InitGUI();                 // Reset # of coins
 
-/*        cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
-        if (lbuff[1] != 0)
-        {
-          lbuff[0] = 32;  cpu1.pc = htod(lbuff);
-        }
-        else  cpu1.cc = 0xFF;                         // Set CC register
+#if 0
+                               cpu1.pc = ((grom1[0xFFFE]<<8) | grom1[0xFFFF]); // Reset 6809 #1
+                               if (lbuff[1] != 0)
+                               {
+                               lbuff[0] = 32;  cpu1.pc = htod(lbuff);
+                               }
+                               else  cpu1.cc = 0xFF;                         // Set CC register
 
-        cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
-        cpu2.cc = 0xFF;                              // Set CC register
-        while(iclock < 8000)  // was 17000, 20000, 5000
-        {
-          Execute6809(&cpu1, 1);  Execute6809(&cpu2, 1);
-        }*/
+                               cpu2.pc = ((grom2[0xFFFE]<<8) | grom2[0xFFFF]); // Reset 6809 #2
+                               cpu2.cc = 0xFF;                              // Set CC register
+                               while(iclock < 8000)  // was 17000, 20000, 5000
+                               {
+                               Execute6809(&cpu1, 1);  Execute6809(&cpu2, 1);
+                               }
+#endif
+#if 0
 WriteLog("--> CPU clock #1: %u\n", cpu1.clock);
-                               // Will *this* help video sync?
+                               // Will *this* help video sync? NO
                                while (cpu1.clock < 8000)                               // was 17000, 20000, 5000
                                {
                                        Execute6809(&cpu1, 1);
                                        Execute6809(&cpu2, 1);
                                }
+#endif
                        }
 
 WriteLog("About to set up screen...\n");
@@ -1270,7 +1258,7 @@ WriteLog("About to set up screen...\n");
                        }
                        SDL_SetPalette(screen, SDL_LOGPAL | SDL_PHYSPAL, colors, 0, 256);
 
-#if 1
+#if 0
        // This confirms that we're getting video to the screen...
        SDL_LockSurface(screen);
 
@@ -1317,6 +1305,8 @@ WriteLog("About to enter main loop...\n");
                                        if (game_over_switch == 0)
                                                gram1[0x4380] = 0; // Kill music!
                                }
+//testing... (works)
+//gram1[0x423D] = 1;
                                //gram1[0x423D] = self_test;                    // Reset DSW1-1
                                gram1[0x4268] = 0;                                              // Reset Video test
                                gram1[0x427A] = 0;  gram1[0x427C] = 0;
@@ -1497,37 +1487,53 @@ WriteLog("About to enter main loop...\n");
                                        Execute6809(&cpu2, 10);
                                }
 //                     }
-
-//                             if (enable_cpu)
-                               if (true)
+//F12 is used above, but the values are ignored. So we'll do it here too.
+                               if (keys[SDLK_F12])
                                {
+                                       cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
+                                       cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
+                               }
+                               if (keys[SDLK_d])                               // (D) start disassembly
+                                       disasm = true;
 #if 0
-//                                     if (irqGoA)
-                                               cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
-
-                                       Execute6809(&cpu1, 25000);
-                                       cpu1.clock -= 25000;                            // Remove 25K ticks from clock (in case it overflowed)
+       if (keys[SDLK_k])
+               gram1[0x5606] = 0x00;
+       if (keys[SDLK_l])
+       {
+               gram1[0x5607] = 0x01; // Hangs here... (CPU #1 waiting...)
+               WriteLog("\nMAIN: Stuffed $01 in $5607!!!\n\n");
+       }
+       if (keys[SDLK_o])
+       {
+               gram1[0x5FF3] = 0x02;
+               WriteLog("\nMAIN: Stuffed $02 in $5FF3!!!\n\n");
+       }
+#endif
 
-//                                     if (irqGoB)
-                                               cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
 
-                                       Execute6809(&cpu2, 25000);
-                                       cpu2.clock -= 25000;                            // Remove 25K ticks from clock (in case it overflowed)//*/
-#else
+                               if (enable_cpu)
+//                             if (true)
+                               {
+                                       // We can do this here because we're not executing the cores yet.
                                        cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;
                                        cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;
 //                                     while (cpu1.clock < 25000)
-                                       for(uint32 i=0; i<250; i++)
+// 1.538 MHz = 25633.333... cycles per frame (1/60 s)
+// 25600 cycles/frame
+// Setting interleave to 25 and below causes the V6809 core to hang...
+// 32 gets to the title screen before hanging...
+// 40 works, until it doesn't... :-P
+// 640 * 40
+// 800 * 32
+// Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
+                                       for(uint32 i=0; i<640; i++)
+//                                     for(uint32 i=0; i<1280; i++)
                                        {
                                                // Gay, but what are ya gonna do?
                                                // There's better ways, such as keeping track of when slave writes to master, etc...
-                                               Execute6809(&cpu1, 100);
-                                               Execute6809(&cpu2, 100);
+                                               Execute6809(&cpu1, 40);
+                                               Execute6809(&cpu2, 40);
                                        }
-
-                                       cpu1.clock -= 25000;                            // Remove 25K ticks from clock (in case it overflowed)
-                                       cpu2.clock -= 25000;                            // Remove 25K ticks from clock (in case it overflowed)//*/
-#endif
                                } // END: enable_cpu
 
 //        if (refresh_++ == 1)                // 30 Hz...
@@ -1539,11 +1545,13 @@ WriteLog("About to enter main loop...\n");
 //          refresh_ = (refresh2 ? 1 : 0);    // 60/30 Hz...
 //        }
 
+#if 0
 //temp, for testing...
 BlitChar(screen, chr_rom, gram1);
-
+#endif
                                // Speed throttling happens here...
                                while (SDL_GetTicks() - oldTicks < 16)  // Actually, it's 16.66... Need to account for that somehow
+//                             while (SDL_GetTicks() - oldTicks < 32)  // Actually, it's 16.66... Need to account for that somehow
                                        SDL_Delay(1);                           // Release our timeslice...
 
                                oldTicks = SDL_GetTicks();
@@ -1656,19 +1664,6 @@ BlitChar(screen, chr_rom, gram1);
 
        SDL_Quit();                                                                     // Shut down SDL
 
-#if 0
-       delete[] gram1;                                                                         // Deallocate RAM & ROM spaces
-       delete[] grom1;
-       delete[] gram2;
-       delete[] grom2;
-       delete[] chr_rom;
-       delete[] grom3;
-       delete[] grom4;
-       delete[] data_rom;
-       delete[] spr_rom;
-       delete[] voice_rom;
-#endif
-
        for(int i=0; i<16; i++)
                if (psg_adrs[i])
                        delete[] psg_adrs[i];                           // Deallocate if loaded
@@ -1681,3 +1676,130 @@ BlitChar(screen, chr_rom, gram1);
 
        return 1;
 }
+
+#if 0
+Hitachi uC runs at 6.144 MHz
+YM2151 runs at 3.579580 MHz
+
+
+Rolling Thunder Memory map
+--------------------------
+Most of the decoding is done by custom chips (CUS47 and CUS41), so the memory
+map is inferred by program behaviour. The customs also handle internally irq
+and watchdog.
+The main CPU memory map is the same in all games because CUS47 is used by all
+games. The sub CPU and sound CPU, on the other hand, change because CUS41 is
+replaced by other chips.
+All RAM is shared between main and sub CPU, except for sound RAM which is shared
+between main and sound CPU; the portion of object RAM that is overlapped by sound
+RAM is used exclusively by the sub CPU.
+
+MAIN CPU:
+
+Address             Dir Data     Name      Description
+------------------- --- -------- --------- -----------------------
+000x xxxx xxxx xxxx R/W xxxxxxxx SCROLL0   tilemap 0/1 RAM (shared with sub CPU)
+001x xxxx xxxx xxxx R/W xxxxxxxx SCROLL1   tilemap 2/3 RAM (shared with sub CPU)
+0100 00xx xxxx xxxx R/W xxxxxxxx SOUND     sound RAM (through CUS30, shared with MCU)
+0100 0000 xxxx xxxx R/W xxxxxxxx           portion holding the sound wave data
+0100 0001 00xx xxxx R/W xxxxxxxx           portion holding the sound registers
+010x xxxx xxxx xxxx R/W xxxxxxxx OBJECT    work RAM (shared with sub CPU) [1]
+0101 1xxx xxxx xxxx R/W xxxxxxxx           portion holding sprite registers
+011x xxxx xxxx xxxx R   xxxxxxxx ROM 9D    program ROM (banked) [2]
+1xxx xxxx xxxx xxxx R   xxxxxxxx ROM 9C    program ROM
+1000 00-- ---- ----   W --------           watchdog reset (RES generated by CUS47)
+1000 01-- ---- ----   W --------           main CPU irq acknowledge (IRQ generated by CUS47)
+1000 1x-- ---- ----   W -------- BANK      tile gfx bank select (data is in A10) (latch in CUS47)
+1001 00-- ---- -x0x   W xxxxxxxx LATCH0    tilemap 0/1 X scroll + priority
+1001 00-- ---- -x10   W xxxxxxxx LATCH0    tilemap 0/1 Y scroll
+1001 00-- ---- --11   W ------xx BAMNKM    ROM 9D bank select
+1001 01-- ---- -x0x   W xxxxxxxx LATCH1    tilemap 2/3 X scroll + priority
+1001 01-- ---- -x10   W xxxxxxxx LATCH1    tilemap 2/3 Y scroll
+1001 01-- ---- --11   W ------xx BAMNKS    ROM 12D bank select
+1100 00-- ---- ----   W xxxxxxxx BACKCOLOR background color
+
+[1] Note that this is partially overlapped by sound RAM
+[2] In Rolling Thunder and others, replaced by the ROM/voice expansion board
+
+
+SUB CPU:
+
+Address             Dir Data     Name      Description
+------------------- --- -------- --------- -----------------------
+000x xxxx xxxx xxxx R/W xxxxxxxx SUBOBJ    work RAM (shared with main CPU)
+0001 1xxx xxxx xxxx R/W xxxxxxxx           portion holding sprite registers
+001x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR0   tilemap 0/1 RAM (shared with main CPU)
+010x xxxx xxxx xxxx R/W xxxxxxxx SUBSCR1   tilemap 2/3 RAM (shared with main CPU)
+011x xxxx xxxx xxxx R   xxxxxxxx ROM 12D   program ROM (banked) [1]
+1xxx xxxx xxxx xxxx R   xxxxxxxx ROM 12C   program ROM
+1000 0--- ---- ----   W --------           watchdog reset (MRESET generated by CUS41)
+1000 1--- ---- ----   W --------           main CPU irq acknowledge (generated by CUS41)
+1101 0--- ---- -x0x   W xxxxxxxx LATCH0    tilemap 0/1 X scroll + priority
+1101 0--- ---- -x10   W xxxxxxxx LATCH0    tilemap 0/1 Y scroll
+1101 0--- ---- --11   W ------xx BAMNKM    ROM 9D bank select
+1101 1--- ---- -x0x   W xxxxxxxx LATCH1    tilemap 2/3 X scroll + priority
+1101 1--- ---- -x10   W xxxxxxxx LATCH1    tilemap 2/3 Y scroll
+1101 1--- ---- --11   W ------xx BAMNKS    ROM 12D bank select
+
+[1] Only used by Rolling Thunder
+
+
+MCU:
+
+Address             Dir Data     Name      Description
+------------------- --- -------- --------- -----------------------
+0000 0000 xxxx xxxx                        MCU internal registers, timers, ports and RAM
+0001 xxxx xxxx xxxx R/W xxxxxxxx RAM 3F    sound RAM (through CUS30, partially shared with main CPU)
+0001 0000 xxxx xxxx R/W xxxxxxxx           portion holding the sound wave data
+0001 0001 00xx xxxx R/W xxxxxxxx           portion holding the sound registers
+0010 0--- --00 ---x R/W xxxxxxxx YMCS      YM2151
+0010 0--- --01 ----                        n.c.
+0010 0--- --10 ---- R   xxxxxxxx PORTA     switch inputs
+0010 0--- --11 ---- R   xxxxxxxx PORTB     dip switches
+01xx xxxx xxxx xxxx R   xxxxxxxx ROM 6B    program ROM (lower half)
+10xx xxxx xxxx xxxx R   xxxxxxxx ROM 6B    program ROM (upper half)
+1011 0--- ---- ----   W                    unknown (CUS41)
+1011 1--- ---- ----   W                    unknown (CUS41)
+1111 xxxx xxxx xxxx R   xxxxxxxx           MCU internal ROM
+
+
+Notes:
+-----
+- we are using an unusually high CPU interleave factor (800) to avoid hangs
+  in rthunder. The two 6809 in this game synchronize using a semaphore at
+  5606/5607 (CPU1) 1606/1607 (CPU2). CPU1 clears 5606, does some quick things,
+  and then increments 5606. While it does its quick things (which require
+  about 40 clock cycles) it expects CPU2 to clear 5607.
+  Raising the interleave factor to 1000 makes wndrmomo crash during attract
+  mode. I haven't investigated on the cause.
+
+- There are two watchdogs, one per CPU (or maybe three). Handling them
+  separately is necessary to allow entering service mode without manually
+  resetting in rthunder and genpeitd: only one of the CPUs stops writing to
+  the watchdog.
+
+- The sprite hardware buffers spriteram: the program writes the sprite list to
+  offsets 4-9 of every 16-byte block, then at the end writes to offset 0x1ff2 of
+  sprite RAM to signal the chip that the list is complete. The chip will copy
+  the list from 4-9 to 10-15 and use it from there. This has not been verified
+  on the real hardware, but it is the most logical way of doing it.
+  Emulating this behaviour and not using an external buffer is important in
+  rthunder: when you insert a coin, the whole sprite RAM is cleared, but 0x1ff2
+  is not written to. If we buffered spriteram to an external buffer, this would
+  cause dangling sprites because the buffer would not be updated.
+
+- spriteram buffering fixes sprite lag, but causes a glitch in rthunder when
+  entering a door. The *closed* door is made of tiles, but the *moving* door is
+  made of sprites. Since sprites are delayed by 1 frame, when you enter a door
+  there is one frame where neither the tile-based closed door nor the
+  sprite-based moving door is shown, so it flickers. This behavior has been
+  confirmed on a real PCB.
+
+TODO:
+----
+- The two unknown writes for the MCU are probably watchdog reset and irq acknowledge,
+  but they don't seem to work as expected. During the first few frames they are
+  written out of order and hooking them up in the usual way causes the MCU to
+  stop receiving interrupts.
+
+#endif