]> Shamusworld >> Repos - thunder/blobdiff - src/thunder.cpp
Code cleanup, final fix for sprite lag problem.
[thunder] / src / thunder.cpp
index bd0cc855ab903cfa0409ce278aaccd06c5f3a747..2914a96c5ae80eab26a7347e3e388609e6aa77ca 100644 (file)
@@ -2,7 +2,7 @@
 // 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 <string>
@@ -32,7 +33,6 @@
 #include "video.h"
 #include "ym2151.h"
 
-
 #define ROM1   "rt3-1b.9c"
 #define ROM2   "rt3-2b.12c"
 #define ROM3   "rt3-3.12d"
@@ -62,7 +62,6 @@
 #define PROM5  "mb7112e.6u"
 #define MCUROM "rt1-mcu.bin"
 
-
 // Global defines
 
 uint8_t gram1[0x10000], grom1[0x10000], grom2[0x10000];
@@ -80,11 +79,13 @@ uint32_t banksw1, banksw2;
 // 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
 //
@@ -107,14 +108,11 @@ uint8_t MainReadMemory(uint16_t addr)
        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)
@@ -129,25 +127,15 @@ void MainWriteMemory(uint16_t address, uint8_t data)
                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)
 //
@@ -166,14 +154,11 @@ uint8_t SubReadMemory(uint16_t address)
        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;
@@ -185,20 +170,12 @@ void SubWriteMemory(uint16_t address, uint8_t data)
                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)
@@ -221,7 +198,6 @@ uint8_t MCUReadMemory(uint16_t address)
        return mcuMem[address];
 }
 
-
 void MCUWriteMemory(uint16_t address, uint8_t data)
 {
        static uint8_t ymRegister;
@@ -254,32 +230,27 @@ void MCUWriteMemory(uint16_t address, uint8_t data)
        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!)
@@ -298,19 +269,18 @@ bool LoadImg(const char * filename, uint8_t * mem, uint32_t address, uint32_t le
                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)
        {
@@ -321,7 +291,7 @@ bool ReadColorPROMs(void)
                fclose(file1);
        }
 
-       file1 = fopen("./ROMs/"PROM4, "rb");
+       file1 = fopen("./ROMs/" PROM4, "rb");
 
        if (file1)
        {
@@ -332,8 +302,8 @@ bool ReadColorPROMs(void)
                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)
@@ -355,7 +325,7 @@ bool ReadColorPROMs(void)
 
        // 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)
        {
@@ -366,19 +336,18 @@ bool ReadColorPROMs(void)
        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;
        }
 
@@ -403,12 +372,12 @@ bool UnpackFonts(void)
        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;
        }
 
@@ -436,7 +405,6 @@ bool UnpackFonts(void)
        return true;
 }
 
-
 //
 // Main loop
 //
@@ -444,16 +412,14 @@ int main(int argc, char * argv[])
 {
        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");
@@ -532,17 +498,17 @@ int main(int argc, char * argv[])
        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;
 
@@ -553,7 +519,7 @@ int main(int argc, char * argv[])
        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)
@@ -579,9 +545,6 @@ WriteLog("About to set up screen...\n");
 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)
        {
@@ -590,11 +553,6 @@ WriteLog("About to enter main loop...\n");
 // 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;
@@ -948,9 +906,9 @@ WriteLog("About to enter main loop...\n");
 #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
@@ -959,21 +917,33 @@ WriteLog("About to enter main loop...\n");
 // 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)
@@ -992,126 +962,3 @@ WriteLog("About to enter main loop...\n");
 
        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
-