// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** Namco 51XX This custom chip is a Fujitsu MB8843 MCU programmed to act as an I/O device with built-in coin management. It is also apparently used as a protection device. It keeps track of the players scores, and checks if a high score has been obtained or bonus lives should be awarded. The main CPU has a range of commands to increment/decrement the score by various fixed amounts. The device is used to its full potential only by Bosconian; Xevious uses it too, but only to do a protection check on startup. CMD = command from main CPU ANS = answer to main CPU The chip reads/writes the I/O ports when the /IRQ is pulled down. Pin 21 determines whether a read or write should happen (1=R, 0=W). +------+ EX|1 42|Vcc X|2 41|K3 /RESET|3 40|K2 /IRQ|4 39|K1 SO|5 38|K0 SI|6 37|R15 /SC /TO|7 36|R14 /TC|8 35|R13 P0|9 34|R12 P1|10 33|R11 P2|11 32|R10 P3|12 31|R9 O0|13 30|R8 O1|14 29|R7 O2|15 28|R6 O3|16 27|R5 O4|17 26|R4 O5|18 25|R3 O6|19 24|R2 O7|20 23|R1 GND|21 22|R0 +------+ commands: 00: nop 01 + 4 arguments: set coinage (xevious, possibly because of a bug, is different) 02: go in "credit" mode and enable start buttons 03: disable joystick remapping 04: enable joystick remapping 05: go in "switch" mode 06: nop 07: nop ***************************************************************************/ #include "emu.h" #include "namco51.h" #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define READ_PORT(num) m_in_##num(space, 0) #define WRITE_PORT(num,data) m_out_##num(space, 0, data) WRITE8_MEMBER( namco_51xx_device::write ) { data &= 0x07; LOG(("%s: custom 51XX write %02x\n",machine().describe_context(),data)); if (m_coincred_mode) { switch (m_coincred_mode--) { case 4: m_coins_per_cred[0] = data; break; case 3: m_creds_per_coin[0] = data; break; case 2: m_coins_per_cred[1] = data; break; case 1: m_creds_per_coin[1] = data; break; } } else { switch (data) { case 0: // nop break; case 1: // set coinage m_coincred_mode = 4; /* this is a good time to reset the credits counter */ m_credits = 0; { /* kludge for a possible bug in Xevious */ static const game_driver *namcoio_51XX_driver = nullptr; static int namcoio_51XX_kludge = 0; /* Only compute namcoio_51XX_kludge when gamedrv changes */ if (namcoio_51XX_driver != &machine().system()) { namcoio_51XX_driver = &machine().system(); if (strcmp(namcoio_51XX_driver->name, "xevious") == 0 || strcmp(namcoio_51XX_driver->parent, "xevious") == 0) namcoio_51XX_kludge = 1; else namcoio_51XX_kludge = 0; } if (namcoio_51XX_kludge) { m_coincred_mode = 6; m_remap_joy = 1; } } break; case 2: // go in "credits" mode and enable start buttons m_mode = 1; m_in_count = 0; break; case 3: // disable joystick remapping m_remap_joy = 0; break; case 4: // enable joystick remapping m_remap_joy = 1; break; case 5: // go in "switch" mode m_mode = 0; m_in_count = 0; break; default: logerror("unknown 51XX command %02x\n",data); break; } } } /* joystick input mapping The joystick is parsed and a number corresponding to the direction is returned, according to the following table: 0 7 1 6 8 2 5 3 4 The values for directions impossible to obtain on a joystick have not been verified on Namco original hardware, but they are the same in all the bootlegs, so we can assume they are right. */ static const int joy_map[16] = /* LDRU, LDR, LDU, LD, LRU, LR, LU, L, DRU, DR, DU, D, RU, R, U, center */ { 0xf, 0xe, 0xd, 0x5, 0xc, 0x9, 0x7, 0x6, 0xb, 0x3, 0xa, 0x4, 0x1, 0x2, 0x0, 0x8 }; READ8_MEMBER( namco_51xx_device::read ) { LOG(("%s: custom 51XX read\n",machine().describe_context())); if (m_mode == 0) /* switch mode */ { switch ((m_in_count++) % 3) { default: case 0: return READ_PORT(0) | (READ_PORT(1) << 4); case 1: return READ_PORT(2) | (READ_PORT(3) << 4); case 2: return 0; // nothing? } } else /* credits mode */ { switch ((m_in_count++) % 3) { default: case 0: // number of credits in BCD format { int in,toggle; in = ~(READ_PORT(0) | (READ_PORT(1) << 4)); toggle = in ^ m_lastcoins; m_lastcoins = in; if (m_coins_per_cred[0] > 0) { if (m_credits >= 99) { WRITE_PORT(1,1); // coin lockout } else { WRITE_PORT(1,0); // coin lockout /* check if the user inserted a coin */ if (toggle & in & 0x10) { m_coins[0]++; WRITE_PORT(0,0x04); // coin counter WRITE_PORT(0,0x0c); if (m_coins[0] >= m_coins_per_cred[0]) { m_credits += m_creds_per_coin[0]; m_coins[0] -= m_coins_per_cred[0]; } } if (toggle & in & 0x20) { m_coins[1]++; WRITE_PORT(0,0x08); // coin counter WRITE_PORT(0,0x0c); if (m_coins[1] >= m_coins_per_cred[1]) { m_credits += m_creds_per_coin[1]; m_coins[1] -= m_coins_per_cred[1]; } } if (toggle & in & 0x40) { m_credits++; } } } else m_credits = 100; // free play if (m_mode == 1) { int on = (machine().first_screen()->frame_number() & 0x10) >> 4; if (m_credits >= 2) WRITE_PORT(0,0x0c | 3*on); // lamps else if (m_credits >= 1) WRITE_PORT(0,0x0c | 2*on); // lamps else WRITE_PORT(0,0x0c); // lamps off /* check for 1 player start button */ if (toggle & in & 0x04) { if (m_credits >= 1) { m_credits--; m_mode = 2; WRITE_PORT(0,0x0c); // lamps off } } /* check for 2 players start button */ else if (toggle & in & 0x08) { if (m_credits >= 2) { m_credits -= 2; m_mode = 2; WRITE_PORT( 0,0x0c); // lamps off } } } } if (~READ_PORT( 1) & 0x08) /* check test mode switch */ return 0xbb; return (m_credits / 10) * 16 + m_credits % 10; case 1: { int joy = READ_PORT(2) & 0x0f; int in,toggle; in = ~READ_PORT(0); toggle = in ^ m_lastbuttons; m_lastbuttons = (m_lastbuttons & 2) | (in & 1); /* remap joystick */ if (m_remap_joy) joy = joy_map[joy]; /* fire */ joy |= ((toggle & in & 0x01)^1) << 4; joy |= ((in & 0x01)^1) << 5; return joy; } case 2: { int joy = READ_PORT(3) & 0x0f; int in,toggle; in = ~READ_PORT(0); toggle = in ^ m_lastbuttons; m_lastbuttons = (m_lastbuttons & 1) | (in & 2); /* remap joystick */ if (m_remap_joy) joy = joy_map[joy]; /* fire */ joy |= ((toggle & in & 0x02)^2) << 3; joy |= ((in & 0x02)^2) << 4; return joy; } } } } /*************************************************************************** DEVICE INTERFACE ***************************************************************************/ static ADDRESS_MAP_START( namco_51xx_map_io, AS_IO, 8, namco_51xx_device ) // AM_RANGE(MB88_PORTK, MB88_PORTK) AM_READ(namco_51xx_K_r) // AM_RANGE(MB88_PORTO, MB88_PORTO) AM_WRITE(namco_51xx_O_w) // AM_RANGE(MB88_PORTR0, MB88_PORTR0) AM_READ(namco_51xx_R0_r) // AM_RANGE(MB88_PORTR2, MB88_PORTR2) AM_READ(namco_51xx_R2_r) ADDRESS_MAP_END static MACHINE_CONFIG_FRAGMENT( namco_51xx ) MCFG_CPU_ADD("mcu", MB8843, DERIVED_CLOCK(1,1)) /* parent clock, internally divided by 6 */ MCFG_CPU_IO_MAP(namco_51xx_map_io) MCFG_DEVICE_DISABLE() MACHINE_CONFIG_END ROM_START( namco_51xx ) ROM_REGION( 0x400, "mcu", 0 ) ROM_LOAD( "51xx.bin", 0x0000, 0x0400, CRC(c2f57ef8) SHA1(50de79e0d6a76bda95ffb02fcce369a79e6abfec) ) ROM_END const device_type NAMCO_51XX = &device_creator; namco_51xx_device::namco_51xx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, NAMCO_51XX, "Namco 51xx", tag, owner, clock, "namco51", __FILE__), m_cpu(*this, "mcu"), m_in_0(*this), m_in_1(*this), m_in_2(*this), m_in_3(*this), m_out_0(*this), m_out_1(*this), m_lastcoins(0), m_lastbuttons(0), m_mode(0), m_coincred_mode(0), m_remap_joy(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void namco_51xx_device::device_start() { /* resolve our read callbacks */ m_in_0.resolve_safe(0); m_in_1.resolve_safe(0); m_in_2.resolve_safe(0); m_in_3.resolve_safe(0); /* resolve our write callbacks */ m_out_0.resolve_safe(); m_out_1.resolve_safe(); save_item(NAME(m_lastcoins)); save_item(NAME(m_lastbuttons)); save_item(NAME(m_credits)); save_item(NAME(m_coins)); save_item(NAME(m_coins_per_cred)); save_item(NAME(m_creds_per_coin)); save_item(NAME(m_in_count)); save_item(NAME(m_mode)); save_item(NAME(m_coincred_mode)); save_item(NAME(m_remap_joy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void namco_51xx_device::device_reset() { /* reset internal registers */ m_credits = 0; m_coins[0] = 0; m_coins_per_cred[0] = 1; m_creds_per_coin[0] = 1; m_coins[1] = 0; m_coins_per_cred[1] = 1; m_creds_per_coin[1] = 1; m_in_count = 0; } //------------------------------------------------- // device_mconfig_additions - return a pointer to // the device's machine fragment //------------------------------------------------- machine_config_constructor namco_51xx_device::device_mconfig_additions() const { return MACHINE_CONFIG_NAME( namco_51xx ); } //------------------------------------------------- // device_rom_region - return a pointer to the // the device's ROM definitions //------------------------------------------------- const tiny_rom_entry *namco_51xx_device::device_rom_region() const { return ROM_NAME(namco_51xx ); }