From 66e483652e30a6227c660077279545124a1982c8 Mon Sep 17 00:00:00 2001 From: David Haywood Date: Mon, 6 Aug 2018 13:01:02 +0100 Subject: Implement 315-5838 compression / encryption chip used by Decathete [Peter Wilhelmsen, Morten Shearman Kirkegaard, Samuel Neves, David Haywood] (#3827) * decathlete prot refactoring (nw) * it works (nw) * some tidyup (nw) * rom interface to prevent recursive reads etc. (nw) * refactoring + restore some (conditional) debug code for edhunter so we can continue study (nw) --- src/mame/drivers/model2.cpp | 50 ++- src/mame/drivers/stv.cpp | 85 ++++- src/mame/includes/model2.h | 5 + src/mame/includes/stv.h | 11 +- src/mame/machine/315-5838_317-0229_comp.cpp | 499 +++++++++++++--------------- src/mame/machine/315-5838_317-0229_comp.h | 106 +++--- 6 files changed, 419 insertions(+), 337 deletions(-) diff --git a/src/mame/drivers/model2.cpp b/src/mame/drivers/model2.cpp index 9de895032a9..3bf90552b6d 100644 --- a/src/mame/drivers/model2.cpp +++ b/src/mame/drivers/model2.cpp @@ -2720,10 +2720,16 @@ MACHINE_CONFIG_START(model2a_state::model2a_5881) MCFG_SET_READ_CALLBACK(model2_state, crypt_read_callback) MACHINE_CONFIG_END +void model2a_state::sega5838_map(address_map &map) +{ + // TODO: shares the ram at 1d80000, add it to main map so it can be shared here + //map(0x000000, 0xffffff).bankr("protbank"); +} + MACHINE_CONFIG_START(model2a_state::model2a_0229) model2a(config); MCFG_DEVICE_ADD("317_0229", SEGA315_5838_COMP, 0) -// MCFG_SET_5838_READ_CALLBACK(model2_state, crypt_read_callback) + MCFG_DEVICE_ADDRESS_MAP(0, sega5838_map) MACHINE_CONFIG_END void model2a_state::zeroguna(machine_config &config) @@ -6559,13 +6565,47 @@ void model2_state::init_sgt24h() //ROM[0x5b3e8/4] = 0x08000004; } -void model2_state::init_doa() +READ32_MEMBER(model2_state::doa_prot_r) +{ + // doa only reads 16-bits at a time, while STV reads 32-bits + + uint32 ret = 0; + + if (mem_mask&0xffff0000) ret |= (m_0229crypt->data_r(space,0,0xffff)<<16); + if (mem_mask&0x0000ffff) ret |= m_0229crypt->data_r(space,0,0xffff); + + return ret; +} + +READ32_MEMBER(model2_state::doa_unk_r) { - m_0229crypt->install_doa_protection(); + uint32_t retval = 0; + // this actually looks a busy status flag + m_prot_a = !m_prot_a; + if (m_prot_a) + retval = 0xffff; + else + retval = 0xfff0; + + return retval; +} + + +void model2_state::init_doa() +{ uint32_t *ROM = (uint32_t *)memregion("maincpu")->base(); - ROM[0x630/4] = 0x08000004; - ROM[0x808/4] = 0x08000004; + ROM[0x630 / 4] = 0x08000004; + ROM[0x808 / 4] = 0x08000004; + + // protection ram is 1d80000 + + m_maincpu->space(AS_PROGRAM).install_read_handler(0x01d8400c, 0x01d8400f, read32_delegate(FUNC(model2_state::doa_unk_r), this)); // is this protection related? it's in the same ram range but other games with the device don't use the address for any kind of status + m_maincpu->space(AS_PROGRAM).install_write_handler(0x01d87ff0, 0x01d87ff3, write32_delegate(FUNC(sega_315_5838_comp_device::srcaddr_w), (sega_315_5838_comp_device*)m_0229crypt)); // set compressed data source address (always set 0, data is in RAM) + m_maincpu->space(AS_PROGRAM).install_write_handler(0x01d87ff4, 0x01d87ff7, write32_delegate(FUNC(sega_315_5838_comp_device::data_w_doa), (sega_315_5838_comp_device*)m_0229crypt)); // upload tab + m_maincpu->space(AS_PROGRAM).install_read_handler(0x01d87ff8, 0x01d87ffb, read32_delegate(FUNC(model2_state::doa_prot_r), this)); // read decompressed data + + m_0229crypt->set_hack_mode(sega_315_5838_comp_device::HACK_MODE_DOA); } // Model 2 (TGPs, Model 1 sound board) diff --git a/src/mame/drivers/stv.cpp b/src/mame/drivers/stv.cpp index 5821fbfddff..92f90dcfbd2 100644 --- a/src/mame/drivers/stv.cpp +++ b/src/mame/drivers/stv.cpp @@ -951,12 +951,67 @@ void stv_state::init_ffreveng() init_stv(); } +READ32_MEMBER(stv_state::decathlt_prot_higher_r) +{ + if (m_newprotection_element) + { + m_newprotection_element = false; + m_5838crypt->debug_helper(1); + } + + m_protbank->set_entry(1); // if the protection device is accessed at this address data is fetched from 0x03000000 + uint32 ret = 0; + if (mem_mask & 0xffff0000) ret |= (m_5838crypt->data_r(space, offset, mem_mask)<<16); + if (mem_mask & 0x0000ffff) ret |= m_5838crypt->data_r(space, offset, mem_mask); + return ret; +} + +READ32_MEMBER(stv_state::decathlt_prot_lower_r) +{ + if (m_newprotection_element) + { + m_newprotection_element = false; + m_5838crypt->debug_helper(0); + } + + m_protbank->set_entry(0); // if the protection device is accessed at this address data is fetched from 0x02000000 + uint32 ret = 0; + if (mem_mask & 0xffff0000) ret |= (m_5838crypt->data_r(space, offset, mem_mask)<<16); + if (mem_mask & 0x0000ffff) ret |= m_5838crypt->data_r(space, offset, mem_mask); + return ret; +} + +WRITE32_MEMBER(stv_state::decathlt_prot_srcaddr_w) +{ + m_newprotection_element = true; + m_5838crypt->srcaddr_w(space,offset,data,mem_mask); +} + void stv_state::init_decathlt() { - m_5838crypt->install_decathlt_protection(); + m_maincpu->space(AS_PROGRAM).install_write_handler(0x37FFFF0, 0x37FFFF3, write32_delegate(FUNC(stv_state::decathlt_prot_srcaddr_w), this)); // set compressed data source address + m_maincpu->space(AS_PROGRAM).install_write_handler(0x37FFFF4, 0x37FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::data_w), (sega_315_5838_comp_device*)m_5838crypt)); // upload tables + m_maincpu->space(AS_PROGRAM).install_read_handler(0x37FFFF8, 0x37FFFFb, read32_delegate(FUNC(stv_state::decathlt_prot_higher_r), this)); // read decompressed data + + m_maincpu->space(AS_PROGRAM).install_write_handler(0x27FFFF0, 0x27FFFF3, write32_delegate(FUNC(stv_state::decathlt_prot_srcaddr_w), this)); // set compressed data source address + m_maincpu->space(AS_PROGRAM).install_write_handler(0x27FFFF4, 0x27FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::data_w), (sega_315_5838_comp_device*)m_5838crypt)); // upload tables + m_maincpu->space(AS_PROGRAM).install_read_handler(0x27FFFF8, 0x27FFFFb, read32_delegate(FUNC(stv_state::decathlt_prot_lower_r), this)); // read decompressed data + + m_protbank->configure_entry(0, memregion("cart")->base()); + m_protbank->configure_entry(1, memregion("cart")->base() + 0x1000000); + m_protbank->set_entry(0); + + m_newprotection_element = false; + init_stv(); } +void stv_state::init_decathlt_nokey() +{ + init_decathlt(); + m_5838crypt->set_hack_mode(sega_315_5838_comp_device::HACK_MODE_NO_KEY); +} + void stv_state::init_nameclv3() { m_maincpu->sh2drc_add_pcflush(0x601eb4c); @@ -1146,21 +1201,17 @@ MACHINE_CONFIG_START(stv_state::stvcd) MACHINE_CONFIG_END -uint16_t stv_state::crypt_read_callback_ch1(uint32_t addr) -{ - return m_maincpu->space().read_word(0x02000000 + 0x1000000 + (addr * 2)); -} - -uint16_t stv_state::crypt_read_callback_ch2(uint32_t addr) +void stv_state::sega5838_map(address_map &map) { - return m_maincpu->space().read_word(0x02000000 + 0x0000000 + (addr * 2)); + map(0x000000, 0xffffff).bankr("protbank"); } MACHINE_CONFIG_START(stv_state::stv_5838) stv(config); + MCFG_DEVICE_ADD("315_5838", SEGA315_5838_COMP, 0) - MCFG_SET_5838_READ_CALLBACK_CH1(stv_state, crypt_read_callback_ch1) - MCFG_SET_5838_READ_CALLBACK_CH2(stv_state, crypt_read_callback_ch2) + MCFG_DEVICE_ADDRESS_MAP(0, sega5838_map) + MACHINE_CONFIG_END @@ -3697,16 +3748,16 @@ GAME( 1997, pclub2kc, stvbios, stv, stv, stvpc_state, init_stv, GAME( 1997, pclubyo2, stvbios, stv, stv, stvpc_state, init_stv, ROT0, "Atlus", "Print Club Yoshimoto V2 (J 970422 V1.100)", MACHINE_NOT_WORKING ) -GAME( 1997, pclove, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Atlus", "Print Club LoveLove (J 970421 V1.000)", MACHINE_NOT_WORKING ) // uses the same type of protection as decathlete!! -GAME( 1997, pclove2, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Atlus", "Print Club LoveLove Ver 2 (J 970825 V1.000)", MACHINE_NOT_WORKING ) // ^ -GAME( 1997, pcpooh2, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Atlus", "Print Club Winnie-the-Pooh Vol. 2 (J 971218 V1.000)", MACHINE_NOT_WORKING ) // ^ -GAME( 1998, pcpooh3, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Atlus", "Print Club Winnie-the-Pooh Vol. 3 (J 980406 V1.000)", MACHINE_NOT_WORKING ) // ^ +GAME( 1997, pclove, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Atlus", "Print Club LoveLove (J 970421 V1.000)", MACHINE_NOT_WORKING ) // uses the same type of protection as decathlete!! +GAME( 1997, pclove2, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Atlus", "Print Club LoveLove Ver 2 (J 970825 V1.000)", MACHINE_NOT_WORKING ) // ^ +GAME( 1997, pcpooh2, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Atlus", "Print Club Winnie-the-Pooh Vol. 2 (J 971218 V1.000)", MACHINE_NOT_WORKING ) // ^ +GAME( 1998, pcpooh3, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Atlus", "Print Club Winnie-the-Pooh Vol. 3 (J 980406 V1.000)", MACHINE_NOT_WORKING ) // ^ GAME( 1998, stress, stvbios, stv, stv, stvpc_state, init_stv, ROT0, "Sega", "Stress Busters (J 981020 V1.000)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND ) -GAME( 1996, nameclub, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Sega", "Name Club (J 960315 V1.000)", MACHINE_NOT_WORKING ) // uses the same type of protection as decathlete!! -GAME( 1996, nclubv2, stvbios, stv_5838, stv, stvpc_state, init_decathlt, ROT0, "Sega", "Name Club Ver.2 (J 960315 V1.000)", MACHINE_NOT_WORKING ) // ^ (has the same datecode as nameclub, probably incorrect unless both were released today) -GAME( 1997, nclubv3, stvbios, stv, stv, stvpc_state, init_nameclv3, ROT0, "Sega", "Name Club Ver.3 (J 970723 V1.000)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) // no protection +GAME( 1996, nameclub, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Sega", "Name Club (J 960315 V1.000)", MACHINE_NOT_WORKING ) // uses the same type of protection as decathlete!! +GAME( 1996, nclubv2, stvbios, stv_5838, stv, stvpc_state, init_decathlt_nokey, ROT0, "Sega", "Name Club Ver.2 (J 960315 V1.000)", MACHINE_NOT_WORKING ) // ^ (has the same datecode as nameclub, probably incorrect unless both were released today) +GAME( 1997, nclubv3, stvbios, stv, stv, stvpc_state, init_nameclv3, ROT0, "Sega", "Name Club Ver.3 (J 970723 V1.000)", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NOT_WORKING ) // no protection diff --git a/src/mame/includes/model2.h b/src/mame/includes/model2.h index 4fed6b0a84c..2b8258ae99c 100644 --- a/src/mame/includes/model2.h +++ b/src/mame/includes/model2.h @@ -185,6 +185,9 @@ protected: DECLARE_WRITE32_MEMBER(model2_serial_w); DECLARE_WRITE16_MEMBER(horizontal_sync_w); DECLARE_WRITE16_MEMBER(vertical_sync_w); + DECLARE_READ32_MEMBER(doa_prot_r); + DECLARE_READ32_MEMBER(doa_unk_r); + int m_prot_a; void raster_init(memory_region *texture_rom); void geo_init(memory_region *polygon_rom); @@ -456,6 +459,8 @@ public: DECLARE_MACHINE_RESET(model2a); + void sega5838_map(address_map &map); + void manxtt(machine_config &config); void manxttdx(machine_config &config); void model2a(machine_config &config); diff --git a/src/mame/includes/stv.h b/src/mame/includes/stv.h index dac9786053f..8acb0fb20a2 100644 --- a/src/mame/includes/stv.h +++ b/src/mame/includes/stv.h @@ -20,6 +20,7 @@ public: m_cart3(*this, "stv_slot3"), m_cart4(*this, "stv_slot4"), m_rax(*this, "rax"), + m_protbank(*this, "protbank"), m_eeprom(*this, "eeprom"), m_cryptdevice(*this, "315_5881"), m_5838crypt(*this, "315_5838"), @@ -40,6 +41,7 @@ public: void init_batmanfr(); void init_finlarch(); void init_decathlt(); + void init_decathlt_nokey(); void init_sanjeon(); void init_puyosun(); void init_winterht(); @@ -132,6 +134,13 @@ private: uint32_t m_abus_protenable; uint32_t m_abus_protkey; + READ32_MEMBER(decathlt_prot_higher_r); + READ32_MEMBER(decathlt_prot_lower_r); + void sega5838_map(address_map &map); + optional_memory_bank m_protbank; + bool m_newprotection_element; // debug helper only + WRITE32_MEMBER(decathlt_prot_srcaddr_w); + uint32_t m_a_bus[4]; DECLARE_READ32_MEMBER( common_prot_r ); @@ -145,8 +154,6 @@ private: optional_device m_5838crypt; optional_device m_hopper; uint16_t crypt_read_callback(uint32_t addr); - uint16_t crypt_read_callback_ch1(uint32_t addr); - uint16_t crypt_read_callback_ch2(uint32_t addr); DECLARE_READ8_MEMBER(pdr1_input_r); DECLARE_READ8_MEMBER(pdr2_input_r); diff --git a/src/mame/machine/315-5838_317-0229_comp.cpp b/src/mame/machine/315-5838_317-0229_comp.cpp index f9f80522ada..6a30032f579 100644 --- a/src/mame/machine/315-5838_317-0229_comp.cpp +++ b/src/mame/machine/315-5838_317-0229_comp.cpp @@ -1,6 +1,7 @@ // license:BSD-3-Clause -// copyright-holders:David Haywood -/* Sega Compression (and possibly encryption) device +// copyright-holders:David Haywood, Samuel Neves, Peter Wilhelmsen, Morten Shearman Kirkegaard + +/* Sega Compression (and encryption) device 315-5838 - Decathlete (ST-V) 317-0229 - Dead or Alive (Model 2A) @@ -13,10 +14,7 @@ Package Type: TQFP100 - Decathlete accesses the chip at 2 different addresses, however, I don't think there - are 2 channels / sets of registers, instead the 2nd set of addresses are just a - mirror that allows access to a different set of source roms; the tables etc. are - re-uploaded before every transfer. + Decathlete accesses the chip at 2 different addresses. Dead of Alive has the source data in RAM, not ROM. This is similar to how some 5881 games were set up, with the ST-V versions decrypting @@ -31,56 +29,34 @@ Dead or Alive decrypts a string that is checked on startup, nothing else. - Looking at the values read I don't think there is any address based encryption, for - example many blocks where you'd expect a zero fill start with repeating patterns - of 8f708f70 (different lengths) channel would appear to relate to compressed 0x00 data - - read addr 0071253c, blah_r 8f708f70 - read count count 00000004 - read addr 00712540, blah_r 8f708f70 - read count count 00000008 - read addr 00712544, blah_r 8f708f70 - read count count 0000000c - read addr 00712548, blah_r 8f708f70 - read count count 00000010 - read addr 0071254c, blah_r 8f708f70 - read count count 00000014 - read addr 00712550, blah_r 8f708f70 - read count count 00000018 - read addr 00712554, blah_r 8f708f70 - read count count 0000001c - */ #include "emu.h" #include "machine/315-5838_317-0229_comp.h" -DEFINE_DEVICE_TYPE(SEGA315_5838_COMP, sega_315_5838_comp_device, "sega315_5838", "Sega 315-5838 / 317-0229 Compression (Encryption?)") - -//#define DEBUG_DATA_DUMP +DEFINE_DEVICE_TYPE(SEGA315_5838_COMP, sega_315_5838_comp_device, "sega315_5838", "Sega 315-5838 / 317-0229 Compression and Encryption") -sega_315_5838_comp_device::sega_315_5838_comp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, SEGA315_5838_COMP, tag, owner, clock) +sega_315_5838_comp_device::sega_315_5838_comp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, SEGA315_5838_COMP, tag, owner, clock), + device_rom_interface(mconfig, *this, 24), + m_hackmode(0) { } - - void sega_315_5838_comp_device::device_start() { - m_decathlt_lastcount = 0; - m_decathlt_prot_uploadmode = 0; - m_decathlt_prot_uploadoffset = 0; - - for (auto & elem : m_channel) - { - elem.m_read_ch.bind_relative_to(*owner()); - - } } void sega_315_5838_comp_device::device_reset() { m_srcoffset = 0; - m_decathlt_lastcount = 0; - m_decathlt_prot_uploadmode = 0; - m_decathlt_prot_uploadoffset = 0; - m_protstate = 0; + m_srcstart = 0; + m_abort = false; } +void sega_315_5838_comp_device::rom_bank_updated() +{ +} /************************** * @@ -88,292 +64,297 @@ void sega_315_5838_comp_device::device_reset() * **************************/ -#ifdef DEBUG_DATA_DUMP -FILE* tempfile; -#endif - - -READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot1_r) +// this part is likely specific to the decathlete type chip and will differ for the others +uint16_t sega_315_5838_comp_device::decipher(uint16_t c) { - return genericdecathlt_prot_r(mem_mask, 0); -} + // TODO: use BIT macros instead of working in bytes + uint16_t p = 0; + uint16_t x[16]; -READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot2_r) -{ - return genericdecathlt_prot_r(mem_mask, 1); + for (int b = 0; b < 16; ++b) + { + x[b] = (c >> b) & 1; + } + + p |= (x[7] ^ x[9] ^ x[14] ? 0 : x[5] ^ x[12]) ^ x[14]; + p |= (((x[7] ^ x[9])&(x[12] ^ x[14] ^ x[5])) ^ x[14] ^ 1) << 1; + p |= ((x[6] & x[8]) ^ (x[6] & x[15]) ^ (x[8] & x[15]) ^ 1) << 2; + p |= (x[11] ^ x[14] ^ 1) << 3; + p |= ((x[7] & (x[1] ^ x[8] ^ x[12])) ^ x[12]) << 4; + p |= ((x[6] | x[8]) ^ (x[8] & x[15])) << 5; + p |= (x[4] ^ (x[3] | x[10])) << 6; + p |= ((x[14] & (x[5] ^ x[12])) ^ x[7] ^ x[9] ^ 1) << 7; + p |= (x[4] ^ x[13] ^ 1) << 8; + p |= (x[6] ^ (x[8] | (x[15] ^ 1))) << 9; + p |= (x[7] ^ (x[12] | (x[1] ^ x[8] ^ x[7] ^ 1))) << 10; + p |= (x[3] ^ x[10] ^ 1) << 11; + p |= (x[0] ^ x[2]) << 12; + p |= (x[8] ^ x[1] ? x[12] : x[7]) << 13; + p |= (x[0] ^ x[11] ^ x[14] ^ 1) << 14; + p |= (x[10] ^ 1) << 15; + + return p; } -uint32_t sega_315_5838_comp_device::genericdecathlt_prot_r(uint32_t mem_mask, int channel) +uint8_t sega_315_5838_comp_device::get_decompressed_byte(void) { -// uint32_t *fake0 = (uint32_t*)memregion( ":fake0" )->base(); -// uint32_t retvalue = 0xffff; - - switch (m_srcoffset) + if (m_hackmode == HACK_MODE_NONE) { - default: + // real algorithm, when we have a cipher function + for (;;) + { + if (m_abort) + { + return 0xff; + } + + if (m_num_bits_compressed == 0) + { + m_val_compressed = decipher(source_word_r()); + m_num_bits_compressed = 16; + } + + m_num_bits_compressed--; + m_val <<= 1; + m_val |= 1 & (m_val_compressed >> m_num_bits_compressed); + m_num_bits++; + + for (int i = 0; i < 12; i++) + { + if (m_num_bits != m_compstate.tree[i].len) continue; + if (m_val < (m_compstate.tree[i].pattern >> (12 - m_num_bits))) continue; + if ( + (m_num_bits < 12) && + (m_val >= (m_compstate.tree[i + 1].pattern >> (12 - m_num_bits))) + ) continue; + + int j = m_compstate.tree[i].idx + m_val - (m_compstate.tree[i].pattern >> (12 - m_num_bits)); + + m_val = 0; + m_num_bits = 0; + + return m_compstate.dictionary[j]; + } + } + } + else + { + // modes where we don't have the real cipher yet, to aid with data logging etc. + // this code will go away eventually + uint8_t ret = 0; - m_decathlt_lastcount++; + // scrreader is words, this is bytes, for unknown compression we want to log the same number of bytes as we read, so log every other access + if (!(m_srcoffset & 1)) + { + uint16_t temp = read_word((m_srcoffset*2)^2); + logerror("%s: read data %02x\n", machine().describe_context(), temp); +#ifdef SEGA315_DUMP_DEBUG + if (m_fp) + { + fwrite(&temp, 1, 2, m_fp); + } +#endif + } + + if (m_hackmode == HACK_MODE_DOA) + { + // this is the single decompressed string DOA needs, note, 2 spaces at start, might indicate a dummy read like with 5881 on Model 2 + // it reads 50 bytes from the device. PC = 2C20 is a pass, PC = 2C28 is a fail + const uint8_t prot[51] = " TECMO LTD. DEAD OR ALIVE 1996.10.22 VER. 1.00"; + if (m_srcoffset<50) ret = prot[m_srcoffset]; + else ret = 0x00; + logerror("%s: doa read %08x %c\n", machine().describe_context(), m_srcoffset, ret); + } + else if (m_hackmode == HACK_MODE_NO_KEY) + { + ret = machine().rand(); + } - uint32_t tempdata = 0; - tempdata |= m_channel[channel].m_read_ch(m_srcoffset) << 0; - m_srcoffset++; - tempdata |= m_channel[channel].m_read_ch(m_srcoffset) << 16; m_srcoffset++; + m_srcoffset &= 0x00ffffff; + return ret; + } +} +READ16_MEMBER(sega_315_5838_comp_device::data_r) +{ + return (get_decompressed_byte() << 8) | (get_decompressed_byte() << 0); +} - #ifdef DEBUG_DATA_DUMP - //printf("read addr %08x, blah_r %08x - read count count %08x\n", m_srcoffset*2, tempdata, m_decathlt_lastcount*4); - fwrite(&tempdata, 1, 4, tempfile); - #else - logerror("read addr %08x, blah_r %08x - read count count %08x\n", m_srcoffset*2, tempdata, m_decathlt_lastcount*4); - #endif - - return tempdata; -#if 0 - case 0x03228e4: - if (fake0) retvalue = fake0[(((0x20080/4)+m_decathlt_lastcount))]; - m_decathlt_lastcount++; - return retvalue; - - case 0x00a9f3a: - if (fake0) retvalue = fake0[(((0x00000/4)+m_decathlt_lastcount))]; - m_decathlt_lastcount++; - return retvalue; - - case 0x0213ab4: - if (fake0) retvalue = fake0[(((0x40000/4)+m_decathlt_lastcount))]; - m_decathlt_lastcount++; - return retvalue; - - case 0x01efaf0: - if (fake0) retvalue = fake0[(((0x60000/4)+m_decathlt_lastcount))]; - m_decathlt_lastcount++; - return retvalue; - - case 0x033f16c: - case 0x038929c: - - - - case 0x00de05a: - case 0x0334258: - case 0x019fb82: - case 0x033dbf6: - case 0x0011ac6: - case 0x00060dc: - case 0x0000002: - case 0x0008c90: - case 0x035cdc8: - case 0x0327960: - case 0x0329b8c: - case 0x00d6e92: - case 0x000081e: - case 0x00035d6: - case 0x00089a6: - case 0x03315f4: - case 0x0023fe0: - case 0x001e290: - case 0x0026e86: - case 0x0012494: - case 0x001b35a: - case 0x0018424: - - return retvalue; -#endif +uint16_t sega_315_5838_comp_device::source_word_r() +{ + uint16_t tempdata = read_word((m_srcoffset*2)^2); + m_srcoffset++; + m_srcoffset &= 0x00ffffff; + + if (m_srcoffset == m_srcstart) // if we've wrapped around to where we started something has gone wrong with the transfer, abandon + m_abort = true; + +#ifdef SEGA315_DUMP_DEBUG + if (m_fp) + { + fwrite(&tempdata, 1, 2, m_fp); } +#endif - return 0xffffffff; + return tempdata; } -void sega_315_5838_comp_device::set_prot_addr(uint32_t data, uint32_t mem_mask, int channel) +void sega_315_5838_comp_device::set_prot_addr(uint32_t data, uint32_t mem_mask) { -// printf("set_prot_addr\n"); COMBINE_DATA(&m_srcoffset); + m_srcoffset &= 0x00ffffff; + m_srcstart = m_srcoffset; + m_abort = false; + + m_num_bits_compressed = 0; + m_val_compressed = 0; + m_num_bits = 0; + m_val = 0; +} - //if (m_decathlt_part==0) logerror("%d, last read count was %06x\n",channel, m_decathlt_lastcount*4); - m_decathlt_lastcount = 0; +void sega_315_5838_comp_device::debug_helper(int id) +{ +#ifdef SEGA315_DUMP_DEBUG - if (mem_mask == 0x0000ffff) + if (m_fp) { - printf("set source address to %08x (channel %d)\n", m_srcoffset, channel); + fclose(m_fp); } - -#ifdef DEBUG_DATA_DUMP - if (mem_mask == 0x0000ffff) + if (1) { - if (tempfile) - fclose(tempfile); - char filename[256]; - sprintf(filename, "%d_compressed_%08x", channel, m_srcoffset * 2); - tempfile = fopen(filename, "w+b"); - - // the table and dictionary are uploaded repeatedly, usually before groups of data transfers but - // it's always the same tables (one pair for each channel) + sprintf(filename, "%d_%08x_table_tree_len", id, m_srcoffset * 2); + m_fp = fopen(filename, "w+b"); + for (int i = 0; i < 12; i++) { - FILE* fp; - sprintf(filename, "%d_compressed_table1_%08x", channel, m_srcoffset * 2); - fp = fopen(filename, "w+b"); - fwrite(&m_decathlt_prottable1, 24, 2, fp); - fclose(fp); + fwrite(&m_compstate.tree[i].len, 1, 1, m_fp); } + fclose(m_fp); + } + if (1) + { + char filename[256]; + sprintf(filename, "%d_%08x_table_tree_idx", id, m_srcoffset * 2); + m_fp = fopen(filename, "w+b"); + for (int i = 0; i < 12; i++) { - FILE* fp; - sprintf(filename, "%d_compressed_dictionary_%08x", channel, m_srcoffset * 2); - fp = fopen(filename, "w+b"); - fwrite(&m_decathlt_dictionaryy, 128, 2, fp); - fclose(fp); + fwrite(&m_compstate.tree[i].idx, 1, 1, m_fp); } + fclose(m_fp); } -#endif -} - -void sega_315_5838_comp_device::set_upload_mode(uint16_t data, int channel) -{ - if ((data == 0x8000) || (data == 0x0000)) - { - // logerror("changed to upload mode 1\n"); - m_decathlt_prot_uploadmode = 1; - m_decathlt_prot_uploadoffset = 0; - } - else if ((data == 0x8080) || (data == 0x0080)) - { - m_decathlt_prot_uploadmode = 2; - m_decathlt_prot_uploadoffset = 0; - } - else + if (1) { - fatalerror("unknown upload mode\n"); + char filename[256]; + sprintf(filename, "%d_%08x_table_tree_pattern", id, m_srcoffset * 2); + m_fp = fopen(filename, "w+b"); + for (int i = 0; i < 12; i++) + { + fwrite(&m_compstate.tree[i].pattern, 1, 2, m_fp); + } + fclose(m_fp); } -} -void sega_315_5838_comp_device::upload_table_data(uint16_t data, int channel) -{ - if (m_decathlt_prot_uploadmode == 1) + if (1) { - if (m_decathlt_prot_uploadoffset >= 24) + char filename[256]; + sprintf(filename, "%d_%08x_table_dictionary", id, m_srcoffset * 2); + m_fp = fopen(filename, "w+b"); + for (int i = 0; i < 256; i++) { - fatalerror("upload mode 1 error, too big\n"); - return; + fwrite(&m_compstate.dictionary[i], 1, 1, m_fp); } - - //logerror("uploading table 1 %04x %04x\n",m_decathlt_prot_uploadoffset, data&0xffff); - m_decathlt_prottable1[m_decathlt_prot_uploadoffset] = data & 0xffff; - m_decathlt_prot_uploadoffset++; - printf("unk table 1 %04x (channel %d)\n", data & 0xffff, channel); + fclose(m_fp); } - else if (m_decathlt_prot_uploadmode == 2) + + if (1) { - if (m_decathlt_prot_uploadoffset >= 128) - { - fatalerror("upload mode 2 error, too big\n"); - return; - } + char filename[256]; + sprintf(filename, "%d_%08x_table_data", id, m_srcoffset * 2); + m_fp = fopen(filename, "w+b"); + // leave open for writing - //logerror("uploading table 2 %04x %04x\n",m_decathlt_prot_uploadoffset, data&0xffff); - m_decathlt_dictionaryy[m_decathlt_prot_uploadoffset] = data & 0xffff; - m_decathlt_prot_uploadoffset++; - printf("dictionary %04x (channel %d)\n", data & 0xffff, channel); } + +#endif } -void sega_315_5838_comp_device::write_prot_data(uint32_t data, uint32_t mem_mask, int channel, int rev_words) + +void sega_315_5838_comp_device::set_table_upload_mode_w(uint16_t val) { - if (mem_mask==0xffff0000) - { - if (rev_words==0) set_upload_mode(data >> 16, channel); - else upload_table_data(data >>16, channel); - } - else if (mem_mask == 0x0000ffff) + m_compstate.mode = val; + + if (!(m_compstate.mode & 0x80)) // 0x8000 and 0x0000 { - if (rev_words==0) upload_table_data(data & 0xffff, channel); - else set_upload_mode(data & 0xffff, channel); + m_compstate.it2 = 0; } - else + else // 0x8080 and 0x0080 { - fatalerror("write_prot_data invalid mem_mask\b"); + m_compstate.id = 0; } } - - -WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_w_doa ) { write_prot_data(data, mem_mask, 0, 1); } -WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_w) { write_prot_data(data, mem_mask, 0, 0); } -WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot2_w) { write_prot_data(data, mem_mask, 1, 0); } - -WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_srcaddr_w ) { set_prot_addr(data, mem_mask, 0); } -WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot2_srcaddr_w) { set_prot_addr(data, mem_mask, 1); } - - -void sega_315_5838_comp_device::install_decathlt_protection() +void sega_315_5838_comp_device::upload_table_data_w(uint16_t val) { - //todo, install these in the driver, they differ between games - cpu_device* cpu = (cpu_device*)machine().device(":maincpu"); + if (!(m_compstate.mode & 0x80)) // 0x8000 and 0x0000 + { + assert(m_compstate.it2 / 2 < 12); - cpu->space(AS_PROGRAM).install_write_handler(0x37FFFF0, 0x37FFFF3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_srcaddr_w), this)); // set compressed data source address - cpu->space(AS_PROGRAM).install_write_handler(0x37FFFF4, 0x37FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_w), this)); // upload tables - cpu->space(AS_PROGRAM).install_read_handler(0x37FFFF8, 0x37FFFFb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_r), this)); // read decompressed data + if ((m_compstate.it2 & 1) == 0) + { + m_compstate.tree[m_compstate.it2 / 2].len = (0xFF00 & val) >> 8; + m_compstate.tree[m_compstate.it2 / 2].idx = (0x00FF & val) >> 0; + } + else + { + m_compstate.tree[m_compstate.it2 / 2].pattern = val; + } + m_compstate.it2++; + } + else // 0x8080 and 0x0080 + { + assert(m_compstate.id < 255); - // the device is addressed here too, uploading a different set of tables and accessing a different part of ROM - cpu->space(AS_PROGRAM).install_write_handler(0x27FFFF0, 0x27FFFF3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_srcaddr_w), this)); // set compressed data source address - cpu->space(AS_PROGRAM).install_write_handler(0x27FFFF4, 0x27FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_w), this)); // upload tables - cpu->space(AS_PROGRAM).install_read_handler(0x27FFFF8, 0x27FFFFb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_r), this)); // read decompressed data + m_compstate.dictionary[m_compstate.id++] = (0xFF00 & val) >> 8; + m_compstate.dictionary[m_compstate.id++] = (0x00FF & val) >> 0; + } } - -READ32_MEMBER(sega_315_5838_comp_device::doa_prot_r) +void sega_315_5838_comp_device::write_prot_data(uint32_t data, uint32_t mem_mask, int rev_words) { - uint32_t retval = 0; - - if (offset == 0x7ff8/4) + if (mem_mask==0xffff0000) { - // PC=2c20 - retval = m_protram[m_protstate+1] | m_protram[m_protstate]<<8; - m_protstate+=2; - printf("doa_prot_read %08x %08x %08x\n", offset*4, retval, mem_mask); + if (rev_words == 0) + { + set_table_upload_mode_w(data >> 16); + } + else + { + upload_table_data_w(data >> 16); + } } - else if (offset == 0x400c/4) // todo, is this actually part of the protection? it's in the address range, but decathlete doesn't have it afaik. + else if (mem_mask == 0x0000ffff) { - // this actually looks a busy status flag - m_prot_a = !m_prot_a; - if (m_prot_a) - retval = 0xffff; + if (rev_words == 0) + { + upload_table_data_w(data & 0xffff); + } else - retval = 0xfff0; + { + set_table_upload_mode_w(data & 0xffff); + } } else { - printf("doa_prot_read %08x %08x %08x\n", offset*4, retval, mem_mask); - logerror("Unhandled Protection READ @ %x mask %x %s\n", offset, mem_mask, machine().describe_context()); + fatalerror("write_prot_data invalid mem_mask\b"); } - - return retval; } +WRITE32_MEMBER( sega_315_5838_comp_device::data_w_doa ) { write_prot_data(data, mem_mask, 1); } +WRITE32_MEMBER( sega_315_5838_comp_device::data_w) { write_prot_data(data, mem_mask, 0); } +WRITE32_MEMBER( sega_315_5838_comp_device::srcaddr_w ) { set_prot_addr(data, mem_mask); } -WRITE32_MEMBER(sega_315_5838_comp_device::doa_prot_w) -{ - printf("doa_prot_w %08x %08x %08x\n", offset*4, data, mem_mask); - - m_protstate = 0; -} - - -void sega_315_5838_comp_device::install_doa_protection() -{ - //todo, install these in the driver, they differ between games - cpu_device* cpu = (cpu_device*)machine().device(":maincpu"); - - m_protstate = 0; - strcpy((char *)m_protram, " TECMO LTD. DEAD OR ALIVE 1996.10.22 VER. 1.00"); // this is the single decompressed string DOA needs, note, 2 spaces at start, might indicate a dummy read like with 5881 on Model 2 - - cpu->space(AS_PROGRAM).install_readwrite_handler(0x01d80000, 0x01dfffff, read32_delegate(FUNC(sega_315_5838_comp_device::doa_prot_r), this), write32_delegate(FUNC(sega_315_5838_comp_device::doa_prot_w), this)); - cpu->space(AS_PROGRAM).install_write_handler(0x01d87ff0, 0x01d87ff3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_srcaddr_w), this)); // set compressed data source address (always set 0, data is in RAM) - cpu->space(AS_PROGRAM).install_write_handler(0x01d87ff4, 0x01d87ff7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_w_doa), this)); // upload tab -// cpu->space(AS_PROGRAM).install_read_handler(0x01d87ff8, 0x01d87ffb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_r), this)); // read decompressed data - -} diff --git a/src/mame/machine/315-5838_317-0229_comp.h b/src/mame/machine/315-5838_317-0229_comp.h index 07c7c044fdf..ebd26ae38b4 100644 --- a/src/mame/machine/315-5838_317-0229_comp.h +++ b/src/mame/machine/315-5838_317-0229_comp.h @@ -1,83 +1,81 @@ // license:BSD-3-Clause -// copyright-holders:David Haywood +// copyright-holders:David Haywood, Samuel Neves, Peter Wilhelmsen, Morten Shearman Kirkegaard #ifndef MAME_MACHINE_315_5838_371_0229_COMP_H #define MAME_MACHINE_315_5838_371_0229_COMP_H #pragma once -typedef device_delegate sega_dec_read_delegate; +// #define SEGA315_DUMP_DEBUG // dump stuff to files to help with decryption efforts DECLARE_DEVICE_TYPE(SEGA315_5838_COMP, sega_315_5838_comp_device) -#define MCFG_SET_5838_READ_CALLBACK_CH1( _class, _method) \ - downcast(*device).set_read_cb_ch1(sega_m2_read_delegate(&_class::_method, #_class "::" #_method, nullptr, (_class *)nullptr)); - -#define MCFG_SET_5838_READ_CALLBACK_CH2( _class, _method) \ - downcast(*device).set_read_cb_ch2(sega_m2_read_delegate(&_class::_method, #_class "::" #_method, nullptr, (_class *)nullptr)); - -class sega_315_5838_comp_device : public device_t +class sega_315_5838_comp_device : public device_t, + public device_rom_interface { public: // construction/destruction sega_315_5838_comp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - template void set_read_cb_ch1(Object &&readcb) { m_channel[0].m_read_ch = std::forward(readcb); } - template void set_read_cb_ch2(Object &&readcb) { m_channel[1].m_read_ch = std::forward(readcb); } - - DECLARE_READ32_MEMBER(decathlt_prot1_r); - DECLARE_READ32_MEMBER(decathlt_prot2_r); - uint32_t genericdecathlt_prot_r(uint32_t mem_mask, int channel); - - void write_prot_data(uint32_t data, uint32_t mem_mask, int channel, int rev_words); - - void upload_table_data(uint16_t data, int channel); - void set_upload_mode(uint16_t data, int channel); - void set_prot_addr(uint32_t data, uint32_t mem_mask, int channel); + DECLARE_READ16_MEMBER(data_r); - DECLARE_WRITE32_MEMBER(decathlt_prot1_w_doa); - DECLARE_WRITE32_MEMBER(decathlt_prot1_w); - DECLARE_WRITE32_MEMBER(decathlt_prot2_w); - DECLARE_WRITE32_MEMBER(decathlt_prot1_srcaddr_w); - DECLARE_WRITE32_MEMBER(decathlt_prot2_srcaddr_w); + DECLARE_WRITE32_MEMBER(data_w_doa); + DECLARE_WRITE32_MEMBER(data_w); + DECLARE_WRITE32_MEMBER(srcaddr_w); - void install_decathlt_protection(); - void install_doa_protection(); + void debug_helper(int id); - DECLARE_READ32_MEMBER(doa_prot_r); - DECLARE_WRITE32_MEMBER(doa_prot_w); + enum + { + HACK_MODE_NONE = 0, + HACK_MODE_NO_KEY, + HACK_MODE_DOA + }; + + void set_hack_mode(int mode) { m_hackmode = mode; } protected: virtual void device_start() override; virtual void device_reset() override; -private: - static constexpr unsigned CHANNELS = 2; + virtual void rom_bank_updated() override; - sega_dec_read_delegate m_read_ch2; +private: + uint16_t source_word_r(); - uint16_t m_decathlt_prottable1[24]; - uint16_t m_decathlt_dictionaryy[128]; + void write_prot_data(uint32_t data, uint32_t mem_mask, int rev_words); + void set_prot_addr(uint32_t data, uint32_t mem_mask); uint32_t m_srcoffset; - - uint32_t m_decathlt_lastcount; - uint32_t m_decathlt_prot_uploadmode; - uint32_t m_decathlt_prot_uploadoffset; - - - // Decathlete specific variables and functions (see machine/decathlt.c) - struct channel_type - { - sega_dec_read_delegate m_read_ch; - - }; - - channel_type m_channel[2]; - - // Doa - int m_protstate; - int m_prot_a; - uint8_t m_protram[256]; + uint32_t m_srcstart; // failsafe + bool m_abort; + + struct { + uint16_t mode; + struct { + uint8_t len; /* in bits */ + uint8_t idx; /* in the dictionary */ + uint16_t pattern; /* of the first node */ + } tree[12]; + int it2; + uint8_t dictionary[256]; + int id; + } m_compstate; + + void set_table_upload_mode_w(uint16_t val); + void upload_table_data_w(uint16_t val); + + uint8_t get_decompressed_byte(void); + uint16_t decipher(uint16_t c); + + int m_num_bits_compressed; + uint16_t m_val_compressed; + int m_num_bits; + uint16_t m_val; + + int m_hackmode; +#ifdef SEGA315_DUMP_DEBUG + FILE* m_fp; +#endif }; #endif // MAME_MACHINE_315_5838_371_0229_COMP_H -- cgit v1.2.3