From 1c684345f338d3453af99f5ce9309b7dddf29b8d Mon Sep 17 00:00:00 2001 From: Phil Bennett Date: Mon, 13 Jun 2016 09:10:46 -0700 Subject: - Reverse-engineered Moto Frenzy security FPGA and implemented decryption code [Morten Shearman Kirkegaard, Samuel Neves, Peter Wilhelmsen] New games added or promoted from NOT_WORKING status Moto Frenzy [Morten Shearman Kirkegaard, Samuel Neves, Peter Wilhelmsen] --- src/mame/drivers/atarigx2.cpp | 42 +++--- src/mame/includes/atarigx2.h | 8 +- src/mame/machine/atarixga.cpp | 299 ++++++++++++++++++++++++++++++++++++++++++ src/mame/machine/atarixga.h | 54 ++++++++ 4 files changed, 383 insertions(+), 20 deletions(-) create mode 100644 src/mame/machine/atarixga.cpp create mode 100644 src/mame/machine/atarixga.h (limited to 'src/mame') diff --git a/src/mame/drivers/atarigx2.cpp b/src/mame/drivers/atarigx2.cpp index 5f73dfd9434..178fda2567b 100644 --- a/src/mame/drivers/atarigx2.cpp +++ b/src/mame/drivers/atarigx2.cpp @@ -5,6 +5,9 @@ Atari GX2 hardware driver by Aaron Giles + + Moto Frenzy protection reverse engineered by: + Morten Shearman Kirkegaard, Samuel Neves, Peter Wilhelmsen Games supported: * Space Lords (1992) @@ -12,7 +15,7 @@ * Road Riot's Revenge Rally (1993) Known bugs: - * protection devices unknown + * Unemulated protection for Space Lords and Road Riot's Revenge **************************************************************************** @@ -125,10 +128,11 @@ WRITE32_MEMBER(atarigx2_state::mo_command_w) /************************************* * - * Protection? + * Protection (non-working, legacy) * *************************************/ +/* Note: Will all eventually be handled in machine/atarixga.cpp */ WRITE32_MEMBER(atarigx2_state::atarigx2_protection_w) { @@ -143,16 +147,16 @@ WRITE32_MEMBER(atarigx2_state::atarigx2_protection_w) logerror("%06X:Protection W@%04X = %04X\n", pc, offset * 4 + 2, data); } - COMBINE_DATA(&m_protection_base[offset]); + COMBINE_DATA(&m_protection_ram[offset]); if (ACCESSING_BITS_16_31) { - m_last_write = m_protection_base[offset] >> 16; + m_last_write = m_protection_ram[offset] >> 16; m_last_write_offset = offset*2; } if (ACCESSING_BITS_0_15) { - m_last_write = m_protection_base[offset] & 0xffff; + m_last_write = m_protection_ram[offset] & 0xffff; m_last_write_offset = offset*2+1; } } @@ -1138,7 +1142,7 @@ READ32_MEMBER(atarigx2_state::atarigx2_protection_r) { 0xffffffff, 0xffff } }; - UINT32 result = m_protection_base[offset]; + UINT32 result = m_protection_ram[offset]; if (offset == 0x300) result |= 0x80000000; @@ -1175,6 +1179,12 @@ READ32_MEMBER(atarigx2_state::atarigx2_protection_r) } +READ32_MEMBER( atarigx2_state::rrreveng_prot_r ) +{ + return 0; +} + + /************************************* * * Main CPU memory handlers @@ -1185,7 +1195,6 @@ static ADDRESS_MAP_START( main_map, AS_PROGRAM, 32, atarigx2_state ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x000000, 0x07ffff) AM_ROM AM_RANGE(0xc80000, 0xc80fff) AM_RAM - AM_RANGE(0xca0000, 0xca0fff) AM_READWRITE(atarigx2_protection_r, atarigx2_protection_w) AM_SHARE("protection_base") AM_RANGE(0xd00000, 0xd1ffff) AM_READ(a2d_data_r) AM_RANGE(0xd20000, 0xd20fff) AM_DEVREADWRITE8("eeprom", atari_eeprom_device, read, write, 0xff00ff00) AM_RANGE(0xd40000, 0xd40fff) AM_RAM_DEVWRITE("palette", palette_device, write) AM_SHARE("palette") @@ -1459,6 +1468,8 @@ static MACHINE_CONFIG_START( atarigx2, atarigx2_state ) MCFG_MACHINE_RESET_OVERRIDE(atarigx2_state,atarigx2) + MCFG_DEVICE_ADD("xga", ATARI_XGA, 0); + MCFG_ATARI_EEPROM_2816_ADD("eeprom") /* video hardware */ @@ -2209,6 +2220,7 @@ ROM_END DRIVER_INIT_MEMBER(atarigx2_state,spclords) { m_playfield_base = 0x000; + m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xca0000, 0xca0fff, read32_delegate(FUNC(atarigx2_state::atarigx2_protection_r),this), write32_delegate(FUNC(atarigx2_state::atarigx2_protection_w),this)); } @@ -2237,22 +2249,18 @@ XMEM=68.A23*E.A22*!E.A21*68.A20 = 1101 xxxx = d0 +68.A23*E.A22*!E.A21*!68.A20*68.A19 = 1100 1xxx = c80000-cfffff +!68.A23*!E.A22*!E.A21 = 000x xxxx = 000000-1fffff */ -} - -READ32_MEMBER(atarigx2_state::rrreveng_prot_r) -{ - return 0; + m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xca0000, 0xca0fff, read32_delegate(FUNC(atari_xga_device::read),&(*m_xga)), write32_delegate(FUNC(atari_xga_device::write),&(*m_xga))); } DRIVER_INIT_MEMBER(atarigx2_state,rrreveng) { m_playfield_base = 0x000; + m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xca0000, 0xca0fff, read32_delegate(FUNC(atarigx2_state::atarigx2_protection_r),this), write32_delegate(FUNC(atarigx2_state::atarigx2_protection_w),this)); m_maincpu->space(AS_PROGRAM).install_read_handler(0xca0fc0, 0xca0fc3, read32_delegate(FUNC(atarigx2_state::rrreveng_prot_r),this)); } - /************************************* * * Game driver(s) @@ -2264,10 +2272,10 @@ GAME( 1992, spclordsb, spclords, atarigx2_0x400, spclords, atarigx2_state, spclo GAME( 1992, spclordsg, spclords, atarigx2_0x400, spclords, atarigx2_state, spclords, ROT0, "Atari Games", "Space Lords (rev A, German)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) GAME( 1992, spclordsa, spclords, atarigx2_0x400, spclords, atarigx2_state, spclords, ROT0, "Atari Games", "Space Lords (rev A)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) -GAME( 1992, motofren, 0, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) -GAME( 1992, motofrenmd, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Mini Deluxe)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) -GAME( 1992, motofrenft, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Field Test Version)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) -GAME( 1992, motofrenmf, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Mini Deluxe Field Test Version)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) +GAME( 1992, motofren, 0, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy", 0 ) +GAME( 1992, motofrenmd, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Mini Deluxe)", 0 ) +GAME( 1992, motofrenft, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Field Test Version)", 0 ) +GAME( 1992, motofrenmf, motofren, atarigx2_0x200, motofren, atarigx2_state, motofren, ROT0, "Atari Games", "Moto Frenzy (Mini Deluxe Field Test Version)", 0 ) GAME( 1993, rrreveng, 0, atarigx2_0x400, rrreveng, atarigx2_state, rrreveng, ROT0, "Atari Games", "Road Riot's Revenge (prototype, Sep 06, 1994)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) GAME( 1993, rrrevenga, rrreveng, atarigx2_0x400, rrreveng, atarigx2_state, rrreveng, ROT0, "Atari Games", "Road Riot's Revenge (prototype, Jan 27, 1994, set 1)", MACHINE_UNEMULATED_PROTECTION | MACHINE_NOT_WORKING ) diff --git a/src/mame/includes/atarigx2.h b/src/mame/includes/atarigx2.h index e0393e3b22e..1cf61d8ecac 100644 --- a/src/mame/includes/atarigx2.h +++ b/src/mame/includes/atarigx2.h @@ -7,8 +7,8 @@ *************************************************************************/ #include "machine/atarigen.h" +#include "machine/atarixga.h" #include "audio/atarijsa.h" -#include "includes/slapstic.h" class atarigx2_state : public atarigen_state @@ -17,8 +17,8 @@ public: atarigx2_state(const machine_config &mconfig, device_type type, const char *tag) : atarigen_state(mconfig, type, tag), m_jsa(*this, "jsa"), + m_xga(*this, "xga"), m_mo_command(*this, "mo_command"), - m_protection_base(*this, "protection_base"), m_playfield_tilemap(*this, "playfield"), m_alpha_tilemap(*this, "alpha"), m_rle(*this, "rle") @@ -27,9 +27,9 @@ public: UINT16 m_playfield_base; required_device m_jsa; + required_device m_xga; required_shared_ptr m_mo_command; - required_shared_ptr m_protection_base; required_device m_playfield_tilemap; required_device m_alpha_tilemap; @@ -41,8 +41,10 @@ public: UINT16 m_playfield_xscroll; UINT16 m_playfield_yscroll; + // LEGACY PROTECTION UINT16 m_last_write; UINT16 m_last_write_offset; + UINT32 m_protection_ram[0x1000]; virtual void update_interrupts() override; virtual void scanline_update(screen_device &screen, int scanline) override; diff --git a/src/mame/machine/atarixga.cpp b/src/mame/machine/atarixga.cpp new file mode 100644 index 00000000000..ac423c9c641 --- /dev/null +++ b/src/mame/machine/atarixga.cpp @@ -0,0 +1,299 @@ +// license:BSD-3-Clause +// copyright-holders:Morten Shearman Kirkegaard, Samuel Neves, Peter Wilhelmsen +/************************************************************************* + + atarixga.cpp + + Atari XGA encryption FPGA + + ************************************************************************** + + Part numbers: + + 136094-0072 Moto Frenzy + 136095-0072 Space Lords + ? Road Riot's Revenge + 136094-0004A Primal Rage + ? T-Mek + +*************************************************************************/ + +#include "atarixga.h" + + +extern const device_type ATARI_XGA = &device_creator; + +atari_xga_device::atari_xga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) +: device_t(mconfig, ATARI_XGA, "Atari XGA", tag, owner, clock, "xga", __FILE__), + m_mode(FPGA_RESET), + m_address(0), + m_ciphertext(0) +{ +} + + + +/************************************* + * + * Initialization + * + *************************************/ + +void atari_xga_device::device_start() +{ + m_ram = std::make_unique(RAM_WORDS); + + save_pointer(NAME(m_ram.get()), RAM_WORDS * sizeof(UINT16)); + save_item(NAME(m_address)); + save_item(NAME(m_ciphertext)); +} + +void atari_xga_device::device_reset() +{ + memset(m_ram.get(), 0, RAM_WORDS * sizeof(UINT16)); + m_mode = FPGA_RESET; + m_address = 0; + m_ciphertext = 0; +} + + + +/************************************* + * + * Definitions + * + *************************************/ + +// TODO: Add definitions for other games +// Moto Frenzy + +/* key 0x10 is special, it has 15 "identical twins". */ +static const UINT8 kmap[128] = +{ + 0x6B,0x11,0x1B,0x19,0x4B,0x50,0x17,0x09, + 0x5D,0x69,0x43,0x33,0x0F,0x0C,0x28,0x3F, + 0x00,0x20,0x15,0x3C,0x57,0x38,0x00,0x07, + 0x49,0x25,0x61,0x2F,0x2B,0x4E,0x64,0x00, + 0x45,0x41,0x6D,0x52,0x31,0x66,0x22,0x59, + 0x00,0x70,0x6F,0x5B,0x46,0x6E,0x67,0x5A, + 0x26,0x30,0x2C,0x65,0x21,0x3D,0x58,0x00, + 0x5E,0x44,0x0D,0x40,0x6C,0x1C,0x51,0x0A, + 0x35,0x2A,0x13,0x4D,0x63,0x00,0x00,0x3A, + 0x00,0x48,0x54,0x24,0x60,0x1E,0x2E,0x01, + 0x56,0x03,0x37,0x00,0x04,0x00,0x05,0x06, + 0x00,0x55,0x1F,0x02,0x36,0x14,0x00,0x3B, + 0x5F,0x0E,0x1D,0x0B,0x27,0x2D,0x3E,0x00, + 0x00,0x5C,0x47,0x68,0x42,0x53,0x32,0x23, + 0x4A,0x62,0x4F,0x00,0x00,0x16,0x39,0x08, + 0x6A,0x34,0x10,0x29,0x12,0x1A,0x4C,0x18 +}; + + + +/************************************* + * + * Decryption + * + *************************************/ + +UINT16 atari_xga_device::ctz(UINT16 x) +{ + UINT16 n = 0; + if (x == 0) return 16; + if (!(x & 0x00FF)) n += 8, x >>= 8; + if (!(x & 0x000F)) n += 4, x >>= 4; + if (!(x & 0x0003)) n += 2, x >>= 2; + if (!(x & 0x0001)) n += 1, x >>= 1; + return n; +} + +size_t atari_xga_device::popcount(UINT16 x) +{ + size_t count = 0; + while (x != 0) + { + count += 1; + x &= x - 1; + } + return count; +} + +UINT16 atari_xga_device::parity(UINT16 x) +{ + return popcount(x) & 1; +} + +UINT16 atari_xga_device::lfsr1(UINT16 x) +{ + UINT16 bit = parity(x & 0x8016); + return (x << 1) | bit; +} + +UINT16 atari_xga_device::lfsr2(UINT16 x) +{ + UINT16 bit = parity(x & 0x002D); + return (x >> 1) | (bit << 15); +} + +UINT16 atari_xga_device::powers2(UINT8 k, UINT16 x) +{ + static const UINT16 L[16] = + { + 0x5E85,0xBD0B,0x2493,0x17A3, + 0x2F47,0x0005,0x000B,0x0017, + 0x002F,0x005E,0x00BD,0x017A, + 0x02F4,0x05E8,0x0BD0,0x17A1 + }; + + UINT16 t = (x == 16) ? (L[4] ^ L[5]) : L[x]; + + for (size_t i = 0; i < k; ++i) + { + t = lfsr1(t); + } + + return t; +} + +UINT16 atari_xga_device::decipher(UINT8 k, UINT16 c) +{ + UINT16 bit, i, p = 0; + + /* Only 128 keys internally, if high bit set, + then find the 7-bit "twin" by xor 0xA8. */ + if (k & 0x80) + k ^= 0xA8; + + k = kmap[k]; + + if ((c & (c - 1)) == 0) + { + return powers2(k, ctz(c)); + } + + for (bit = 0; bit < 5; ++bit) + { + if ((c >> bit) & 1) + { + p ^= powers2(k, bit); + } + } + + for (bit = 5; bit < 16; ++bit) + { + if ((c >> bit) & 1) + { + p ^= powers2(k, bit + 1); + } + } + + UINT16 x = 0x8010; + for (i = 0; i < k + 3; ++i) + { + if (x == c) + { + return (p == 1) ? 0 : lfsr2(p); + } + x = lfsr2(x); + } + + return p; +} + + + +/************************************* + * + * Write/Read access + * + *************************************/ + +WRITE32_MEMBER(atari_xga_device::write) +{ + switch (m_mode) + { + case FPGA_RESET: + return; + + case FPGA_SETKEY: + /* Write table to FPGA SRAM. */ + if (ACCESSING_BITS_16_31) + m_ram[offset << 1] = UINT16 (data >> 16); + if (ACCESSING_BITS_0_15) + m_ram[(offset << 1) + 1] = UINT16(data & 0xFFFF); + + break; + + case FPGA_DECIPHER: + /* Send Ciphertext to FPGA for decryption. */ + if (ACCESSING_BITS_16_31) + { + m_address = offset << 2; + m_ciphertext = UINT16(data >> 16); + } + if (ACCESSING_BITS_0_15) + { + m_address = (offset << 2) + 2; + m_ciphertext = UINT16(data & 0xFFFF); + } + break; + } +} + +READ32_MEMBER(atari_xga_device::read) +{ + UINT32 plaintext = 0; + + switch (offset << 2) + { + case 0x0FC0: + m_mode = FPGA_RESET; + break; + case 0x0010: + m_mode = FPGA_SETKEY; + break; + case 0x0020: + m_mode = FPGA_DECIPHER; + break; + } + + if (m_mode == FPGA_RESET) + { + return 0; + } + + if (m_mode == FPGA_DECIPHER) + { + UINT16 address = (offset << 2) - 0x400; + + if (ACCESSING_BITS_0_15) + address += 2; + + /* Reply with decrypted plaintext */ + if (address == m_address) + { + UINT16 key_offset, key_byte; + + /* Algorithm to select key byte based on offset. */ + key_offset = ((((address >> 4) & 1) ^ 1) << 0) + ^ ((((address >> 2) & 1) ^ 0) << 1) + ^ ((((address >> 8) & 1) ^ 0) << 2) + ^ ((((address >> 3) & 1) ^ 1) << 3) + ^ ((((address >> 1) & 1) ^ 1) << 4) + ^ ((((address >> 6) & 1) ^ 0) << 5) + ^ ((((address >> 7) & 1) ^ 0) << 6) + ^ ((((address >> 5) & 1) ^ 1) << 7) + ^ ((((address >> 9) & 1) ^ 0) << 8) + ^ ((((address >> 10) & 1) ^ 0) << 9); + key_byte = m_ram[key_offset]; + + /* And now for the full magic. */ + plaintext = decipher(key_byte, m_ciphertext); + + if (ACCESSING_BITS_16_31) + plaintext <<= 16; + } + } + + return plaintext; +} diff --git a/src/mame/machine/atarixga.h b/src/mame/machine/atarixga.h new file mode 100644 index 00000000000..710aedb22c2 --- /dev/null +++ b/src/mame/machine/atarixga.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders:Morten Shearman Kirkegaard, Samuel Neves, Peter Wilhelmsen +/************************************************************************* + + atarixga.h + + Atari XGA encryption FPGA + +*************************************************************************/ + +#ifndef __MACHINE_ATARIXGA__ +#define __MACHINE_ATARIXGA__ + +extern const device_type ATARI_XGA; + +class atari_xga_device : public device_t +{ +public: + // construction/destruction + atari_xga_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_WRITE32_MEMBER(write); + DECLARE_READ32_MEMBER(read); + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + static const size_t RAM_WORDS = 2048; + + enum fpga_mode + { + FPGA_RESET, + FPGA_SETKEY, + FPGA_DECIPHER + }; + + UINT16 powers2(UINT8 k, UINT16 x); + UINT16 lfsr2(UINT16 x); + UINT16 lfsr1(UINT16 x); + UINT16 parity(UINT16 x); + size_t popcount(UINT16 x); + UINT16 ctz(UINT16 x); + UINT16 decipher(UINT8 k, UINT16 c); + + fpga_mode m_mode; + UINT16 m_address; // last written address + UINT16 m_ciphertext; // last written ciphertext + std::unique_ptr m_ram; // CY7C185-45PC, only 16-Kbit used +}; + + +#endif -- cgit v1.2.3