/***************************************************************************
Namco 50XX
This custom chip is a Fujitsu MB8842 MCU programmed to act 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).
+------+
EXTAL|1 28|Vcc
XTAL|2 27|CMD7
/RESET|3 26|CMD6
ANS0|4 25|CMD5
ANS1|5 24|CMD4
ANS2|6 23|/IRQ
ANS3|7 22|n.c.
ANS4|8 21|R/W
ANS5|9 20|n.c.
ANS6|10 19|n.c.
ANS7|11 18|n.c.
CMD0|12 17|n.c.
CMD1|13 16|CMD3
GND|14 15|CMD2
+------+
Commands:
0x = nop
1x = reset scores
2x = set first bonus score (followed by 3 bytes)
3x = set interval bonus score (followed by 3 bytes)
4x = ?
5x = set high score (followed by 3 bytes)
60 = switch to player 1
68 = switch to player 2
70 = switch to increment score
7x = switch to decrement score
score increments/decrements:
80 = 5
81 = 10
82 = 15
83 = 20
84 = 25
85 = 30
86 = 40
87 = 50
88 = 60
89 = 70
8A = 80
8B = 90
8C = 100
8D = 200
8E = 300
8F = 500
9x same as 8x but *10
Ax same as 8x but *100
B0h = 10
B1h = 20
B2h = 30
B3h = 40
B4h = 50
B5h = 60
B6h = 80
B7h = 100
B8h = 120
B9h = 140
BAh = 160
BBh = 180
BCh = 200
BDh = 400
BEh = 600
BFh = 1000
Cx same as Bx but *10
Dx same as Bx but *100
E0 = 15
E1 = 30
E2 = 45
E3 = 60
E4 = 75
E5 = 90
E6 = 120
E7 = 150
E8 = 180
E9 = 210
EA = 240
EB = 270
EC = 300
ED = 600
EE = 900
EF = 1500
Fx same as Ex but *10
When reading, the score for the currently selected player is returned. The first
byte also contains flags.
Byte 0: BCD Score (fs------) and flags
Byte 1: BCD Score (--ss----)
Byte 2: BCD Score (----ss--)
Byte 3: BCD Score (------ss)
Flags: 80=high score, 40=first bonus, 20=interval bonus, 10=?
***************************************************************************/
#include "driver.h"
#include "namco50.h"
#include "cpu/mb88xx/mb88xx.h"
static UINT8 latched_cmd[2];
static UINT8 latched_rw[2];
static UINT8 portO[2];
static TIMER_CALLBACK( namco_50xx_latch_callback )
{
latched_cmd[0] = param;
latched_rw[0] = 1;
}
static TIMER_CALLBACK( namco_50xx_2_latch_callback )
{
latched_cmd[1] = param;
latched_rw[1] = 1;
}
static TIMER_CALLBACK( namco_50xx_readrequest_callback )
{
latched_rw[0] = 0;
}
static TIMER_CALLBACK( namco_50xx_2_readrequest_callback )
{
latched_rw[1] = 0;
}
static READ8_HANDLER( namco_50xx_K_r )
{
return latched_cmd[0] >> 4;
}
static READ8_HANDLER( namco_50xx_R0_r )
{
return latched_cmd[0] & 0x0f;
}
static READ8_HANDLER( namco_50xx_R2_r )
{
return latched_rw[0] & 1;
}
static READ8_HANDLER( namco_50xx_2_K_r )
{
return latched_cmd[1] >> 4;
}
static READ8_HANDLER( namco_50xx_2_R0_r )
{
return latched_cmd[1] & 0x0f;
}
static READ8_HANDLER( namco_50xx_2_R2_r )
{
return latched_rw[1] & 1;
}
static WRITE8_HANDLER( namco_50xx_O_w )
{
UINT8 out = (data & 0x0f);
if (data & 0x10)
portO[0] = (portO[0] & 0x0f) | (out << 4);
else
portO[0] = (portO[0] & 0xf0) | (out);
}
static WRITE8_HANDLER( namco_50xx_2_O_w )
{
UINT8 out = (data & 0x0f);
if (data & 0x10)
portO[1] = (portO[1] & 0x0f) | (out << 4);
else
portO[1] = (portO[1] & 0xf0) | (out);
}
ADDRESS_MAP_START( namco_50xx_map_program, ADDRESS_SPACE_PROGRAM, 8 )
AM_RANGE(0x000, 0x7ff) AM_ROM
ADDRESS_MAP_END
ADDRESS_MAP_START( namco_50xx_map_data, ADDRESS_SPACE_DATA, 8 )
AM_RANGE(0x00, 0x7f) AM_RAM
ADDRESS_MAP_END
ADDRESS_MAP_START( namco_50xx_map_io, ADDRESS_SPACE_IO, 8 )
AM_RANGE(MB88_PORTK, MB88_PORTK) AM_READ(namco_50xx_K_r)
AM_RANGE(MB88_PORTO, MB88_PORTO) AM_WRITE(namco_50xx_O_w)
AM_RANGE(MB88_PORTR0, MB88_PORTR0) AM_READ(namco_50xx_R0_r)
AM_RANGE(MB88_PORTR2, MB88_PORTR2) AM_READ(namco_50xx_R2_r)
ADDRESS_MAP_END
ADDRESS_MAP_START( namco_50xx_2_map_program, ADDRESS_SPACE_PROGRAM, 8 )
AM_RANGE(0x000, 0x7ff) AM_ROM
ADDRESS_MAP_END
ADDRESS_MAP_START( namco_50xx_2_map_data, ADDRESS_SPACE_DATA, 8 )
AM_RANGE(0x00, 0x7f) AM_RAM
ADDRESS_MAP_END
ADDRESS_MAP_START( namco_50xx_2_map_io, ADDRESS_SPACE_IO, 8 )
AM_RANGE(MB88_PORTK, MB88_PORTK) AM_READ(namco_50xx_2_K_r)
AM_RANGE(MB88_PORTO, MB88_PORTO) AM_WRITE(namco_50xx_2_O_w)
AM_RANGE(MB88_PORTR0, MB88_PORTR0) AM_READ(namco_50xx_2_R0_r)
AM_RANGE(MB88_PORTR2, MB88_PORTR2) AM_READ(namco_50xx_2_R2_r)
ADDRESS_MAP_END
static TIMER_CALLBACK( namco_50xx_irq_clear )
{
cpunum_set_input_line(param, 0, CLEAR_LINE);
}
static void namco_50xx_irq_set(int cpunum)
{
cpunum_set_input_line(cpunum, 0, ASSERT_LINE);
// The execution time of one instruction is ~4us, so we must make sure to
// give the cpu time to poll the /IRQ input before we clear it.
// The input clock to the 06XX interface chip is 64H, that is
// 18432000/6/64 = 48kHz, so it makes sense for the irq line to be
// asserted for one clock cycle ~= 21us.
timer_set(ATTOTIME_IN_USEC(21), cpunum, namco_50xx_irq_clear);
}
void namco_50xx_write(UINT8 data)
{
int cpunum = mame_find_cpu_index(Machine, CPUTAG_50XX);
if (cpunum == -1)
return;
timer_call_after_resynch(data, namco_50xx_latch_callback);
namco_50xx_irq_set(cpunum);
}
void namco_50xx_2_write(UINT8 data)
{
int cpunum = mame_find_cpu_index(Machine, CPUTAG_50XX_2);
if (cpunum == -1)
return;
timer_call_after_resynch(data, namco_50xx_2_latch_callback);
namco_50xx_irq_set(cpunum);
}
void namco_50xx_read_request(void)
{
int cpunum = mame_find_cpu_index(Machine, CPUTAG_50XX);
if (cpunum == -1)
return;
timer_call_after_resynch(0, namco_50xx_readrequest_callback);
namco_50xx_irq_set(cpunum);
}
void namco_50xx_2_read_request(void)
{
int cpunum = mame_find_cpu_index(Machine, CPUTAG_50XX_2);
if (cpunum == -1)
return;
timer_call_after_resynch(0, namco_50xx_2_readrequest_callback);
namco_50xx_irq_set(cpunum);
}
UINT8 namco_50xx_read(void)
{
UINT8 res = portO[0];
namco_50xx_read_request();
return res;
}
UINT8 namco_50xx_2_read(void)
{
UINT8 res = portO[1];
namco_50xx_2_read_request();
return res;
}