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