summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/scnxx562.h
blob: cec9d6a8fb3ccb7810ec7b134276395821a4bebe (plain) (tree)



























































































































































































                                                                                                       
                                                                                   

                                           
                                                                                   










                                               

                                        










































                                              
                        


                    
                        









                      
 










                                 





                                           



            







                                               









                                         







                                               



            







                                               



            










                                               



            










                                                   



            
















                                                   



            






                                                   



            
                                                   



            


                                                   



            



                                                   




                                                                                















                                   







                                   



















                                   


















                                                                               















                                                 















                                    


















                                                              

                  


                                                    

                            

                                                         
 

                                              

                            
                                             





                             
                                                      






                                       
                                         
































                                                                                                                                                                                                   
                       









                                                                        
                                                                      


































































































                                                                                                          
// license:BSD-3-Clause
// copyright-holders:Joakim Larsson Edstrom
/***************************************************************************

    Philips DUSCC - Dual Serial Communications Controller emulation

****************************************************************************

         Chan B                     Chan A  Chan B                     Chan A
         =======   _____   _____  ========  =======   _____   _____  ========
          IACKN  1|*    \_/     |48 VCC      IACKN  1|*    \_/     |48 VDD
             A3  2|             |47 A4          A3  2|             |47 A4
             A2  3|             |46 A5          A2  3|             |46 A5
             A1  4|             |45 A6          A1  4|             |45 A6
    RTxDAK/GPI1  5|             |44   RTxDAK/GPI1   5|             |44 RTxDAK/GP1
           IRQN  6|             |43 X1/CLK    IRQN  6|             |43 X1/CLK
           RDYN  7|             |42 X2      RESETN  7|             |42 X2
     RTS/SYNOUT  8|             |41    RTS/SYNOUT   8|             |41 RTS/SYNOUT
           TRxC  9|             |40 TRxC      TRxC  9|             |40 TRxC
           RTxC 10|             |39 RTxC      RTxC 10|             |39 RTxC
       DCD/SYNI 11|             |38     DCD/SYNI   11|             |38 DCD/SYNI
            RxD 12|             |37 RxD        RxD 12|             |37 RxD
            TxD 13|  SCN26562   |36 TxD        TxD 13|  SCN68562   |36 TxD
     TxDAK/GPI2 14|  SCN26C562  |35   TxDAK/GPI2   14|  SCN68C562  |35 TxDAK/GPI2
    RTxDRQ/GPO1 15|             |34   RTxDRQ/GPO1  15|             |34 RTxDRQ/GPO1
 TxDRQ/RTS/GPO2 16|             |33 TxDRQ/RTS/GPO2 16|             |33 TxDRQ/RTS/GPO2
         CTS/LC 17|             |32 CTS/LC  CTS/LC 17|             |32 CTS/LC
             D7 18|             |31 D0          D7 18|             |31 D0
             D6 19|             |30 D1          D6 19|             |30 D1
             D5 20|             |29 D2          D5 20|             |29 D2
             D4 21|             |28 D3          D4 21|             |28 D3
            RDN 22|             |27 EOPN    DTACKN 22|             |27 DONEN
         RESETN 23|             |26 WRN       DTCN 23|             |26 R/WN
            GND 24|_____________|25 CEN        CND 24|_____________|25 CSN
                    Intel Bus                          Motorola Bus

***************************************************************************/

#ifndef __SCNXX562_H__
#define __SCNXX562_H__

#include "emu.h"

//**************************************************************************
//  DEVICE CONFIGURATION MACROS
//**************************************************************************

#define LOCAL_BRG 0

/* Variant ADD macros - use the right one to enable the right feature set! */
#define MCFG_DUSCC26562_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \
	MCFG_DEVICE_ADD(_tag, DUSCC26562, _clock) \
	MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb)

#define MCFG_DUSCC26C562_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \
	MCFG_DEVICE_ADD(_tag, DUSCC26C562, _clock) \
	MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb)

#define MCFG_DUSCC68562_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \
	MCFG_DEVICE_ADD(_tag, DUSCC68562, _clock) \
	MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb)

#define MCFG_DUSCC68C562_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \
	MCFG_DEVICE_ADD(_tag, DUSCC68C562, _clock) \
	MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb)

/* generic ADD macro - Avoid using it directly, see above for correct variant instead */
#define MCFG_DUSCC_ADD(_tag, _clock, _rxa, _txa, _rxb, _txb) \
	MCFG_DEVICE_ADD(_tag, DUSCC, _clock) \
	MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb)

/* Generic macros */
#define MCFG_DUSCC_OFFSETS(_rxa, _txa, _rxb, _txb) \
	duscc_device::configure_channels(*device, _rxa, _txa, _rxb, _txb);

// Port A callbacks
#define MCFG_DUSCC_OUT_TXDA_CB(_devcb) \
	devcb = &duscc_device::set_out_txda_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_DTRA_CB(_devcb) \
	devcb = &duscc_device::set_out_dtra_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_RTSA_CB(_devcb) \
	devcb = &duscc_device::set_out_rtsa_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_SYNCA_CB(_devcb) \
	devcb = &duscc_device::set_out_synca_callback(*device, DEVCB_##_devcb);

// Port B callbacks
#define MCFG_DUSCC_OUT_TXDB_CB(_devcb) \
	devcb = &duscc_device::set_out_txdb_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_DTRB_CB(_devcb) \
	devcb = &duscc_device::set_out_dtrb_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_RTSB_CB(_devcb) \
	devcb = &duscc_device::set_out_rtsb_callback(*device, DEVCB_##_devcb);

#define MCFG_DUSCC_OUT_SYNCB_CB(_devcb) \
	devcb = &duscc_device::set_out_syncb_callback(*device, DEVCB_##_devcb);



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

// ======================> duscc_channel

class duscc_device;

class duscc_channel : public device_t,
		public device_serial_interface
{
	friend class duscc_device;

public:
	duscc_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

	// device-level overrides
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;

	// device_serial_interface overrides
	virtual void tra_callback() override;
	virtual void tra_complete() override;
	virtual void rcv_callback() override;
	virtual void rcv_complete() override;

	// read register handlers
	UINT8 do_dusccreg_cmr1_r();
	UINT8 do_dusccreg_cmr2_r();
	UINT8 do_dusccreg_s1r_r();
	UINT8 do_dusccreg_s2r_r();
	UINT8 do_dusccreg_tpr_r();
	UINT8 do_dusccreg_ttr_r();
	UINT8 do_dusccreg_rpr_r();
	UINT8 do_dusccreg_rtr_r();
	UINT8 do_dusccreg_ctprh_r();
	UINT8 do_dusccreg_ctprl_r();
	UINT8 do_dusccreg_ctcr_r();
	UINT8 do_dusccreg_omr_r();
	UINT8 do_dusccreg_cth_r();
	UINT8 do_dusccreg_ctl_r();
	UINT8 do_dusccreg_pcr_r();
	UINT8 do_dusccreg_ccr_r();
	UINT8 do_dusccreg_rxfifo_r();
	UINT8 do_dusccreg_rsr_r();
	UINT8 do_dusccreg_trsr_r();
	UINT8 do_dusccreg_ictsr_r();
	UINT8 do_dusccreg_gsr_r();
	UINT8 do_dusccreg_ier_r();
	UINT8 do_dusccreg_cid_r();
	UINT8 do_dusccreg_ivr_r();
	UINT8 do_dusccreg_icr_r();
	UINT8 do_dusccreg_ivrm_r();
	UINT8 do_dusccreg_mrr_r();
	UINT8 do_dusccreg_ier1_r();
	UINT8 do_dusccreg_ier2_r();
	UINT8 do_dusccreg_ier3_r();
	UINT8 do_dusccreg_trcr_r();
	UINT8 do_dusccreg_rflr_r();
	UINT8 do_dusccreg_ftlr_r();
	UINT8 do_dusccreg_trmsr_r();
	UINT8 do_dusccreg_telr_r();

	// write register handlers
	void do_dusccreg_cmr1_w(UINT8 data);
	void do_dusccreg_cmr2_w(UINT8 data);
	void do_dusccreg_s1r_w(UINT8 data);
	void do_dusccreg_s2r_w(UINT8 data);
	void do_dusccreg_tpr_w(UINT8 data);
	void do_dusccreg_ttr_w(UINT8 data);
	void do_dusccreg_rpr_w(UINT8 data);
	void do_dusccreg_rtr_w(UINT8 data);
	void do_dusccreg_ctprh_w(UINT8 data);
	void do_dusccreg_ctprl_w(UINT8 data);
	void do_dusccreg_ctcr_w(UINT8 data);
	void do_dusccreg_omr_w(UINT8 data);
	void do_dusccreg_pcr_w(UINT8 data);
	void do_dusccreg_ccr_w(UINT8 data);
	void do_dusccreg_txfifo_w(UINT8 data);
	void do_dusccreg_rsr_w(UINT8 data);
	void do_dusccreg_trsr_w(UINT8 data);
	void do_dusccreg_ictsr_w(UINT8 data);
	void do_dusccreg_gsr_w(UINT8 data);
	void do_dusccreg_ier_w(UINT8 data);
	//  void do_dusccreg_rea_w(UINT8 data); // Short cutted non complex feature
	void do_dusccreg_ivr_w(UINT8 data);
	void do_dusccreg_icr_w(UINT8 data);
	//  void do_dusccreg_sea_w(UINT8 data); // Short cutted non complex feature
	void do_dusccreg_mrr_w(UINT8 data);
	void do_dusccreg_ier1_w(UINT8 data);
	void do_dusccreg_ier2_w(UINT8 data);
	void do_dusccreg_ier3_w(UINT8 data);
	void do_dusccreg_trcr_w(UINT8 data);
	void do_dusccreg_ftlr_w(UINT8 data);
	void do_dusccreg_trmsr_w(UINT8 data);

	UINT8 read(offs_t &offset);
	void write(UINT8 data, offs_t &offset);

	//  UINT8 data_read();
	//  void data_write(UINT8 data);

	void receive_data(UINT8 data);
	void m_tx_fifo_rp_step();
	void m_rx_fifo_rp_step();
	UINT8 m_rx_fifo_rp_data();

	DECLARE_WRITE_LINE_MEMBER( write_rx );
	DECLARE_WRITE_LINE_MEMBER( cts_w );
	DECLARE_WRITE_LINE_MEMBER( dcd_w );
	DECLARE_WRITE_LINE_MEMBER( ri_w );
	DECLARE_WRITE_LINE_MEMBER( rxc_w );
	DECLARE_WRITE_LINE_MEMBER( txc_w );
	DECLARE_WRITE_LINE_MEMBER( sync_w );

	int m_rxc;
	int m_txc;
	int m_tra;
	int m_rcv;

	// Register state
	UINT8 m_cmr1;
	UINT8 m_cmr2;
	UINT8 m_s1r;
	UINT8 m_s2r;
	UINT8 m_tpr;
	UINT8 m_ttr;
	UINT8 m_rpr;
	UINT8 m_rtr;
	UINT8 m_ctprh;
	UINT8 m_ctprl;
	UINT8 m_ctcr;
	UINT8 m_omr;
	UINT8 m_cth;
	UINT8 m_ctl;
	UINT8 m_pcr;
	UINT8 m_ccr;
	UINT8 m_txfifo[4];
	UINT8 m_rxfifo[4];
	UINT8 m_rsr;
	UINT8 m_trsr;
	UINT8 m_ictsr;
	UINT8 m_gsr;
	UINT8 m_ier;
	//  UINT8 m_rea;
	UINT8 m_cid;
	UINT8 m_ivr;
	UINT8 m_icr;
	//  UINT8 m_sea;
	UINT8 m_ivrm;
	UINT8 m_mrr;
	UINT8 m_ier1;
	UINT8 m_ier2;
	UINT8 m_ier3;
	UINT8 m_trcr;
	UINT8 m_rflr;
	UINT8 m_ftlr;
	UINT8 m_trmsr;
	UINT8 m_telr;

protected:
	enum
	{
		INT_TRANSMIT = 0,
		INT_EXTERNAL = 1,
		INT_RECEIVE  = 2,
		INT_SPECIAL  = 3
	};

	enum
	{
		REG_CCR_RESET_TX    = 0x00,
		REG_CCR_ENABLE_TX   = 0x02,
		REG_CCR_DISABLE_TX  = 0x03,
		REG_CCR_RESET_RX    = 0x40,
		REG_CCR_ENABLE_RX   = 0x42,
		REG_CCR_DISABLE_RX  = 0x43
	};

	enum
	{
		REG_CMR1_PARITY         = 0x20,
		REG_CMR1_PMMODE_MASK    = 0x18,
		REG_CMR1_PMMODE_NONE    = 0x00,
		REG_CMR1_PMMODE_RES     = 0x01,
		REG_CMR1_PMMODE_PARITY  = 0x10,
		REG_CMR1_PMMODE_FORCED  = 0x11,
		REG_CMR1_CPMODE_MASK    = 0x07,
		REG_CMR1_CPMODE_ASYNC   = 0x07
	};

	enum
	{
		REG_CMR2_DTI_MASK = 0x38,
		REG_CMR2_DTI_NODMA = 0x38
	};

	enum
	{
		REG_RPR_DATA_BITS_MASK  = 0x03,
		REG_RPR_DATA_BITS_5BIT  = 0x00,
		REG_RPR_DATA_BITS_6BIT  = 0x01,
		REG_RPR_DATA_BITS_7BIT  = 0x02,
		REG_RPR_DATA_BITS_8BIT  = 0x03,
		REG_RPR_DCD             = 0x04,
		REG_RPR_STRIP_PARITY    = 0x08,
		REG_RPR_RTS             = 0x10
	};

	enum
	{
		REG_TPR_DATA_BITS_MASK  = 0x03,
		REG_TPR_DATA_BITS_5BIT  = 0x00,
		REG_TPR_DATA_BITS_6BIT  = 0x01,
		REG_TPR_DATA_BITS_7BIT  = 0x02,
		REG_TPR_DATA_BITS_8BIT  = 0x03,
		REG_TPR_CTS             = 0x04,
		REG_TPR_RTS             = 0x08,
		REG_TPR_STOP_BITS_MASK  = 0xf0
	};

	enum
	{
		REG_TTR_EXT             = 0x80,
		REG_TTR_TXCLK_MASK      = 0x70,
		REG_TTR_TXCLK_1XEXT     = 0x00,
		REG_TTR_TXCLK_16XEXT    = 0x10,
		REG_TTR_TXCLK_DPLL      = 0x20,
		REG_TTR_TXCLK_BRG       = 0x30,
		REG_TTR_TXCLK_2X_OTHER  = 0x40,
		REG_TTR_TXCLK_32X_OTHER = 0x50,
		REG_TTR_TXCLK_2X_OWN    = 0x60,
		REG_TTR_TXCLK_32X_OWN   = 0x70,
		REG_TTR_BRG_RATE_MASK   = 0x0f,
	};

	enum
	{
		REG_RTR_EXT             = 0x80,
		REG_RTR_RXCLK_MASK      = 0x70,
		REG_RTR_RXCLK_1XEXT     = 0x00,
		REG_RTR_RXCLK_16XEXT    = 0x10,
		REG_RTR_RXCLK_BRG       = 0x20,
		REG_RTR_RXCLK_CT        = 0x30,
		REG_RTR_RXCLK_DPLL_64X_X1   = 0x40,
		REG_RTR_RXCLK_DPLL_32X_EXT  = 0x50,
		REG_RTR_RXCLK_DPLL_32X_BRG  = 0x60,
		REG_RTR_RXCLK_DPLL_32X_CT   = 0x70,
		REG_RTR_BRG_RATE_MASK       = 0x0f,
	};

	enum
	{
		REG_PCR_X2_IDC              = 0x80,
		REG_PCR_GP02_RTS            = 0x40,
		REG_PCR_SYNOUT_RTS          = 0x20,
		REG_PCR_RTXC_MASK           = 0x18,
		REG_PCR_RTXC_INPUT          = 0x00,
		REG_PCR_RTXC_CNTR_OUT       = 0x08,
		REG_PCR_RTXC_TXCLK_OUT      = 0x10,
		REG_PCR_RTXC_RXCLK_OUT      = 0x18,
		REG_PCR_TRXC_MASK           = 0x07,
		REG_PCR_TRXC_INPUT          = 0x00,
		REG_PCR_TRXC_CRYST_OUT      = 0x01,
		REG_PCR_TRXC_DPLL_OUT       = 0x02,
		REG_PCR_TRXC_CNTR_OUT       = 0x03,
		REG_PCR_TRXC_TXBRG_OUT      = 0x04,
		REG_PCR_TRXC_RXBRG_OUT      = 0x05,
		REG_PCR_TRXC_TXCLK_OUT      = 0x06,
		REG_PCR_TRXC_RXCLK_OUT      = 0x07,
	};

	enum
	{
		REG_OMR_TXRCL_MASK          = 0xe0,
		REG_OMR_TXRCL_8BIT          = 0xe0,
		REG_OMR_TXRDY_ACTIVATED     = 0x10,
		REG_OMR_RXRDY_ACTIVATED     = 0x08,
		REG_OMR_GP02                = 0x04,
		REG_OMR_GP01                = 0x02,
		REG_OMR_RTS                 = 0x01,
	};

	enum
	{
		REG_RSR_OVERRUN_ERROR       = 0x20,
	};

	enum
	{
		REG_ICTSR_DELTA_CTS         = 0x10,
		REG_ICTSR_DCD               = 0x08,
		REG_ICTSR_CTS               = 0x04,
	};

	enum
	{
		REG_GSR_CHAN_A_RXREADY      = 0x01,
		REG_GSR_CHAN_B_RXREADY      = 0x10,
		REG_GSR_CHAN_A_TXREADY      = 0x02,
		REG_GSR_CHAN_B_TXREADY      = 0x20,
	};

	// Register offsets, stripped from channel bit 0x20 but including A7 bit
	enum
	{
		REG_CMR1    = 0x00,
		REG_CMR2    = 0x01,
		REG_S1R     = 0x02,
		REG_S2R     = 0x03,
		REG_TPR     = 0x04,
		REG_TTR     = 0x05,
		REG_RPR     = 0x06,
		REG_RTR     = 0x07,
		REG_CTPRH   = 0x08,
		REG_CTPRL   = 0x09,
		REG_CTCR    = 0x0a,
		REG_OMR     = 0x0b,
		REG_CTH     = 0x0c,
		REG_CTL     = 0x0d,
		REG_PCR     = 0x0e,
		REG_CCR     = 0x0f,
		REG_TXFIFO_0= 0x10,
		REG_TXFIFO_1= 0x11,
		REG_TXFIFO_2= 0x12,
		REG_TXFIFO_3= 0x13,
		REG_RXFIFO_0= 0x14,
		REG_RXFIFO_1= 0x15,
		REG_RXFIFO_2= 0x16,
		REG_RXFIFO_3= 0x17,
		REG_RSR     = 0x18,
		REG_TRSR    = 0x19,
		REG_ICTSR   = 0x1a,
		REG_GSR     = 0x1b,
		REG_IER     = 0x1c,
		REG_REA     = 0x1d,
		REG_CID     = 0x1d,
		REG_IVR     = 0x1e,
		REG_ICR     = 0x1f,
		REG_SEA     = 0x1d,
		REG_IVRM    = 0x1e,
		REG_MRR     = 0x1f,
		REG_IER1    = 0x42,
		REG_IER2    = 0x43,
		REG_IER3    = 0x45,
		REG_TRCR    = 0x47,
		REG_RFLR    = 0x4e,
		REG_FTLR    = 0x5c,
		REG_TRMSR   = 0x5e,
		REG_TELR    = 0x5f,
	};

	enum
	{
		TIMER_ID_BAUD,
		TIMER_ID_XTAL,
		TIMER_ID_RTXC,
		TIMER_ID_TRXC
	};

	UINT16 m_brg_rx_rate;
	UINT16 m_brg_tx_rate;
	UINT16 m_brg_const;

	// TODO: Implement the 14.4K, 56K and 64K bauds available on the CDUSCC
	static unsigned int get_baudrate(unsigned int br)
	{
		switch (br)
		{
		case 0x00: return   50; break;
		case 0x01: return   75; break;
		case 0x02: return   110; break;
		case 0x03: return   134; break;
		case 0x04: return   150; break;
		case 0x05: return   200; break;
		case 0x06: return   300; break;
		case 0x07: return   600; break;
		case 0x08: return   1050; break;
		case 0x09: return   1200; break;
		case 0x0a: return   2000; break;
		case 0x0b: return   2400; break;
		case 0x0c: return   4800; break;
		case 0x0d: return   9600; break;
		case 0x0e: return   19200; break;
		case 0x0f: return   38400; break;
		};
		return 0;
	}

	void update_serial();
	void set_dtr(int state);
	void set_rts(int state);

	int get_tx_clock_mode();
	int get_rx_clock_mode();
	stop_bits_t get_stop_bits();
	int get_rx_word_length();
	int get_tx_word_length();

	/* FIFOs and rx/tx status */
	/* Receiver */
	UINT8 m_rx_data_fifo[16];   // data FIFO
	UINT8 m_rx_error_fifo[16];  // error FIFO
	int m_rx_fifo_rp;           // FIFO read pointer
	int m_rx_fifo_wp;           // FIFO write pointer
	int m_rx_fifo_sz;           // FIFO size
	UINT8 m_rx_error;           // current error

	/* Transmitter */
	UINT8 m_tx_data_fifo[16];   // data FIFO
	UINT8 m_tx_error_fifo[16];  // error FIFO
	int m_tx_fifo_rp;           // FIFO read pointer
	int m_tx_fifo_wp;           // FIFO write pointer
	int m_tx_fifo_sz;           // FIFO size
	UINT8 m_tx_error;           // current error

	int m_rx_clock;     // receive clock pulse count
	int m_rx_first;     // first character received
	int m_rx_break;     // receive break condition
	//  UINT8 m_rx_rr0_latch;   // read register 0 latched

	int m_rxd;
	int m_ri;       // ring indicator latch
	int m_cts;      // clear to send latch
	int m_dcd;      // data carrier detect latch

	// transmitter state
	UINT8 m_tx_data;    // transmit data register
	int m_tx_clock;     // transmit clock pulse count

	int m_dtr;      // data terminal ready
	int m_rts;      // request to send

	// synchronous state
	UINT16 m_sync;      // sync character

	int m_rcv_mode;
	int m_index;
	duscc_device *m_uart;

	// CDUSCC specifics
	int m_a7;       // Access additional registers
};


// ======================> duscc_device


class duscc_device :  public device_t
//      ,public device_z80daisy_interface
{
	friend class duscc_channel;

public:
	// construction/destruction
	duscc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source);
	duscc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

	template<class _Object> static devcb_base &set_out_txda_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_txda_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_dtra_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_dtra_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_rtsa_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_rtsa_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_synca_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_synca_cb.set_callback(object); }

	template<class _Object> static devcb_base &set_out_txdb_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_txdb_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_dtrb_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_dtrb_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_rtsb_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_rtsb_cb.set_callback(object); }
	template<class _Object> static devcb_base &set_out_syncb_callback(device_t &device, _Object object) { return downcast<duscc_device &>(device).m_out_syncb_cb.set_callback(object); }

	static void configure_channels(device_t &device, int rxa, int txa, int rxb, int txb)
	{
#if 0 // TODO: Fix this, need a way to set external rx/tx clocks for the channels
		duscc_device &dev = downcast<duscc_device &>(device);
		dev.m_chanA->m_rxc = rxa;
		dev.m_chanA->m_txc = txa;
		dev.m_chanB->m_rxc = rxb;
		dev.m_chanB->m_txc = txb;
#endif
	}

	DECLARE_READ8_MEMBER( read );
	DECLARE_WRITE8_MEMBER( write );

	// interrupt acknowledge
	//  int m1_r();

	DECLARE_WRITE_LINE_MEMBER( rxa_w ) { m_chanA->write_rx(state); }
	DECLARE_WRITE_LINE_MEMBER( rxb_w ) { m_chanB->write_rx(state); }
	DECLARE_WRITE_LINE_MEMBER( ctsa_w ) { m_chanA->cts_w(state); }
	DECLARE_WRITE_LINE_MEMBER( ctsb_w ) { m_chanB->cts_w(state); }
	DECLARE_WRITE_LINE_MEMBER( dcda_w ) { m_chanA->dcd_w(state); }
	DECLARE_WRITE_LINE_MEMBER( dcdb_w ) { m_chanB->dcd_w(state); }
	DECLARE_WRITE_LINE_MEMBER( ria_w ) { m_chanA->ri_w(state); }
	DECLARE_WRITE_LINE_MEMBER( rib_w ) { m_chanB->ri_w(state); }
#if 0
	DECLARE_WRITE_LINE_MEMBER( rxca_w ) { m_chanA->rxc_w(state); }
	DECLARE_WRITE_LINE_MEMBER( rxcb_w ) { m_chanB->rxc_w(state); }
	DECLARE_WRITE_LINE_MEMBER( txca_w ) { m_chanA->txc_w(state); }
	DECLARE_WRITE_LINE_MEMBER( txcb_w ) { m_chanB->txc_w(state); }
	DECLARE_WRITE_LINE_MEMBER( rxtxcb_w ) { m_chanB->rxc_w(state); m_chanB->txc_w(state); }
#endif
	DECLARE_WRITE_LINE_MEMBER( synca_w ) { m_chanA->sync_w(state); }
	DECLARE_WRITE_LINE_MEMBER( syncb_w ) { m_chanB->sync_w(state); }

protected:
	// device-level overrides
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual machine_config_constructor device_mconfig_additions() const override;

	// internal interrupt management
	void check_interrupts();
	void reset_interrupts();
	UINT8 modify_vector(UINT8 vect, int i, UINT8 src);
	void trigger_interrupt(int index, int state);
	int get_channel_index(duscc_channel *ch) { return (ch == m_chanA) ? 0 : 1; }

	// Variants in the DUSCC family
	enum
	{
		TYPE_DUSCC       = 0x001,
		TYPE_DUSCC26562  = 0x002,
		TYPE_DUSCC26C562 = 0x004,
		TYPE_DUSCC68562  = 0x008,
		TYPE_DUSCC68C562 = 0x010,
	};

#define SET_NMOS   ( duscc_device::TYPE_DUSCC26562 | duscc_device::TYPE_DUSCC68562 )
#define SET_CMOS   ( duscc_device::TYPE_DUSCC26C562 | duscc_device::TYPE_DUSCC68C562 )

	enum
	{
		CHANNEL_A = 0,
		CHANNEL_B
	};

	required_device<duscc_channel> m_chanA;
	required_device<duscc_channel> m_chanB;

	// internal state
#if 0
	int m_rxca;
	int m_txca;
	int m_rxcb;
	int m_txcb;
#endif

	devcb_write_line    m_out_txda_cb;
	devcb_write_line    m_out_dtra_cb;
	devcb_write_line    m_out_rtsa_cb;
	devcb_write_line    m_out_synca_cb;

	devcb_write_line    m_out_txdb_cb;
	devcb_write_line    m_out_dtrb_cb;
	devcb_write_line    m_out_rtsb_cb;
	devcb_write_line    m_out_syncb_cb;

	int m_int_state[6]; // interrupt state

	int m_variant;
};

// device type definition
extern const device_type DUSCC;
extern const device_type DUSCC_CHANNEL;
extern const device_type DUSCC26562;
extern const device_type DUSCC26C562;
extern const device_type DUSCC68562;
extern const device_type DUSCC68C562;

class duscc26562_device : public duscc_device
{
public :
	duscc26562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

class duscc26C562_device : public duscc_device
{
public :
	duscc26C562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

class duscc68562_device : public duscc_device
{
public :
	duscc68562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

class duscc68C562_device : public duscc_device
{
public :
	duscc68C562_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
};

#endif // __SCNXX562_H__