summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/vr0uart.cpp
blob: eaf62aa107306503d8c39e79d7f5c4bfaf092701 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// license:BSD-3-Clause
// copyright-holders:Angelo Salese
/***************************************************************************

    MagicEyes VRender0 UART sub-device

    Device by Angelo Salese

    TODO:
    - The only current example (Trivia R Us touchscreen) expects to read
      stuff before transmitting anything, except for loopback test and a
      signal break enabling at POST (!?).

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

#include "emu.h"
#include "vrender0.h"

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(VRENDER0_UART, vr0uart_device, "vr0uart", "MagicEyes VRender0 UART")


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

//-------------------------------------------------
//  vr0uart_device - constructor
//-------------------------------------------------

vr0uart_device::vr0uart_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, VRENDER0_UART, tag, owner, clock),
	  device_serial_interface(mconfig, *this)
{
}

void vr0uart_device::regs_map(address_map &map)
{
	map(0x00, 0x03).rw(FUNC(vr0uart_device::control_r), FUNC(vr0uart_device::control_w));
	map(0x04, 0x07).r(FUNC(vr0uart_device::status_r));
	map(0x08, 0x0b).w(FUNC(vr0uart_device::transmit_buffer_w));
	map(0x0c, 0x0f).r(FUNC(vr0uart_device::receive_buffer_r));
	map(0x10, 0x13).rw(FUNC(vr0uart_device::baud_rate_div_r), FUNC(vr0uart_device::baud_rate_div_w));
}

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

void vr0uart_device::device_start()
{
	save_item(NAME(m_ucon));
	save_item(NAME(m_ubdr));
	save_item(NAME(m_ustat));
}

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

void vr0uart_device::device_reset()
{
	m_ucon = 0x001;
	m_ubdr = 1;
	m_urxb_fifo.clear();

	update_serial_config();
}

inline void vr0uart_device::tx_send_byte(uint8_t val)
{
	transmit_register_setup(val);
	m_ustat |= 0x20;
}

inline uint32_t vr0uart_device::calculate_baud_rate()
{
	uint32_t div_rate = ((m_ubdr & 0xffff) + 1) * 16;
	// TODO: external / internal serial clock config
	return (this->clock() / 2) / div_rate;
}

void vr0uart_device::update_serial_config()
{
	const parity_t parity_modes[4] = { PARITY_NONE, PARITY_NONE, PARITY_EVEN, PARITY_ODD };

	uint8_t word_length = m_ucon & 1 ? 8 : 7;
	parity_t parity_mode = parity_modes[(m_ucon & 0xc) >> 2];
	stop_bits_t stop_bits = m_ucon & 2 ? STOP_BITS_2 : STOP_BITS_1;

	set_data_frame(1, word_length, parity_mode, stop_bits);

	if (m_ucon & 0x100) // UART Enable
	{
		uint32_t clock_rate = calculate_baud_rate();
		set_rcv_rate(clock_rate);
		set_tra_rate(clock_rate);
	}
	else
	{
		set_rcv_rate(0);
		set_tra_rate(0);
	}
}

void vr0uart_device::tra_callback()
{
	int bit = transmit_register_get_data_bit();
	m_ustat |= 0x40;
	m_parent->write_line_tx(m_channel_num, bit);
}

void vr0uart_device::tra_complete()
{
	m_ustat &= ~0x60;
	m_parent->IntReq(m_channel_num ? 18 : 15);
}

void vr0uart_device::rcv_complete()
{
	receive_register_extract();
	if (is_receive_parity_error())
		m_ustat |= 2;
	if (is_receive_framing_error())
		m_ustat |= 4;

	if (!m_urxb_fifo.full())
	{
		// TODO: break detection
		m_urxb_fifo.enqueue(get_received_char());
	}
	else
		m_ustat |= 1; // overrun

	if (m_ucon & 0x20 && m_ustat & 0xf)
		m_parent->IntReq(m_channel_num ? 16 : 13);
	else
		m_parent->IntReq(m_channel_num ? 17 : 14);
}


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

/*
 * ---x ---- ---- UART enable
 * ---- x--- ---- Loopback Test
 * ---- -x-- ---- Send Break mode
 * ---- --x- ---- Generate interrupt on break or error
 * ---- ---x ---- Serial Clock selection (1=external)
 * ---- ---- xx-- Parity Mode (0x=No Parity, 10=Even, 11=Odd)
 * ---- ---- --x- Stop Bits (1=2 bits, 0=1 Bit)
 * ---- ---- ---x Word Length (1=8 bits, 0=7 bits)
 */
READ32_MEMBER( vr0uart_device::control_r )
{
	return m_ucon;
}

WRITE32_MEMBER( vr0uart_device::control_w )
{
	COMBINE_DATA(&m_ucon);
	update_serial_config();
}

/*
 * xxxx ---- ---- Receive FIFO count
 * ---- -x-- ---- Tx buffer not empty, Tx holding data
 * ---- --x- ---- Tx buffer not empty
 * ---- ---x ---- Rx buffer not empty
 * ---- ---- x--- Break detect
 * ---- ---- -x-- Frame error
 * ---- ---- --x- Parity error
 * ---- ---- ---x Overrun Error
 */
READ32_MEMBER( vr0uart_device::status_r )
{
	uint32_t res = m_ustat;
	if (!m_urxb_fifo.empty())
	{
		res |= 0x10;
		res |= (m_urxb_fifo.queue_length() << 8);
	}
	// Break detect and errors are cleared by reading this
	m_ustat &= ~0xf;
	return res;
}

WRITE32_MEMBER( vr0uart_device::transmit_buffer_w )
{
	if (ACCESSING_BITS_0_7)
		tx_send_byte(data & 0xff);
}

READ32_MEMBER( vr0uart_device::receive_buffer_r )
{
	// TODO: unknown value & behaviour attempting to read this on empty FIFO (stall?)
	uint8_t res = 0;

	if (ACCESSING_BITS_0_7 && !m_urxb_fifo.empty())
		res = m_urxb_fifo.dequeue();

	return res;
}

READ32_MEMBER( vr0uart_device::baud_rate_div_r )
{
	return m_ubdr;
}

WRITE32_MEMBER( vr0uart_device::baud_rate_div_w )
{
	COMBINE_DATA(&m_ubdr);
	update_serial_config();
}