1 // license:BSD-3-Clause
2 // copyright-holders:Vas Crabb
3
4 #include "emu.h"
5 #include "ti8x.h"
6
7 #define LOG_GENERAL (1U << 0)
8 #define LOG_BITPROTO (1U << 1)
9 #define LOG_BYTEPROTO (1U << 2)
10
11 //#define VERBOSE (LOG_GENERAL | LOG_BITPROTO | LOG_BYTEPROTO)
12 #define LOG_OUTPUT_FUNC device().logerror
13 #include "logmacro.h"
14
15 #define LOGBITPROTO(...) LOGMASKED(LOG_BITPROTO, __VA_ARGS__)
16 #define LOGBYTEPROTO(...) LOGMASKED(LOG_BYTEPROTO, __VA_ARGS__)
17
18
19
20 DEFINE_DEVICE_TYPE(TI8X_LINK_PORT, ti8x_link_port_device, "ti8x_link_port", "TI-8x Link Port")
21
22
ti8x_link_port_device(machine_config const & mconfig,char const * tag,device_t * owner,uint32_t clock)23 ti8x_link_port_device::ti8x_link_port_device(
24 machine_config const &mconfig,
25 char const *tag,
26 device_t *owner,
27 uint32_t clock)
28 : ti8x_link_port_device(mconfig, TI8X_LINK_PORT, tag, owner, clock)
29 {
30 }
31
32
ti8x_link_port_device(machine_config const & mconfig,device_type type,char const * tag,device_t * owner,uint32_t clock)33 ti8x_link_port_device::ti8x_link_port_device(
34 machine_config const &mconfig,
35 device_type type,
36 char const *tag,
37 device_t *owner,
38 uint32_t clock)
39 : device_t(mconfig, type, tag, owner, clock)
40 , device_single_card_slot_interface<device_ti8x_link_port_interface>(mconfig, *this)
41 , m_tip_handler(*this)
42 , m_ring_handler(*this)
43 , m_dev(nullptr)
44 , m_tip_in(true)
45 , m_tip_out(true)
46 , m_ring_in(true)
47 , m_ring_out(true)
48 {
49 }
50
51
WRITE_LINE_MEMBER(ti8x_link_port_device::tip_w)52 WRITE_LINE_MEMBER(ti8x_link_port_device::tip_w)
53 {
54 if (bool(state) != m_tip_out)
55 {
56 m_tip_out = bool(state);
57 if (m_dev)
58 m_dev->input_tip(m_tip_out ? 1 : 0);
59 }
60 }
61
62
WRITE_LINE_MEMBER(ti8x_link_port_device::ring_w)63 WRITE_LINE_MEMBER(ti8x_link_port_device::ring_w)
64 {
65 if (bool(state) != m_ring_out)
66 {
67 m_ring_out = bool(state);
68 if (m_dev)
69 m_dev->input_ring(m_ring_out ? 1 : 0);
70 }
71 }
72
73
device_start()74 void ti8x_link_port_device::device_start()
75 {
76 m_tip_handler.resolve_safe();
77 m_ring_handler.resolve_safe();
78
79 save_item(NAME(m_tip_in));
80 save_item(NAME(m_tip_out));
81 save_item(NAME(m_ring_in));
82 save_item(NAME(m_ring_out));
83
84 m_tip_in = m_tip_out = true;
85 m_ring_in = m_ring_out = true;
86 }
87
88
device_config_complete()89 void ti8x_link_port_device::device_config_complete()
90 {
91 m_dev = get_card_device();
92 }
93
94
95
device_ti8x_link_port_interface(machine_config const & mconfig,device_t & device)96 device_ti8x_link_port_interface::device_ti8x_link_port_interface(
97 machine_config const &mconfig,
98 device_t &device)
99 : device_interface(device, "ti8xlink")
100 , m_port(dynamic_cast<ti8x_link_port_device *>(device.owner()))
101 {
102 }
103
104
105
device_ti8x_link_port_bit_interface(machine_config const & mconfig,device_t & device)106 device_ti8x_link_port_bit_interface::device_ti8x_link_port_bit_interface(
107 machine_config const &mconfig,
108 device_t &device)
109 : device_ti8x_link_port_interface(mconfig, device)
110 , m_error_timer(nullptr)
111 , m_bit_phase(IDLE)
112 , m_tx_bit_buffer(EMPTY)
113 , m_tip_in(true)
114 , m_ring_in(true)
115 {
116 }
117
118
interface_pre_start()119 void device_ti8x_link_port_bit_interface::interface_pre_start()
120 {
121 device_ti8x_link_port_interface::interface_pre_start();
122
123 if (!m_error_timer)
124 m_error_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_ti8x_link_port_bit_interface::bit_timeout), this));
125
126 m_bit_phase = IDLE;
127 m_tx_bit_buffer = EMPTY;
128 m_tip_in = m_ring_in = true;
129 }
130
131
interface_post_start()132 void device_ti8x_link_port_bit_interface::interface_post_start()
133 {
134 device_ti8x_link_port_interface::interface_post_start();
135
136 device().save_item(NAME(m_bit_phase));
137 device().save_item(NAME(m_tx_bit_buffer));
138 device().save_item(NAME(m_tip_in));
139 device().save_item(NAME(m_ring_in));
140 }
141
142
interface_pre_reset()143 void device_ti8x_link_port_bit_interface::interface_pre_reset()
144 {
145 device_ti8x_link_port_interface::interface_pre_reset();
146
147 m_error_timer->reset();
148 m_bit_phase = (m_tip_in && m_ring_in) ? IDLE : WAIT_IDLE;
149 m_tx_bit_buffer = EMPTY;
150
151 output_tip(1);
152 output_ring(1);
153 }
154
155
send_bit(bool data)156 void device_ti8x_link_port_bit_interface::send_bit(bool data)
157 {
158 LOGBITPROTO("queue %d bit\n", data ? 1 : 0);
159
160 if (EMPTY != m_tx_bit_buffer)
161 device().logerror("device_ti8x_link_port_bit_interface: warning: transmit buffer overrun\n");
162
163 m_tx_bit_buffer = data ? PENDING_1 : PENDING_0;
164 if (IDLE == m_bit_phase)
165 check_tx_bit_buffer();
166 else if (WAIT_IDLE == m_bit_phase)
167 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
168 }
169
170
accept_bit()171 void device_ti8x_link_port_bit_interface::accept_bit()
172 {
173 switch (m_bit_phase)
174 {
175 // can't accept a bit that isn't being held
176 case IDLE:
177 case WAIT_ACK_0:
178 case WAIT_ACK_1:
179 case WAIT_REL_0:
180 case WAIT_REL_1:
181 case ACK_0:
182 case ACK_1:
183 case WAIT_IDLE:
184 fatalerror("device_ti8x_link_port_bit_interface: attempt to accept bit when not holding");
185 break;
186
187 // release the acknowledgement - if the ring doesn't rise we've lost sync
188 case HOLD_0:
189 assert(m_tip_in);
190
191 output_ring(1);
192 if (m_ring_in)
193 {
194 LOGBITPROTO("accepted 0 bit\n");
195 check_tx_bit_buffer();
196 }
197 else
198 {
199 LOGBITPROTO("accepted 0 bit, ring low (collision) - waiting for bus idle\n");
200 m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout
201 m_bit_phase = WAIT_IDLE;
202 bit_collision();
203 }
204 break;
205
206 // release the acknowledgement - if the tip doesn't rise we've lost sync
207 case HOLD_1:
208 assert(m_ring_in);
209
210 output_tip(1);
211 if (m_tip_in)
212 {
213 LOGBITPROTO("accepted 1 bit\n");
214 check_tx_bit_buffer();
215 }
216 else
217 {
218 LOGBITPROTO("accepted 1 bit, tip low (collision) - waiting for bus idle\n");
219 m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout
220 m_bit_phase = WAIT_IDLE;
221 bit_collision();
222 }
223 break;
224
225 // something very bad happened (heap smash?)
226 default:
227 throw false;
228 }
229 }
230
231
WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_tip)232 WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_tip)
233 {
234 m_tip_in = bool(state);
235 switch (m_bit_phase)
236 {
237 // if tip falls while idle, it's the beginning of an incoming 0
238 case IDLE:
239 if (!m_tip_in)
240 {
241 LOGBITPROTO("falling edge on tip, acknowledging 0 bit\n");
242 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
243 m_bit_phase = ACK_0;
244 output_ring(0);
245 }
246 break;
247
248 // we're driving tip low in this state, ignore it
249 case WAIT_ACK_0:
250 case ACK_1:
251 case HOLD_1:
252 break;
253
254 // tip must fall to acknowledge outgoing 1
255 case WAIT_ACK_1:
256 if (!m_tip_in)
257 {
258 LOGBITPROTO("falling edge on tip, 1 bit acknowledged, confirming\n");
259 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
260 m_bit_phase = WAIT_REL_1;
261 output_ring(1);
262 }
263 break;
264
265 // if tip falls now, we've lost sync
266 case WAIT_REL_0:
267 case HOLD_0:
268 if (!m_tip_in)
269 {
270 LOGBITPROTO("falling edge on tip, lost sync, waiting for bus idle\n");
271 m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout
272 m_bit_phase = WAIT_IDLE;
273 output_ring(1);
274 bit_collision();
275 }
276 break;
277
278 // tip must rise to complete outgoing 1 sequence
279 case WAIT_REL_1:
280 if (m_tip_in)
281 {
282 assert(!m_ring_in);
283
284 LOGBITPROTO("rising edge on tip, 1 bit sent\n");
285 check_tx_bit_buffer();
286 bit_sent();
287 }
288 break;
289
290 // tip must rise to accept our acknowledgement
291 case ACK_0:
292 if (m_tip_in)
293 {
294 LOGBITPROTO("rising edge on tip, 0 bit acknowledge confirmed, holding\n");
295 m_error_timer->reset();
296 m_bit_phase = HOLD_0;
297 bit_received(false);
298 }
299 break;
300
301 // if the bus is available, check for bit to send
302 case WAIT_IDLE:
303 if (m_tip_in && m_ring_in)
304 {
305 LOGBITPROTO("rising edge on tip, bus idle detected\n");
306 check_tx_bit_buffer();
307 }
308 break;
309
310 // something very bad happened (heap smash?)
311 default:
312 throw false;
313 }
314 }
315
316
WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_ring)317 WRITE_LINE_MEMBER(device_ti8x_link_port_bit_interface::input_ring)
318 {
319 m_ring_in = bool(state);
320 switch (m_bit_phase)
321 {
322 // if ring falls while idle, it's the beginning of an incoming 1
323 case IDLE:
324 if (!m_ring_in)
325 {
326 LOGBITPROTO("falling edge on ring, acknowledging 1 bit\n");
327 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
328 m_bit_phase = ACK_1;
329 output_tip(0);
330 }
331 break;
332
333 // ring must fall to acknowledge outgoing 0
334 case WAIT_ACK_0:
335 if (!m_ring_in)
336 {
337 LOGBITPROTO("falling edge on ring, 0 bit acknowledged, confirming\n");
338 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
339 m_bit_phase = WAIT_REL_0;
340 output_tip(1);
341 }
342 break;
343
344 // we're driving ring low in this state, ignore it
345 case WAIT_ACK_1:
346 case ACK_0:
347 case HOLD_0:
348 break;
349
350 // ring must rise to complete outgoing 0 sequence
351 case WAIT_REL_0:
352 if (m_ring_in)
353 {
354 assert(!m_tip_in);
355
356 LOGBITPROTO("rising edge on ring, 0 bit sent\n");
357 check_tx_bit_buffer();
358 bit_sent();
359 }
360 break;
361
362 // if ring falls now, we've lost sync
363 case WAIT_REL_1:
364 case HOLD_1:
365 if (!m_ring_in)
366 {
367 LOGBITPROTO("falling edge on ring, lost sync, waiting for bus idle\n");
368 m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout
369 m_bit_phase = WAIT_IDLE;
370 output_tip(1);
371 bit_collision();
372 }
373 break;
374
375 // ring must rise to accept our acknowledgement
376 case ACK_1:
377 if (m_ring_in)
378 {
379 LOGBITPROTO("rising edge on ring, 1 bit acknowledge confirmed, holding\n");
380 m_error_timer->reset();
381 m_bit_phase = HOLD_1;
382 bit_received(true);
383 }
384 break;
385
386 // if the bus is available, check for bit to send
387 case WAIT_IDLE:
388 if (m_tip_in && m_ring_in)
389 {
390 LOGBITPROTO("rising edge on tip, bus idle detected\n");
391 check_tx_bit_buffer();
392 }
393 break;
394
395 // something very bad happened (heap smash?)
396 default:
397 throw false;
398 }
399 }
400
401
TIMER_CALLBACK_MEMBER(device_ti8x_link_port_bit_interface::bit_timeout)402 TIMER_CALLBACK_MEMBER(device_ti8x_link_port_bit_interface::bit_timeout)
403 {
404 switch (m_bit_phase)
405 {
406 // something very bad happened (heap smash?)
407 case IDLE:
408 case HOLD_0:
409 case HOLD_1:
410 default:
411 throw false;
412
413 // receive timeout
414 case ACK_0:
415 case ACK_1:
416 LOGBITPROTO("timeout acknowledging %d bit\n", (ACK_0 == m_bit_phase) ? 0 : 1);
417 output_tip(1);
418 output_ring(1);
419 if (m_tip_in && m_ring_in)
420 {
421 check_tx_bit_buffer();
422 }
423 else
424 {
425 LOGBITPROTO("waiting for bus idle\n");
426 m_error_timer->reset((EMPTY == m_tx_bit_buffer) ? attotime::never : attotime(1, 0)); // TODO: configurable timeout
427 m_bit_phase = WAIT_IDLE;
428 }
429 bit_receive_timeout();
430 break;
431
432 // send timeout:
433 case WAIT_IDLE:
434 assert(EMPTY != m_tx_bit_buffer);
435 case WAIT_ACK_0:
436 case WAIT_ACK_1:
437 case WAIT_REL_0:
438 case WAIT_REL_1:
439 LOGBITPROTO("timeout sending bit\n");
440 m_error_timer->reset();
441 m_bit_phase = (m_tip_in && m_ring_in) ? IDLE : WAIT_IDLE;
442 m_tx_bit_buffer = EMPTY;
443 output_tip(1);
444 output_ring(1);
445 bit_send_timeout();
446 break;
447 }
448 }
449
450
check_tx_bit_buffer()451 void device_ti8x_link_port_bit_interface::check_tx_bit_buffer()
452 {
453 assert(m_tip_in);
454 assert(m_ring_in);
455
456 switch (m_tx_bit_buffer)
457 {
458 // nothing to do
459 case EMPTY:
460 LOGBITPROTO("no pending bit, entering idle state\n");
461 m_error_timer->reset();
462 m_bit_phase = IDLE;
463 break;
464
465 // pull tip low and wait for acknowledgement
466 case PENDING_0:
467 LOGBITPROTO("sending 0 bit, pulling tip low\n");
468 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
469 m_bit_phase = WAIT_ACK_0;
470 m_tx_bit_buffer = EMPTY;
471 output_tip(0);
472 break;
473
474 // pull ring low and wait for acknowledgement
475 case PENDING_1:
476 LOGBITPROTO("sending 1 bit, pulling ring low\n");
477 m_error_timer->reset(attotime(1, 0)); // TODO: configurable timeout
478 m_bit_phase = WAIT_ACK_1;
479 m_tx_bit_buffer = EMPTY;
480 output_ring(0);
481 break;
482
483 // something very bad happened (heap smash?)
484 default:
485 throw false;
486 }
487 }
488
489
490
device_ti8x_link_port_byte_interface(machine_config const & mconfig,device_t & device)491 device_ti8x_link_port_byte_interface::device_ti8x_link_port_byte_interface(
492 machine_config const &mconfig,
493 device_t &device)
494 : device_ti8x_link_port_bit_interface(mconfig, device)
495 , m_tx_byte_buffer(0U)
496 , m_rx_byte_buffer(0U)
497 {
498 }
499
500
interface_pre_start()501 void device_ti8x_link_port_byte_interface::interface_pre_start()
502 {
503 device_ti8x_link_port_bit_interface::interface_pre_start();
504
505 m_tx_byte_buffer = m_rx_byte_buffer = 0U;
506 }
507
508
interface_post_start()509 void device_ti8x_link_port_byte_interface::interface_post_start()
510 {
511 device_ti8x_link_port_bit_interface::interface_post_start();
512
513 device().save_item(NAME(m_tx_byte_buffer));
514 device().save_item(NAME(m_rx_byte_buffer));
515 }
516
517
interface_pre_reset()518 void device_ti8x_link_port_byte_interface::interface_pre_reset()
519 {
520 device_ti8x_link_port_bit_interface::interface_pre_reset();
521
522 m_tx_byte_buffer = m_rx_byte_buffer = 0U;
523 }
524
525
send_byte(u8 data)526 void device_ti8x_link_port_byte_interface::send_byte(u8 data)
527 {
528 if (m_tx_byte_buffer)
529 device().logerror("device_ti8x_link_port_byte_interface: warning: transmit buffer overrun\n");
530
531 LOGBYTEPROTO("sending byte 0x%02X\n", data);
532 m_tx_byte_buffer = 0x0080 | u16(data >> 1);
533 send_bit(BIT(data, 0));
534 }
535
536
accept_byte()537 void device_ti8x_link_port_byte_interface::accept_byte()
538 {
539 assert(BIT(m_rx_byte_buffer, 8));
540
541 LOGBYTEPROTO("accepting final bit of byte\n");
542 m_rx_byte_buffer = 0U;
543 accept_bit();
544 }
545
546
bit_collision()547 void device_ti8x_link_port_byte_interface::bit_collision()
548 {
549 LOGBYTEPROTO("bit collection, clearing byte buffers\n");
550 m_tx_byte_buffer = m_rx_byte_buffer = 0U;
551 byte_collision();
552 }
553
554
bit_send_timeout()555 void device_ti8x_link_port_byte_interface::bit_send_timeout()
556 {
557 LOGBYTEPROTO("bit send timeout, clearing send byte buffer\n");
558 m_tx_byte_buffer = 0U;
559 byte_send_timeout();
560 }
561
562
bit_receive_timeout()563 void device_ti8x_link_port_byte_interface::bit_receive_timeout()
564 {
565 LOGBYTEPROTO("bit receive timeout, clearing receive byte buffer\n");
566 m_rx_byte_buffer = 0U;
567 byte_receive_timeout();
568 }
569
570
bit_sent()571 void device_ti8x_link_port_byte_interface::bit_sent()
572 {
573 assert(m_tx_byte_buffer);
574
575 bool const data(BIT(m_tx_byte_buffer, 0));
576 if (m_tx_byte_buffer >>= 1)
577 {
578 LOGBYTEPROTO("bit sent, sending next bit of byte\n");
579 send_bit(data);
580 }
581 else
582 {
583 assert(data);
584
585 LOGBYTEPROTO("final bit of byte sent\n");
586 byte_sent();
587 }
588 }
589
590
bit_received(bool data)591 void device_ti8x_link_port_byte_interface::bit_received(bool data)
592 {
593 assert(!BIT(m_rx_byte_buffer, 8));
594
595 m_rx_byte_buffer = (!m_rx_byte_buffer ? 0x8000 : (m_rx_byte_buffer >> 1)) | (data ? 0x0080U : 0x0000U);
596 if (BIT(m_rx_byte_buffer, 8))
597 {
598 LOGBYTEPROTO("received final bit of byte 0x%02X\n", u8(m_rx_byte_buffer));
599 byte_received(u8(m_rx_byte_buffer));
600 }
601 else
602 {
603 LOGBYTEPROTO("bit received, accepting\n");
604 accept_bit();
605 }
606 }
607
608
609
610 #include "bitsocket.h"
611 #include "graphlinkhle.h"
612 #include "teeconn.h"
613 #include "tispeaker.h"
614
default_ti8x_link_devices(device_slot_interface & device)615 void default_ti8x_link_devices(device_slot_interface &device)
616 {
617 device.option_add("bitsock", TI8X_BIT_SOCKET);
618 device.option_add("glinkhle", TI8X_GRAPH_LINK_HLE);
619 device.option_add("tee", TI8X_TEE_CONNECTOR);
620 device.option_add("monospkr", TI8X_SPEAKER_MONO);
621 device.option_add("stereospkr", TI8X_SPEAKER_STEREO);
622 }
623