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
This commit is contained in:
David Haywood 2021-12-14 14:30:33 +00:00 committed by GitHub
parent 45710aacf1
commit edc0dfb627
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 242 additions and 77 deletions

View File

@ -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",

View File

@ -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 */

View File

@ -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;
};

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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 )

View File

@ -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);

View File

@ -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<cpu_device> m_maincpu;
required_device<gfxdecode_device> m_gfxdecode;
required_device<palette_device> m_palette;
optional_device<gaelco_vram_encryption_device> m_vramcrypt;
optional_device<cpu_device> m_audiocpu;
optional_device<generic_latch_8_device> m_soundlatch;
optional_device<ls259_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;
};

View File

@ -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);

View File

@ -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<palette_device> m_palette;
required_device<gaelco_wrally_sprites_device> m_sprites;
required_memory_bank m_okibank;
required_device<gaelco_vram_encryption_device> m_vramcrypt;
required_shared_ptr<uint16_t> m_videoram;
required_shared_ptr<uint16_t> m_vregs;

View File

@ -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"
static int decrypt(int const param1, int const param2, int const enc_prev_word, int const dec_prev_word, int const enc_word)
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)
{
}
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;
}

View File

@ -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

View File

@ -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.
***************************************************************************/