From 3b02100587889ce35e0be5f052c498d7910316af Mon Sep 17 00:00:00 2001 From: Sergio G Date: Fri, 8 Mar 2024 18:18:33 +0100 Subject: [PATCH] nmk/nmk214.cpp: Added NMK214 graphics unscrambling device. (#12039) The current implementation is less than ideal due to inflexibility of device_gfx_interface. nmk/nmk16.cpp: Hooked up NMK214 device for sabotenb. --- src/mame/nmk/nmk16.cpp | 130 +++++++++++++++++++++--- src/mame/nmk/nmk16.h | 38 ++++++- src/mame/nmk/nmk214.cpp | 213 ++++++++++++++++++++++++++++++++++++++++ src/mame/nmk/nmk214.h | 54 ++++++++++ 4 files changed, 415 insertions(+), 20 deletions(-) create mode 100644 src/mame/nmk/nmk214.cpp create mode 100644 src/mame/nmk/nmk214.h diff --git a/src/mame/nmk/nmk16.cpp b/src/mame/nmk/nmk16.cpp index 4a747a8a601..fe487e8947f 100644 --- a/src/mame/nmk/nmk16.cpp +++ b/src/mame/nmk/nmk16.cpp @@ -208,6 +208,7 @@ Reference of music tempo: #include "sound/ymopn.h" #include "sound/ymopl.h" +#include "multibyte.h" #include "screen.h" #include "speaker.h" @@ -4358,14 +4359,43 @@ u8 tdragon_prot_state::mcu_port6_r() void tdragon_prot_state::machine_start() { + nmk16_state::machine_start(); + save_item(NAME(m_bus_status)); } void tdragon_prot_state::machine_reset() { + nmk16_state::machine_reset(); + m_bus_status = 0x04; } +void tdragon_prot_214_state::device_post_load() +{ + tdragon_prot_state::device_post_load(); + + if (m_gfx_unscramble_enabled && !m_gfx_decoded) + { + // Loaded a state saved after graphics were decoded before decoding graphics + // Force graphics unscrambling now + decode_nmk214(); + m_gfxdecode->gfx(1)->mark_all_dirty(); // background tiles + m_gfxdecode->gfx(2)->mark_all_dirty(); // sprites + m_bg_tilemap[0]->mark_all_dirty(); + m_gfx_decoded = true; + } +} + +void tdragon_prot_214_state::machine_start() +{ + tdragon_prot_state::machine_start(); + + save_item(NAME(m_init_data_nmk214)); + save_item(NAME(m_init_clock_nmk214)); + save_item(NAME(m_gfx_unscramble_enabled)); +} + void tdragon_prot_state::mcu_side_shared_w(offs_t offset, u8 data) { LOG("%s: mcu_side_shared_w offset %08x (data %02x)\n", machine().describe_context(), offset, data); @@ -4899,32 +4929,74 @@ void nmk16_state::bjtwin(machine_config &config) nmk112.set_rom1_tag("oki2"); } -// the 215 writes minimal data here, probably only enables the decryption logic, rather than supplying the decryption table -void tdragon_prot_state::mcu_port3_to_214_w(u8 data) +// The 215 writes minimal data here to select an unscrambling configuration hardwired inside the NMK214 chip. +void tdragon_prot_214_state::mcu_port3_to_214_w(u8 data) { - // startup only - logerror("%s: mcu_port3_to_214_w (data %02x)\n", machine().describe_context(), data); + LOG("%s: mcu_port3_to_214_w (data %02x)\n", machine().describe_context(), data); + + // Startup only. Bit 2 on Port 3 of MCU (NMK215) acts as strobe/clock (rising edge) for storing the byte previously set on the Port 7 + // of MCU (NMK215) into the internal registers of both NMK214 devices + if (m_init_clock_nmk214 == 0 && BIT(data, 2) == 1) + { + // Value is sent to both devices at the same time. Internally, each one evaluates if the value should be stored or not based on + // bit 3 of the incoming value matches to the operation mode configured on each device: + m_nmk214[0]->set_init_config(m_init_data_nmk214); + m_nmk214[1]->set_init_config(m_init_data_nmk214); + + // Force decode gfx after setting both nmk214 init config, just for testing purposes. Real devices perform the decoding on the fly + // for each byte/word fetch from GFX ROMs + if (!m_gfx_unscramble_enabled && m_nmk214[0]->is_device_initialized() && m_nmk214[1]->is_device_initialized()) + { + m_gfx_unscramble_enabled = true; + if (!m_gfx_decoded) + { + decode_nmk214(); + m_gfxdecode->gfx(1)->mark_all_dirty(); // background tiles + m_gfxdecode->gfx(2)->mark_all_dirty(); // sprites + m_bg_tilemap[0]->mark_all_dirty(); + m_gfx_decoded = true; + } + } + } + + m_init_clock_nmk214 = BIT(data, 2); } -void tdragon_prot_state::mcu_port7_to_214_w(u8 data) +void tdragon_prot_214_state::mcu_port7_to_214_w(u8 data) { - // startup only - logerror("%s: mcu_port7_to_214_w (data %02x)\n", machine().describe_context(), data); + LOG("%s: mcu_port7_to_214_w (data %02x)\n", machine().describe_context(), data); + + // Startup only. Value written here is kept as outputs on Port 7 of MCU (NMK215) in order to be read by the NMK214 devices when the + // clock signal is sent (Bit 2 of Port 3) + m_init_data_nmk214 = data; } -void tdragon_prot_state::saboten_prot(machine_config &config) +// Bitswaps that represent how the address bus of the NMK214s (13 bits) are hooked up related to the address bus of the ROMs. +// Every element represents the bit number in the ROM address bus that should be taken for each NMK214 address bus position, starting by the LSB +static const std::array nmk214_sprites_address_bitswap = {0, 1, 2, 3, 10, 12, 13, 14, 15, 16, 17, 18, 19}; +static const std::array nmk214_bg_address_bitswap = {0, 1, 2, 3, 11, 13, 14, 15, 16, 17, 18, 19, 20}; + +void tdragon_prot_214_state::saboten_prot(machine_config &config) { bjtwin(config); TMP90840(config, m_protcpu, 4000000); // Toshiba TMP90840 marked as NMK-215, with 8Kbyte internal ROM, 256bytes internal RAM - m_protcpu->set_addrmap(AS_PROGRAM, &tdragon_prot_state::tdragon_prot_map); - m_protcpu->port_write<6>().set(FUNC(tdragon_prot_state::mcu_port6_w)); - m_protcpu->port_read<5>().set(FUNC(tdragon_prot_state::mcu_port5_r)); - m_protcpu->port_read<6>().set(FUNC(tdragon_prot_state::mcu_port6_r)); + m_protcpu->set_addrmap(AS_PROGRAM, &tdragon_prot_214_state::tdragon_prot_map); + m_protcpu->port_write<6>().set(FUNC(tdragon_prot_214_state::mcu_port6_w)); + m_protcpu->port_read<5>().set(FUNC(tdragon_prot_214_state::mcu_port5_r)); + m_protcpu->port_read<6>().set(FUNC(tdragon_prot_214_state::mcu_port6_r)); // the 215 has these hooked up, going to the 214 - m_protcpu->port_write<3>().set(FUNC(tdragon_prot_state::mcu_port3_to_214_w)); - m_protcpu->port_write<7>().set(FUNC(tdragon_prot_state::mcu_port7_to_214_w)); + m_protcpu->port_write<3>().set(FUNC(tdragon_prot_214_state::mcu_port3_to_214_w)); + m_protcpu->port_write<7>().set(FUNC(tdragon_prot_214_state::mcu_port7_to_214_w)); + + NMK214(config, m_nmk214[0], 0); // Descrambling device for sprite GFX data + m_nmk214[0]->set_mode(0); + m_nmk214[0]->set_input_address_bitswap(nmk214_sprites_address_bitswap); + + NMK214(config, m_nmk214[1], 0); // Descrambling device for BG GFX data + m_nmk214[1]->set_mode(1); + m_nmk214[1]->set_input_address_bitswap(nmk214_bg_address_bitswap); config.set_maximum_quantum(attotime::from_hz(6000)); } @@ -5118,6 +5190,32 @@ void nmk16_state::decode_gfx() } } +void tdragon_prot_214_state::decode_nmk214() +{ + u8 *rom; + int len; + + // background tiles + rom = memregion("bgtile")->base(); + len = memregion("bgtile")->bytes(); + for (int A = 0; A < len; A++) + { + rom[A] = m_nmk214[1]->decode_byte(A, rom[A]); + } + + // sprites + rom = memregion("sprites")->base(); + len = memregion("sprites")->bytes(); + for (int A = 0; A < (len - 1); A += 2) + { + // sprite ROM is 16-bit big Endian + u16 word = get_u16be(&rom[A]); + + // A is a byte address, divide by 2 to give word address for NMK214 + put_u16be(&rom[A], m_nmk214[0]->decode_word(A/2, word)); + } +} + void nmk16_state::decode_tdragonb() { /* Descrambling Info Again Taken from Raine, Huge Thanks to Antiriad and the Raine Team for @@ -8981,8 +9079,8 @@ GAME( 1994, raphero, arcadian, raphero, raphero, nmk16_state, init_ GAME( 1994, rapheroa, arcadian, raphero, raphero, nmk16_state, init_banked_audiocpu, ROT270, "NMK (Media Trading license)", "Rapid Hero (Media Trading)", 0 ) // ^^ - note that all ROM sets have Media Trading(aka Media Shoji) in the tile graphics, but this is the only set that shows it on the titlescreen // both sets of both these games show a date of 9th Mar 1992 in the test mode, they look like different revisions so I doubt this is accurate -GAME( 1992, sabotenb, 0, saboten_prot, sabotenb, tdragon_prot_state, init_nmk, ROT0, "NMK / Tecmo", "Saboten Bombers (set 1)", MACHINE_NO_COCKTAIL ) -GAME( 1992, sabotenba, sabotenb, saboten_prot, sabotenb, tdragon_prot_state, init_nmk, ROT0, "NMK / Tecmo", "Saboten Bombers (set 2)", MACHINE_NO_COCKTAIL ) +GAME( 1992, sabotenb, 0, saboten_prot, sabotenb, tdragon_prot_214_state, empty_init,ROT0, "NMK / Tecmo", "Saboten Bombers (set 1)", MACHINE_NO_COCKTAIL ) +GAME( 1992, sabotenba, sabotenb, saboten_prot, sabotenb, tdragon_prot_214_state, empty_init,ROT0, "NMK / Tecmo", "Saboten Bombers (set 2)", MACHINE_NO_COCKTAIL ) GAME( 1992, cactus, sabotenb, bjtwin, sabotenb, nmk16_state, init_nmk, ROT0, "bootleg", "Cactus (bootleg of Saboten Bombers)", MACHINE_NO_COCKTAIL ) // PCB marked 'Cactus', no title screen GAME( 1993, bjtwin, 0, bjtwin, bjtwin, nmk16_state, init_bjtwin, ROT270, "NMK", "Bombjack Twin (set 1)", MACHINE_NO_COCKTAIL ) diff --git a/src/mame/nmk/nmk16.h b/src/mame/nmk/nmk16.h index 70367b56d93..8824724256b 100644 --- a/src/mame/nmk/nmk16.h +++ b/src/mame/nmk/nmk16.h @@ -7,6 +7,7 @@ #pragma once #include "nmk004.h" +#include "nmk214.h" #include "nmk16spr.h" #include "seibusound.h" @@ -241,14 +242,13 @@ public: tdragon_prot_state(const machine_config &mconfig, device_type type, const char *tag) : nmk16_state(mconfig, type, tag), m_protcpu(*this, "protcpu") - {} + { + } void tdragon_prot(machine_config &config); void hachamf_prot(machine_config &config); - void saboten_prot(machine_config &config); protected: - virtual void machine_start() override; virtual void machine_reset() override; @@ -263,10 +263,40 @@ protected: u8 mcu_port6_r(); u8 mcu_port7_r(); // NMK-113 uses this + u8 m_bus_status; +}; + +class tdragon_prot_214_state : public tdragon_prot_state +{ +public: + tdragon_prot_214_state(const machine_config &mconfig, device_type type, const char *tag) : + tdragon_prot_state(mconfig, type, tag), + m_nmk214(*this, "nmk214_%u", 0U), + m_init_data_nmk214(0), + m_init_clock_nmk214(0), + m_gfx_unscramble_enabled(false), + m_gfx_decoded(false) + { + } + + void saboten_prot(machine_config &config); + +protected: + virtual void device_post_load() override; + virtual void machine_start() override; + +private: + void decode_nmk214(); + void mcu_port3_to_214_w(u8 data); void mcu_port7_to_214_w(u8 data); - u8 m_bus_status; + required_device_array m_nmk214; + + u8 m_init_data_nmk214; + u8 m_init_clock_nmk214; + bool m_gfx_unscramble_enabled; + bool m_gfx_decoded; // excluded from save states }; class afega_state : public nmk16_state diff --git a/src/mame/nmk/nmk214.cpp b/src/mame/nmk/nmk214.cpp new file mode 100644 index 00000000000..ee101c41ae3 --- /dev/null +++ b/src/mame/nmk/nmk214.cpp @@ -0,0 +1,213 @@ +// license:BSD-3-Clause +// copyright-holders:Sergio Galiano +/* + NMK214 GFX Descrambler emulation + + This device is used for descrambling the GFX data on some game PCBs from NMK (nmk16). + It works in tandem with NMK215, that's a Toshiba MCU which sends initialization data to NMK214 in order to do the + descrambling process. + Every game PCB using it has two NMK214 chips, one for sprites and another for background tiles. + It can work in two different modes: word and byte: + For sprites it always works in word mode; for backgrounds it always works in byte mode. + There are 8 hard-wired internal configurations. The data received from NMK215 selects one of those them at startup. + That init data is stored in the device when bit 3 matches with the operation mode wired directly on the PCB. + The descrambling process is essentially a dynamic bitswap of the incoming word/byte data, doing a different bitswap + based on the address of the data to be descrambled. + The input address bus on the device is used to determine which bitswap do for each word/byte, and it's usually + hooked differently for sprites and background tiles, so an 'input_address_bitswap' is included to get the effective + address the device will use. +*/ + + +#include "emu.h" +#include "nmk214.h" + + +namespace { + +const std::array default_input_address_bitswap = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + +// Static byte values for each of the 8 different hardwired configurations. +// One bit will be taken from each byte in the selected config (row), and those +// 3 bits will be used to select the output bitswap down below: +const u8 init_configs[8][3] = +{ + {0xaa, 0xcc, 0xf0}, // Predefined config 0 + {0x55, 0x39, 0x1e}, // Predefined config 1 + {0xc5, 0x69, 0x5c}, // Predefined config 2 + {0x35, 0x5c, 0xc5}, // Predefined config 3 + {0x78, 0x1d, 0x2e}, // Predefined config 4 + {0x55, 0x33, 0x0f}, // Predefined config 5 + {0xa5, 0xb8, 0x36}, // Predefined config 6 + {0x8b, 0x69, 0x2e} // Predefined config 7 +}; + +// 3 values for each configuration to determine which lines from input address +// bus are used to select a bit from hardwired config byte values above: +const u8 selection_address_bits[8][3] = +{ + {0x8, 0x9, 0xa}, // A8, A9 and A10 for predefined config 0 + {0x6, 0x8, 0xb}, // A6, A8 and A11 for predefined config 1 + {0x3, 0x9, 0xc}, // A3, A9 and A12 for predefined config 2 + {0x3, 0x7, 0xa}, // A3, A7 and A10 for predefined config 3 + {0x2, 0x5, 0xb}, // A2, A5 and A11 for predefined config 4 + {0x1, 0x4, 0xa}, // A1, A4 and A10 for predefined config 5 + {0x2, 0x4, 0xa}, // A2, A4 and A10 for predefined config 6 + {0x0, 0x4, 0xc} // A0, A4 and A12 for predefined config 7 +}; + +// Output word data bitswaps hardwired inside the chip. +// Each value in the same column represents which bit from input data word is +// used for current bit position in the output word. +const u8 output_word_bitswaps[8][16] = +{ +// D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 D14 D15 + {0x2, 0x3, 0x7, 0x8, 0xc, 0x4, 0xb, 0x9, 0x1, 0xf, 0xa, 0x5, 0xe, 0x6, 0xd, 0x0}, // word bitswap 0 + {0x0, 0x3, 0x8, 0x7, 0xa, 0xc, 0x4, 0x1, 0xf, 0x9, 0x6, 0xd, 0xe, 0xb, 0x5, 0x2}, // word bitswap 1 + {0x9, 0x8, 0x2, 0x3, 0x6, 0x5, 0xd, 0xf, 0x7, 0x0, 0xc, 0xb, 0xa, 0x4, 0xe, 0x1}, // word bitswap 2 + {0x0, 0x3, 0x9, 0xf, 0xd, 0xc, 0xb, 0x1, 0x2, 0x7, 0xe, 0x6, 0x4, 0xa, 0x5, 0x8}, // word bitswap 3 + {0x1, 0x3, 0xf, 0x7, 0xd, 0xa, 0xe, 0x9, 0x0, 0x8, 0xc, 0x4, 0x6, 0x5, 0xb, 0x2}, // word bitswap 4 + {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, // word bitswap 5 + {0x0, 0xf, 0x3, 0x2, 0xe, 0x4, 0x6, 0x7, 0x8, 0x9, 0x5, 0xd, 0xc, 0xb, 0xa, 0x1}, // word bitswap 6 + {0xf, 0x2, 0x3, 0x1, 0xb, 0xe, 0xd, 0x8, 0x7, 0x0, 0x4, 0xc, 0x6, 0xa, 0x5, 0x9} // word bitswap 7 +}; + +// Output byte data bitswaps hardwired inside the chip. +// Values on the table are calculated from the above word-bitswaps based on the +// following input data lines correlation (that's how the data lines are hooked +// up in real hardware): +/* + BYTE mode | WORD mode + ----------|---------- + D0 | D13 + D1 | D10 + D2 | D4 + D3 | D12 + D4 | D6 + D5 | D14 + D6 | D11 + D7 | D5 +*/ +const u8 output_byte_bitswaps[8][8] = +{ +// D0 D1 D2 D3 D4 D5 D6 D7 + {0x4, 0x1, 0x3, 0x5, 0x6, 0x0, 0x7, 0x2}, // byte bitswap 0 + {0x6, 0x4, 0x1, 0x5, 0x2, 0x7, 0x0, 0x3}, // byte bitswap 1 + {0x2, 0x3, 0x4, 0x1, 0x0, 0x5, 0x6, 0x7}, // byte bitswap 2 + {0x1, 0x5, 0x0, 0x2, 0x6, 0x7, 0x4, 0x3}, // byte bitswap 3 + {0x7, 0x3, 0x0, 0x4, 0x5, 0x6, 0x2, 0x1}, // byte bitswap 4 + {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, // byte bitswap 5 + {0x6, 0x7, 0x5, 0x3, 0x4, 0x1, 0x0, 0x2}, // byte bitswap 6 + {0x1, 0x2, 0x6, 0x4, 0x0, 0x7, 0x3, 0x5} // byte bitswap 7 +}; + +} // anonymous namespace + + +DEFINE_DEVICE_TYPE(NMK214, nmk214_device, "nmk214", "NMK214 Graphics Descrambler") + +nmk214_device::nmk214_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, NMK214, tag, owner, clock) + , m_mode(0) + , m_input_address_bitswap(default_input_address_bitswap) + , m_init_config(0) + , m_device_initialized(false) +{ +} + +u8 nmk214_device::get_bitswap_select_value(u32 addr) const noexcept +{ + // as the address lines could be hooked up in different order/positions on + // the game PCB, reorder it and get the effective address to use: + const u32 effective_address = bitswap<13>(addr, + m_input_address_bitswap[12], + m_input_address_bitswap[11], + m_input_address_bitswap[10], + m_input_address_bitswap[9], + m_input_address_bitswap[8], + m_input_address_bitswap[7], + m_input_address_bitswap[6], + m_input_address_bitswap[5], + m_input_address_bitswap[4], + m_input_address_bitswap[3], + m_input_address_bitswap[2], + m_input_address_bitswap[1], + m_input_address_bitswap[0]); + + // get selection address using only 3 bits of the effective address, based + // on the initial config selected while device initialization: + const u8 selection_address = bitswap<3>(effective_address, + selection_address_bits[m_init_config][2], + selection_address_bits[m_init_config][1], + selection_address_bits[m_init_config][0]); + + // get the bitswap selection value, using the previously computed selection + // address and the selected internal config values: + return (BIT(init_configs[m_init_config][0], selection_address) << 0) + | (BIT(init_configs[m_init_config][1], selection_address) << 1) + | (BIT(init_configs[m_init_config][2], selection_address) << 2); +} + +u16 nmk214_device::decode_word(u32 addr, u16 data) const noexcept +{ + // compute the select value to choose which bitswap apply to the input word, + // based on the address where the data is located + const u8 bitswap_select = get_bitswap_select_value(addr); + + return bitswap<16>(data, + output_word_bitswaps[bitswap_select][15], + output_word_bitswaps[bitswap_select][14], + output_word_bitswaps[bitswap_select][13], + output_word_bitswaps[bitswap_select][12], + output_word_bitswaps[bitswap_select][11], + output_word_bitswaps[bitswap_select][10], + output_word_bitswaps[bitswap_select][9], + output_word_bitswaps[bitswap_select][8], + output_word_bitswaps[bitswap_select][7], + output_word_bitswaps[bitswap_select][6], + output_word_bitswaps[bitswap_select][5], + output_word_bitswaps[bitswap_select][4], + output_word_bitswaps[bitswap_select][3], + output_word_bitswaps[bitswap_select][2], + output_word_bitswaps[bitswap_select][1], + output_word_bitswaps[bitswap_select][0]); +} + +u8 nmk214_device::decode_byte(u32 addr, u8 data) const noexcept +{ + // compute the select value to choose which bitswap apply to the input byte, + // based on the address where the data is located + u8 bitswap_select = get_bitswap_select_value(addr); + + return bitswap<8>(data, + output_byte_bitswaps[bitswap_select][7], + output_byte_bitswaps[bitswap_select][6], + output_byte_bitswaps[bitswap_select][5], + output_byte_bitswaps[bitswap_select][4], + output_byte_bitswaps[bitswap_select][3], + output_byte_bitswaps[bitswap_select][2], + output_byte_bitswaps[bitswap_select][1], + output_byte_bitswaps[bitswap_select][0]); +} + +void nmk214_device::set_init_config(u8 init_config) noexcept +{ + // store config data only if Bit 3 matches with the operation mode of the device + if (BIT(init_config, 3) == m_mode) + { + m_init_config = init_config & 0x7; + m_device_initialized = true; + } +} + +void nmk214_device::device_start() +{ + save_item(NAME(m_init_config)); + save_item(NAME(m_device_initialized)); +} + +void nmk214_device::device_reset() +{ + m_init_config = 0; + m_device_initialized = false; +} diff --git a/src/mame/nmk/nmk214.h b/src/mame/nmk/nmk214.h new file mode 100644 index 00000000000..89be9e83c01 --- /dev/null +++ b/src/mame/nmk/nmk214.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders:Sergio Galiano +#ifndef MAME_NMK_NMK214_H +#define MAME_NMK_NMK214_H + +#pragma once + +#include + + +class nmk214_device : public device_t +{ +public: + nmk214_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + void set_mode(const u8 mode) { m_mode = mode; } + void set_input_address_bitswap(const std::array &input_address_bitswap) { m_input_address_bitswap = input_address_bitswap; } + + void set_init_config(u8 init_config) noexcept; + bool is_device_initialized() const noexcept { return m_device_initialized; } + + u16 decode_word(u32 addr, u16 data) const noexcept; + u8 decode_byte(u32 addr, u8 data) const noexcept; + +protected: + virtual void device_start() override; + virtual void device_reset() override; + +private: + // Operation mode - in practice, only LSB is used. + // Allowed values: 0 or 1. + // This is hard-wired on the PCB, with opposite values for the two devices. + u8 m_mode; + + // Input address lines bitswap. + // Represents how the 13 NMK214 address lines are wired to the graphics ROM address bus. + std::array m_input_address_bitswap; + + // Selects between eight internal configurations. + // Bits 0 to 2 select the configuration. + // Bit 3 must be set to match the operation mode for the configuration to take effect. + u8 m_init_config; + + // Indicates that the device has been configured and can perform descrambling. + bool m_device_initialized; + + + // Gets the bitswap index for a given data address. + u8 get_bitswap_select_value(u32 addr) const noexcept; +}; + +DECLARE_DEVICE_TYPE(NMK214, nmk214_device) + +#endif // MAME_NMK_NMK214_H