From edc0dfb6272720e483be72352629a653492a00bf Mon Sep 17 00:00:00 2001 From: David Haywood <28625134+DavidHaywood@users.noreply.github.com> Date: Tue, 14 Dec 2021 14:30:33 +0000 Subject: Research into thoop and squash 'refresh rate' protection [Victor Fernandez (City Game), Peter Ferrie, David Haywood] (#8916) * Research into thoop and squash 'refrsh rate' protection [Victor Fernandez (City Game), Peter Ferrie, David Haywood] - prevents freeze when dying in stage 4 of Thunder Hoop, and bad text on continue in Squash --- scripts/target/mame/arcade.lua | 2 +- src/devices/cpu/mcs51/mcs51.cpp | 33 ++++++++++- src/devices/cpu/mcs51/mcs51.h | 6 +- src/devices/sound/gaelco.cpp | 15 +++++ src/devices/sound/gaelco.h | 7 ++- src/mame/drivers/gaelco.cpp | 120 +++++++++++++++++++++++++++------------- src/mame/drivers/gaelco2.cpp | 8 +-- src/mame/drivers/wrally.cpp | 6 +- src/mame/includes/gaelco.h | 7 ++- src/mame/includes/gaelcrpt.h | 5 -- src/mame/includes/wrally.h | 3 + src/mame/machine/gaelcrpt.cpp | 64 ++++++++++++++------- src/mame/machine/gaelcrpt.h | 35 ++++++++++++ src/mame/video/gaelco.cpp | 8 ++- 14 files changed, 242 insertions(+), 77 deletions(-) delete mode 100644 src/mame/includes/gaelcrpt.h create mode 100644 src/mame/machine/gaelcrpt.h diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 98d2bf30bd5..399b5912f25 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -2032,7 +2032,7 @@ files { MAME_DIR .. "src/mame/includes/gaelco.h", MAME_DIR .. "src/mame/video/gaelco.cpp", MAME_DIR .. "src/mame/machine/gaelcrpt.cpp", - MAME_DIR .. "src/mame/includes/gaelcrpt.h", + MAME_DIR .. "src/mame/machine/gaelcrpt.h", MAME_DIR .. "src/mame/drivers/gaelco2.cpp", MAME_DIR .. "src/mame/includes/gaelco2.h", MAME_DIR .. "src/mame/machine/gaelco2.cpp", diff --git a/src/devices/cpu/mcs51/mcs51.cpp b/src/devices/cpu/mcs51/mcs51.cpp index 2e963cec1b7..f9e9afc1470 100644 --- a/src/devices/cpu/mcs51/mcs51.cpp +++ b/src/devices/cpu/mcs51/mcs51.cpp @@ -2059,8 +2059,13 @@ void mcs51_cpu_device::execute_run() /* decrement the timed access window */ if (m_features & FEATURE_DS5002FP) + { m_ds5002fp.ta_window = (m_ds5002fp.ta_window ? (m_ds5002fp.ta_window - 1) : 0x00); + if (m_ds5002fp.rnr_delay > 0) + m_ds5002fp.rnr_delay-=m_inst_cycles; + } + /* If the chip entered in idle mode, end the loop */ if ((m_features & FEATURE_CMOS) && GET_IDL) return; @@ -2182,6 +2187,7 @@ void mcs51_cpu_device::device_start() save_item(NAME(m_irq_active) ); save_item(NAME(m_ds5002fp.previous_ta) ); save_item(NAME(m_ds5002fp.ta_window) ); + save_item(NAME(m_ds5002fp.rnr_delay) ); save_item(NAME(m_ds5002fp.range) ); save_item(NAME(m_uart.data_out)); save_item(NAME(m_uart.bits_to_send)); @@ -2326,6 +2332,7 @@ void mcs51_cpu_device::device_reset() m_ds5002fp.previous_ta = 0; m_ds5002fp.ta_window = 0; m_ds5002fp.range = (GET_RG1 << 1) | GET_RG0; + m_ds5002fp.rnr_delay = 160; } m_uart.data_out = 0; @@ -2469,6 +2476,26 @@ void ds5002fp_device::sfr_write(size_t offset, uint8_t data) m_data.write_byte((size_t) offset | 0x100, data); } + +uint8_t ds5002fp_device::handle_rnr() +{ + if (m_ds5002fp.rnr_delay <= 0) + { + m_ds5002fp.rnr_delay = 160; // delay before another random number can be read + return machine().rand(); + } + else + return 0x00; +} + +bool ds5002fp_device::is_rnr_ready() +{ + if (m_ds5002fp.rnr_delay <= 0) + return true; + else + return false; +} + uint8_t ds5002fp_device::sfr_read(size_t offset) { switch (offset) @@ -2478,8 +2505,10 @@ uint8_t ds5002fp_device::sfr_read(size_t offset) case ADDR_CRCH: DS5_LOGR(CRCH, data); break; case ADDR_MCON: DS5_LOGR(MCON, data); break; case ADDR_TA: DS5_LOGR(TA, data); break; - case ADDR_RNR: DS5_LOGR(RNR, data); break; - case ADDR_RPCTL: DS5_LOGR(RPCTL, data); return 0x80; break; /* touchgo stalls unless bit 7 is set, why? documentation is unclear */ + case ADDR_RNR: DS5_LOGR(RNR, data); + return handle_rnr(); + case ADDR_RPCTL: DS5_LOGR(RPCTL, data); /* touchgo stalls unless bit 7 is set, RNR status (Random Number status) */ + return (is_rnr_ready() ? 0x80 : 0x00); /* falling through to sfr_read for the remaining bits stops high score data loading? */ case ADDR_RPS: DS5_LOGR(RPS, data); break; case ADDR_PCON: SET_PFW(0); /* reset PFW flag */ diff --git a/src/devices/cpu/mcs51/mcs51.h b/src/devices/cpu/mcs51/mcs51.h index c3470d075e4..d13ce60ab66 100644 --- a/src/devices/cpu/mcs51/mcs51.h +++ b/src/devices/cpu/mcs51/mcs51.h @@ -158,7 +158,7 @@ protected: /* Serial Port TX/RX Callbacks */ devcb_write8 m_serial_tx_cb; //Call back function when sending data out of serial port devcb_read8 m_serial_rx_cb; //Call back function to retrieve data when receiving serial port data - + /* DS5002FP */ struct { uint8_t previous_ta; /* Previous Timed Access value */ @@ -168,6 +168,7 @@ protected: uint8_t mcon; /* bootstrap loader MCON register */ uint8_t rpctl; /* bootstrap loader RPCTL register */ uint8_t crc; /* bootstrap loader CRC register */ + int32_t rnr_delay; /* delay before new random number available */ } m_ds5002fp; // for the debugger @@ -604,6 +605,9 @@ protected: virtual void sfr_write(size_t offset, uint8_t data) override; virtual uint8_t sfr_read(size_t offset) override; + uint8_t handle_rnr(); + bool is_rnr_ready(); + private: optional_memory_region m_region; }; diff --git a/src/devices/sound/gaelco.cpp b/src/devices/sound/gaelco.cpp index 2fd45bfe6f9..62553b44413 100644 --- a/src/devices/sound/gaelco.cpp +++ b/src/devices/sound/gaelco.cpp @@ -281,12 +281,27 @@ void gaelco_gae1_device::device_start() if (LOG_WAVE) wavraw = util::wav_open("gae1_snd.wav", rate, 2); + + for (int ch = 0; ch < NUM_CHANNELS; ch++) + { + save_item(NAME(m_channel[ch].active), ch); + save_item(NAME(m_channel[ch].loop), ch); + save_item(NAME(m_channel[ch].chunkNum), ch); + } + + save_item(NAME(m_sndregs)); } void gaelco_gae1_device::device_reset() { for (int ch = 0; ch < NUM_CHANNELS; ch++) + { m_channel[ch].active = 0; + m_channel[ch].loop = 0; + m_channel[ch].chunkNum = 0; + } + + std::fill(std::begin(m_sndregs), std::end(m_sndregs), 0.0); } void gaelco_gae1_device::device_stop() diff --git a/src/devices/sound/gaelco.h b/src/devices/sound/gaelco.h index 2d0180e290f..349a2a1071b 100644 --- a/src/devices/sound/gaelco.h +++ b/src/devices/sound/gaelco.h @@ -60,13 +60,16 @@ private: }; sound_stream *m_stream; /* our stream */ - int m_banks[4]; /* start of each ROM bank */ - sound_channel m_channel[NUM_CHANNELS]; /* 7 stereo channels */ + // live + sound_channel m_channel[NUM_CHANNELS]; /* 7 stereo channels */ uint16_t m_sndregs[0x38]; // Table for converting from 8 to 16 bits with volume control int16_t m_volume_table[VOLUME_LEVELS][256]; + + // config + int m_banks[4]; /* start of each ROM bank */ }; DECLARE_DEVICE_TYPE(GAELCO_GAE1, gaelco_gae1_device) diff --git a/src/mame/drivers/gaelco.cpp b/src/mame/drivers/gaelco.cpp index 31247c79bde..55d6a421674 100644 --- a/src/mame/drivers/gaelco.cpp +++ b/src/mame/drivers/gaelco.cpp @@ -14,28 +14,84 @@ Year Game PCB NOTES 1992 Squash REF 922804/1 Encrypted Video RAM 1992 Thunder Hoop REF 922804/1 Encrypted Video RAM 1995 Biomechanical Toy REF 922804/2 Unprotected -1996 Maniac Square REF 922804/2 Prototype - -TODO: Figure out why Thunder Hoop crashes if you die on Level 4 - This can be bypassed by killing yourself at the same time as - the Level 3 boss dies, suggesting the end stage animation is - somehow corrupting the game state. Could this be a bug in - the supported revision of the game? It doesn't depend on - CPU clock, vblank timing, there are no unmapped reads or - writes of significance. Could it be related to a dipswitch - setting? +1992 Maniac Square REF 922804/2 Prototype Priorities for all games - the games don't make extensive enough use of the priority scheme to properly draw any conclusions. +------------------------------------------------------------- + Note about 57.42 'FRAMERATE_922804' screen refresh + frequency and protection checks. + + In thoop there's a timing loop at 0x49e-4ac. It's + counting frames between interrupt-triggers. + + 0x49e writes the count to 0xffdb62. + + While fighting the second-stage boss, when the pink + things fly out, 0x8970 is called. 0x8988 fetches + from 0xffdb62. If the value is > 0xdd1 (via 0x898a) + or < 0xdb1 (via 0x8992), then 0x89ac sets 0xffdc45 + to 5. + + At 60hz the value returned is 0xd29, which causes + the fail condition to trigger. Values >=57.3 or + <=57.7 give a result within the required range. The + failure is not obvious at this point. + + While fighting the third boss, 0xc2e8 is called. + After passing checks to know exactly when to trigger + (specifically, after the boss is defeated and the + power-up animation is finishes), 0xc350 checks if + 0xffdc45 is 5. If it is, then we reach 0xc368, which + 0xc368 sets 0xffe08e to 0x27. Again the failure is + not obvious at this point. + + 0xffe08e is checked during player respawn after + losing a life or continuing at 0x16d00, with an + explicit compare against 0x27, if this condition is + met, then the game will intentionally corrupt memory + and crash. + + Many of these checks are done with obfuscated code + to hide the target addresses eg. + + writing 0x27 to 0xffe08e + 00C35C: lea $ffc92b.l, A4 + 00C362: adda.l #$1763, A4 + 00C368: move.b #$27, (A4) + + This makes it more difficult to find where the checks + are being performed as an additional layer of + security + + Squash has a similar timing loop, but with the + expected values adjusted due to the different 68000 + clock on the otherwise identical Squash PCB (10Mhz on + Squash vs. 12Mhz on Thunder Hoop) In the case of + Squash the most obvious sign of failure is bad + 'Insert Coin' sprites at the bottom of the screen + after a continue. + + A refresh rate of 57.42, while not yet accurately + measured, allows a video of thoop to stay in sync with + MAME over a 10 minute period. + + No checks have been observed in Biomechanical Toy, + the Maniac Square prototype, or the Last KM prototype. + + Big Karnak runs on a different board type and does fail + if the CPU clock is set to 10Mhz rather than 12Mhz, it + also has additional checks which may still fail and + need more extensive research to determine exactly what + is being timed. + ***************************************************************************/ #include "emu.h" #include "includes/gaelco.h" -#include "includes/gaelcrpt.h" - #include "cpu/m6809/m6809.h" #include "cpu/m68000/m68000.h" #include "sound/okim6295.h" @@ -88,7 +144,7 @@ void gaelco_state::irqack_w(uint16_t data) void gaelco_state::vram_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask) { // osd_printf_debug("vram_encrypted_w!!\n"); - data = gaelco_decrypt(*m_maincpu, offset, data, 0x0f, 0x4228); + data = m_vramcrypt->gaelco_decrypt(*m_maincpu, offset, data); vram_w(offset, data, mem_mask); } @@ -96,23 +152,7 @@ void gaelco_state::vram_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_m void gaelco_state::encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask) { // osd_printf_debug("encrypted_w!!\n"); - data = gaelco_decrypt(*m_maincpu, offset, data, 0x0f, 0x4228); - COMBINE_DATA(&m_screenram[offset]); -} - -/*********** Thunder Hoop Encryption Related Code ******************/ - -void gaelco_state::thoop_vram_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask) -{ - // osd_printf_debug("vram_encrypted_w!!\n"); - data = gaelco_decrypt(*m_maincpu, offset, data, 0x0e, 0x4228); - vram_w(offset, data, mem_mask); -} - -void gaelco_state::thoop_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask) -{ - // osd_printf_debug("encrypted_w!!\n"); - data = gaelco_decrypt(*m_maincpu, offset, data, 0x0e, 0x4228); + data = m_vramcrypt->gaelco_decrypt(*m_maincpu, offset, data); COMBINE_DATA(&m_screenram[offset]); } @@ -191,8 +231,8 @@ void gaelco_state::squash_map(address_map &map) void gaelco_state::thoop_map(address_map &map) { map(0x000000, 0x0fffff).rom(); // ROM - map(0x100000, 0x101fff).ram().w(FUNC(gaelco_state::thoop_vram_encrypted_w)).share("videoram"); // Video RAM - map(0x102000, 0x103fff).ram().w(FUNC(gaelco_state::thoop_encrypted_w)).share("screenram"); // Screen RAM + map(0x100000, 0x101fff).ram().w(FUNC(gaelco_state::vram_encrypted_w)).share("videoram"); // Video RAM + map(0x102000, 0x103fff).ram().w(FUNC(gaelco_state::encrypted_w)).share("screenram"); // Screen RAM map(0x108000, 0x108007).writeonly().share("vregs"); // Video Registers map(0x10800c, 0x10800d).w(FUNC(gaelco_state::irqack_w)); // INT 6 ACK/Watchdog timer map(0x200000, 0x2007ff).ram().w(m_palette, FUNC(palette_device::write16)).share("palette"); // Palette @@ -707,7 +747,7 @@ void gaelco_state::maniacsq(machine_config &config) // Video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(60); + screen.set_refresh_hz(FRAMERATE_922804); screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */); screen.set_size(32*16, 32*16); screen.set_visarea(0, 320-1, 16, 256-1); @@ -736,6 +776,9 @@ void gaelco_state::squash(machine_config &config) config.set_maximum_quantum(attotime::from_hz(600)); + GAELCO_VRAM_ENCRYPTION(config, m_vramcrypt); + m_vramcrypt->set_params(0x0f, 0x4228); + LS259(config, m_outlatch); // B8 m_outlatch->q_out_cb<0>().set(FUNC(gaelco_state::coin1_lockout_w)).invert(); m_outlatch->q_out_cb<1>().set(FUNC(gaelco_state::coin2_lockout_w)).invert(); @@ -745,7 +788,7 @@ void gaelco_state::squash(machine_config &config) // Video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(58); + screen.set_refresh_hz(FRAMERATE_922804); screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */); screen.set_size(32*16, 32*16); screen.set_visarea(0, 320-1, 16, 256-1); @@ -774,6 +817,9 @@ void gaelco_state::thoop(machine_config &config) config.set_maximum_quantum(attotime::from_hz(600)); + GAELCO_VRAM_ENCRYPTION(config, m_vramcrypt); + m_vramcrypt->set_params(0x0e, 0x4228); + LS259(config, m_outlatch); // B8 m_outlatch->q_out_cb<0>().set(FUNC(gaelco_state::coin1_lockout_w)); // not inverted m_outlatch->q_out_cb<1>().set(FUNC(gaelco_state::coin2_lockout_w)); // not inverted @@ -783,7 +829,7 @@ void gaelco_state::thoop(machine_config &config) // Video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(60); + screen.set_refresh_hz(FRAMERATE_922804); screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500) /* not accurate */); screen.set_size(32*16, 32*16); screen.set_visarea(0, 320-1, 16, 256-1); @@ -1184,6 +1230,6 @@ GAME( 1995, biomtoyb, biomtoy, maniacsq, biomtoy, gaelco_state, empty_init, RO GAME( 1994, biomtoyc, biomtoy, maniacsq, biomtoyc, gaelco_state, empty_init, ROT0, "Gaelco", "Biomechanical Toy (Ver. 1.0.1870)", MACHINE_SUPPORTS_SAVE ) GAME( 1994, bioplayc, biomtoy, maniacsq, bioplayc, gaelco_state, empty_init, ROT0, "Gaelco", "Bioplaything Cop (Ver. 1.0.1823, prototype)", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND ) // copyright based on Ver. 1.0.1870 GAME( 1992, maniacsp, 0, maniacsq, maniacsq, gaelco_state, empty_init, ROT0, "Gaelco", "Maniac Square (prototype)", MACHINE_SUPPORTS_SAVE ) // The prototype version was an earlier project, said to be from 1992, game was rewritten in 1996 -GAME( 1995, lastkm, 0, maniacsq, lastkm, gaelco_state, empty_init, ROT0, "Gaelco", "Last KM (Ver 1.0.0275)", MACHINE_SUPPORTS_SAVE ) // used on 'Salter' exercise bikes +GAME( 1995, lastkm, 0, maniacsq, lastkm, gaelco_state, empty_init, ROT0, "Gaelco", "Last KM (Ver 1.0.0275, prototype)", MACHINE_SUPPORTS_SAVE ) // Similar 'bike controller' idea to the Salter gym equipment Gaelco developed, but in game form GAME( 1992, squash, 0, squash, squash, gaelco_state, empty_init, ROT0, "Gaelco", "Squash (Ver. 1.0)", MACHINE_SUPPORTS_SAVE ) -GAME( 1992, thoop, 0, thoop, thoop, gaelco_state, empty_init, ROT0, "Gaelco", "Thunder Hoop (Ver. 1, Checksum 02A09F7D)", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING ) // could be other versions, still Ver. 1 but different checksum listed on boot +GAME( 1992, thoop, 0, thoop, thoop, gaelco_state, empty_init, ROT0, "Gaelco", "Thunder Hoop (Ver. 1, Checksum 02A09F7D)", MACHINE_SUPPORTS_SAVE ) // could be other versions, still Ver. 1 but different checksum listed on boot diff --git a/src/mame/drivers/gaelco2.cpp b/src/mame/drivers/gaelco2.cpp index c0b17c8a2c5..51dc2c9f610 100644 --- a/src/mame/drivers/gaelco2.cpp +++ b/src/mame/drivers/gaelco2.cpp @@ -2722,10 +2722,10 @@ GAME( 1994, aligatorun, aligator, alighunt, alighunt, gaelco2_state, i GAME( 1994, aligatoruna, aligator, alighunt, alighunt, gaelco2_state, init_alighunt, ROT0, "Gaelco", "Alligator Hunt (unprotected, set 2)", 0 ) // strange version, starts on space stages, but clearly a recompile not a trivial hack of the above, show version maybe? GAME( 1994, aligatorp, aligator, alighunt_d5002fp, alighunt, gaelco2_state, empty_init, ROT0, "Gaelco", "Alligator Hunt (protected, prototype?)", MACHINE_NOT_WORKING ) // requires different protection program / data -GAME( 1995, touchgo, 0, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (World)", MACHINE_IMPERFECT_SOUND ) -GAME( 1995, touchgon, touchgo, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (Non North America)", MACHINE_IMPERFECT_SOUND ) -GAME( 1995, touchgoe, touchgo, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (earlier revision)", MACHINE_IMPERFECT_SOUND ) -GAME( 1995, touchgok, touchgo, touchgo, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (Korea, unprotected)", MACHINE_IMPERFECT_SOUND ) // doesn't say 'Korea' but was sourced there, shows 2 copyright lines like the 'earlier revision' +GAME( 1995, touchgo, 0, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (World)", 0 ) +GAME( 1995, touchgon, touchgo, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (Non North America)", 0 ) +GAME( 1995, touchgoe, touchgo, touchgo_d5002fp, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (earlier revision)", 0 ) +GAME( 1995, touchgok, touchgo, touchgo, touchgo, gaelco2_state, init_touchgo, ROT0, "Gaelco", "Touch & Go (Korea, unprotected)", 0 ) // doesn't say 'Korea' but was sourced there, shows 2 copyright lines like the 'earlier revision' GAME( 1995, wrally2, 0, wrally2, wrally2, wrally2_state, init_wrally2, ROT0, "Gaelco", "World Rally 2: Twin Racing (mask ROM version)", 0 ) GAME( 1995, wrally2a, wrally2, wrally2, wrally2, wrally2_state, empty_init, ROT0, "Gaelco", "World Rally 2: Twin Racing (EPROM version)", 0 ) diff --git a/src/mame/drivers/wrally.cpp b/src/mame/drivers/wrally.cpp index 86245961faa..c8503826455 100644 --- a/src/mame/drivers/wrally.cpp +++ b/src/mame/drivers/wrally.cpp @@ -126,7 +126,6 @@ The PCB has a layout that can either use the 4 rom set of I7, I9, I11 & I13 or l #include "emu.h" #include "includes/wrally.h" -#include "includes/gaelcrpt.h" #include "machine/gaelco_ds5002fp.h" @@ -167,7 +166,7 @@ uint8_t wrally_state::shareram_r(offs_t offset) void wrally_state::vram_w(offs_t offset, uint16_t data, uint16_t mem_mask) { - data = gaelco_decrypt(*m_maincpu, offset, data, 0x1f, 0x522a); + data = m_vramcrypt->gaelco_decrypt(*m_maincpu, offset, data); COMBINE_DATA(&m_videoram[offset]); m_tilemap[(offset & 0x1fff) >> 12]->mark_tile_dirty(((offset << 1) & 0x1fff) >> 2); @@ -376,6 +375,9 @@ void wrally_state::wrally(machine_config &config) ds5002.set_addrmap(0, &wrally_state::mcu_hostmem_map); config.set_perfect_quantum("gaelco_ds5002fp:mcu"); + GAELCO_VRAM_ENCRYPTION(config, m_vramcrypt); + m_vramcrypt->set_params(0x1f, 0x522a); + // Video hardware screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); screen.set_refresh_hz(60); diff --git a/src/mame/includes/gaelco.h b/src/mame/includes/gaelco.h index 487188b671a..5d8ce7ad409 100644 --- a/src/mame/includes/gaelco.h +++ b/src/mame/includes/gaelco.h @@ -8,6 +8,7 @@ #include "machine/gen_latch.h" #include "machine/74259.h" +#include "machine/gaelcrpt.h" #include "emupal.h" #include "tilemap.h" @@ -19,6 +20,7 @@ public: m_maincpu(*this, "maincpu"), m_gfxdecode(*this, "gfxdecode"), m_palette(*this, "palette"), + m_vramcrypt(*this, "vramcrypt"), m_audiocpu(*this, "audiocpu"), m_soundlatch(*this, "soundlatch"), m_outlatch(*this, "outlatch"), @@ -40,6 +42,7 @@ private: required_device m_maincpu; required_device m_gfxdecode; required_device m_palette; + optional_device m_vramcrypt; optional_device m_audiocpu; optional_device m_soundlatch; optional_device m_outlatch; @@ -61,8 +64,6 @@ private: void oki_bankswitch_w(uint8_t data); void vram_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); void encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - void thoop_vram_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); - void thoop_encrypted_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); void vram_w(offs_t offset, u16 data, u16 mem_mask); void irqack_w(uint16_t data); @@ -87,4 +88,6 @@ private: /* per-game configuration */ uint8_t m_sprite_palette_force_high; + + static constexpr double FRAMERATE_922804 = 57.42; }; diff --git a/src/mame/includes/gaelcrpt.h b/src/mame/includes/gaelcrpt.h deleted file mode 100644 index e699263d7bd..00000000000 --- a/src/mame/includes/gaelcrpt.h +++ /dev/null @@ -1,5 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Manuel Abadia -/*----------- defined in machine/gaelcrpt.cpp -----------*/ - -uint16_t gaelco_decrypt(cpu_device &cpu, int offset, int data, int param1, int param2); diff --git a/src/mame/includes/wrally.h b/src/mame/includes/wrally.h index ad964279361..ce16874af5e 100644 --- a/src/mame/includes/wrally.h +++ b/src/mame/includes/wrally.h @@ -6,6 +6,7 @@ #pragma once #include "machine/74259.h" +#include "machine/gaelcrpt.h" #include "video/gaelco_wrally_sprites.h" #include "emupal.h" #include "tilemap.h" @@ -21,6 +22,7 @@ public: m_palette(*this, "palette"), m_sprites(*this, "sprites"), m_okibank(*this, "okibank"), + m_vramcrypt(*this, "vramcrypt"), m_videoram(*this, "videoram"), m_vregs(*this, "vregs"), m_spriteram(*this, "spriteram"), @@ -66,6 +68,7 @@ private: required_device m_palette; required_device m_sprites; required_memory_bank m_okibank; + required_device m_vramcrypt; required_shared_ptr m_videoram; required_shared_ptr m_vregs; diff --git a/src/mame/machine/gaelcrpt.cpp b/src/mame/machine/gaelcrpt.cpp index ba7abfa66e6..786baf00c6f 100644 --- a/src/mame/machine/gaelcrpt.cpp +++ b/src/mame/machine/gaelcrpt.cpp @@ -6,12 +6,27 @@ Gaelco video RAM encryption Thanks to GAELCO SA for information on the algorithm. +TODO: the device must be able to know a 32-bit write was from the same + opcode WITHOUT looking at the host program counter. + */ #include "emu.h" -#include "includes/gaelcrpt.h" +#include "gaelcrpt.h" + +DEFINE_DEVICE_TYPE(GAELCO_VRAM_ENCRYPTION, gaelco_vram_encryption_device, "gaelco_vram_crypt", "Gaelco VRAM Encryption") + + +gaelco_vram_encryption_device::gaelco_vram_encryption_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, GAELCO_VRAM_ENCRYPTION, tag, owner, clock), + m_param1(0), + m_param2(0) +{ +} + + -static int decrypt(int const param1, int const param2, int const enc_prev_word, int const dec_prev_word, int const enc_word) +int gaelco_vram_encryption_device::decrypt(int const enc_prev_word, int const dec_prev_word, int const enc_word) { int const swap = (BIT(dec_prev_word, 8) << 1) | BIT(dec_prev_word, 7); int const type = (BIT(dec_prev_word,12) << 1) | BIT(dec_prev_word, 2); @@ -26,7 +41,7 @@ static int decrypt(int const param1, int const param2, int const enc_prev_word, case 3: res = bitswap<16>(enc_word, 3, 8, 1,13,14, 4,15, 0,10, 2, 7,12, 6,11, 9, 5); break; } - res ^= param2; + res ^= m_param2; switch (type) { @@ -67,11 +82,11 @@ static int decrypt(int const param1, int const param2, int const enc_prev_word, break; } - k ^= param1; + k ^= m_param1; res = (res & 0xffc0) | ((res + k) & 0x003f); - res ^= param1; + res ^= m_param1; switch (type) { @@ -109,46 +124,57 @@ static int decrypt(int const param1, int const param2, int const enc_prev_word, break; } - k ^= param1; + k ^= m_param1; res = (res & 0x003f) | ((res + (k << 6)) & 0x07c0) | ((res + (k << 11)) & 0xf800); - res ^= (param1 << 6) | (param1 << 11); + res ^= (m_param1 << 6) | (m_param1 << 11); return bitswap<16>(res, 2,6,0,11,14,12,7,10,5,4,8,3,9,1,13,15); } -uint16_t gaelco_decrypt(cpu_device &cpu, int offset, int data, int param1, int param2) +uint16_t gaelco_vram_encryption_device::gaelco_decrypt(cpu_device &cpu, int offset, int data) { - static int lastpc, lastoffset, lastencword, lastdecword; - int thispc = cpu.pc(); // int savedata = data; /* check if 2nd half of 32 bit */ - if(lastpc == thispc && offset == lastoffset + 1) + if(m_lastpc == thispc && offset == m_lastoffset + 1) { - lastpc = 0; - data = decrypt(param1, param2, lastencword, lastdecword, data); + m_lastpc = 0; + data = decrypt(m_lastencword, m_lastdecword, data); } else { /* code as 1st word */ - lastpc = thispc; - lastoffset = offset; - lastencword = data; + m_lastpc = thispc; + m_lastoffset = offset; + m_lastencword = data; /* high word returned */ - data = decrypt(param1, param2, 0, 0, data); + data = decrypt(0, 0, data); - lastdecword = data; + m_lastdecword = data; -// logerror("%s : data1 = %4x > %4x @ %8x\n",machine().describe_context(),savedata,data,lastoffset); +// logerror("%s : data1 = %4x > %4x @ %8x\n",machine().describe_context(),savedata,data,m_lastoffset); } return data; } + +void gaelco_vram_encryption_device::device_start() +{ + save_item(NAME(m_lastpc)); + save_item(NAME(m_lastoffset)); + save_item(NAME(m_lastencword)); + save_item(NAME(m_lastdecword)); +} + +void gaelco_vram_encryption_device::device_reset() +{ + m_lastpc = m_lastoffset = m_lastencword = m_lastdecword = -1; +} diff --git a/src/mame/machine/gaelcrpt.h b/src/mame/machine/gaelcrpt.h new file mode 100644 index 00000000000..c3d71374a4c --- /dev/null +++ b/src/mame/machine/gaelcrpt.h @@ -0,0 +1,35 @@ +// license:BSD-3-Clause +// copyright-holders:Manuel Abadia + +#ifndef MAME_MACHINE_GAELCRPT_H +#define MAME_MACHINE_GAELCRPT_H + +#pragma once + +DECLARE_DEVICE_TYPE(GAELCO_VRAM_ENCRYPTION, gaelco_vram_encryption_device) + + +class gaelco_vram_encryption_device : public device_t +{ +public: + // construction/destruction + gaelco_vram_encryption_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + void set_params(uint8_t param1, uint16_t param2) { m_param1 = param1; m_param2 = param2; } + + uint16_t gaelco_decrypt(cpu_device &cpu, int offset, int data); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + int decrypt(int const enc_prev_word, int const dec_prev_word, int const enc_word); + + int32_t m_lastpc, m_lastoffset, m_lastencword, m_lastdecword; + + // config + uint8_t m_param1; + uint16_t m_param2; +}; + +#endif // MAME_MACHINE_GAELCRPT_H diff --git a/src/mame/video/gaelco.cpp b/src/mame/video/gaelco.cpp index 4db6543e04e..4b6fba30585 100644 --- a/src/mame/video/gaelco.cpp +++ b/src/mame/video/gaelco.cpp @@ -8,8 +8,12 @@ TODO: verify priority implementations - understand bad sprites in Squash after the continue screen, these do not - occur on real hardware. + + NOTE: + if Squash fails a protection check it will leave bad 'Insert Coin' text + on the screen after a continue, this is not a sprite emulation bug, the + machine expects a 68k clock of around 10Mhz and a refresh of around 57.4 + to pass the protection, see notes in main driver file. ***************************************************************************/ -- cgit v1.2.3