summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/cops.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/cops.cpp')
-rw-r--r--src/mame/drivers/cops.cpp930
1 files changed, 930 insertions, 0 deletions
diff --git a/src/mame/drivers/cops.cpp b/src/mame/drivers/cops.cpp
new file mode 100644
index 00000000000..eb156f80377
--- /dev/null
+++ b/src/mame/drivers/cops.cpp
@@ -0,0 +1,930 @@
+// license:BSD-3-Clause
+// copyright-holders:Mariusz Wojcieszek, James Wallace
+/***************************************************************************
+
+ Nova 'LaserMax'/Atari Games Cops
+ (hardware developed by Nova Productions Limited)
+ Preliminary driver by Mariusz Wojcieszek, James Wallace
+
+ Cops uses a Sony CD-ROM in addition to the regular setup, purely to play
+ Bad Boys by Inner Circle, so there is muscial accompaniment to areas
+ where the laserdisc audio is muted.
+
+ NOTES: To boot up Revelations, turn the refill key (R) and press button A.
+ TODO: There are probably more ROMs for Revelations, the disc contains
+ full data for a picture based memory game called 'Vision Quest'.
+
+ LaserMax memory map needs sorting out, Cops uses a subset of what's
+ actually available
+
+ The UK version COPS appears to want to communicate with the LDP in a
+ different way.
+***************************************************************************/
+
+
+#include "emu.h"
+#include "cpu/m6502/m6502.h"
+#include "machine/6522via.h"
+#include "sound/sn76496.h"
+
+//#include "machine/mos6551.h"
+
+#include "cops.lh"
+
+#define LOG_CDROM 0
+#define LOG_DACIA 0
+
+#define CMP_REGISTER 0
+#define AUX_REGISTER 1
+
+#define MAIN_CLOCK XTAL_4MHz
+
+class cops_state : public driver_device
+{
+public:
+ cops_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_maincpu(*this, "maincpu"),
+ m_sn(*this, "snsnd"),
+ m_irq(0)
+ { }
+
+ // devices
+ required_device<cpu_device> m_maincpu;
+ required_device<sn76489_device> m_sn;
+
+ // screen updates
+ UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+
+protected:
+ // driver_device overrides
+ virtual void machine_start();
+ virtual void machine_reset();
+
+ virtual void video_start();
+
+public:
+ DECLARE_WRITE8_MEMBER(io1_w);
+ DECLARE_READ8_MEMBER(io1_r);
+ DECLARE_WRITE8_MEMBER(io2_w);
+ DECLARE_READ8_MEMBER(io2_r);
+ DECLARE_WRITE_LINE_MEMBER(via1_irq);
+ DECLARE_WRITE_LINE_MEMBER(via2_irq);
+ void dacia_receive(UINT8 data);
+ void update_dacia_irq();
+ DECLARE_WRITE8_MEMBER(dacia_w);
+ DECLARE_READ8_MEMBER(dacia_r);
+ DECLARE_WRITE8_MEMBER(via1_b_w);
+ DECLARE_WRITE8_MEMBER(via1_cb1_w);
+ DECLARE_WRITE8_MEMBER(cdrom_data_w);
+ DECLARE_WRITE8_MEMBER(cdrom_ctrl_w);
+ DECLARE_READ8_MEMBER(cdrom_data_r);
+ DECLARE_DRIVER_INIT(cops);
+ int m_irq;
+
+ UINT8 m_lcd_addr_l, m_lcd_addr_h;
+ UINT8 m_lcd_data_l, m_lcd_data_h;
+
+ UINT8 m_dacia_irq1_reg;
+ UINT8 m_dacia_rts1;
+ UINT8 m_dacia_dtr1;
+ UINT8 m_parity_1;
+ UINT8 m_parity_mode_1;
+ UINT8 m_bpc_1;
+ int m_dacia_ic_div_1;
+ UINT8 m_dacia_echo1;
+ UINT8 m_dacia_stp_1;
+ UINT8 m_dacia_reg1;
+ UINT8 m_dacia_fe1;
+ UINT8 m_dacia_cmp1;
+ UINT8 m_dacia_cmpval1;
+
+ UINT8 m_dacia_cts;
+ UINT8 m_dacia_dcd;
+ UINT8 m_dacia_trans;
+
+ UINT8 m_dacia_receiver_data;
+ UINT8 m_dacia_receiver_full;
+
+ UINT8 m_cdrom_ctrl;
+ UINT8 m_cdrom_data;
+
+ UINT8 m_sn_data;
+ UINT8 m_sn_cb1;
+
+ // LDP-1450
+ UINT8 m_ld_command_to_send[5];
+ UINT8 m_ld_command_total_bytes;
+ UINT8 m_ld_command_current_byte;
+ UINT8 m_ld_frame[5];
+ UINT8 m_ld_frame_index;
+ emu_timer *m_ld_timer;
+ TIMER_CALLBACK_MEMBER(ld_timer_callback);
+ enum LD_INPUT_STATE
+ {
+ LD_INPUT_GET_COMMAND = 0,
+ LD_INPUT_TEXT_COMMAND,
+ LD_INPUT_TEXT_GET_X,
+ LD_INPUT_TEXT_GET_Y,
+ LD_INPUT_TEXT_GET_MODE,
+ LD_INPUT_TEXT_GET_STRING,
+ LD_INPUT_TEXT_GET_SET_WINDOW
+ } m_ld_input_state;
+
+ UINT8 generate_isr();
+ void laserdisc_w(UINT8 data);
+ void laserdisc_response_w(UINT8 data);
+ DECLARE_PALETTE_INIT( cops );
+};
+
+const int timer_divide_select[16] =
+{
+ 73728,
+ 33538,
+ 27408,
+ 24576,
+ 12288,
+ 6144,
+ 3072,
+ 2048,
+ 1536,
+ 1024,
+ 768,
+ 512,
+ 384,
+ 192,
+ 96,
+ 16
+};
+
+void cops_state::video_start()
+{
+}
+
+UINT32 cops_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
+{
+ return 0;
+}
+
+/*************************************
+ *
+ * Sony CDU33A-02 CDROM
+ *
+ *************************************/
+
+WRITE8_MEMBER(cops_state::cdrom_data_w)
+{
+ const char *regs[4] = { "CMD", "PARAM", "WRITE", "CTRL" };
+ m_cdrom_data = BITSWAP8(data,0,1,2,3,4,5,6,7);
+ UINT8 reg = ((m_cdrom_ctrl & 4) >> 1) | ((m_cdrom_ctrl & 8) >> 3);
+ if (LOG_CDROM) logerror("%s:cdrom_data_w(reg = %s, data = %02x)\n", machine().describe_context(), regs[reg & 0x03], m_cdrom_data);
+}
+
+WRITE8_MEMBER(cops_state::cdrom_ctrl_w)
+{
+ if (LOG_CDROM) logerror("%s:cdrom_ctrl_w(%02x)\n", machine().describe_context(), data);
+ m_cdrom_ctrl = data;
+}
+
+READ8_MEMBER(cops_state::cdrom_data_r)
+{
+ const char *regs[4] = { "STATUS", "RESULT", "READ", "FIFOST" };
+ UINT8 reg = ((m_cdrom_ctrl & 4) >> 1) | ((m_cdrom_ctrl & 8) >> 3);
+ if (LOG_CDROM) logerror("%s:cdrom_data_r(reg = %s)\n", machine().describe_context(), regs[reg & 0x03]);
+ return machine().rand()&0xff;
+}
+/*************************************
+ *
+ * LDP-1450 Laserdisc
+ *
+ *************************************/
+
+TIMER_CALLBACK_MEMBER(cops_state::ld_timer_callback)
+{
+ m_dacia_receiver_full = 1;
+
+ if ( m_ld_command_current_byte < m_ld_command_total_bytes )
+ {
+ dacia_receive(m_ld_command_to_send[m_ld_command_current_byte]);
+ m_ld_command_current_byte++;
+ if ( m_ld_command_current_byte == m_ld_command_total_bytes )
+ {
+ m_ld_command_current_byte = m_ld_command_total_bytes = 0;
+ }
+ }
+}
+
+void cops_state::laserdisc_response_w(UINT8 data)
+{
+ if ( m_ld_command_total_bytes >= 5 )
+ {
+ logerror( "LD Overflow!\n" );
+ }
+ m_ld_command_to_send[m_ld_command_total_bytes++] = data;
+}
+
+void cops_state::laserdisc_w(UINT8 data)
+{
+ switch( m_ld_input_state )
+ {
+ case LD_INPUT_TEXT_GET_X:
+ m_ld_input_state = LD_INPUT_TEXT_GET_Y;
+ break;
+ case LD_INPUT_TEXT_GET_Y:
+ m_ld_input_state = LD_INPUT_TEXT_GET_MODE;
+ break;
+ case LD_INPUT_TEXT_GET_MODE:
+ m_ld_input_state = LD_INPUT_GET_COMMAND;
+ break;
+ case LD_INPUT_TEXT_GET_SET_WINDOW:
+ m_ld_input_state = LD_INPUT_GET_COMMAND;
+ break;
+ case LD_INPUT_TEXT_GET_STRING:
+ if ( data == 0x1a )
+ {
+ m_ld_input_state = LD_INPUT_GET_COMMAND;
+ }
+ break;
+ case LD_INPUT_TEXT_COMMAND:
+ case LD_INPUT_GET_COMMAND:
+ {
+ switch( data )
+ {
+ case 0x00: /* text handling (start gotoxy) */
+ if ( m_ld_input_state == LD_INPUT_TEXT_COMMAND )
+ {
+ m_ld_input_state = LD_INPUT_TEXT_GET_X;
+ }
+ break;
+ case 0x01: /* text handling (end of text)*/
+ if ( m_ld_input_state == LD_INPUT_TEXT_COMMAND )
+ {
+ m_ld_input_state = LD_INPUT_TEXT_GET_STRING;
+ }
+ break;
+ case 0x02: /* text 'set window' command */
+ if ( m_ld_input_state == LD_INPUT_TEXT_COMMAND )
+ {
+ m_ld_input_state = LD_INPUT_TEXT_GET_SET_WINDOW;
+ }
+ break;
+ case 0x1a: /* text sent */
+ break;
+ case 0x24: /* Audio On */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x26: /* Video off */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x27: /* Video on */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x30: /* Digit */
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ if ( m_ld_frame_index >= 5 )
+ {
+ m_ld_frame_index = 0;
+ }
+ m_ld_frame[m_ld_frame_index++] = data;
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x3a: /* Play (answer should have delay) */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x3f: /* Stop */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x40: /* Enter */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x43: /* Search */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x46: /* Channel 1 on */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x47: /* Channel 1 off */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x48: /* Channel 2 on */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x49: /* Channel 2 off */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x4f: /* Still */
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x55: /* 'frame mode' (unknown function) */
+ break;
+ case 0x56: /* C. L. (Reset) */
+ m_ld_input_state = LD_INPUT_GET_COMMAND;
+ laserdisc_response_w(0x0a);
+ break;
+ case 0x60: /* Addr Inq (get current frame number) */
+ for ( int i = 0; i < 5; i++ )
+ {
+ laserdisc_response_w(m_ld_frame[i]);
+ }
+ break;
+ case 0x80: /* text start */
+ m_ld_input_state = LD_INPUT_TEXT_COMMAND;
+ break;
+ case 0x81: /* Turn on text */
+ break;
+ case 0x82: /* Turn off text */
+ break;
+ default:
+ logerror("Laserdisc command %02x\n", data);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/*************************************
+ *
+ * 6552 DACIA
+ *
+ *************************************/
+
+ void cops_state::update_dacia_irq()
+{
+ UINT8 isr = generate_isr();
+ //remove bits
+ isr &= ~m_dacia_irq1_reg;
+ m_maincpu->set_input_line(INPUT_LINE_NMI, isr? ASSERT_LINE:CLEAR_LINE);
+}
+
+void cops_state::dacia_receive(UINT8 data)
+{
+ if (m_dacia_cmp1)
+ {
+ if (m_dacia_cmpval1 == data)
+ {
+ m_dacia_receiver_data = data;
+ m_dacia_receiver_full = 1;
+ update_dacia_irq();
+ m_dacia_cmp1 =0;
+ m_dacia_cts =1;
+ m_dacia_trans =1;
+ }
+ }
+ else
+ {
+ m_dacia_receiver_data = data;
+ m_dacia_receiver_full = 1;
+ update_dacia_irq();
+ m_dacia_cts =1;
+ m_dacia_trans =1;
+ }
+}
+
+UINT8 cops_state::generate_isr()
+{
+ UINT8 isr =0;
+
+ isr |= m_dacia_receiver_full;
+ isr |= (m_dacia_cmp1 << 1);
+ isr |= (m_dacia_trans <<4);
+
+ if (isr)
+ {
+ isr |= 0x40;
+ }
+ return isr;
+}
+
+READ8_MEMBER(cops_state::dacia_r)
+{
+ switch(offset & 0x07)
+ {
+ case 0: /* ISR1: Interrupt Status Register */
+ {
+ UINT8 isr = generate_isr();
+ m_dacia_trans =0;
+ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+ return isr;
+ }
+
+ case 1: /* CSR1: Control Status Register */
+ {
+ UINT8 csr =0;
+ csr |= m_dacia_rts1;
+ csr |= (m_dacia_dtr1 << 1);
+ csr |= (m_dacia_cts <<4);
+ csr |= (m_dacia_fe1 <<7);
+ if (LOG_DACIA) logerror("CSR1 %02x\n",csr);
+ return csr;
+ }
+
+ case 3: /* RDR1: Receive data register */
+ m_dacia_receiver_full = 0;
+ m_dacia_fe1=0;
+ if (LOG_DACIA) logerror("RDR1 %02x\n",m_dacia_receiver_data);
+ return m_dacia_receiver_data;
+ default:
+ if (LOG_DACIA) logerror("%s:dacia_r(%02x)\n", machine().describe_context(), offset);
+ return 0;
+ }
+}
+
+WRITE8_MEMBER(cops_state::dacia_w)
+{
+ switch(offset & 0x07)
+ {
+ case 0: /* IRQ enable Register 1 */
+ {
+ m_dacia_irq1_reg &= ~0x80;
+
+ if (data & 0x80) //enable bits
+ {
+ m_dacia_irq1_reg |= (data & 0x7f);
+ }
+ else // disable bits
+ {
+ m_dacia_irq1_reg &= ~(data & 0x7f);
+ }
+ if (LOG_DACIA) logerror("DACIA IRQ 1 Register: %02x\n", m_dacia_irq1_reg);
+ update_dacia_irq();
+ break;
+ }
+
+ case 1: /* Control / Format Register 1 */
+ {
+ if (data & 0x80) //Format Register
+ {
+ m_dacia_rts1 = (data & 0x01);
+ m_dacia_dtr1 = (data & 0x02 ? 1:0);
+ m_parity_1 = (data & 0x04);
+ m_parity_mode_1 = ((data & 0x18) >> 3);
+ m_bpc_1 = ((data & 0x60) >> 5) +5;
+ if (LOG_DACIA) logerror("DACIA Format Register: %02x\n", data);
+ }
+ else // Control register
+ {
+ m_dacia_ic_div_1 = timer_divide_select[data & 0x15];
+ m_dacia_echo1 = (data & 0x10);
+ m_dacia_stp_1 = (data & 0x20 ? 2:1);
+ if (data & 0x40)
+ {
+ m_dacia_reg1 = AUX_REGISTER;
+ }
+ else
+ {
+ m_dacia_reg1 = CMP_REGISTER;
+ }
+ if (LOG_DACIA) logerror("DACIA TIME %02d\n", XTAL_3_6864MHz / m_dacia_ic_div_1);
+
+ m_ld_timer->adjust(attotime::from_hz(XTAL_3_6864MHz / m_dacia_ic_div_1), 0, attotime::from_hz(XTAL_3_6864MHz / m_dacia_ic_div_1));
+
+ if (LOG_DACIA) logerror("DACIA Ctrl Register: %02x\n", data);
+
+ }
+ break;
+ }
+ case 2: /* Compare / Aux Ctrl Register 1 */
+ {
+ if (m_dacia_reg1 == CMP_REGISTER)
+ {
+ m_dacia_cmp1 =1;
+ m_dacia_cmpval1=data;
+ if (LOG_DACIA) logerror("DACIA Compare mode: %02x \n", data);
+// update_dacia_irq();
+ }
+ else
+ {
+ if (LOG_DACIA) logerror("DACIA Aux ctrl: %02x \n", data);
+ }
+ }
+ case 3: /* Transmit Data Register 1 */
+ if (LOG_DACIA) logerror("DACIA Transmit: %02x %c\n", data, (char)data);
+ laserdisc_w(data);
+ break;
+ default:
+ if (LOG_DACIA) logerror("%s:dacia_w(%02x,%02x)\n", machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+/*************************************
+ *
+ * I/O
+ *
+ *************************************/
+
+READ8_MEMBER(cops_state::io1_r)
+{
+ switch( offset & 0x0f )
+ {
+ case 0x08: /* SW0 */
+ return ioport("SW0")->read();
+ case 0x09: /* SW1 */
+ return ioport("SW1")->read();
+ case 0x0a: /* SW2 */
+ return ioport("SW2")->read();
+ default:
+ logerror("Unknown io1_r, offset = %03x\n", offset);
+ return 0;
+ }
+}
+
+WRITE8_MEMBER(cops_state::io1_w)
+{
+ int i;
+ char output_name[16];
+ UINT16 display_data;
+
+ switch( offset & 0x0f )
+ {
+ case 0x00: /* WOP0 Alpha display*/
+ m_lcd_addr_l = data;
+ break;
+ case 0x01: /* WOP1 Alpha display*/
+ m_lcd_addr_h = data;
+ {
+ // update display
+ const UINT16 addrs_table[] = { 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0002, 0x0001, 0x0080,
+ 0x1000, 0x0800, 0x0400, 0x2000, 0x4000, 0x0200, 0x0100, 0x8000 };
+ UINT16 addr = m_lcd_addr_l | (m_lcd_addr_h << 8);
+ for (i = 0; i < 16; i++ )
+ {
+ if (addr == addrs_table[i])
+ {
+ break;
+ }
+ }
+
+ if (i < 16)
+ {
+ sprintf(output_name, "digit%d", i);
+ display_data = m_lcd_data_l | (m_lcd_data_h << 8);
+ display_data = BITSWAP16(display_data, 4, 5, 12, 1, 0, 11, 10, 6, 7, 2, 9, 3, 15, 8, 14, 13);
+ output_set_value(output_name, display_data);
+ }
+ }
+ break;
+ case 0x02: /* WOP2 Alpha display*/
+ m_lcd_data_l = data;
+ break;
+ case 0x03: /* WOP3 Alpha display*/
+ m_lcd_data_h = data;
+ break;
+ case 0x04: /* WOP4 */
+ output_set_value("Offroad Right 4 Lamp", data & 0x80);
+ output_set_value("Offroad Right 3 Lamp", data & 0x40);
+ output_set_value("Offroad Right 2 Lamp", data & 0x20);
+ output_set_value("Offroad Right 1 Lamp", data & 0x10);
+ output_set_value("Offroad Left 4 Lamp", data & 0x08);
+ output_set_value("Offroad Left 3 Lamp", data & 0x04);
+ output_set_value("Offroad Left 2 Lamp", data & 0x02);
+ output_set_value("Offroad Left 1 Lamp", data & 0x01);
+ break;
+ case 0x05: /* WOP5 */
+ output_set_value("Damage Lamp", data & 0x80);
+ output_set_value("Stop Lamp", data & 0x40);
+ output_set_value("Gun Active Right Lamp", data & 0x20);
+ output_set_value("Vest Hit 2 Lamp", data & 0x10);
+ output_set_value("Vest Hit 3 Lamp", data & 0x04);
+ output_set_value("Gun Active Left Lamp", data & 0x02);
+ output_set_value("Vest Hit 1 Lamp", data & 0x01);
+ break;
+ case 0x06: /* WOP6 */
+ logerror("WOP6: data = %02x\n", data);
+ break;
+ case 0x07: /* WOP? */
+ logerror("WOP7: data = %02x\n", data);
+ break;
+ case 0x08: /* WOP0 */
+ logerror("WOP8: data = %02x\n", data);
+ break;
+ default:
+ logerror("Unknown io1_w, offset = %03x, data = %02x\n", offset, data);
+ break;
+ }
+}
+
+READ8_MEMBER(cops_state::io2_r)
+{
+ switch( offset & 0x0f )
+ {
+ case 0x03:
+ return ioport("STEER")->read();
+ default:
+ logerror("Unknown io2_r, offset = %02x\n", offset);
+ return 0;
+ }
+}
+
+WRITE8_MEMBER(cops_state::io2_w)
+{
+ switch( offset & 0x0f )
+ {
+ case 0x02:
+ output_set_value("Flash Red Lamp", data & 0x01);
+ output_set_value("Flash Blue Lamp", data & 0x80);
+ if ( data & ~0x91 ) logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data);
+ break;
+ case 0x04:
+ output_set_value("Bullet Lamp 6", data & 0x20);
+ output_set_value("Bullet Lamp 5", data & 0x10);
+ output_set_value("Bullet Lamp 4", data & 0x08);
+ output_set_value("Bullet Lamp 3", data & 0x04);
+ output_set_value("Bullet Lamp 2", data & 0x02);
+ output_set_value("Bullet Lamp 1", data & 0x01);
+ if ( data & ~0x3f ) logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data);
+ break;
+ default:
+ logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data);
+ break;
+ }
+}
+
+/*************************************
+ *
+ * VIA 1 (U18)
+ * PA0-2 Steer
+ * PA3 ?
+ * PA4-6 Fade?
+ * PA7 STK (system rom banking)
+ * PB0-7 SN76489 data bus
+ * CA1-2 n.c.
+ * CB1 /WE SN76489
+ * IRQ IRQ
+ *
+ *************************************/
+
+WRITE_LINE_MEMBER(cops_state::via1_irq)
+{
+ if ( state == ASSERT_LINE )
+ {
+ m_irq |= 1;
+ }
+ else
+ {
+ m_irq &= ~1;
+ }
+ m_maincpu->set_input_line(M6502_IRQ_LINE, m_irq ? ASSERT_LINE : CLEAR_LINE);
+}
+
+WRITE8_MEMBER(cops_state::via1_b_w)
+{
+ m_sn_data = BITSWAP8(data,0,1,2,3,4,5,6,7);
+ if (m_sn_cb1)
+ {
+ m_sn->write(space,0,m_sn_data);
+ }
+}
+
+WRITE8_MEMBER(cops_state::via1_cb1_w)
+{
+ m_sn_cb1 = data;
+}
+
+/*************************************
+ *
+ * VIA 2 (U27)
+ * PA0-7 GUN
+ * PB0-7 GUN
+ * IRQ IRQ
+ *
+ *************************************/
+
+WRITE_LINE_MEMBER(cops_state::via2_irq)
+{
+ if ( state == ASSERT_LINE )
+ {
+ m_irq |= 2;
+ }
+ else
+ {
+ m_irq &= ~2;
+ }
+ m_maincpu->set_input_line(M6502_IRQ_LINE, m_irq ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+/*************************************
+ *
+ * Main CPU memory handlers
+ *
+ *************************************/
+
+static ADDRESS_MAP_START( cops_map, AS_PROGRAM, 8, cops_state )
+ AM_RANGE(0x0000, 0x1fff) AM_RAM
+ AM_RANGE(0x2000, 0x9fff) AM_ROM AM_REGION("program", 0)
+ AM_RANGE(0xa000, 0xafff) AM_READWRITE(io1_r, io1_w)
+ AM_RANGE(0xb000, 0xb00f) AM_DEVREADWRITE("via6522_1", via6522_device, read, write) /* VIA 1 */
+ AM_RANGE(0xb800, 0xb80f) AM_DEVREADWRITE("via6522_2", via6522_device, read, write) /* VIA 2 */
+ AM_RANGE(0xc000, 0xcfff) AM_READWRITE(io2_r, io2_w)
+// AM_RANGE(0xd000, 0xd003) AM_DEVREADWRITE("acia6551_1", mos6551_device, read, write )
+// AM_RANGE(0xd004, 0xd007) AM_DEVREADWRITE("acia6551_2", mos6551_device, read, write )
+ AM_RANGE(0xd000, 0xd007) AM_READWRITE(dacia_r, dacia_w)
+ AM_RANGE(0xd800, 0xd80f) AM_DEVREADWRITE("via6522_3", via6522_device, read, write) /* VIA 3 */
+ AM_RANGE(0xe000, 0xffff) AM_ROMBANK("sysbank1")
+ADDRESS_MAP_END
+
+static INPUT_PORTS_START( cops )
+ PORT_START("SW0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Switch A") PORT_CODE(KEYCODE_A) PORT_IMPULSE(1)
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Switch C") PORT_CODE(KEYCODE_C) PORT_IMPULSE(1)
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("PROGRAM") PORT_CODE(KEYCODE_P)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_IMPULSE(1)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Switch B") PORT_CODE(KEYCODE_B) PORT_IMPULSE(1)
+
+ PORT_START("SW1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) // N.C.
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // Gas pedal
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("20P LEVEL") PORT_CODE(KEYCODE_Q)
+ PORT_BIT( 0x38, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("100P LEVEL") PORT_CODE(KEYCODE_W)
+
+ PORT_START("SW2")
+ PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED )
+
+ PORT_START("STEER")
+ PORT_BIT( 0xff, 0x80, IPT_PADDLE ) PORT_SENSITIVITY(50) PORT_KEYDELTA(10)
+INPUT_PORTS_END
+
+static INPUT_PORTS_START( revlatns )
+ PORT_START("SW0")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("C")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("COLLECT")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN5 )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_IMPULSE(1)
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN6 )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Continue")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B")
+
+ PORT_START("SW1")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_NAME("Refill Key") PORT_CODE(KEYCODE_R) PORT_TOGGLE
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_INTERLOCK) PORT_NAME("Cashbox Door") PORT_CODE(KEYCODE_Q) PORT_TOGGLE
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("20P LEVEL") PORT_CODE(KEYCODE_Q)
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("*")
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("100P LEVEL") PORT_CODE(KEYCODE_W)
+
+ PORT_START("SW2")
+ PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_NAME("50p")
+ PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_NAME("20p")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_COIN3 ) PORT_NAME("10p")
+ PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN4 ) PORT_NAME("100p")
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SPECIAL )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SPECIAL )
+ PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL )
+ PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL )
+
+INPUT_PORTS_END
+
+void cops_state::machine_start()
+{
+ m_ld_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(cops_state::ld_timer_callback),this));
+ m_dacia_ic_div_1 = timer_divide_select[0];
+
+ m_ld_timer->adjust(attotime::from_hz(167*5), 0, attotime::from_hz(167*5));
+}
+
+void cops_state::machine_reset()
+{
+ m_irq = 0;
+ m_lcd_addr_l = m_lcd_addr_h = 0;
+ m_lcd_data_l = m_lcd_data_h = 0;
+
+ m_dacia_cts = 0;
+ m_dacia_dcd = 0;
+
+ m_dacia_irq1_reg = 0x80;
+ m_dacia_rts1 = 1;
+ m_dacia_dtr1 = 1;
+ m_dacia_fe1 = 1;
+ m_dacia_receiver_full = 1;
+ m_ld_input_state = LD_INPUT_GET_COMMAND;
+ m_ld_command_current_byte = m_ld_command_total_bytes = 0;
+ m_ld_frame_index = 0;
+}
+
+
+PALETTE_INIT_MEMBER( cops_state,cops )
+{
+}
+
+DRIVER_INIT_MEMBER(cops_state,cops)
+{
+ //The hardware is designed and programmed to use multiple system ROM banks, but for some reason it's hardwired to bank 2.
+ //For documentation's sake, here's the init
+ UINT8 *rom = memregion("system")->base();
+ membank("sysbank1")->configure_entries(0, 4, &rom[0x0000], 0x2000);
+ membank("sysbank1")->set_entry(2);
+}
+
+static MACHINE_CONFIG_START( cops, cops_state )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu",M6502,MAIN_CLOCK/2)
+ MCFG_CPU_PROGRAM_MAP(cops_map)
+
+ /* video hardware */
+ MCFG_SCREEN_ADD("screen", RASTER)
+ MCFG_SCREEN_REFRESH_RATE(60)
+ MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0))
+ MCFG_SCREEN_UPDATE_DRIVER(cops_state, screen_update)
+ MCFG_SCREEN_SIZE(32*8, 32*8)
+ MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 2*8, 30*8-1)
+ MCFG_SCREEN_PALETTE("palette")
+
+ MCFG_PALETTE_ADD("palette", 8)
+ MCFG_PALETTE_INIT_OWNER(cops_state,cops)
+
+ /* via */
+ MCFG_DEVICE_ADD("via6522_1", VIA6522, 0)
+ MCFG_VIA6522_IRQ_HANDLER(WRITELINE(cops_state, via1_irq))
+ MCFG_VIA6522_WRITEPB_HANDLER(WRITE8(cops_state, via1_b_w))
+ MCFG_VIA6522_CB1_HANDLER(WRITE8(cops_state, via1_cb1_w))
+
+ MCFG_DEVICE_ADD("via6522_2", VIA6522, 0)
+ MCFG_VIA6522_IRQ_HANDLER(WRITELINE(cops_state, via2_irq))
+
+ MCFG_DEVICE_ADD("via6522_3", VIA6522, 0)
+ MCFG_VIA6522_READPA_HANDLER(READ8(cops_state, cdrom_data_r))
+ MCFG_VIA6522_WRITEPA_HANDLER(WRITE8(cops_state, cdrom_data_w))
+ MCFG_VIA6522_WRITEPB_HANDLER(WRITE8(cops_state, cdrom_ctrl_w))
+
+ /* acia */
+// MCFG_MOS6551_ADD("acia6551_1", XTAL_1_8432MHz, NULL)
+// MCFG_MOS6551_ADD("acia6551_2", XTAL_1_8432MHz, NULL)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_MONO("mono")
+
+ /* TODO: Verify clock */
+ MCFG_SOUND_ADD("snsnd", SN76489, MAIN_CLOCK/2)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
+
+MACHINE_CONFIG_END
+
+
+/***************************************************************************
+
+ Game driver(s)
+
+***************************************************************************/
+
+ROM_START( cops )
+ ROM_REGION( 0x8000, "program", 0 )
+ ROM_LOAD( "cops_prg.dat", 0x0000, 0x8000, CRC(a5c02366) SHA1(b135d72fcfe737a113c984b0b8dd78428f248414) )
+
+ ROM_REGION( 0x8000, "system", 0 )
+ ROM_LOAD( "cops_sys.dat", 0x0000, 0x8000, CRC(0060e5d0) SHA1(b8c9f6fde6a315e33fa7946e5d3bb4ea2fbe76a8) )
+
+ DISK_REGION( "audiocd" )
+ DISK_IMAGE_READONLY( "copscd", 0, NO_DUMP )
+
+ DISK_REGION( "laserdisc" )
+ DISK_IMAGE_READONLY( "cops", 0, NO_DUMP )
+ROM_END
+
+ROM_START( copsuk )
+ ROM_REGION( 0x8000, "program", 0 )
+ ROM_LOAD( "cops1b_uk.bin", 0x0000, 0x8000, CRC(f095ee95) SHA1(86bb517331d81ae3a8f3b87df67c321013c6aae4) )
+
+ ROM_REGION( 0x8000, "system", 0 )
+ ROM_LOAD( "cops_sys.dat", 0x0000, 0x8000, CRC(0060e5d0) SHA1(b8c9f6fde6a315e33fa7946e5d3bb4ea2fbe76a8) )
+
+ DISK_REGION( "audiocd" )
+ DISK_IMAGE_READONLY( "copscd", 0, NO_DUMP )
+
+ DISK_REGION( "laserdisc" )
+ DISK_IMAGE_READONLY( "cops", 0, NO_DUMP )
+ROM_END
+
+ROM_START( revlatns )
+ ROM_REGION( 0x8000, "program", 0 )
+ ROM_LOAD( "revelations_prog.bin", 0x0000, 0x8000, CRC(5ab41ac3) SHA1(0f7027551da17011576cf077e2f199729bb10482) )
+
+ ROM_REGION( 0x8000, "system", 0 )
+ ROM_LOAD( "revelations_sys.bin", 0x0000, 0x8000, CRC(43e5e3ec) SHA1(fa44b102b5aa7ad2421c575abdc67f1c29f23bc1) )
+
+ DISK_REGION( "laserdisc" )
+ DISK_IMAGE_READONLY( "revlatns", 0, NO_DUMP )
+ROM_END
+
+
+GAMEL( 1994, cops, 0, cops, cops, cops_state, cops, ROT0, "Atari Games", "Cops (USA)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops )
+GAMEL( 1994, copsuk, cops,cops, cops, cops_state, cops, ROT0, "Nova Productions / Deith Leisure","Cops (UK)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops )
+GAMEL( 1994, revlatns, 0, cops, revlatns, cops_state, cops, ROT0, "Nova Productions", "Revelations", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops )