// license:BSD-3-Clause
// copyright-holders:Sandro Ronco
/**********************************************************************

    NEC uPD65031 'BLINK' emulation

    the uPD65031 manages almost everything in the Z88:
    - memory bankswitch
    - interrupts
    - RTC
    - LCD
    - keyboard
    - serial
    - speaker

    TODO:
    - coma and snooze mode
    - UART Loopback mode

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


#include "emu.h"
#include "upd65031.h"

#define VERBOSE 0
#include "logmacro.h"


// device type definition
DEFINE_DEVICE_TYPE(UPD65031, upd65031_device, "upd65031", "NEC uPD65031")


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

namespace {

static constexpr uint32_t SPEAKER_ALARM_FREQ = 3200;

// internal registers
enum
{
	// write registers
	REG_PB0  = 0x70,        // pixel base 0
	REG_PB1  = 0x71,        // pixel base 1
	REG_PB2  = 0x72,        // pixel base 2
	REG_PB3  = 0x73,        // pixel base 3
	REG_SBR  = 0x74,        // screen base register

	REG_COM  = 0xb0,        // command register
	REG_INT  = 0xb1,        // interrupt control
	REG_EPR  = 0xb3,        // EPROM programming
	REG_TACK = 0xb4,        // RTC acknowledge
	REG_TMK  = 0xb5,        // RTC interrupt mask
	REG_ACK  = 0xb6,        // interrupt acknowledge

	REG_SR0  = 0xd0,        // segment register 0
	REG_SR1  = 0xd1,        // segment register 1
	REG_SR2  = 0xd2,        // segment register 2
	REG_SR3  = 0xd3,        // segment register 3

	REG_RXC  = 0xe2,        // UART receiver control
	REG_TXD  = 0xe3,        // UART transmit data
	REG_TXC  = 0xe4,        // UART transmit control
	REG_UMK  = 0xe5,        // UART interrupt mask
	REG_UAK  = 0xe6,        // UART interrupt acknowledge


	// read registers
	REG_STA  = 0xb1,        // interrupt status
	REG_KBD  = 0xb2,        // keyboard read
	REG_TSTA = 0xb5,        // RTC interrupt status

	REG_TIM0 = 0xd0,        // RTC 5ms counter
	REG_TIM1 = 0xd1,        // RTC seconds counter (6 bits)
	REG_TIM2 = 0xd2,        // RTC minutes counter
	REG_TIM3 = 0xd3,        // RTC minutes/256 counter
	REG_TIM4 = 0xd4,        // RTC minutes/65536 counter (5 bits)

	REG_RXD  = 0xe0,        // UART receive data register
	REG_RXE  = 0xe1,        // UART extended receiver data
	REG_UIT  = 0xe5         // UART interrupt status
};

//mode
enum
{
	STATE_AWAKE = 0,
	STATE_SNOOZE,
	STATE_COMA
};

// interrupt status
static constexpr uint8_t STA_FLAPOPEN = 0x80;   // Flap status
static constexpr uint8_t STA_A19      = 0x40;   // High level on A19 occurred during Coma
static constexpr uint8_t STA_FLAP     = 0x20;   // Flap interrupt
static constexpr uint8_t STA_UART     = 0x10;   // UART interrupt
static constexpr uint8_t STA_BTL      = 0x08;   // Battery low interrupt
static constexpr uint8_t STA_KEY      = 0x04;   // Keyboard interrupt
static constexpr uint8_t STA_TIME     = 0x01;   // RTC interrupt

// interrupt control
static constexpr uint8_t INT_KWAIT    = 0x80;   // Reading the keyboard will Snooze
static constexpr uint8_t INT_A19      = 0x40;   // A19 high will exit Coma mode
static constexpr uint8_t INT_FLAP     = 0x20;   // Enable Flap open interrupt
static constexpr uint8_t INT_UART     = 0x10;   // Enable UART interrupt
static constexpr uint8_t INT_BTL      = 0x08;   // Enable Battery low interrupt
static constexpr uint8_t INT_KEY      = 0x04;   // Enable Keyboard interrupt
static constexpr uint8_t INT_TIME     = 0x02;   // Enable RTC interrupt
static constexpr uint8_t INT_GINT     = 0x01;   // Global interrupts mask

// acknowledge interrupts
static constexpr uint8_t ACK_A19     = 0x40;   // Acknowledge A19 interrupt
static constexpr uint8_t ACK_FLAP    = 0x20;   // Acknowledge Flap interrupt
static constexpr uint8_t ACK_BTL     = 0x08;   // Acknowledge battery low interrupt
static constexpr uint8_t ACK_KEY     = 0x04;   // Acknowledge keyboard interrupt

// command register
static constexpr uint8_t COM_SRUN     = 0x80;   // Speaker source (0: manual, 1: auto)
static constexpr uint8_t COM_SBIT     = 0x40;   // Speaker source for SRUN=1 (0: 3200Hz, 1: TxD)
static constexpr uint8_t COM_OVERP    = 0x20;   // Overprogram EPROMs
static constexpr uint8_t COM_RESTIM   = 0x10;   // RTC reset
static constexpr uint8_t COM_PROGRAM  = 0x08;   // EPROM programming
static constexpr uint8_t COM_RAMS     = 0x04;   // Enable boot ROM bank
static constexpr uint8_t COM_VPPON    = 0x02;   // Programming voltage ON
static constexpr uint8_t COM_LCDON    = 0x01;   // LCD ON

// EPROM programming register
static constexpr uint8_t EPR_PD       = 0xc0;   // Two bits representing the length of delay period
static constexpr uint8_t EPR_PGMD     = 0x20;   // State of program pulse during delay period
static constexpr uint8_t EPR_EOED     = 0x10;   // State of EOE during delay period
static constexpr uint8_t EPR_SE3D     = 0x08;   // State of slot 3 select during delay period
static constexpr uint8_t EPR_PGMP     = 0x04;   // State of program pulse during porch period
static constexpr uint8_t EPR_EOEP     = 0x02;   // State of EOE during porch period
static constexpr uint8_t EPR_SE3P     = 0x01;   // State of slot 3 select during porch period

// RTC interrupt status
static constexpr uint8_t TSTA_MIN     = 0x04;   // Minute interrupt has occurred
static constexpr uint8_t TSTA_SEC     = 0x02;   // Second interrupt has occurred
static constexpr uint8_t TSTA_TICK    = 0x01;   // Tick interrupt has occurred

// UART extended receive data
static constexpr uint8_t RXE_FE       = 0x20;   // Frame error
static constexpr uint8_t RXE_RXDB     = 0x10;   // RXD line state
static constexpr uint8_t RXE_TCLK     = 0x08;   // Transmit clock
static constexpr uint8_t RXE_RCLK     = 0x04;   // Receive clock
static constexpr uint8_t RXE_PAR      = 0x02;   // Parity bit
static constexpr uint8_t RXE_START    = 0x01;   // Start bit (should be zero)

// UART receive control
static constexpr uint8_t RXC_SHTW     = 0x80;   // Short word mode
static constexpr uint8_t RXC_LOOP     = 0x40;   // Loopback mode
static constexpr uint8_t RXC_UART     = 0x20;   // Reset
static constexpr uint8_t RXC_ARTS     = 0x10;   // Auto RTS mode
static constexpr uint8_t RXC_IRTS     = 0x08;   // Invert RTS
static constexpr uint8_t RXC_BAUD     = 0x07;   // Baud rate

// UART transmit control
static constexpr uint8_t TXC_UTEST    = 0x80;   // Fast baud rate
static constexpr uint8_t TXC_IDCD     = 0x40;   // DCD interrupt when low (0 for when high)
static constexpr uint8_t TXC_ICTS     = 0x20;   // CTD interrupt when low (0 for when high)
static constexpr uint8_t TXC_ATX      = 0x10;   // Auto transmit mode
static constexpr uint8_t TXC_ITX      = 0x08;   // Invert Tx
static constexpr uint8_t TXC_BAUD     = 0x07;   // Baud rate

// UART interrupt status
static constexpr uint8_t UIT_RSRD     = 0x80;   // Receive shift register full
static constexpr uint8_t UIT_DCDI     = 0x40;   // DCD interrupt
static constexpr uint8_t UIT_CTSI     = 0x20;   // CTS interrupt
static constexpr uint8_t UIT_TDRE     = 0x10;   // Transmit register empty
static constexpr uint8_t UIT_RDRF     = 0x04;   // Receive register full
static constexpr uint8_t UIT_DCD      = 0x02;   // Inverse of the DCD line level
static constexpr uint8_t UIT_CTS      = 0x01;   // Inverse of the CTS line level

// UART interrupt mask
static constexpr uint8_t UMK_DCD      = 0x40;   // DCD interrupts are enabled
static constexpr uint8_t UMK_CTS      = 0x20;   // CTS interrupts are enabled
static constexpr uint8_t UMK_TDRE     = 0x10;   // Transmit data register empty interrupt enabled
static constexpr uint8_t UMK_RDRF     = 0x04;   // Receive data register full interrupt enabled

// UART interrupt acknowledge register
static constexpr uint8_t UAK_DCD      = 0x40;   // Acknowledge DCD interrupt
static constexpr uint8_t UAK_CTS      = 0x20;   // Acknowledge CTS interrupt

} // anonymous namespace

//**************************************************************************
//  INLINE HELPERS
//**************************************************************************

inline void upd65031_device::interrupt_refresh()
{
	if ((m_int & INT_GINT) && ((m_int & m_sta & 0x7c) || ((m_int & INT_TIME) && (m_sta & STA_TIME))))
	{
		LOG("%s: set int\n", machine().describe_context());

		m_write_int(ASSERT_LINE);
	}
	else
	{
		LOG("%s: clear int\n", machine().describe_context());

		m_write_int(CLEAR_LINE);
	}
}


inline void upd65031_device::update_rtc_interrupt()
{
	// any ints occurred?
	if ((m_int & INT_GINT) && (m_int & INT_TIME) && (m_tsta & (TSTA_MIN | TSTA_SEC | TSTA_TICK)))
		m_sta |= STA_TIME;
	else
		m_sta &= ~STA_TIME;
}

inline void upd65031_device::update_uart_interrupt()
{
	if ((m_int & INT_UART) && (m_uit & m_umk))
		m_sta |= STA_UART;
	else
		m_sta &= ~STA_UART;

	interrupt_refresh();
}

inline void upd65031_device::update_tx(int state)
{
	m_txd_line = state;
	m_write_txd(m_txd_line);

	if ((m_com & COM_SRUN) && (m_com & COM_SBIT))
		m_write_spkr(m_txd_line);
}

inline void upd65031_device::set_mode(int mode)
{
	if (m_mode != mode)
	{
		m_mode = mode;

		switch(mode)
		{
		case STATE_AWAKE:
			//TODO
			break;
		case STATE_SNOOZE:
			//TODO
			break;
		case STATE_COMA:
			//TODO
			break;
		}
	}
}


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  upd65031_device - constructor
//-------------------------------------------------

upd65031_device::upd65031_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, UPD65031, tag, owner, clock),
	device_serial_interface(mconfig, *this),
	m_read_kb(*this),
	m_write_int(*this),
	m_write_nmi(*this),
	m_write_spkr(*this),
	m_write_txd(*this),
	m_write_rts(*this),
	m_write_dtr(*this),
	m_write_vpp(*this),
	m_screen_update_cb(*this),
	m_out_mem_cb(*this),
	m_sta(0),
	m_int(0)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void upd65031_device::device_start()
{
	// resolve callbacks
	m_read_kb.resolve_safe(0);
	m_write_int.resolve_safe();
	m_write_nmi.resolve_safe();
	m_write_spkr.resolve_safe();
	m_write_txd.resolve_safe();
	m_write_rts.resolve_safe();
	m_write_dtr.resolve_safe();
	m_write_vpp.resolve_safe();

	// bind delegates
	m_screen_update_cb.resolve();
	m_out_mem_cb.resolve();

	// allocate timers
	m_rtc_timer = timer_alloc(FUNC(upd65031_device::rtc_tick), this);
	m_flash_timer = timer_alloc(FUNC(upd65031_device::flash_tick), this);
	m_speaker_timer = timer_alloc(FUNC(upd65031_device::speaker_tick), this);
	m_rtc_timer->adjust(attotime::from_msec(5), 0, attotime::from_msec(5));
	m_flash_timer->adjust(attotime::from_hz(2), 0, attotime::from_hz(2));
	m_speaker_timer->reset();

	// state saving
	save_item(NAME(m_mode));
	save_item(NAME(m_lcd_regs));
	save_item(NAME(m_tim));
	save_item(NAME(m_sr));
	save_item(NAME(m_sta));
	save_item(NAME(m_int));
	save_item(NAME(m_ack));
	save_item(NAME(m_tsta));
	save_item(NAME(m_tmk));
	save_item(NAME(m_tack));
	save_item(NAME(m_com));
	save_item(NAME(m_uit));
	save_item(NAME(m_umk));
	save_item(NAME(m_txc));
	save_item(NAME(m_rxe));
	save_item(NAME(m_rxc));
	save_item(NAME(m_txd_line));
	save_item(NAME(m_flash));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void upd65031_device::device_reset()
{
	memset(m_lcd_regs, 0, sizeof(m_lcd_regs));
	memset(m_tim, 0, sizeof(m_tim));
	memset(m_sr, 0, sizeof(m_sr));
	m_sta = 0;
	m_int = 0;
	m_ack = 0;
	m_tsta = 0;
	m_tmk = TSTA_TICK | TSTA_SEC | TSTA_MIN;
	m_tack = 0;
	m_com = 0;
	m_flash = 0;
	m_mode = 0;
	m_uit = UIT_TDRE;   // Transmit register empty
	m_umk = 0x00;
	m_rxe = 0x00;
	m_rxc = RXC_SHTW | 0x05;            // 9600 baud, 1 Stop Bit
	m_txc = TXC_IDCD | TXC_ICTS | 0x05; // 9600 baud
	m_txd_line = 0;
	set_mode(STATE_AWAKE);

	if (!m_out_mem_cb.isnull())
	{
		// reset bankswitch
		m_out_mem_cb(0, 0, 0);
		m_out_mem_cb(1, 0, 0);
		m_out_mem_cb(2, 0, 0);
		m_out_mem_cb(3, 0, 0);
	}

	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
	set_rate(9600);
	transmit_register_reset();
	receive_register_reset();
	m_write_rts(1);
	m_write_dtr(1);
	m_write_vpp(0);
}


//-------------------------------------------------
//  timer events
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(upd65031_device::rtc_tick)
{
	// if a key is pressed sets the interrupt
	if ((m_int & INT_GINT) && (m_int & INT_KEY) && m_read_kb(0) != 0xff)
	{
		LOG("%s: Keyboard interrupt!\n", machine().describe_context());

		// awakes CPU from snooze on key down
		if (m_mode == STATE_SNOOZE)
			set_mode(STATE_AWAKE);

		m_sta |= STA_KEY;
	}
	else
	{
		m_sta &= ~STA_KEY;
	}

	// hold clock at reset? - in this mode it doesn't update
	if (!(m_com & COM_RESTIM))
	{
		bool irq_change = false;

		// update 5 millisecond counter
		m_tim[0]++;

		// tick
		if (m_tim[0] & 1)
		{
			// set tick int has occurred
			if (m_tmk & TSTA_TICK)
			{
				m_tsta |= TSTA_TICK;
				irq_change = true;
			}
		}

		if (m_tim[0] == 200)
		{
			m_tim[0] = 0;
			m_tim[1]++;

			if (m_tim[1] == 32) // on the rising edge of TIM1 bit 5
			{
				// set minutes int has occurred
				if (m_tmk & TSTA_MIN)
				{
					m_tsta |= TSTA_MIN;
					irq_change = true;
				}
			}

			if (m_tim[1] == 60)
			{
				m_tim[1] = 0;
				m_tim[2]++;

				if (m_tim[2] == 0) // overflowed from 255
				{
					m_tim[3]++;

					if (m_tim[3] == 0) // overflowed from 255
					{
						m_tim[4]++;

						if (m_tim[4] == 32)
							m_tim[4] = 0;
					}
				}
			}
		}

		if ((m_int & INT_GINT) && (m_int & INT_TIME) && irq_change && !(m_sta & STA_FLAPOPEN))
		{
			set_mode(STATE_AWAKE);

			update_rtc_interrupt();
		}

		// refresh interrupt
		interrupt_refresh();
	}
}

TIMER_CALLBACK_MEMBER(upd65031_device::flash_tick)
{
	m_flash = !m_flash;
}

TIMER_CALLBACK_MEMBER(upd65031_device::speaker_tick)
{
	m_speaker_state = !m_speaker_state;
	m_write_spkr(m_speaker_state ? 1 : 0);
}


//-------------------------------------------------
//  screen_update
//-------------------------------------------------

uint32_t upd65031_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	if (!m_screen_update_cb.isnull() && (m_com & COM_LCDON))
		m_screen_update_cb(bitmap, m_lcd_regs[4], m_lcd_regs[2], m_lcd_regs[3], m_lcd_regs[0], m_lcd_regs[1], m_flash);
	else
		bitmap.fill(0, cliprect);

	return 0;
}

//-------------------------------------------------
//  read -
//-------------------------------------------------

uint8_t upd65031_device::read(offs_t offset)
{
	uint8_t port = offset & 0xff;

	switch (port)
	{
		case REG_STA:   // read interrupt status
			return m_sta;

		case REG_KBD:
		{
			// if set, reading the keyboard will put into snooze
			if (m_int & INT_KWAIT)
			{
				set_mode(STATE_SNOOZE);

				LOG("%s: entering snooze!\n", machine().describe_context());
			}

			uint8_t data = m_read_kb(offset>>8);

			LOG("%s: key r %02x %02x\n", machine().describe_context(), offset>>8, data);

			return data;
		}

		// read real time clock status
		case REG_TSTA:
			LOG("%s: tsta r %02x\n", machine().describe_context(), m_tsta);
			return m_tsta & 0x07;

		// read real time clock counters
		case REG_TIM0:
			LOG("%s: TIM0 r %02x\n", machine().describe_context(), m_tim[0]);
			return m_tim[0];
		case REG_TIM1:
			LOG("%s: TIM1 r %02x\n", machine().describe_context(), m_tim[1]);
			return m_tim[1];
		case REG_TIM2:
			LOG("%s: TIM2 r %02x\n", machine().describe_context(), m_tim[2]);
			return m_tim[2];
		case REG_TIM3:
			LOG("%s: TIM3 r %02x\n", machine().describe_context(), m_tim[3]);
			return m_tim[3];
		case REG_TIM4:
			LOG("%s: TIM4 r %02x\n", machine().describe_context(), m_tim[4]);
			return m_tim[4];

		// UART
		case REG_RXD:   // UART receive data register
			m_uit &= ~UIT_RDRF;
			update_uart_interrupt();
			if (m_rxc & RXC_ARTS)  // Auto RTS mode
				m_write_rts(1);
			return get_received_char();

		case REG_RXE:   // UART extended receive data
			return m_rxe;

		case REG_UIT:   // UART interrupt status
			return m_uit;

		default:
			logerror("%s: blink r %04x\n", machine().describe_context(), offset);
			return 0;
	}
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

void upd65031_device::write(offs_t offset, uint8_t data)
{
	static const int uart_div[] = { 1 << 17, 1 << 15, 1 << 14, 1 << 13, 1 << 12, 1 << 10, 1 << 9, 1 << 8 };
	uint8_t port = offset & 0xff;

	switch (port)
	{
		// gfx registers
		case REG_PB0:
		case REG_PB1:
		case REG_PB2:
		case REG_PB3:
		case REG_SBR:
			m_lcd_regs[port - REG_PB0] = ((offset & 0xff00) | data);
			break;

		case REG_COM:   // command register
			LOG("%s: com w %02x\n", machine().describe_context(), data);

			// reset clock?
			if (data & COM_RESTIM)
				m_tim[0] = m_tim[1] = m_tim[2] = m_tim[3] = m_tim[4] = 0;

			if ((data & COM_SRUN) && !(data & COM_SBIT))
			{
				// constant tone used for keyclick and alarm
				m_speaker_timer->adjust(attotime::from_hz(SPEAKER_ALARM_FREQ), 0, attotime::from_hz(SPEAKER_ALARM_FREQ));
			}
			else
			{
				if (!(data & COM_SRUN))
				{
					// speaker controlled by SBIT
					m_speaker_state = BIT(data, 6);
					m_write_spkr(m_speaker_state);
				}
				else
				{
					// speaker controlled by txd line
					m_write_spkr(m_txd_line);
				}

				m_speaker_timer->reset();
			}

			// bit 2 controls the lower 8kb of memory
			if (BIT(m_com^data, 2) && !m_out_mem_cb.isnull())
				m_out_mem_cb(0, m_sr[0], BIT(data, 2));

			m_write_vpp(BIT(data, 1));

			m_com = data;
			break;

		case REG_INT:   // interrupt control
			LOG("%s: int w %02x\n", machine().describe_context(), data);

			m_int = data;

			// refresh ints
			update_rtc_interrupt();
			interrupt_refresh();
			break;

		case REG_EPR:   // EPROM programming register
			LOG("%s: epr w %02x\n", machine().describe_context(), data);
			break;

		case REG_TACK:  // rtc interrupt acknowledge
			LOG("%s: tack w %02x\n", machine().describe_context(), data);

			// clear ints that have occurred
			m_tsta &= ~(data & 0x07);
			m_tack = data;

			// refresh ints
			update_rtc_interrupt();
			interrupt_refresh();
			break;

		case REG_TMK:   // write rtc interrupt mask
			LOG("%s: tmk w %02x\n", machine().describe_context(), data);

			m_tmk = data & 0x07;
			break;

		case REG_ACK:   // acknowledge ints
			LOG("%s: ack w %02x\n", machine().describe_context(), data);

			m_ack = data;
			m_sta &= ~(data & 0x7f);

			// refresh ints
			interrupt_refresh();
			break;

		// Segment registers
		case REG_SR0:
		case REG_SR1:
		case REG_SR2:
		case REG_SR3:
			if (!m_out_mem_cb.isnull() && m_sr[port & 3] != data)
				m_out_mem_cb(port & 3, data, BIT(m_com, 2));

			m_sr[port & 3] = data;
			break;

		// UART
		case REG_RXC:   // UART receive control
			LOG("%s: UART receive control %02x\n", machine().describe_context(), data);

			if ((m_rxc & RXC_BAUD) != (data & RXC_BAUD))
				set_rcv_rate(clock() / uart_div[data & RXC_BAUD]);

			if ((m_rxc ^ data) & RXC_SHTW)
				set_data_frame(1, 8, PARITY_NONE, (data & RXC_SHTW) ? STOP_BITS_1 : STOP_BITS_2);

			if (data & RXC_LOOP)
				logerror("%s: Unsupported UART Loopback mode\n", machine().describe_context());

			if (!(data & RXC_ARTS))
				m_write_rts((data & RXC_IRTS) ? 0 : 1);

			m_rxc = data;
			break;

		case REG_TXD:   // UART transmit data
			transmit_register_setup(data);
			m_uit &= ~UIT_TDRE;
			update_uart_interrupt();
			break;

		case REG_TXC:   // UART transmit control
			LOG("%s: UART transmit control %02x\n", machine().describe_context(), data);

			if ((m_txc & TXC_BAUD) != (data & TXC_BAUD))
				set_tra_rate(clock() / uart_div[data & TXC_BAUD]);

			if (!(data & TXC_ATX) && ((m_txc ^ data) & TXC_ITX))
				update_tx((data & TXC_ITX) ? 0 : 1);

			m_txc = data;
			break;

		case REG_UMK:   // UART interrupt mask
			LOG("%s: UART interrupt mask %02x\n", machine().describe_context(), data);

			m_umk = data;
			update_uart_interrupt();
			break;

		case REG_UAK:   // UART interrupt acknowledge
			LOG("%s: UART interrupt acknowledge %02x\n", machine().describe_context(), data);

			m_uit &= ~(data & m_umk & (UAK_CTS | UAK_DCD));
			update_uart_interrupt();
			break;

		default:
			logerror("%s: blink w %04x = %02x\n", machine().describe_context(), offset, data);
			break;
	}
}

void upd65031_device::tra_callback()
{
	update_tx(transmit_register_get_data_bit() ^ BIT(m_txc, 3));
}

void upd65031_device::tra_complete()
{
	m_uit |= UIT_TDRE;
	update_uart_interrupt();
}

void upd65031_device::rcv_complete()
{
	receive_register_extract();

	m_uit |= UIT_RDRF;

	if (m_rxc & RXC_ARTS)  // Auto RTS mode
		m_write_rts(0);

	// Frame error
	if (is_receive_framing_error())
		m_rxe |= RXE_FE;
	else
		m_rxe &= ~RXE_FE;

	update_uart_interrupt();
}

WRITE_LINE_MEMBER( upd65031_device::cts_w )
{
	if (state == BIT(m_uit, 0))
	{
		m_uit = (m_uit & ~UIT_CTS) | (state ? 0 : UIT_CTS);
		if (state != BIT(m_txc, 5))
		{
			m_uit |= UIT_CTSI;
			update_uart_interrupt();
		}
	}
}

WRITE_LINE_MEMBER( upd65031_device::dcd_w )
{
	if (state == BIT(m_uit, 1))
	{
		m_uit = (m_uit & ~UIT_DCD) | (state ? 0 : UIT_DCD);
		if (state != BIT(m_txc, 6))
		{
			m_uit |= UIT_DCDI;
			update_uart_interrupt();
		}
	}
}

//-------------------------------------------------
//  flp line
//-------------------------------------------------

WRITE_LINE_MEMBER( upd65031_device::flp_w )
{
	if (!(m_sta & STA_FLAPOPEN) && state)
	{
		// set interrupt on rising edge
		m_sta |= STA_FLAP;

		interrupt_refresh();
	}

	if (state)
		m_sta |= STA_FLAPOPEN;
	else
		m_sta &= ~STA_FLAPOPEN;
}

//-------------------------------------------------
//  battery low line
//-------------------------------------------------

WRITE_LINE_MEMBER( upd65031_device::btl_w )
{
	if (state)
		m_sta |= STA_BTL;
	else
		m_sta &= ~STA_BTL;
}
