X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fthunder.cpp;h=2914a96c5ae80eab26a7347e3e388609e6aa77ca;hb=8ba4c4438d796f83851cd53914dff928193ed658;hp=bd0cc855ab903cfa0409ce278aaccd06c5f3a747;hpb=d22f243fc585a46e4c29d00bd32727d9f9f4055c;p=thunder diff --git a/src/thunder.cpp b/src/thunder.cpp index bd0cc85..2914a96 100644 --- a/src/thunder.cpp +++ b/src/thunder.cpp @@ -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 // @@ -12,9 +12,10 @@ // 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 #include @@ -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 -