X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fthunder.cpp;h=17ea566f26774da6ad315789ed1df2f5584a855e;hb=7c0ff1ece391810183f1d37923a66bf30de480ee;hp=fde0f3ef48289d66db1b384421d113357cab70ac;hpb=87647f03b4d7cf53948ed8f9f25ad30da5e596bf;p=thunder diff --git a/src/thunder.cpp b/src/thunder.cpp index fde0f3e..17ea566 100755 --- a/src/thunder.cpp +++ b/src/thunder.cpp @@ -95,10 +95,10 @@ using namespace std; // Yes! SDL_Surface * screen; -uint8 * gram, * grom; // Allocate RAM & ROM pointers -uint8 * gram1, * gram2, * grom1, * grom2; // Actual memory -uint8 * grom3, * grom4, * data_rom, * spr_rom, * voice_rom; -uint8 * chr_rom; // Character ROM pointer +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 V6809REGS cpu1, cpu2; @@ -126,6 +126,8 @@ uint8 * fm_adrs[14]; fstream tr; // Tracelog hook uint16 pcx; // Where we at? +static uint8 * keys; // SDL raw keyboard matrix + static char op_mat1[256] = { 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 5, 5, 0, 0, 4, 4, 0, 5, 8, 0, 8, 5, 6, 6, @@ -288,6 +290,8 @@ uint8 RdMem(uint16 addr) { uint8 b; + // $4000-4300 is RAM shared with the microcontroller... + if (addr < 0x8000) { if (addr > 0x5FFF) @@ -306,6 +310,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; @@ -319,6 +324,12 @@ void WrMem(uint16 addr, uint8 b) //} tr << endl; }//*/ +#if 1 + 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 @@ -347,8 +358,16 @@ void WrMem(uint16 addr, uint8 b) BlitChar(screen, chr_rom, gram1); refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz... } +//Seems they're more regular than this... // irqGoA = true; // Will this work??? no... - cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok??? +// cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok??? + // IRQ Ack (may also be frame go... +// cpu1.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; +#if 1 + if (disasm) + WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", b); +#endif + ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); } } @@ -387,6 +406,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; @@ -400,10 +420,17 @@ void WrMemB(uint16 addr, uint8 b) //} tr << endl; }//*/ +#if 1 + if (addr == 0x0182) + { + WriteLog("\nWriteMem: CPU #2 writing $%02X to $0182 ($4182)!\n\n", b); + } +#endif - if (addr == 0x8800) +// Bah. Dunno if this is accurate or not! +// if (addr == 0x8800) // irqGoB = true; // Will it work??? no... - cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok??? +// cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ;//wil wok??? if (addr == 0x6000) SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1 if (addr == 0x6400) @@ -427,6 +454,16 @@ void WrMemB(uint16 addr, uint8 b) if (addr > 0x5FFF) gram1[addr] = b; } + if (addr == 0x8800) + { + // IRQ Ack (may also be frame go...) +// cpu2.cpuFlags &= ~V6809_ASSERT_LINE_IRQ; +#if 1 + if (disasm) + WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", b); +#endif + ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ); + } } // @@ -437,25 +474,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 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"); @@ -1272,7 +1267,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); @@ -1319,6 +1314,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; @@ -1332,52 +1329,41 @@ WriteLog("About to enter main loop...\n"); // SDL key handling... -#warning "MAKE REPOSITORY FOR THIS THING! !!! FIX !!!" -#warning "AND USE THE OLD SOURCE FOR THE BASE!!!" -#warning "KEYS ARE FUCKED UP! !!! FIX !!!" -// keyPressed = 0; // Reset keypress - while (SDL_PollEvent(&event) != 0) // Bleed out all pending key events... - { - if (event.type == SDL_KEYDOWN) - keys[event.key.keysym.scancode] = 1; - if (event.type == SDL_KEYUP) - keys[event.key.keysym.scancode] = 0; - } + SDL_PumpEvents(); // Force key events into the buffer. -// { - if (keys[0x01]) + if (keys[SDLK_ESCAPE]) running = false; // ESC to exit... if (debounce) debounce--; // Debounce toggle keys... else { - if (keys[0x3B]) + if (keys[SDLK_F1]) { self_test = !self_test; // Self-test (F1-toggle) debounce = 10; // Key debounce value... } - if (keys[0x3C]) + if (keys[SDLK_F2]) { gram1[0x4268] = 1; // Video test (F2) debounce = 10; // Key debounce value... } - if (keys[0x58]) + if (keys[SDLK_F12]) { scr_type = !scr_type; // Toggle screen (F12) debounce = 10; // Key debounce value... } - if (keys[0x3D]) + if (keys[SDLK_F3]) { show_scr = !show_scr; // Toggle bkgrnd (F3) debounce = 10; } - if (keys[0x40]) + if (keys[SDLK_F6]) { enable_cpu = !enable_cpu; // Toggle CPUs (F6) debounce = 10; } - if (keys[0x3F]) + if (keys[SDLK_F5]) { refresh2 = !refresh2; // Toggle 30/60Hz (F5) SetRefreshRate(refresh2); // Inform GUI of refresh @@ -1387,13 +1373,13 @@ WriteLog("About to enter main loop...\n"); SpawnMsg(M30FPS); debounce = 10; // Key debounce value... } - if (keys[0x3E]) // Do PCX snapshot (F4) + if (keys[SDLK_F4]) // Do PCX snapshot (F4) { SpawnSound(USERSOUND, SCAMERA); SnapPCX(screen); debounce = 10; } - if (keys[0x0F]) // Tab active/deactivate GUI + if (keys[SDLK_TAB]) // Tab active/deactivate GUI { if (ShowGUI()) DeactivateGUI(); @@ -1403,47 +1389,47 @@ WriteLog("About to enter main loop...\n"); } } //if (keys[0x3E]) gram1[0x4247] = 1; // Screen hold DS (F4) - if (keys[77+128]) // Right arrow + if (keys[SDLK_RIGHT]) // Right arrow { if (ShowGUI()) SelectRight(); // If GUI active... else { - if (!keys[75+128]) // Disallow opposite directions @ same time + if (!keys[SDLK_LEFT]) // Disallow opposite directions @ same time gram1[0x427F] = 1; // Stick right } } - if (keys[75+128]) + if (keys[SDLK_LEFT]) { if (ShowGUI()) SelectLeft(); // If GUI active... else { - if (!keys[77+128]) // Disallow opposite directions@same time + if (!keys[SDLK_RIGHT]) // Disallow opposite directions@same time gram1[0x4281] = 1; // Left arrow } } - if (keys[72+128]) + if (keys[SDLK_UP]) { if (ShowGUI()) SelectUp(); // If GUI active... else { - if (!keys[80+128]) // Disallow opposite directions@same time + if (!keys[SDLK_DOWN]) // Disallow opposite directions@same time gram1[0x427B] = 1; // Up arrow } } - if (keys[80+128]) + if (keys[SDLK_DOWN]) { if (ShowGUI()) SelectDown(); // If GUI active... else { - if (!keys[72+128]) // Disallow opposite directions@same time + if (!keys[SDLK_UP]) // Disallow opposite directions@same time gram1[0x427D] = 1; // Down arrow } } - if (keys[28]) // Return + if (keys[SDLK_RETURN]) // Return { uint8 retval = UserSelectedSomething(); if (retval == EXIT) @@ -1454,93 +1440,109 @@ WriteLog("About to enter main loop...\n"); SetRefreshRate(refresh2); } } - if (keys[0x02]) - gram1[0x427A] = 1; // (1) - if (keys[0x03]) - gram1[0x427C] = 1; // (2) - if (keys[0x04]) - gram1[0x427E] = 1; // (3) - if (keys[0x06]) - gram1[0x4280] = 1; // (5) - if (keys[0x10]|keys[29]) - gram1[0x4276] = 1; // (Q) Jump - if (keys[0x11]) - gram1[0x426A] = 1; // (W) + if (keys[SDLK_1]) + gram1[0x427A] = 1; // (1) + if (keys[SDLK_2]) + gram1[0x427C] = 1; // (2) + if (keys[SDLK_3]) + gram1[0x427E] = 1; // (3) + if (keys[SDLK_5]) + gram1[0x4280] = 1; // (5) + if (keys[SDLK_q] | keys[29]) + gram1[0x4276] = 1; // (Q) Jump + if (keys[SDLK_w]) + gram1[0x426A] = 1; // (W) if (fire_debounce) fire_debounce--; - if (keys[0x12]|keys[56]) // (E) Fire + if (keys[SDLK_e] | keys[56]) // (E) Fire { if (!fire_debounce) { gram1[0x4278] = 1; + if (gram1[0x3F08] == 0xFF) // Ugly kludge for debouncing gun - { fire_debounce = 8; - } else - { fire_debounce = 2; - } } } - if (keys[0x13]) - gram1[0x426C] = 1; // (R) - if (keys[0x14]) - gram1[0x4262] = 1; // (T) - if (keys[0x15]) - gram1[0x4260] = 1; // (Y) - if (keys[0x44]) - gram1[0x41A5]++; // Coin? (F10) - if (keys[0x2C]) - gram1[0x4189]++; // ? (Z) credits l dig - if (keys[0x2D]) - gram1[0x418A]++; // ? (X) credits r dig - if (keys[0x2E]) - gram1[0x418C]++; // ? (C) Start - if (keys[0x2F]) - gram1[0x418D]++; // ? (V) - if (keys[0x41]) - SpawnSound(USERSOUND, 0); // Do user sound (F7) -// if (keys[0x42]) + if (keys[SDLK_r]) + gram1[0x426C] = 1; // (R) + if (keys[SDLK_t]) + gram1[0x4262] = 1; // (T) + if (keys[SDLK_y]) + gram1[0x4260] = 1; // (Y) + if (keys[SDLK_F10]) + gram1[0x41A5]++; // Coin? (F10) + if (keys[SDLK_z]) + gram1[0x4189]++; // ? (Z) credits l dig + if (keys[SDLK_x]) + gram1[0x418A]++; // ? (X) credits r dig + if (keys[SDLK_c]) + gram1[0x418C]++; // ? (C) Start + if (keys[SDLK_v]) + gram1[0x418D]++; // ? (V) + if (keys[SDLK_F7]) + SpawnSound(USERSOUND, 0); // Do user sound (F7) +// if (keys[SDLK_F8]) // { -// gram1[0x4380] = 0; // (F8) kill music (this worx) -// charbase = false; // Switch chars out... +// gram1[0x4380] = 0; // (F8) kill music (this worx) +// charbase = false; // Switch chars out... // } -// if (keys[0x43]) gram1[0x4285] = 1; // (F9) strobe unknown loc - if (keys[0x45]) // (F11) +// if (keys[SDLK_F9]) gram1[0x4285] = 1; // (F9) strobe unknown loc + if (keys[SDLK_F11]) // (F11) { Execute6809(&cpu1, 10); 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]) { -/*// if (irqGoA) - cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ; - - Execute6809(&cpu1, 25000); - cpu1.clock -= 25000; // Remove 25K ticks from clock (in case it overflowed) - -// if (irqGoB) - cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ; + cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET; + cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET; + } + if (keys[SDLK_d]) // (D) start disassembly + disasm = true; +#if 0 + 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 - Execute6809(&cpu2, 25000); - cpu2.clock -= 25000; // Remove 25K ticks from clock (in case it overflowed)//*/ -// cpu1.cpuFlags |= V6809_ASSERT_LINE_IRQ; -// cpu2.cpuFlags |= V6809_ASSERT_LINE_IRQ; - while (cpu1.clock < 25000) + 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) +// 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? - Execute6809(&cpu1, 5); - Execute6809(&cpu2, 5); + // There's better ways, such as keeping track of when slave writes to master, etc... + 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)//*/ - } // END: enable_cpu // if (refresh_++ == 1) // 30 Hz... @@ -1551,11 +1553,14 @@ WriteLog("About to enter main loop...\n"); // BlitChar(screen, chr_rom, gram1); // refresh_ = (refresh2 ? 1 : 0); // 60/30 Hz... // } + +#if 1 //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(); @@ -1666,29 +1671,140 @@ BlitChar(screen, chr_rom, gram1); active = false; //break; // Quit } - SDL_Quit(); // Shut down SDL - - 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; + SDL_Quit(); // Shut down SDL for(int i=0; i<16; i++) - if (psg_adrs[i] != NULL) - delete[] psg_adrs[i]; // Deallocate if loaded + if (psg_adrs[i]) + delete[] psg_adrs[i]; // Deallocate if loaded for(int i=0; i<14; i++) - if (fm_adrs[i] != NULL) - delete[] fm_adrs[i]; // Deallocate if loaded + if (fm_adrs[i]) + delete[] fm_adrs[i]; // Deallocate if loaded -// tr.close(); // Close tracelog LogDone(); return 1; } + +#if 0 +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