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