//
#include "sound.h"
- #include <SDL2/SDL.h>
+ #include <SDL.h>
#include "psg.h"
#include "resource.h"
#include "ym2151.h"
-
-#define SAMPLE_RATE 48000
+#define SAMPLE_RATE 48000
+#define BUFFER_SIZE 512
// Function prototypes
void SoundFunc(void *, Uint8 *, int);
extern uint8_t voice_rom[]; // PCM data pointer
static bool soundInitialized = false;
-const float sampleBase = (float)SAMPLE_RATE/6000.0; // Voice is between 5512.5 and 6000 Hz
-bool snd_go = false;
-bool chan1_go = false, chan2_go = false;//, chan3_go = false;
-bool /*chan4_go = false, chan5_go = false,*/ chan6_go = false;
-uint8_t * sndp1, * sndp2, /* sndp3, * sndp4, * sndp5,*/ * sndp6;
-uint32_t rom_pos, end_pos;
+const float sampleBase = (float)SAMPLE_RATE / 6000.0; // Voice is between 5512.5 and 6000 Hz
+bool chan1_go = false, chan2_go = false;
+bool chan6_go = false;
+uint8_t * sndp1, * sndp2, * sndp6;
uint32_t spos1, end_pos1;
uint32_t spos2, end_pos2;
-//uint32_t spos3, end_pos3;
-//uint32_t spos4, end_pos4;
-//uint32_t spos5, end_pos5;
uint32_t spos6, end_pos6;
-float sample1;
-int16_t prevSamp1;
-//int8_t delta_x1;
-float sample2;
-int16_t prevSamp2;
-//int8_t delta_x2;
+float sample1, sample2;
+int16_t prevSamp1, prevSamp2;
uint16_t snd_num;
-uint8_t * snd_array[3] = { sunknown, scya, scamera }; // From RESOURCE.H
+// From RESOURCE.H
+uint8_t * snd_array[3] = { sunknown, scya, scamera };
uint32_t snd_lens[3] = { sunknownlen, scyalen, scameralen };
-
void InitSound(void)
{
// params 1, 4 & 5 are useless
-// if (YMInit(1, 3579580, 22050, 16, 512))//, (SAMPLE **)sbp))
- if (YMInit(1, 3579580, SAMPLE_RATE, 16, 512))
+// if (YMInit(1, 3579580, SAMPLE_RATE, 16, 512))
+ if (YMInit(3579580, SAMPLE_RATE))
{
printf("SOUND: Could not init YM2151 emulator!\n");
- return;// -1;
+ return;
}
InitPSG(SAMPLE_RATE);
desired.freq = SAMPLE_RATE;
desired.format = AUDIO_S16;
desired.channels = 1;
- desired.samples = 1024;
+ desired.samples = BUFFER_SIZE * 2; // Size is in BYTES, so x2
desired.callback = SoundFunc;
desired.userdata = NULL;
- // Also, should check to see if it got the hardware it needed, correct sample size, etc.
+ // Also, should check to see if it got the hardware it needed, correct sample size, etc. (actually, SDL guarantees we get what we asked for--I think)
if (SDL_OpenAudio(&desired, &obtained) < 0)
{
soundInitialized = false;
soundInitialized = true;
}
-
void SpawnSound(int type, int snd, int channel/* = 0*/)
{
-// extern uint32_t psg_lens[16];
-// extern uint8_t * psg_adrs[16];
-// extern uint32_t voc_lens[32];
-// extern uint8_t * voc_adrs[32];
-// extern uint32_t fm_lens[14];
-// extern uint8_t * fm_adrs[14];
-
if (!soundInitialized)
return;
snd_num = snd;
-// SpawnMsg(MSHOWNUMS);
// Voice type sounds...
if (type == GAMESOUND)
chan2_go = true;
}
}
-#if 0
- else if (type == PSGSOUND)
- {
- if (snd_num & 0x10) // Second channel?
- {
- spos3 = 0;
- end_pos3 = psg_lens[snd_num & 0x0F];
- sndp3 = psg_adrs[snd_num & 0x0F];
- chan3_go = true;
-
- if (spos3 == end_pos3)
- chan3_go = false; // No sound loaded, so don't do it!
- }
- else // First channel
- {
- spos4 = 0;
- end_pos4 = psg_lens[snd_num & 0x0F];
- sndp4 = psg_adrs[snd_num & 0x0F];
- chan4_go = true;
-
- if (spos4 == end_pos4)
- chan4_go = false; // No sound loaded, so don't do it!
- }
- }
-#endif
-#if 0
- else if (type == FMSOUND)
- {
- spos5 = 0;
- end_pos5 = fm_lens[snd_num];
- sndp5 = fm_adrs[snd_num];
- chan5_go = true;
-
- if (spos5 == end_pos5)
- chan5_go = false; // No sound loaded, so don't do it!
- }
-#endif
else if (type == USERSOUND)
{
spos6 = 0;
}
}
-
void SoundFunc(void * userdata, Uint8 * buffer, int num)
{
- static bool psg1go = false, psg2go = false;
- // Length / 2 is because it's set up for 16-bit samples
-// YM2151UpdateOne(buffer, length / 2);
- // Have to go into ym2151.cpp and change this manually back to 8 bit
+ // We do num / 2 because num is in BYTES, and the buffer uses signed WORDs.
YM2151UpdateOne(buffer, num / 2);
-// return;
UpdatePSG(buffer, num / 2);
// 0-22 different sounds...
- uint16_t cnt = 0;//, sample;
+ uint16_t cnt = 0;
uint8_t start_samp1, end_samp1, start_samp2, end_samp2;
int16_t samp1 = 0, samp2 = 0, samp6 = 0; // Zero samples...
- if (!(chan1_go || chan2_go /*|| chan3_go || chan4_go || chan5_go*/ || chan6_go))
+ if (!(chan1_go || chan2_go || chan6_go))
return;
while (cnt != (num / 2))
{
uint8_t voiceSample = voice_rom[spos2++];
samp2 = ((int16_t)voiceSample - 128) * 160;
-// samp2 = voice_rom[spos2++];
if (voiceSample == 0xFF)
{
sample2 -= 1.0;
}
-#if 0
- if (chan3_go)
- {
- samp3 = ((psg1go ? sndp3[spos3++] : sndp3[spos3]) - 128) * 160;
- psg1go = !psg1go;
-
- if (spos3 == end_pos3)
- {
- chan3_go = false;
- samp3 = 0; // Kill channel 3 if done...
- }
- }
-
- if (chan4_go)
- {
- samp4 = ((psg2go ? sndp4[spos4++] : sndp4[spos4]) - 128) * 160;
- psg2go = !psg2go;
-
- if (spos4 == end_pos4)
- {
- chan4_go = false;
- samp4 = 0; // Kill channel 4 if done...
- }
- }
-
- if (chan5_go)
- {
- samp5 = sndp5[spos5++];
-
- if (spos5 == end_pos5)
- {
- chan5_go = false;
- samp5 = 128; // Kill channel 5 if done...
- }
- }
-#endif
if (chan6_go)
{
samp6 = sndp6[spos6++];
}
// Mix 'em...
-// sample = samp1 + samp2 + samp3 + samp4 + samp5 + samp6 - 640;
int32_t sample = samp1 + samp2 + samp6;
sample += ((int16_t *)buffer)[cnt];
// If it overflowed, clip it
-// if (sample & 0xFFFF0000)
-// sample = (sample & 0x8000 ? 0x00 : 0xFF);
-// sample = (sample & 0x80000000 ? 0x0000 : 0xFFFF);
if (sample > 32767)
sample = 32767;
else if (sample < -32767)
((int16_t *)buffer)[cnt++] = (int16_t)sample;
}
}
-
// Thunder: A Rolling Thunder Emulator
//
// by James Hammons
-// (C) 2004, 2014 Underground Software
+// (C) 2004, 2023 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
// JLH 08/12/2009 Stabilized emulation so that it works
// JLH 04/04/2014 Converted to SDL 2
// JLH 04/17/2014 Removed a metric fuck-tonne of cruft, added YM2151 & MCU
+// JLH 01/13/2023 Finally fixed the sprite lag problem :-D
//
-#define THUNDER_VERSION "1.2.0"
+#define THUNDER_VERSION "1.2.1"
- #include <SDL2/SDL.h>
+ #include <SDL.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include "video.h"
#include "ym2151.h"
-
#define ROM1 "rt3-1b.9c"
#define ROM2 "rt3-2b.12c"
#define ROM3 "rt3-3.12d"
#define PROM5 "mb7112e.6u"
#define MCUROM "rt1-mcu.bin"
-
// Global defines
uint8_t gram1[0x10000], grom1[0x10000], grom2[0x10000];
// MCU inputs
Byte input1, input2, input3, input4, input5;
+// Copy sprites flag
+bool copySprites = false;
+
// Function prototypes
uint8_t MCUReadMemory(uint16_t address);
void MCUWriteMemory(uint16_t address, uint8_t data);
-
//
// Read a byte from memory
//
return grom1[addr];
}
-
//
// Write a byte to memory
//
void MainWriteMemory(uint16_t address, uint8_t data)
{
- extern bool disasm;
-
if (address == 0x6000)
SpawnSound(GAMESOUND, gram1[0x6200], 0); // Do voice chan 1
if (address == 0x6400)
gram1[address] = data;
if (address == 0x5FF2)
- CopySprites();
+ copySprites = true;
if (address == 0x8800)
charBankSwitch = false; // Char banksw1
if (address == 0x8C00)
charBankSwitch = true; // Char banksw2
- if (address == 0x8400) // Frame go strobe? VBlank acknowledge?
- {
-// BlitChar(charROM, gram1);
-
- // IRQ Ack (may also be frame go...)
- ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
-#if 1
- if (disasm)
- WriteLog("WriteMem: CPU #1 Acknowledging IRQ...\n", data);
-#endif
- }
+ if (address == 0x8400) // Frame go strobe? VBlank acknowledge?
+ ClearLineOfCurrentV6809(V6809_LINE_IRQ); // IRQ Ack
}
-
//
// Read a byte from memory (2nd processor)
//
return grom2[address];
}
-
//
// Write a byte to memory (2nd processor)
//
void SubWriteMemory(uint16_t address, uint8_t data)
{
- extern bool disasm;
-
// Set sprite data bank switch
if (address == 0xD803)
banksw2 = (uint32_t)(data & 0x03) << 13;
gram1[address - 0x2000] = data;
if (address == 0x1FF2)
- CopySprites();
+ copySprites = true;
if (address == 0x8800)
- {
- // IRQ Ack (may also be frame go...)
- ClearLineOfCurrentV6809(V6809_ASSERT_LINE_IRQ);
-#if 1
- if (disasm)
- WriteLog("WriteMem: CPU #2 Acknowledging IRQ...\n", data);
-#endif
- }
+ ClearLineOfCurrentV6809(V6809_LINE_IRQ); // IRQ Ack
}
-
uint8_t MCUReadMemory(uint16_t address)
{
if (address < 0x20)
return mcuMem[address];
}
-
void MCUWriteMemory(uint16_t address, uint8_t data)
{
static uint8_t ymRegister;
mcuMem[address] = data;
}
-
uint8_t V63701ReadPort1(void)
{
// printf("V63701ReadPort1: Read $%02X...\n", input3.byte);
return input3.byte;
}
-
uint8_t V63701ReadPort2(void)
{
return 0xFF;
}
-
void V63701WritePort1(uint8_t data)
{
// printf("V63701WritePort1: Wrote $%02X...\n", data);
}
-
void V63701WritePort2(uint8_t data)
{
// printf("V63701WritePort2: Wrote $%02X...\n", data);
}
-
//
// Generic Load file into image space
// (No error checking performed! Responsibility of caller!)
return false;
}
- fread(&mem[address], 1, length, file);
+ size_t ignored = fread(&mem[address], 1, length, file);
fclose(file);
return true;
}
-
//
// Read color PROMs
//
bool ReadColorPROMs(void)
{
- FILE * file1 = fopen("./ROMs/"PROM3, "rb");
+ FILE * file1 = fopen("./ROMs/" PROM3, "rb");
if (file1)
{
fclose(file1);
}
- file1 = fopen("./ROMs/"PROM4, "rb");
+ file1 = fopen("./ROMs/" PROM4, "rb");
if (file1)
{
fclose(file1);
}
- file1 = fopen("./ROMs/"PROM1, "rb");
- FILE * file2 = fopen("./ROMs/"PROM2, "rb");
+ file1 = fopen("./ROMs/" PROM1, "rb");
+ FILE * file2 = fopen("./ROMs/" PROM2, "rb");
// If open was successful...
if (file1 && file2)
// PROM5 has the following in it (tile address decoder):
// 00: 00 20 40 60 02 22 42 62 04 24 44 64 06 26 46 66
- // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
+ // 10: 88 A8 C8 E8 8A AA CA EA 8C AC CC EC 8E AE CE EE
if (!file1)
{
return true;
}
-
//
// Unpack font data
//
bool UnpackFonts(void)
{
// 0x4000 $800 chars
- FILE * file1 = fopen("./ROMs/"ROM7, "rb");
- FILE * file2 = fopen("./ROMs/"ROM8, "rb");
+ FILE * file1 = fopen("./ROMs/" ROM7, "rb");
+ FILE * file2 = fopen("./ROMs/" ROM8, "rb");
if (!file1 || !file2)
{
- printf("Could not open either "ROM7" or "ROM8"!\n");
+ printf("Could not open either " ROM7 " or " ROM8 "!\n");
return false;
}
fclose(file1);
fclose(file2);
- file1 = fopen("./ROMs/"ROM5, "rb");
- file2 = fopen("./ROMs/"ROM6, "rb");
+ file1 = fopen("./ROMs/" ROM5, "rb");
+ file2 = fopen("./ROMs/" ROM6, "rb");
if (!file1 || !file2)
{
- printf("Could not open either "ROM5" or "ROM6"!\n");
+ printf("Could not open either " ROM5 " or " ROM6 "!\n");
return false;
}
return true;
}
-
//
// Main loop
//
{
InitLog("thunder.log");
- extern bool disasm; // From 'V6809.CPP'
-
bool running; // CPU running state flag...
SDL_Event event; // SDL "event"
uint32_t ticks, oldTicks;
uint8_t frameTickCount = 0;
- printf("THUNDER v"THUNDER_VERSION" by James Hammons\n");
- printf("Serial #20149417 / Prerelease\n");
- printf("© 2003, 2014 Underground Software\n\n");
+ printf("THUNDER v" THUNDER_VERSION " by James Hammons\n");
+ printf("Serial #20230113 / Prerelease\n");
+ printf("© 2003, 2023 Underground Software\n\n");
printf("This emulator is free software. If you paid for it you were RIPPED OFF\n\n");
// SDL_WM_SetCaption("Thunder v"THUNDER_VERSION" ", "Thunder");
memset(&cpu1, 0, sizeof(V6809REGS));
cpu1.RdMem = MainReadMemory;
cpu1.WrMem = MainWriteMemory;
- cpu1.cpuFlags |= V6809_ASSERT_LINE_RESET;
+ cpu1.cpuFlags |= V6809_LINE_RESET;
memset(&cpu2, 0, sizeof(V6809REGS));
cpu2.RdMem = SubReadMemory;
cpu2.WrMem = SubWriteMemory;
- cpu2.cpuFlags |= V6809_ASSERT_LINE_RESET;
+ cpu2.cpuFlags |= V6809_LINE_RESET;
memset(&mcu, 0, sizeof(V63701REGS));
mcu.RdMem = MCUReadMemory;
mcu.WrMem = MCUWriteMemory;
- mcu.cpuFlags |= V63701_ASSERT_LINE_RESET;
+ mcu.cpuFlags |= V63701_LINE_RESET;
running = true;
InitGUI(); // Reset # of coins
// Set up DIP switches...
- input4.bit.b0 = 1; // DSW B-0: Contiues (1 = 6, 0 = 3)
+ input4.bit.b0 = 1; // DSW B-0: Continues (1 = 6, 0 = 3)
input4.bit.b1 = 1; // DSW B-2: ???
input4.bit.b2 = 1; // DSW B-4: Difficulty (1 = normal, 0 = easy)
input4.bit.b3 = 1; // DSW B-6: Bonus lives (70K/200K = 1, 100K/300K = 0)
WriteLog("About to set up audio...\n");
InitSound();
-//memset(scrBuffer, 0xFF, VIRTUAL_SCREEN_WIDTH*VIRTUAL_SCREEN_HEIGHT*sizeof(uint32_t));
-//RenderScreenBuffer();
-
WriteLog("About to enter main loop...\n");
while (running)
{
// Dipswitches are presented to the main CPUs as 0 or 1 at locations
// $423D - $425B by the MCU
-//testing... (works)
-//gram1[0x423D] = 1;
- //gram1[0x423D] = self_test; // Reset DSW1-1
-// gram1[0x4268] = 0; // Reset Video test
-
// SDL key handling...
SDL_Event event;
#endif
// 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;
- mcu.cpuFlags |= V63701_ASSERT_LINE_IRQ;
+ cpu1.cpuFlags |= V6809_LINE_IRQ;
+ cpu2.cpuFlags |= V6809_LINE_IRQ;
+ mcu.cpuFlags |= V63701_LINE_IRQ;
// while (cpu1.clock < 25000)
// 1.538 MHz = 25633.333... cycles per frame (1/60 s)
// 25600 cycles/frame
// 40 works, until it doesn't... :-P
// 640 * 40
// 800 * 32
+// 20 seems to work... :-)
// Interesting, putting IRQs at 30 Hz makes it run at the correct speed. Still hangs in the demo, though.
- for(int i=0; i<640; i++)
+// for(int i=0; i<640; i++)
+ for(int 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, 40);
- Execute6809(&cpu2, 40);
+// Execute6809(&cpu1, 40);
+// Execute6809(&cpu2, 40);
+ Execute6809(&cpu1, 20);
+ Execute6809(&cpu2, 20);
// MCU runs at 1,536,000 Hz
// 1536000 / 60 / 640 == 40
- Execute63701(&mcu, 40);
+// Execute63701(&mcu, 40);
+ Execute63701(&mcu, 20);
}
BlitChar(charROM, gram1);
+ // Make sure that the sprite RAM lags by one frame... :-)
+ if (copySprites)
+ {
+ CopySprites();
+ copySprites = false;
+ }
+
frameTickCount++;
if (frameTickCount == 3)
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:
------
-- 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
-