// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/*************************************************************************
Driver for Williams/Midway X-unit games.
**************************************************************************/
#include "emu.h"
#include "includes/midxunit.h"
#define LOG_IO (1 << 0)
#define LOG_UART (1 << 1)
#define LOG_UNKNOWN (1 << 2)
#define LOG_SOUND (1 << 3)
#define VERBOSE (0)
#include "logmacro.h"
/*************************************
*
* CMOS reads/writes
*
*************************************/
uint8_t midxunit_state::midxunit_cmos_r(offs_t offset)
{
return m_nvram_data[offset];
}
void midxunit_state::midxunit_cmos_w(offs_t offset, uint8_t data)
{
m_nvram_data[offset] = data;
}
/*************************************
*
* General I/O writes
*
*************************************/
void midxunit_state::midxunit_io_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
int oldword, newword;
offset = (offset / 2) % 8;
oldword = m_iodata[offset];
newword = oldword;
COMBINE_DATA(&newword);
switch (offset)
{
case 2:
/* watchdog reset */
// watchdog_reset_w(0,0);
break;
default:
// Gun Outputs for RevX
// Note: The Gun for the Coin slot you use is supposed to rumble when you insert coins, and it doesn't for P3.
// Perhaps an Input is hooked up wrong.
m_gun_recoil[0] = BIT(data, 0);
m_gun_recoil[1] = BIT(data, 1);
m_gun_recoil[2] = BIT(data, 2);
m_gun_led[0] = BIT(~data, 4);
m_gun_led[1] = BIT(~data, 5);
m_gun_led[2] = BIT(~data, 6);
LOGMASKED(LOG_IO, "%s: I/O write to %d = %04X\n", machine().describe_context(), offset, data);
break;
}
m_iodata[offset] = newword;
}
void midxunit_state::midxunit_unknown_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
int offs = offset / 0x40000;
if (offs == 1 && ACCESSING_BITS_0_7)
m_dcs->reset_w(~data & 2);
if (ACCESSING_BITS_0_7 && offset % 0x40000 == 0)
LOGMASKED(LOG_UNKNOWN, "%s: midxunit_unknown_w @ %d = %02X\n", machine().describe_context(), offs, data & 0xff);
}
WRITE_LINE_MEMBER(midxunit_state::adc_int_w)
{
m_adc_int = (state != CLEAR_LINE);
}
/*************************************
*
* General I/O reads
*
*************************************/
uint32_t midxunit_state::midxunit_status_r()
{
/* low bit indicates whether the ADC is done reading the current input */
return (m_pic_status << 1) | (m_adc_int ? 1 : 0);
}
/*************************************
*
* Revolution X UART
*
*************************************/
WRITE_LINE_MEMBER(midxunit_state::midxunit_dcs_output_full)
{
/* only signal if not in loopback state */
if (m_uart[1] != 0x66)
m_maincpu->set_input_line(1, state ? ASSERT_LINE : CLEAR_LINE);
}
uint8_t midxunit_state::midxunit_uart_r(offs_t offset)
{
uint8_t result = 0;
/* switch off the offset */
switch (offset)
{
case 0: /* register 0 must return 0x13 in order to pass the self test */
result = 0x13;
break;
case 1: /* register 1 contains the status */
/* loopback case: data always ready, and always ok to send */
if (m_uart[1] == 0x66)
result |= 5;
/* non-loopback case: bit 0 means data ready, bit 2 means ok to send */
else
{
int temp = m_dcs->control_r();
result |= (temp & 0x800) >> 9;
result |= (~temp & 0x400) >> 10;
machine().scheduler().synchronize();
}
break;
case 3: /* register 3 contains the data read */
/* loopback case: feed back last data wrtten */
if (m_uart[1] == 0x66)
result = m_uart[3];
/* non-loopback case: read from the DCS system */
else
{
LOGMASKED(LOG_SOUND, "%08X:Sound read\n", m_maincpu->pc());
result = m_dcs->data_r();
}
break;
case 5: /* register 5 seems to be like 3, but with in/out swapped */
/* loopback case: data always ready, and always ok to send */
if (m_uart[1] == 0x66)
result |= 5;
/* non-loopback case: bit 0 means data ready, bit 2 means ok to send */
else
{
int temp = m_dcs->control_r();
result |= (temp & 0x800) >> 11;
result |= (~temp & 0x400) >> 8;
machine().scheduler().synchronize();
}
break;
default: /* everyone else reads themselves */
result = m_uart[offset];
break;
}
LOGMASKED(LOG_UART, "%s: UART R @ %X = %02X\n", machine().describe_context(), offset, result);
return result;
}
void midxunit_state::midxunit_uart_w(offs_t offset, uint8_t data)
{
/* switch off the offset */
switch (offset)
{
case 3: /* register 3 contains the data to be sent */
/* loopback case: don't feed through */
if (m_uart[1] == 0x66)
m_uart[3] = data;
/* non-loopback case: send to the DCS system */
else
m_dcs->data_w(data);
break;
case 5: /* register 5 write seems to reset things */
m_dcs->data_r();
break;
default: /* everyone else just stores themselves */
m_uart[offset] = data;
break;
}
LOGMASKED(LOG_UART, "%s: UART W @ %X = %02X\n", machine().describe_context(), offset, data);
}
/*************************************
*
* X-unit init (DCS)
*
* music: ADSP2101
*
*************************************/
/********************** Revolution X **********************/
/*************************************
*
* Machine init
*
*************************************/
void midxunit_state::machine_start()
{
m_gun_recoil.resolve();
m_gun_led.resolve();
m_nvram_data = std::make_unique<uint8_t[]>(0x2000);
m_nvram->set_base(m_nvram_data.get(), 0x2000);
save_item(NAME(m_cmos_write_enable));
save_item(NAME(m_iodata));
save_item(NAME(m_uart));
save_item(NAME(m_adc_int));
save_pointer(NAME(m_nvram_data), 0x2000);
save_item(NAME(m_pic_command));
save_item(NAME(m_pic_data));
save_item(NAME(m_pic_clk));
save_item(NAME(m_pic_status));
}
void midxunit_state::machine_reset()
{
/* reset sound */
m_dcs->reset_w(0);
m_dcs->reset_w(1);
m_pic_command = 0;
m_pic_data = 0;
m_pic_clk = 0;
m_pic_status = 0;
m_dcs->set_io_callbacks(write_line_delegate(*this, FUNC(midxunit_state::midxunit_dcs_output_full)), write_line_delegate(*this));
}
/*************************************
*
* Security chip I/O
*
*************************************/
uint32_t midxunit_state::midxunit_security_r()
{
return m_pic_data;
}
void midxunit_state::midxunit_security_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
if (ACCESSING_BITS_0_7)
m_pic_command = data & 0x0f;
}
void midxunit_state::midxunit_security_clock_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
if (ACCESSING_BITS_0_7)
m_pic_clk = BIT(data, 1);
}
/*************************************
*
* DMA registers (inverted word select)
*
*************************************/
uint32_t midxunit_state::midxunit_dma_r(offs_t offset, uint32_t mem_mask)
{
uint32_t result = 0;
if (ACCESSING_BITS_16_31)
result |= m_video->midtunit_dma_r(offset * 2);
if (ACCESSING_BITS_0_15)
result |= uint32_t(m_video->midtunit_dma_r(offset * 2 + 1)) << 16;
return result;
}
void midxunit_state::midxunit_dma_w(offs_t offset, uint32_t data, uint32_t mem_mask)
{
if (ACCESSING_BITS_16_31)
m_video->midtunit_dma_w(offset * 2, data & 0xffff);
if (ACCESSING_BITS_0_15)
m_video->midtunit_dma_w(offset * 2 + 1, data >> 16);
}