1 // license:LGPL-2.1+
2 // copyright-holders:Michael Zapf
3 /****************************************************************************
4
5 TI-99 RS232 and Parallel interface card
6
7 TI99 RS232 card ('rs232')
8 TMS9902 ('rs232:tms9902_0')
9 TMS9902 ('rs232:tms9902_1')
10 TI99 RS232 attached serial device ('rs232:serdev0')
11 TI99 RS232 attached serial device ('rs232:serdev1')
12 TI99 PIO attached parallel device ('rs232:piodev')
13
14 Currently this emulation does not directly interact with the serial
15 interface on the host computer. However, using a socket connection it is
16 possible to attach an external bridge which interacts with a real UART.
17
18 TI RS232 card wiring
19 --------------------
20 The card uses this wiring (inverters not included)
21
22 +-----+ Pins of the connector
23 | 9902| (common naming)
24 | RIN |---<------- 2 (TD)
25 | XOUT|--->------- 3 (RD)
26 | RTS |--->------- 8 (DCD)
27 | CTS |-<-+------- 20 (DTR)
28 | DSR |-<-+ H--> 6 (DSR)
29 +-----+ +-----> 5 (CTS)
30 /
31 +-----+ /
32 | CRU |--+
33 +-----+
34
35 This wiring is typical for a DCE, not a DTE. The TI RS232 was obviously
36 designed to look like a modem. The advantage is that you can use the same
37 cables for connecting a modem to the RS232 interface or for connecting
38 a second TI via its interface. To connect to a DTE you can use a 1-1
39 wiring cable (1 on 1, 2 on 2 ...)
40
41 The TI manual for the RS232 card suggests the following cables:
42
43 TI RS232 - Modem or other TI RS232
44 2 -----<----- 3
45 3 ----->----- 2
46 6 ----->---- 20 (crossover cable)
47 20 -----<----- 6
48
49 TI RS232 - Terminal (DTE)
50 2 ----<------ 2
51 3 ---->------ 3
52 5 ---->------ 5
53 6 ---->------ 6 (1-1 cable)
54 8 ---->------ 8
55 20 ----<------20
56
57 If we want to use a PC serial interface to play the role of the TI
58 interface we have to map the TI wiring to a suitable wiring for PC
59 interfaces which are designed as DTEs. This is achieved by the functions
60 map_lines_in, map_lines_out.
61
62 Note that we now have to swap the cable types: Use a 1-1 cable to connect
63 another TI or a modem on the other end, and use a crossover cable for
64 another PC (the usual way of connecting).
65
66 RS232 Over IP protocol
67 ----------------------
68 This implementation can make use of such an external bridge. Normal data
69 are forwarded to the bridge and back, while line control is organized via
70 special byte sequences. These sequences are introduced by a 0x1B byte (ESC).
71
72 The protocol has two modes: normal and escape
73
74 normal mode: transmit byte (!= 0x1b) unchanged
75 escape mode: entered by ESC, bytes following:
76 ESC = plain ESC byte
77 length byte[length] = control sequence (length != 0x1b)
78
79 byte[]:
80 All configuration settings are related to a specified UART; UARTs may
81 differ in their capabilities and may require specific settings
82 (e.g. the TMS9902 specifies the line speed by a clock ratio, while
83 others may have indexed, fixed rates or use integers)
84
85 (x=unused)
86
87 1ccc xaaa = configuration of parameter ccc; UART type aaa
88 1111 xaaa rrrr rrrr rrrr 0000 = config receive rate on aaa
89 1110 xaaa rrrr rrrr rrrr 0000 = config transmit rate on aaa
90 1101 xaaa xxxx xxbb = config databits bb (00=5 ... 11=8)
91 1100 xaaa xxxx xxss = config stop bits ss (00=1.5, 01=2, 1x=1)
92 1011 xaaa xxxx xxpp = config parity pp (1x=enable, x1=odd)
93
94 00ab cdef = line state of RTS=a, CTS=b, DSR=c, DCD=d, DTR=e, RI=f
95 01gh i000 = exception g=BRK, h=FRMERR, i=PARERR
96
97 The protocol changes back to normal mode after transmitting the control
98 sequence.
99
100 Michael Zapf
101 February 2012: Rewritten as class
102
103 *****************************************************************************/
104
105 #include "emu.h"
106 #include "ti_rs232.h"
107
108 #define LOG_WARN (1U<<1) // Warnings
109 #define LOG_CONFIG (1U<<2)
110 #define LOG_LINES (1U<<3)
111 #define LOG_SETTING (1U<<4)
112 #define LOG_STATE (1U<<5)
113 #define LOG_MAP (1U<<6)
114 #define LOG_IN (1U<<7)
115 #define LOG_OUT (1U<<8)
116 #define LOG_ILA (1U<<9)
117
118 #define VERBOSE ( LOG_CONFIG | LOG_WARN )
119 #include "logmacro.h"
120
121 DEFINE_DEVICE_TYPE_NS(TI99_RS232, bus::ti99::peb, ti_rs232_pio_device, "ti99_rs232", "TI-99 RS232/PIO interface")
122 DEFINE_DEVICE_TYPE_NS(TI99_RS232_DEV, bus::ti99::peb, ti_rs232_attached_device, "ti99_rs232_atttached", "TI-99 Serial attached device")
123 DEFINE_DEVICE_TYPE_NS(TI99_PIO_DEV, bus::ti99::peb, ti_pio_attached_device, "ti99_pio_attached", "TI-99 Parallel attached device")
124
125 namespace bus { namespace ti99 { namespace peb {
126
127 #define SENILA_0_BIT 0x80
128 #define SENILA_1_BIT 0x40
129
130 #define RECV_MODE_NORMAL 1
131 #define RECV_MODE_ESC 2
132 #define RECV_MODE_ESC_LINES 3
133
134 #define ESC 0x1b
135
136 #define UART0 "uart0"
137 #define UART1 "uart1"
138
139 #define SERDEV0 "serdev0"
140 #define SERDEV1 "serdev1"
141 #define PIODEV "piodev"
142
ti_rs232_pio_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)143 ti_rs232_pio_device::ti_rs232_pio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
144 device_t(mconfig, TI99_RS232, tag, owner, clock),
145 device_ti99_peribox_card_interface(mconfig, *this),
146 m_crulatch(*this, "crulatch"),
147 m_uart0(*this, UART0),
148 m_uart1(*this, UART1),
149 m_serdev0(*this, SERDEV0),
150 m_serdev1(*this, SERDEV1),
151 m_piodev(*this, PIODEV),
152 m_dsrrom(nullptr),
153 m_pio_direction_in(false),
154 m_pio_handshakeout(false),
155 m_pio_handshakein(false),
156 m_pio_spareout(false),
157 m_pio_sparein(false),
158 m_flag0(false),
159 m_led(false),
160 m_pio_out_buffer(0),
161 m_pio_in_buffer(0),
162 m_pio_readable(false),
163 m_pio_writable(false),
164 m_pio_write(false),
165 m_ila(0)
166 {
167 }
168
169
170 /**************************************************************************/
171 /* Ports */
172
ti_rs232_attached_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)173 ti_rs232_attached_device::ti_rs232_attached_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
174 : device_t(mconfig, TI99_RS232_DEV, tag, owner, clock),
175 device_image_interface(mconfig, *this),
176 m_uart(nullptr)
177 {
178 }
179
ti_pio_attached_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)180 ti_pio_attached_device::ti_pio_attached_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
181 : device_t(mconfig, TI99_PIO_DEV, tag, owner, clock),
182 device_image_interface(mconfig, *this)
183 {
184 }
185
186 /*
187 Initialize rs232 unit and open image
188 */
call_load()189 image_init_result ti_rs232_attached_device::call_load()
190 {
191 m_uart->set_clock(true);
192
193 // The following line may cause trouble in the init phase
194 // card->incoming_dtr(devnumber, (m_file!=nullptr)? ASSERT_LINE : CLEAR_LINE);
195
196 return image_init_result::PASS; // OK
197 }
198
call_unload()199 void ti_rs232_attached_device::call_unload()
200 {
201 m_uart->set_clock(false);
202 }
203
204 /*
205 Initialize pio unit and open image
206 */
call_load()207 image_init_result ti_pio_attached_device::call_load()
208 {
209 ti_rs232_pio_device* card = static_cast<ti_rs232_pio_device*>(owner());
210
211 // tell whether the image is readable
212 card->m_pio_readable = true;
213 // tell whether the image is writable
214 card->m_pio_writable = !is_readonly();
215
216 if (card->m_pio_write && card->m_pio_writable)
217 card->m_pio_handshakein = false; // receiver ready
218 else
219 card->m_pio_handshakein = true;
220
221 return image_init_result::PASS; // OK
222 }
223
224 /*
225 close a pio image
226 */
call_unload()227 void ti_pio_attached_device::call_unload()
228 {
229 ti_rs232_pio_device* card = static_cast<ti_rs232_pio_device*>(owner());
230
231 card->m_pio_writable = false;
232 card->m_pio_handshakein = true; /* receiver not ready */
233 card->m_pio_sparein = false;
234 }
235
236 /****************************************************************************/
237
238 /*
239 CRU read
240 */
crureadz(offs_t offset,uint8_t * value)241 void ti_rs232_pio_device::crureadz(offs_t offset, uint8_t *value)
242 {
243 if ((offset & 0xff00)==m_cru_base)
244 {
245 if ((offset & 0x00c0)==0x0000)
246 {
247 uint8_t reply = 0x00;
248 if (m_pio_direction_in) reply |= 0x02;
249 if (m_pio_handshakein) reply |= 0x04;
250 if (m_pio_sparein) reply |= 0x08;
251 if (m_flag0) reply |= 0x10;
252 // The CTS line is realized as CRU bits
253 // Mind that this line is handled as an output going to the remote CTS
254 if ((m_signals[0] & tms9902_device::CTS)!=0) reply |= 0x20;
255 if ((m_signals[1] & tms9902_device::CTS)!=0) reply |= 0x40;
256 if (m_led) reply |= 0x80;
257 *value = BIT(reply, (offset>>1) & 7);
258 return;
259 }
260 if ((offset & 0x00c0)==0x0040)
261 {
262 *value = m_uart0->cruread(offset>>1);
263 return;
264 }
265 if ((offset & 0x00c0)==0x0080)
266 {
267 *value = m_uart1->cruread(offset>>1);
268 return;
269 }
270 }
271 }
272
273 /*
274 CRU write
275 */
cruwrite(offs_t offset,uint8_t data)276 void ti_rs232_pio_device::cruwrite(offs_t offset, uint8_t data)
277 {
278 if ((offset & 0xff00)==m_cru_base)
279 {
280 if ((offset & 0x00c0)==0x0040)
281 {
282 m_uart0->cruwrite(offset>>1, data);
283 return;
284 }
285 if ((offset & 0x00c0)==0x0080)
286 {
287 m_uart1->cruwrite(offset>>1, data);
288 return;
289 }
290
291 int bit = (offset & 0x00ff)>>1;
292 m_crulatch->write_bit(bit, data);
293 }
294 }
295
WRITE_LINE_MEMBER(ti_rs232_pio_device::selected_w)296 WRITE_LINE_MEMBER(ti_rs232_pio_device::selected_w)
297 {
298 m_selected = state;
299 }
300
WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_direction_in_w)301 WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_direction_in_w)
302 {
303 m_pio_direction_in = state;
304 }
305
WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_handshake_out_w)306 WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_handshake_out_w)
307 {
308 m_pio_handshakeout = state;
309 if (m_pio_write && m_pio_writable && (!m_pio_direction_in))
310 { /* PIO in output mode */
311 if (!m_pio_handshakeout)
312 { /* write data strobe */
313 /* write data and acknowledge */
314 uint8_t buf = m_pio_out_buffer;
315 int ret = m_piodev->fwrite(&buf, 1);
316 if (ret)
317 m_pio_handshakein = 1;
318 }
319 else
320 {
321 /* end strobe */
322 /* we can write some data: set receiver ready */
323 m_pio_handshakein = 0;
324 }
325 }
326 if ((!m_pio_write) && m_pio_readable /*&& pio_direction_in*/)
327 { /* PIO in input mode */
328 if (!m_pio_handshakeout)
329 { /* receiver ready */
330 /* send data and strobe */
331 uint8_t buf;
332 if (m_piodev->fread(&buf, 1))
333 m_pio_in_buffer = buf;
334 m_pio_handshakein = 0;
335 }
336 else
337 {
338 /* data acknowledge */
339 /* we can send some data: set transmitter ready */
340 m_pio_handshakein = 1;
341 }
342 }
343 }
344
WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_spareout_w)345 WRITE_LINE_MEMBER(ti_rs232_pio_device::pio_spareout_w)
346 {
347 m_pio_spareout = state;
348 }
349
WRITE_LINE_MEMBER(ti_rs232_pio_device::flag0_w)350 WRITE_LINE_MEMBER(ti_rs232_pio_device::flag0_w)
351 {
352 m_flag0 = state;
353 }
354
WRITE_LINE_MEMBER(ti_rs232_pio_device::cts0_w)355 WRITE_LINE_MEMBER(ti_rs232_pio_device::cts0_w)
356 {
357 // Set the CTS line for RS232/1
358 LOGMASKED(LOG_LINES, "(1/3) Setting CTS* via CRU to %d\n", state);
359 output_line_state(0, tms9902_device::CTS, state ? 0 : tms9902_device::CTS);
360 }
361
WRITE_LINE_MEMBER(ti_rs232_pio_device::cts1_w)362 WRITE_LINE_MEMBER(ti_rs232_pio_device::cts1_w)
363 {
364 // Set the CTS line for RS232/2
365 LOGMASKED(LOG_LINES, "(2/4) Setting CTS* via CRU to %d\n", state);
366 output_line_state(1, tms9902_device::CTS, state ? 0 : tms9902_device::CTS);
367 }
368
WRITE_LINE_MEMBER(ti_rs232_pio_device::led_w)369 WRITE_LINE_MEMBER(ti_rs232_pio_device::led_w)
370 {
371 m_led = state;
372 }
373
374 /*
375 Memory read
376 */
readz(offs_t offset,uint8_t * value)377 void ti_rs232_pio_device::readz(offs_t offset, uint8_t *value)
378 {
379 if (m_senila==ASSERT_LINE)
380 {
381 LOGMASKED(LOG_ILA, "Sensing ILA\n");
382 *value = m_ila;
383 // The card ROM must be unselected, or we get two values
384 // on the data bus
385
386 // Not sure whether this is correct; there is no software that makes
387 // use of it
388 m_ila = 0;
389 }
390 if (in_dsr_space(offset, true) && m_selected)
391 {
392 if ((offset & 0x1000)==0x0000)
393 {
394 *value = m_dsrrom[offset&0x0fff];
395 }
396 else
397 {
398 *value = m_pio_direction_in ? m_pio_in_buffer : m_pio_out_buffer;
399 }
400 }
401 }
402
403 /*
404 Memory write
405 */
write(offs_t offset,uint8_t data)406 void ti_rs232_pio_device::write(offs_t offset, uint8_t data)
407 {
408 if (in_dsr_space(offset, true) && m_selected)
409 {
410 if ((offset & 0x1001)==0x1000)
411 {
412 m_pio_out_buffer = data;
413 }
414 }
415 }
416
417 /**************************************************************************/
418
419 /*
420 The DTR line of the interface card is wired to the CTS and DSR
421 of the UART.
422 */
incoming_dtr(int uartind,line_state value)423 void ti_rs232_pio_device::incoming_dtr(int uartind, line_state value)
424 {
425 LOGMASKED(LOG_LINES, "(RS232/%d) Incoming DTR = %d\n", uartind+1, (value==ASSERT_LINE)? 1:0);
426
427 if (uartind==0)
428 {
429 m_uart0->rcv_cts(value);
430 m_uart0->rcv_dsr(value);
431 }
432 else
433 {
434 m_uart1->rcv_cts(value);
435 m_uart1->rcv_dsr(value);
436 }
437 }
438
439 /*
440 Data transmission
441 */
transmit_data(int uartind,uint8_t value)442 void ti_rs232_pio_device::transmit_data(int uartind, uint8_t value)
443 {
444 uint8_t buf = value;
445 ti_rs232_attached_device *serial = (uartind==0)? m_serdev0 : m_serdev1;
446
447 if (!serial->exists())
448 {
449 LOGMASKED(LOG_CONFIG, "(RS232/%d) No serial output attached\n", uartind+1);
450 return;
451 }
452
453 // Send a double ESC if this is not a control operation
454 if (buf==0x1b)
455 {
456 LOGMASKED(LOG_OUT, "(RS232/%d) send ESC (requires another ESC)\n", uartind+1);
457 serial->fwrite(&buf, 1);
458 }
459 char cbuf = (buf < 0x20 || buf > 0x7e)? '.' : (char)buf;
460 LOGMASKED(LOG_OUT, "(RS232/%d) send %c <%02x>\n", uartind+1, cbuf, buf);
461 serial->fwrite(&buf, 1);
462 }
463
464 /*
465 Map the DCE-like wiring to a DTE-like wiring and vice versa (mapping==0)
466 No handshake
467
468 Emulated PC serial
469 TI RS232 interface
470 XOUT 2 TXD ----->-----( 3) ---> TXD
471 RIN 3 RXD -----<-----( 2) <--- RXD
472 CRU 5 CTS -| |-( 8) <--- DCD (cable)
473 +12V 6 DSR ----->-----(20) ---> DTR
474 RTS 8 DCD ----->-----( 4) ---> RTS
475 DSR+CTS 20 DTR -----<-----( 6) <--- DSR
476 |-( 5) <--- CTS
477
478 Alternative mapping: (mapping==1)
479 RTS/CTS handshake
480
481 Emulated PC serial
482 TI RS232 interface
483 XOUT 2 TXD ----->-----( 3) ---> TXD
484 RIN 3 RXD -----<-----( 2) <--- RXD
485 CRU 5 CTS ----->-----( 4) ---> RTS
486 +12V 6 DSR -| |-( 6) <--- DSR
487 RTS 8 DCD ----->-----(20) ---> DTR
488 DSR+CTS 20 DTR -----<-----( 8) <--- DCD
489 |-( 5) <--- CTS
490
491 Yet another mapping: (mapping==2)
492 CRU-based handshake
493
494 Emulated PC serial
495 TI RS232 interface
496 XOUT 2 TXD ----->-----( 3) ---> TXD
497 RIN 3 RXD -----<-----( 2) <--- RXD
498 CRU 5 CTS ----->-----(20) ---> DTR
499 +12V 6 DSR -| |-( 6) <--- DSR
500 RTS 8 DCD ----->-----( 4) ---> RTS
501 DSR+CTS 20 DTR -----<-----( 5) <--- CTS (cable)
502 |-( 8) <--- DCD
503
504 */
map_lines_out(int uartind,uint8_t value)505 uint8_t ti_rs232_pio_device::map_lines_out(int uartind, uint8_t value)
506 {
507 uint8_t ret = 0;
508 int mapping = ioport("SERIALMAP")->read();
509
510 // 00ab cdef = setting line RTS=a, CTS=b, DSR=c, DCD=d, DTR=e, RI=f
511
512 LOGMASKED(LOG_LINES, "(RS232/%d) out connector pins = 0x%02x; translate for DTE\n", uartind+1, value);
513
514 if (value & tms9902_device::BRK)
515 {
516 LOGMASKED(LOG_MAP, "(RS232/%d) Sending BRK\n", uartind+1);
517 ret |= tms9902_device::EXCEPT | tms9902_device::BRK;
518 }
519
520 if (mapping==0)
521 {
522 // V1
523 if (value & tms9902_device::CTS)
524 {
525 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map CTS line, ignoring\n", uartind+1);
526 }
527 if (value & tms9902_device::DSR)
528 {
529 ret |= tms9902_device::DTR;
530 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
531 }
532 if (value & tms9902_device::DCD)
533 {
534 ret |= tms9902_device::RTS;
535 LOGMASKED(LOG_MAP, "(RS232/%d) Setting RTS line\n", uartind+1);
536 }
537 }
538 else
539 {
540 if (mapping==1)
541 {
542 // V2
543 if (value & tms9902_device::CTS)
544 {
545 ret |= tms9902_device::RTS;
546 LOGMASKED(LOG_MAP, "(RS232/%d) Setting RTS line\n", uartind+1);
547 }
548 if (value & tms9902_device::DCD)
549 {
550 ret |= tms9902_device::DTR;
551 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
552 }
553 }
554 else
555 {
556 // v3
557 if (value & tms9902_device::CTS)
558 {
559 ret |= tms9902_device::DTR;
560 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
561 }
562 if (value & tms9902_device::DSR)
563 {
564 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map DSR line, ignoring\n", uartind+1);
565 }
566 if (value & tms9902_device::DCD)
567 {
568 ret |= tms9902_device::RTS;
569 LOGMASKED(LOG_MAP, "(RS232/%d) Setting RTS line\n", uartind+1);
570 }
571 }
572 }
573
574 return ret;
575 }
576
map_lines_in(int uartind,uint8_t value)577 uint8_t ti_rs232_pio_device::map_lines_in(int uartind, uint8_t value)
578 {
579 uint8_t ret = 0;
580 int mapping = ioport("SERIALMAP")->read();
581
582 // 00ab cdef = setting line RTS=a, CTS=b, DSR=c, DCD=d, DTR=e, RI=f
583
584 LOGMASKED(LOG_LINES, "(RS232/%d) in connector pins = 0x%02x; translate from DTE\n", uartind+1, value);
585
586 if (value & tms9902_device::BRK)
587 {
588 LOGMASKED(LOG_MAP, "(RS232/%d) Getting BRK\n", uartind+1);
589 ret |= tms9902_device::EXCEPT | tms9902_device::BRK;
590 }
591
592 if (mapping==0)
593 {
594 // V1
595 if (value & tms9902_device::CTS)
596 {
597 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map CTS line, ignoring\n", uartind+1);
598 }
599 if (value & tms9902_device::DSR)
600 {
601 ret |= tms9902_device::DTR;
602 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
603 }
604 if (value & tms9902_device::DCD)
605 {
606 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map DCD line, ignoring\n", uartind+1);
607 }
608 }
609 else
610 {
611 if (mapping==1)
612 {
613 if (value & tms9902_device::DCD)
614 {
615 ret |= tms9902_device::DTR;
616 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
617 }
618 if (value & tms9902_device::DSR)
619 {
620 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map DSR line, ignoring\n", uartind+1);
621 }
622 if (value & tms9902_device::CTS)
623 {
624 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map CTS line, ignoring\n", uartind+1);
625 }
626 }
627 else
628 {
629 if (value & tms9902_device::CTS)
630 {
631 ret |= tms9902_device::DTR;
632 LOGMASKED(LOG_MAP, "(RS232/%d) Setting DTR line\n", uartind+1);
633 }
634 if (value & tms9902_device::DSR)
635 {
636 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map DSR line, ignoring\n", uartind+1);
637 }
638 if (value & tms9902_device::DCD)
639 {
640 LOGMASKED(LOG_MAP, "(RS232/%d) Cannot map DCD line, ignoring\n", uartind+1);
641 }
642 }
643 }
644
645 return ret;
646 }
647 /*
648 Receive a character or a line state from the remote site. This method
649 is called by a timer with some sufficiently high polling frequency. Note
650 that the control lines are not subject to baud rates.
651 The higher polling frequency will cause overloads in the TMS9902 which has
652 a one-byte buffer only: Since the data source (e.g. the PC UART and the
653 socket connection) may be buffered, we may get a cluster of bytes in rapid
654 succession. In order to avoid this, this function uses the parameter
655 "baudpoll" which is the ratio of the characters/second and the polling
656 frequency. (char/sec is baud rate divided by 10.)
657 Whenever we receive a character that is passed to the UART, we have to
658 pause for 1/baudpoll iterations before getting the next byte from the
659 data source.
660
661 FIXME: This may fail when the emulated system tries to stop the remote
662 system by deactivating RTS or DTR, but there are still incoming
663 bytes in the socket or PC UART buffer. The buffered bytes may then cause
664 an overflow in the emulated UART, since the application program expects
665 the remote system to stop sending instantly.
666 The only way to handle this is to mirror the activity within the serial
667 bridge: Whenever a RTS=0 or DTR=0 is transmitted to the remote site, the
668 serial bridge must stop delivering data bytes until the handshake opens the
669 channel again.
670 */
receive_data_or_line_state(int uartind)671 void ti_rs232_pio_device::receive_data_or_line_state(int uartind)
672 {
673 uint8_t buffer;
674
675 ti_rs232_attached_device *serial = (uartind==0)? m_serdev0 : m_serdev1;
676 tms9902_device *uart = (uartind==0)? m_uart0 : m_uart1;
677
678 if (!serial->exists())
679 {
680 LOGMASKED(LOG_CONFIG, "(RS232/%d) No serial input attached\n", uartind+1);
681 return;
682 }
683
684 double baudpoll = uart->get_baudpoll();
685
686 // If more than the minimum waiting time since the last data byte has
687 // elapsed, we can get a new value.
688 if (m_time_hold[uartind] > 1.0)
689 {
690 // Buffer empty?
691 if (m_bufpos[uartind] == m_buflen[uartind])
692 {
693 // Get all out of sdlsocket
694 m_buflen[uartind] = serial->fread(m_recvbuf[uartind].get(), 512);
695 m_bufpos[uartind] = 0;
696 if (m_buflen[uartind]==0) return;
697 }
698 buffer = m_recvbuf[uartind][m_bufpos[uartind]++];
699 }
700 else
701 {
702 // number of polls has not yet elapsed; we have to wait.
703 m_time_hold[uartind] += baudpoll;
704 return;
705 }
706
707 char cbuf = (buffer < 0x20 || buffer > 0x7e)? '.' : (char)buffer;
708
709 // No config parameters here, only data or line setting
710 switch (m_recv_mode[uartind])
711 {
712 case RECV_MODE_NORMAL:
713 if (buffer==0x1b)
714 {
715 LOGMASKED(LOG_IN, "(RS232/%d) Received: %c <%02x>, switch to ESC mode\n", uartind+1, cbuf, buffer);
716 m_recv_mode[uartind] = RECV_MODE_ESC;
717 }
718 else
719 {
720 LOGMASKED(LOG_IN, "(RS232/%d) Received: %c <%02x>, pass to UART\n", uartind+1, cbuf, buffer);
721 uart->rcv_data(buffer);
722 m_time_hold[uartind] = 0.0;
723 }
724 break;
725 case RECV_MODE_ESC:
726 if (buffer==0x1b)
727 {
728 m_recv_mode[uartind] = RECV_MODE_NORMAL;
729 LOGMASKED(LOG_STATE, "(RS232/%d) Received another ESC, passing to UART, leaving ESC mode\n", uartind+1);
730 uart->rcv_data(buffer);
731 m_time_hold[uartind] = 0.0;
732 }
733 else
734 {
735 // the byte in buffer is the length byte
736 LOGMASKED(LOG_STATE, "(RS232/%d) Received length byte <%02x> in ESC mode\n", uartind+1, buffer);
737 if (buffer != 1)
738 {
739 LOGMASKED(LOG_WARN, "(RS232/%d) ** ERROR: Expected length byte 1 but got 0x%02x, leaving ESC mode.\n", uartind+1, buffer);
740 m_recv_mode[uartind] = RECV_MODE_NORMAL;
741 }
742 else
743 m_recv_mode[uartind] = RECV_MODE_ESC_LINES;
744 }
745 break;
746
747 case RECV_MODE_ESC_LINES:
748 // Map the real serial interface lines to our emulated connector
749 // The mapping is the same for both directions, so we use the same function
750 if (buffer & tms9902_device::EXCEPT)
751 {
752 // Exception states: BRK, FRMERR, PARERR
753 LOGMASKED(LOG_LINES, "(RS232/%d) Received BRK or ERROR <%02x>\n", uartind+1, buffer);
754 uart->rcv_break(((buffer & tms9902_device::BRK)!=0));
755
756 if (buffer & tms9902_device::FRMERR)
757 uart->rcv_framing_error();
758 if (buffer & tms9902_device::PARERR)
759 uart->rcv_parity_error();
760 }
761 else
762 {
763 buffer = map_lines_in(uartind, buffer);
764 LOGMASKED(LOG_LINES, "(RS232/%d) Received (remapped) <%02x> in ESC mode\n", uartind+1, buffer);
765
766 // The DTR line on the RS232 connector of the board is wired to both the
767 // CTS and the DSR pin of the TMS9902
768 // Apart from the data line, DTR is the only input line
769 incoming_dtr(uartind, (buffer & tms9902_device::DTR)? ASSERT_LINE : CLEAR_LINE);
770 }
771
772 m_recv_mode[uartind] = RECV_MODE_NORMAL;
773 break;
774
775 default:
776 LOGMASKED(LOG_WARN, "(RS232/%d) Unknown mode: %d\n", uartind+1, m_recv_mode[uartind]);
777 }
778 }
779
780 /*
781 Control operations like configuration or line changes
782 */
configure_interface(int uartind,int type,int value)783 void ti_rs232_pio_device::configure_interface(int uartind, int type, int value)
784 {
785 uint8_t bufctrl[4];
786 ti_rs232_attached_device *serial = (uartind==0)? m_serdev0 : m_serdev1;
787 uint8_t esc = ESC;
788
789 if (!serial->exists())
790 {
791 LOGMASKED(LOG_CONFIG, "(RS232/%d) No serial output attached\n", uartind+1);
792 return;
793 }
794
795 serial->fwrite(&esc, 1);
796 bufctrl[0] = 0x02;
797 bufctrl[1] = tms9902_device::CONFIG | tms9902_device::TYPE_TMS9902;
798
799 switch (type) {
800 case tms9902_device::RATERECV:
801 LOGMASKED(LOG_SETTING, "(RS232/%d) Send receive rate %04x\n", uartind+1, value);
802 // value has 12 bits
803 // 1ccc xaaa = config adapter type a
804 // 1111 xaaa rrrr rrrr rrrr 0000 = config receive rate on a
805 // 1110 xaaa rrrr rrrr rrrr 0000 = config transmit rate on a
806 bufctrl[0] = 0x03; // length
807 bufctrl[1] |= tms9902_device::RATERECV;
808 bufctrl[2] = (value & 0x0ff0)>>4;
809 bufctrl[3] = (value & 0x0f)<<4;
810 break;
811 case tms9902_device::RATEXMIT:
812 LOGMASKED(LOG_SETTING, "(RS232/%d) Send transmit rate %04x\n", uartind+1, value);
813 bufctrl[0] = 0x03; // length
814 bufctrl[1] |= tms9902_device::RATEXMIT;
815 bufctrl[2] = (value & 0x0ff0)>>4;
816 bufctrl[3] = (value & 0x0f)<<4;
817 break;
818 case tms9902_device::STOPBITS:
819 LOGMASKED(LOG_SETTING, "(RS232/%d) Send stop bit config %02x\n", uartind+1, value&0x03);
820 bufctrl[1] |= tms9902_device::STOPBITS;
821 bufctrl[2] = (value & 0x03);
822 break;
823 case tms9902_device::DATABITS:
824 LOGMASKED(LOG_SETTING, "(RS232/%d) Send data bit config %02x\n", uartind+1, value&0x03);
825 bufctrl[1] |= tms9902_device::DATABITS;
826 bufctrl[2] = (value & 0x03);
827 break;
828 case tms9902_device::PARITY:
829 LOGMASKED(LOG_SETTING, "(RS232/%d) Send parity config %02x\n", uartind+1, value&0x03);
830 bufctrl[1] |= tms9902_device::PARITY;
831 bufctrl[2] = (value & 0x03);
832 break;
833 default:
834 LOGMASKED(LOG_WARN, "(RS232/%d) Error - unknown config type %02x\n", uartind+1, type);
835 }
836
837 serial->fwrite(bufctrl, bufctrl[0]+1);
838 }
839
set_bit(int uartind,int line,int value)840 void ti_rs232_pio_device::set_bit(int uartind, int line, int value)
841 {
842 switch (line)
843 {
844 case tms9902_device::CTS:
845 LOGMASKED(LOG_LINES, "(RS232/%d) Set CTS(out)=%s\n", uartind+1, (value!=0)? "asserted" : "cleared");
846 break;
847 case tms9902_device::DCD:
848 LOGMASKED(LOG_LINES, "(RS232/%d) Set DCD(out)=%s\n", uartind+1, (value!=0)? "asserted" : "cleared");
849 break;
850 case tms9902_device::BRK:
851 LOGMASKED(LOG_LINES, "(RS232/%d) Set BRK(out)=%s\n", uartind+1, (value!=0)? "asserted" : "cleared");
852 break;
853 }
854
855 if (value!=0)
856 m_signals[uartind] |= line;
857 else
858 m_signals[uartind] &= ~line;
859 }
860
861 /*
862 Line changes
863 */
output_exception(int uartind,int param,uint8_t value)864 void ti_rs232_pio_device::output_exception(int uartind, int param, uint8_t value)
865 {
866 ti_rs232_attached_device *serial = (uartind==0)? m_serdev0 : m_serdev1;
867 uint8_t bufctrl[2];
868 uint8_t esc = ESC;
869
870 if (!serial->exists())
871 {
872 LOGMASKED(LOG_CONFIG, "(RS232/%d) No serial output attached\n", uartind+1);
873 return;
874 }
875
876 serial->fwrite(&esc, 1);
877
878 bufctrl[0] = 1;
879 // 0100 0xxv = exception xx: 02=BRK, 04=FRMERR, 06=PARERR; v=0,1 (only for BRK)
880 // BRK is the only output exception
881 bufctrl[1] = tms9902_device::EXCEPT | param | (value&1);
882 serial->fwrite(bufctrl, 2);
883 }
884
885 /*
886 Line changes
887 */
output_line_state(int uartind,int mask,uint8_t value)888 void ti_rs232_pio_device::output_line_state(int uartind, int mask, uint8_t value)
889 {
890 ti_rs232_attached_device *serial = (uartind==0)? m_serdev0 : m_serdev1;
891 uint8_t bufctrl[2];
892 uint8_t esc = ESC;
893
894 if (!serial->exists())
895 {
896 LOGMASKED(LOG_CONFIG, "(RS232/%d) No serial output attached\n", uartind+1);
897 return;
898 }
899
900 // Send ESC to serial bridge
901 // FIXME: When the socket cannot be set up, MAME crashes here
902 serial->fwrite(&esc, 1);
903
904 // Length 1
905 bufctrl[0] = 1;
906
907 // 01ab cdef = setting line RTS=a, CTS=b, DSR=c, DCD=d, DTR=e, RI=f
908
909 // The CTS line (coming from a CRU bit) is connected to the CTS pin
910 if (mask & tms9902_device::CTS) set_bit(uartind, tms9902_device::CTS, value & tms9902_device::CTS);
911
912 // The RTS line (from 9902) is connected to the DCD pin
913 if (mask & tms9902_device::RTS) set_bit(uartind, tms9902_device::DCD, value & tms9902_device::RTS);
914
915 // The DSR pin is hardwired to +5V
916 set_bit(uartind, tms9902_device::DSR, 1);
917
918 // As of here, the lines are set according to the schematics of the
919 // serial interface.
920
921 // Now translate the signals of the board to those of a DTE-like device
922 // so that we can pass the signal to the real PC serial interface
923 // (can be imagined as if we emulated the cable)
924 bufctrl[1] = map_lines_out(uartind, m_signals[uartind]);
925 serial->fwrite(bufctrl, 2);
926 }
927
928 /***********************************************************************
929 callbacks
930 ************************************************************************/
931 /*
932 Propagates the /INT signal of the UARTs to the /INT line of the pbox.
933 */
WRITE_LINE_MEMBER(ti_rs232_pio_device::int0_callback)934 WRITE_LINE_MEMBER( ti_rs232_pio_device::int0_callback )
935 {
936 int senila_bit = SENILA_0_BIT;
937
938 if (state==ASSERT_LINE) m_ila |= senila_bit;
939 else m_ila &= ~senila_bit;
940
941 m_slot->set_inta(state);
942 }
943
WRITE_LINE_MEMBER(ti_rs232_pio_device::int1_callback)944 WRITE_LINE_MEMBER( ti_rs232_pio_device::int1_callback )
945 {
946 int senila_bit = SENILA_1_BIT;
947
948 if (state==ASSERT_LINE) m_ila |= senila_bit;
949 else m_ila &= ~senila_bit;
950
951 m_slot->set_inta(state);
952 }
953
954 /*
955 Called from the UART when it wants to receive a character
956 However, characters are not passed to it at this point
957 Instead, we check for signal line change or data transmission
958 and call the respective function
959 */
WRITE_LINE_MEMBER(ti_rs232_pio_device::rcv0_callback)960 WRITE_LINE_MEMBER( ti_rs232_pio_device::rcv0_callback )
961 {
962 receive_data_or_line_state(0);
963 }
964
WRITE_LINE_MEMBER(ti_rs232_pio_device::rcv1_callback)965 WRITE_LINE_MEMBER( ti_rs232_pio_device::rcv1_callback )
966 {
967 receive_data_or_line_state(1);
968 }
969
xmit0_callback(uint8_t data)970 void ti_rs232_pio_device::xmit0_callback(uint8_t data)
971 {
972 transmit_data(0, data);
973 }
974
xmit1_callback(uint8_t data)975 void ti_rs232_pio_device::xmit1_callback(uint8_t data)
976 {
977 transmit_data(1, data);
978 }
979
ctrl_callback(int uartind,int offset,uint8_t data)980 void ti_rs232_pio_device::ctrl_callback(int uartind, int offset, uint8_t data)
981 {
982 if ((offset & tms9902_device::CONFIG)!=0)
983 {
984 // We cannot pass the configuration data as they need more than 8 bits.
985 // Could be done by a write16 function as well.
986 configure_interface(uartind, data, (uartind==0)? m_uart0->get_config_value() : m_uart1->get_config_value());
987 }
988 else
989 {
990 if ((offset & tms9902_device::EXCEPT)!=0)
991 {
992 output_exception(uartind, offset & ~tms9902_device::EXCEPT, data);
993 }
994 else
995 {
996 output_line_state(uartind, offset, data);
997 }
998 }
999 }
1000
ctrl0_callback(offs_t offset,uint8_t data)1001 void ti_rs232_pio_device::ctrl0_callback(offs_t offset, uint8_t data)
1002 {
1003 ctrl_callback(0, offset, data);
1004 }
1005
ctrl1_callback(offs_t offset,uint8_t data)1006 void ti_rs232_pio_device::ctrl1_callback(offs_t offset, uint8_t data)
1007 {
1008 ctrl_callback(1, offset, data);
1009 }
1010
device_start()1011 void ti_rs232_pio_device::device_start()
1012 {
1013 m_dsrrom = memregion(TI99_DSRROM)->base();
1014
1015 // Prepare the receive buffers
1016 m_recvbuf[0] = std::make_unique<uint8_t[]>(512);
1017 m_recvbuf[1] = std::make_unique<uint8_t[]>(512);
1018
1019 m_pio_write = true; // required for call_load of pio_attached_device
1020 m_pio_writable = false;
1021 m_pio_handshakein = false;
1022
1023 // We don't save the receive buffers for persistent state
1024 save_pointer(NAME(m_signals),2);
1025 save_pointer(NAME(m_recv_mode),2);
1026 save_pointer(NAME(m_time_hold),2);
1027 save_item(NAME(m_pio_direction_in));
1028 save_item(NAME(m_pio_handshakeout));
1029 save_item(NAME(m_pio_handshakein));
1030 save_item(NAME(m_pio_spareout));
1031 save_item(NAME(m_pio_sparein));
1032 save_item(NAME(m_flag0));
1033 save_item(NAME(m_led));
1034 save_item(NAME(m_pio_out_buffer));
1035 save_item(NAME(m_pio_in_buffer));
1036 save_item(NAME(m_pio_readable));
1037 save_item(NAME(m_pio_writable));
1038 save_item(NAME(m_pio_write));
1039 save_item(NAME(m_ila));
1040 }
1041
device_stop()1042 void ti_rs232_pio_device::device_stop()
1043 {
1044 m_recvbuf[0] = nullptr;
1045 m_recvbuf[1] = nullptr;
1046 }
1047
device_reset()1048 void ti_rs232_pio_device::device_reset()
1049 {
1050 m_pio_direction_in = false;
1051 m_pio_handshakeout = false;
1052 m_pio_spareout = false;
1053 m_flag0 = false;
1054
1055 set_bit(0, tms9902_device::CTS, 0);
1056 set_bit(1, tms9902_device::CTS, 0);
1057
1058 m_led = false;
1059 m_recv_mode[0] = RECV_MODE_NORMAL;
1060 m_recv_mode[1] = RECV_MODE_NORMAL;
1061
1062 m_bufpos[0] = m_bufpos[1] = m_buflen[0] = m_buflen[1] = 0;
1063
1064 m_selected = false;
1065
1066 m_cru_base = (ioport("CRURS232")->read()==0)? 0x1300 : 0x1500;
1067
1068 m_time_hold[0] = m_time_hold[1] = 0.0;
1069
1070 // Both DTRs are pulled up
1071 incoming_dtr(0, ASSERT_LINE);
1072 incoming_dtr(1, ASSERT_LINE);
1073 }
1074
1075 ROM_START( ti_rs232 )
1076 ROM_REGION(0x1000, TI99_DSRROM, 0)
CRC(eab382fb)1077 ROM_LOAD("rs232pio_dsr.u1", 0x0000, 0x1000, CRC(eab382fb) SHA1(ee609a18a21f1a3ddab334e8798d5f2a0fcefa91)) /* TI rs232 DSR ROM */
1078 ROM_END
1079
1080 INPUT_PORTS_START( ti_rs232 )
1081 PORT_START( "CRURS232" )
1082 PORT_DIPNAME( 0x01, 0x00, "TI-RS232 CRU base" )
1083 PORT_DIPSETTING( 0x00, "1300" )
1084 PORT_DIPSETTING( 0x01, "1500" )
1085
1086 PORT_START( "SERIALMAP" )
1087 PORT_CONFNAME( 0x03, 0x00, "Serial cable pin mapping" )
1088 PORT_CONFSETTING( 0x00, "6-20" )
1089 PORT_CONFSETTING( 0x01, "8-20" )
1090 PORT_CONFSETTING( 0x02, "5-20" )
1091 INPUT_PORTS_END
1092
1093 void ti_rs232_pio_device::device_add_mconfig(machine_config &config)
1094 {
1095 TMS9902(config, m_uart0, 3000000);
1096 m_uart0->int_cb().set(FUNC(ti_rs232_pio_device::int0_callback));
1097 m_uart0->rcv_cb().set(FUNC(ti_rs232_pio_device::rcv0_callback));
1098 m_uart0->xmit_cb().set(FUNC(ti_rs232_pio_device::xmit0_callback));
1099 m_uart0->ctrl_cb().set(FUNC(ti_rs232_pio_device::ctrl0_callback));
1100 TMS9902(config, m_uart1, 3000000);
1101 m_uart1->int_cb().set(FUNC(ti_rs232_pio_device::int1_callback));
1102 m_uart1->rcv_cb().set(FUNC(ti_rs232_pio_device::rcv1_callback));
1103 m_uart1->xmit_cb().set(FUNC(ti_rs232_pio_device::xmit1_callback));
1104 m_uart1->ctrl_cb().set(FUNC(ti_rs232_pio_device::ctrl1_callback));
1105 TI99_RS232_DEV(config, m_serdev0, 0);
1106 m_serdev0->connect(m_uart0);
1107 TI99_RS232_DEV(config, m_serdev1, 0);
1108 m_serdev1->connect(m_uart1);
1109
1110 TI99_PIO_DEV(config, m_piodev, 0);
1111
1112 LS259(config, m_crulatch); // U12
1113 m_crulatch->q_out_cb<0>().set(FUNC(ti_rs232_pio_device::selected_w));
1114 m_crulatch->q_out_cb<1>().set(FUNC(ti_rs232_pio_device::pio_direction_in_w));
1115 m_crulatch->q_out_cb<2>().set(FUNC(ti_rs232_pio_device::pio_handshake_out_w));
1116 m_crulatch->q_out_cb<3>().set(FUNC(ti_rs232_pio_device::pio_spareout_w));
1117 m_crulatch->q_out_cb<4>().set(FUNC(ti_rs232_pio_device::flag0_w));
1118 m_crulatch->q_out_cb<5>().set(FUNC(ti_rs232_pio_device::cts0_w));
1119 m_crulatch->q_out_cb<6>().set(FUNC(ti_rs232_pio_device::cts1_w));
1120 m_crulatch->q_out_cb<7>().set(FUNC(ti_rs232_pio_device::led_w));
1121 }
1122
device_rom_region() const1123 const tiny_rom_entry *ti_rs232_pio_device::device_rom_region() const
1124 {
1125 return ROM_NAME( ti_rs232 );
1126 }
1127
device_input_ports() const1128 ioport_constructor ti_rs232_pio_device::device_input_ports() const
1129 {
1130 return INPUT_PORTS_NAME(ti_rs232);
1131 }
1132
1133 } } } // end namespace bus::ti99::peb
1134