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
|
// license:BSD-3-Clause
// copyright-holders: F. Ulivi
/*********************************************************************
rs232_sync_io.cpp
Synchronous I/O on RS232 port
This device provides a bitbanger-based interface to a synchronous
RS232 port.
It has the following functions:
- Provides Tx and/or Rx clock to RS232 port; clocks can be
individually turned off when not needed (e.g. Tx clock can
disabled when receiving in half-duplex mode)
- In the tx direction, it gathers bits from TxD,
packs them into bytes LSB to MSB, then sends them out to
bitbanger
- In the rx direction, it extracts bytes from bitbanger
and serializes them to RxD, LSB to MSB.
- Supports both full-duplex and half-duplex modes
- Generates CTS signal
There's no kind of synchronization to any higher-level framing:
bits are just clocked in/out of the port, packed/unpacked into
bytes and exchanged with bitbanger.
There's also no automatic stuffing of idle line characters, this
function is to be implemented externally if needed.
*********************************************************************/
#include "emu.h"
#include "rs232_sync_io.h"
// Debugging
#include "logmacro.h"
//#undef VERBOSE
//#define VERBOSE LOG_GENERAL
// Bit manipulation
namespace {
template<typename T> constexpr T BIT_MASK(unsigned n)
{
return (T)1U << n;
}
template<typename T> void BIT_CLR(T& w , unsigned n)
{
w &= ~BIT_MASK<T>(n);
}
template<typename T> void BIT_SET(T& w , unsigned n)
{
w |= BIT_MASK<T>(n);
}
}
rs232_sync_io_device::rs232_sync_io_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock)
: device_t(mconfig , RS232_SYNC_IO , tag , owner , clock)
, device_rs232_port_interface(mconfig , *this)
, m_stream(*this , "stream")
, m_rs232_baud(*this , "RS232_BAUD")
, m_rts_duplex(*this , "rtsduplex")
, m_txc_setting(*this , "txcontrol")
, m_rxc_setting(*this , "rxcontrol")
{
}
rs232_sync_io_device:: ~rs232_sync_io_device()
{
}
WRITE_LINE_MEMBER(rs232_sync_io_device::input_txd)
{
m_txd = state;
LOG("TxD %d %u\n" , state , m_tx_counter);
}
WRITE_LINE_MEMBER(rs232_sync_io_device::input_rts)
{
m_rts = state;
}
static INPUT_PORTS_START(rs232_sync_io)
PORT_RS232_BAUD("RS232_BAUD" , RS232_BAUD_2400 , "Baud rate" , rs232_sync_io_device , update_serial)
PORT_START("rtsduplex")
PORT_CONFNAME(1 , 1 , "RTS controls half-duplex")
PORT_CONFSETTING(0 , DEF_STR(Off))
PORT_CONFSETTING(1 , DEF_STR(On))
PORT_START("txcontrol")
PORT_CONFNAME(1 , 1 , "TxC generated")
PORT_CONFSETTING(0 , "Always")
PORT_CONFSETTING(1 , "When Tx is active")
PORT_START("rxcontrol")
PORT_CONFNAME(3 , 1 , "RxC generated")
PORT_CONFSETTING(0 , "Always")
PORT_CONFSETTING(1 , "When Rx is active")
PORT_CONFSETTING(2 , "When a byte is available")
INPUT_PORTS_END
ioport_constructor rs232_sync_io_device::device_input_ports() const
{
return INPUT_PORTS_NAME(rs232_sync_io);
}
void rs232_sync_io_device::device_add_mconfig(machine_config &config)
{
BITBANGER(config , m_stream , 0);
}
void rs232_sync_io_device::device_start()
{
m_clk_timer = timer_alloc(FUNC(rs232_sync_io_device::clock_tick), this);
}
void rs232_sync_io_device::device_reset()
{
output_dcd(0);
output_dsr(0);
output_rxd(1);
output_cts(1);
update_serial(0);
m_clk = false;
m_rx_byte = 0xff;
m_rx_counter = 0;
m_tx_counter = 0;
m_tx_enabled = false;
m_rx_enabled = false;
output_txc(1);
output_rxc(1);
}
TIMER_CALLBACK_MEMBER(rs232_sync_io_device::clock_tick)
{
m_clk = !m_clk;
if (m_clk) {
output_txc(1);
output_rxc(1);
if (m_tx_enabled) {
// Rising edge: capture TxD
m_tx_byte = (m_tx_byte >> 1) & 0x7f;
if (m_txd) {
BIT_SET(m_tx_byte , 7);
}
if (++m_tx_counter >= 8) {
LOG("Tx %02x @%s\n" , m_tx_byte , machine().time().to_string());
m_tx_counter = 0;
m_stream->output(m_tx_byte);
m_tx_enabled = false;
}
}
} else {
if (!m_tx_enabled) {
m_tx_enabled = !m_rts || !m_rts_duplex->read();
output_cts(!m_tx_enabled);
}
if (m_tx_enabled || m_txc_setting->read() == 0) {
output_txc(0);
}
if (!m_rx_enabled) {
m_rx_enabled = m_rts || !m_rts_duplex->read();
if (m_rx_enabled) {
if (m_stream->input(&m_rx_byte , 1) == 0) {
m_rx_byte = ~0;
if (m_rxc_setting->read() == 2) {
m_rx_enabled = false;
}
} else {
LOG("Rx %02x @%s\n" , m_rx_byte , machine().time().to_string());
}
}
}
if (m_rx_enabled || m_rxc_setting->read() == 0) {
output_rxc(0);
}
if (m_rx_enabled) {
// Falling edge: update RxD
output_rxd(BIT(m_rx_byte , 0));
m_rx_byte >>= 1;
if (++m_rx_counter >= 8) {
m_rx_counter = 0;
m_rx_enabled = false;
}
}
}
}
WRITE_LINE_MEMBER(rs232_sync_io_device::update_serial)
{
auto baud_rate = convert_baud(m_rs232_baud->read());
auto period = attotime::from_hz(baud_rate * 2);
m_clk_timer->adjust(period , 0 , period);
}
DEFINE_DEVICE_TYPE(RS232_SYNC_IO, rs232_sync_io_device, "rs232_sync_io", "RS232 Synchronous I/O")
|