1 // license:BSD-3-Clause
2 // copyright-holders:Ariane Fugmann
3 
4 /*
5 Comm PCB
6 --------
7 
8 MODEL-1 COMMUNICATION BD 837-8842 171-6293B (C) SEGA 1992
9 |--------------------------------------------------------------------------------|
10 |                                                                                |
11 |    MB89237A            MB89374                                                 |
12 |       JP4                                                                 LED1 |
13 |    15112.17            Z80                                                     |
14 |    JP2  JP3                                                       75179        |
15 |    MB8464              315-5624                                     JP6        |
16 |                                                       315-5547                 |
17 |        315-5611                                            SW1    PC910     CN4|
18 |                                                                                |
19 |                                                                   PC910     CN5|
20 |     MB8421             MB8431                                JP7               |
21 |                                                                   JP5          |
22 |        JP8                                                                  CN7|
23 |                CN1                                    CN2                      |
24 | |---------------------------------|   |---------------------------------|   CN6|
25 | |---------------------------------|   |---------------------------------|      |
26 |--------------------------------------------------------------------------------|
27 Notes:
28       15112.17 - AMD AM27C100 128k x8 EPROM (DIP32, labelled 'EPR-15112')
29       Z80      - Zilog Z0840008PSC Z80 CPU, running at 8.000MHz (DIP40)
30       MB8464   - Fujitsu MB8464 8k x8 SRAM (DIP28)
31       MB8421   - Fujitsu MB8421-12LP 2k x8 SRAM (SDIP52)
32       MB8431   - Fujitsu MB8431-90LP 2k x8 SRAM (SDIP52)
33       MB89237A - Fujitsu MB89237A DMA-Controller (DIP20) [most likely i8237A clone]
34       MB89374  - Fujitsu MB89374 Data Link Controller (SDIP42)
35       75179    - Texas Instruments SN75179 Differential Driver and Receiver Pair (DIP8)
36       315-5547 - AMI 18CV8PC-25 PAL (DIP20)
37       315-5624 - MMI PAL16L8BCN PAL (DIP20)
38       315-5611 - Lattice GAL16V8A PAL (DIP20)
39       PC910    - Sharp PC910 opto-isolator (x2, DIP8)
40       SW1      - Push Button Switch (enables board)
41       CN1, CN2 - Connectors to join Comm board to Video board
42       CN4      - 8 pin connector (DIFFERENTIAL port)
43       CN5      - 6 pin connector (SERIAL port)
44       CN6, CN7 - TOSLINK-Connectors for network optical cable link
45       JP2      - Jumper, set to 2-3 (connected to EPROM A15)
46       JP3      - Jumper, set to 1-2 (connected to EPROM A16)
47       JP4      - Jumper, set to 1-2
48       JP5      - Jumper, shorted (enables TOSLINK RX channel)
49       JP6      - Jumper, not shorted (enables DIFFERERENTIAL RX channel)
50       JP7      - Jumper, not shorted (enables SERIAL RX channel)
51       JP8      - Jumper, set to 1-2 (selects CLOCK SOURCE)
52 */
53 
54 #include "emu.h"
55 #include "machine/m1comm.h"
56 #include "emuopts.h"
57 
58 #define Z80_TAG     "commcpu"
59 
60 /*************************************
61  *  M1COMM Memory Map
62  *************************************/
m1comm_mem(address_map & map)63 void m1comm_device::m1comm_mem(address_map &map)
64 {
65 	map(0x0000, 0x7fff).rom();
66 	map(0x8000, 0x9fff).ram();
67 	map(0xc000, 0xffff).mask(0x0fff).rw(FUNC(m1comm_device::share_r), FUNC(m1comm_device::share_w));
68 }
69 
70 /*************************************
71  *  M1COMM I/O Map
72  *************************************/
m1comm_io(address_map & map)73 void m1comm_device::m1comm_io(address_map &map)
74 {
75 	map.global_mask(0x7f);
76 	map(0x00, 0x1f).rw(m_dlc, FUNC(mb89374_device::read), FUNC(mb89374_device::write));
77 	map(0x20, 0x2f).rw(m_dma, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
78 	map(0x40, 0x5f).mask(0x01).rw(FUNC(m1comm_device::syn_r), FUNC(m1comm_device::syn_w));
79 	map(0x60, 0x7f).mask(0x01).rw(FUNC(m1comm_device::zfg_r), FUNC(m1comm_device::zfg_w));
80 }
81 
82 ROM_START( m1comm )
83 	ROM_REGION( 0x20000, Z80_TAG, ROMREGION_ERASEFF )
84 	ROM_DEFAULT_BIOS("epr15112")
85 
86 	// found on Virtua Racing and WingWar
87 	ROM_SYSTEM_BIOS( 0, "epr15112", "EPR-15112" )
88 	ROMX_LOAD( "epr-15112.17", 0x0000, 0x20000, CRC(4950e771) SHA1(99014124e0324dd114cb22f55159d18b597a155a), ROM_BIOS(0) )
89 
90 	// found on Virtua Formula
91 	ROM_SYSTEM_BIOS( 1, "epr15624", "EPR-15624" )
92 	ROMX_LOAD( "epr-15624.17", 0x0000, 0x20000, CRC(9b3ba315) SHA1(0cd0983cc8b2f2d6b41617d0d0a24cc6c188e62a), ROM_BIOS(1) )
93 ROM_END
94 
95 //**************************************************************************
96 //  GLOBAL VARIABLES
97 //**************************************************************************
98 
99 DEFINE_DEVICE_TYPE(M1COMM, m1comm_device, "m1comm", "Model-1 Communication Board")
100 
101 //-------------------------------------------------
102 //  device_add_mconfig - add device configuration
103 //-------------------------------------------------
104 
device_add_mconfig(machine_config & config)105 void m1comm_device::device_add_mconfig(machine_config &config)
106 {
107 	Z80(config, m_cpu, 8000000); // 32 MHz / 4
108 	m_cpu->set_memory_map(&m1comm_device::m1comm_mem);
109 	m_cpu->set_io_map(&m1comm_device::m1comm_io);
110 
111 	AM9517A(config, m_dma, 8000000); // 32 MHz / 4
112 	m_dma->out_hreq_callback().set(FUNC(m1comm_device::dma_hreq_w));
113 	m_dma->in_memr_callback().set(FUNC(m1comm_device::dma_mem_r));
114 	m_dma->out_memw_callback().set(FUNC(m1comm_device::dma_mem_w));
115 	m_dma->out_dack_callback<2>().set(m_dlc, FUNC(mb89374_device::pi3_w));
116 	m_dma->out_dack_callback<3>().set(m_dlc, FUNC(mb89374_device::pi2_w));
117 	m_dma->out_eop_callback().set(m_dlc, FUNC(mb89374_device::ci_w));
118 	m_dma->in_ior_callback<2>().set(m_dlc, FUNC(mb89374_device::dma_r));
119 	m_dma->out_iow_callback<3>().set(m_dlc, FUNC(mb89374_device::dma_w));
120 
121 	MB89374(config, m_dlc, 8000000); // 32 MHz / 4
122 	m_dlc->out_po_callback<2>().set(m_dma, FUNC(am9517a_device::dreq3_w));
123 	m_dlc->out_po_callback<3>().set(m_dma, FUNC(am9517a_device::dreq2_w));
124 	m_dlc->out_irq_callback().set(FUNC(m1comm_device::dlc_int7_w));
125 }
126 
127 //-------------------------------------------------
128 //  rom_region - device-specific ROM region
129 //-------------------------------------------------
device_rom_region() const130 const tiny_rom_entry *m1comm_device::device_rom_region() const
131 {
132 	return ROM_NAME( m1comm );
133 }
134 
135 //**************************************************************************
136 //  LIVE DEVICE
137 //**************************************************************************
138 
139 //-------------------------------------------------
140 //  m1comm_device - constructor
141 //-------------------------------------------------
142 
m1comm_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)143 m1comm_device::m1comm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
144 	device_t(mconfig, M1COMM, tag, owner, clock),
145 	m_cpu(*this, Z80_TAG),
146 	m_dma(*this, "commdma"),
147 	m_dlc(*this, "commdlc")
148 {
149 #ifdef M1COMM_SIMULATION
150 	// prepare localhost "filename"
151 	m_localhost[0] = 0;
152 	strcat(m_localhost, "socket.");
153 	strcat(m_localhost, mconfig.options().comm_localhost());
154 	strcat(m_localhost, ":");
155 	strcat(m_localhost, mconfig.options().comm_localport());
156 
157 	// prepare remotehost "filename"
158 	m_remotehost[0] = 0;
159 	strcat(m_remotehost, "socket.");
160 	strcat(m_remotehost, mconfig.options().comm_remotehost());
161 	strcat(m_remotehost, ":");
162 	strcat(m_remotehost, mconfig.options().comm_remoteport());
163 
164 	m_framesync = mconfig.options().comm_framesync() ? 0x01 : 0x00;
165 #endif
166 }
167 
168 //-------------------------------------------------
169 //  device_start - device-specific startup
170 //-------------------------------------------------
171 
device_start()172 void m1comm_device::device_start()
173 {
174 }
175 
176 //-------------------------------------------------
177 //  device_reset - device-specific reset
178 //-------------------------------------------------
179 
device_reset()180 void m1comm_device::device_reset()
181 {
182 	m_syn = 0;
183 	m_zfg = 0;
184 	m_cn = 0;
185 	m_fg = 0;
186 }
187 
device_reset_after_children()188 void m1comm_device::device_reset_after_children()
189 {
190 	m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
191 	m_dma->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
192 	m_dlc->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
193 }
194 
WRITE_LINE_MEMBER(m1comm_device::dma_hreq_w)195 WRITE_LINE_MEMBER(m1comm_device::dma_hreq_w)
196 {
197 	m_cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
198 	m_dma->hack_w(state);
199 }
200 
dma_mem_r(offs_t offset)201 uint8_t m1comm_device::dma_mem_r(offs_t offset)
202 {
203 	return m_cpu->space(AS_PROGRAM).read_byte(offset);
204 }
205 
dma_mem_w(offs_t offset,uint8_t data)206 void m1comm_device::dma_mem_w(offs_t offset, uint8_t data)
207 {
208 	m_cpu->space(AS_PROGRAM).write_byte(offset, data);
209 }
210 
WRITE_LINE_MEMBER(m1comm_device::dlc_int7_w)211 WRITE_LINE_MEMBER(m1comm_device::dlc_int7_w)
212 {
213 	m_cpu->set_input_line_and_vector(0, state ? ASSERT_LINE : CLEAR_LINE, 0xff); // Z80
214 }
215 
syn_r()216 uint8_t m1comm_device::syn_r()
217 {
218 	return m_syn | 0xfc;
219 }
220 
syn_w(uint8_t data)221 void m1comm_device::syn_w(uint8_t data)
222 {
223 	m_syn = data & 0x03;
224 }
225 
zfg_r()226 uint8_t m1comm_device::zfg_r()
227 {
228 	return m_zfg | (~m_fg << 7) | 0x7e;
229 }
230 
zfg_w(uint8_t data)231 void m1comm_device::zfg_w(uint8_t data)
232 {
233 	m_zfg = data & 0x01;
234 }
235 
share_r(offs_t offset)236 uint8_t m1comm_device::share_r(offs_t offset)
237 {
238 	return m_shared[offset];
239 }
240 
share_w(offs_t offset,uint8_t data)241 void m1comm_device::share_w(offs_t offset, uint8_t data)
242 {
243 	m_shared[offset] = data;
244 }
245 
cn_r()246 uint8_t m1comm_device::cn_r()
247 {
248 	return m_cn | 0xfe;
249 }
250 
cn_w(uint8_t data)251 void m1comm_device::cn_w(uint8_t data)
252 {
253 	m_cn = data & 0x01;
254 
255 #ifndef M1COMM_SIMULATION
256 	if (!m_cn)
257 	{
258 		device_reset();
259 		device_reset_after_children();
260 	}
261 	else
262 	{
263 		m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
264 		m_dma->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
265 		m_dlc->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
266 	}
267 #else
268 	if (!m_cn)
269 	{
270 		// reset command
271 		osd_printf_verbose("M1COMM: board disabled\n");
272 		m_linkenable = 0x00;
273 		m_zfg = 0x00;
274 	}
275 	else
276 	{
277 		// init command
278 		osd_printf_verbose("M1COMM: board enabled\n");
279 		m_linkenable = 0x01;
280 		m_linkid = 0x00;
281 		m_linkalive = 0x00;
282 		m_linkcount = 0x00;
283 		m_linktimer = 0x00e8; // 58 fps * 4s
284 	}
285 #endif
286 }
287 
fg_r()288 uint8_t m1comm_device::fg_r()
289 {
290 	return m_fg | (~m_zfg << 7) | 0x7e;
291 }
292 
fg_w(uint8_t data)293 void m1comm_device::fg_w(uint8_t data)
294 {
295 	if (!m_cn)
296 		return;
297 
298 	m_fg = data & 0x01;
299 }
300 
check_vint_irq()301 void m1comm_device::check_vint_irq()
302 {
303 #ifndef M1COMM_SIMULATION
304 	if (m_syn & 0x02)
305 	{
306 		m_cpu->set_input_line_and_vector(0, HOLD_LINE, 0xef); // Z80
307 	}
308 #else
309 	comm_tick();
310 #endif
311 }
312 
313 #ifdef M1COMM_SIMULATION
comm_tick()314 void m1comm_device::comm_tick()
315 {
316 	if (m_linkenable == 0x01)
317 	{
318 		int frameStart = 0x0010;
319 		int frameOffset = 0x0000;
320 		int frameSize = 0x01c4;
321 		int dataSize = frameSize + 1;
322 		int recv = 0;
323 		int idx = 0;
324 
325 		bool isMaster = (m_shared[1] == 0x01);
326 		bool isSlave = (m_shared[1] == 0x02);
327 		bool isRelay = (m_shared[1] == 0x00);
328 
329 		if (m_linkalive == 0x02)
330 		{
331 			// link failed...
332 			m_shared[0] = 0xff;
333 			return;
334 		}
335 		else if (m_linkalive == 0x00)
336 		{
337 			// link not yet established...
338 			m_shared[0] = 0x05;
339 
340 			// check rx socket
341 			if (!m_line_rx)
342 			{
343 				osd_printf_verbose("M1COMM: listen on %s\n", m_localhost);
344 				uint64_t filesize; // unused
345 				osd_file::open(m_localhost, OPEN_FLAG_CREATE, m_line_rx, filesize);
346 			}
347 
348 			// check tx socket
349 			if (!m_line_tx)
350 			{
351 				osd_printf_verbose("M1COMM: connect to %s\n", m_remotehost);
352 				uint64_t filesize; // unused
353 				osd_file::open(m_remotehost, 0, m_line_tx, filesize);
354 			}
355 
356 			// if both sockets are there check ring
357 			if (m_line_rx && m_line_tx)
358 			{
359 				// try to read one message
360 				recv = read_frame(dataSize);
361 				while (recv > 0)
362 				{
363 					// check message id
364 					idx = m_buffer0[0];
365 
366 					// 0xFF - link id
367 					if (idx == 0xff)
368 					{
369 						if (isMaster)
370 						{
371 							// master gets first id and starts next state
372 							m_linkid = 0x01;
373 							m_linkcount = m_buffer0[1];
374 							m_linktimer = 0x00;
375 						}
376 						else
377 						{
378 							// slave get own id, relay does nothing
379 							if (isSlave)
380 							{
381 								m_buffer0[1]++;
382 								m_linkid = m_buffer0[1];
383 							}
384 
385 							// forward message to other nodes
386 							send_frame(dataSize);
387 						}
388 					}
389 
390 					// 0xFE - link size
391 					else if (idx == 0xfe)
392 					{
393 						if (isSlave || isRelay)
394 						{
395 							m_linkcount = m_buffer0[1];
396 
397 							// forward message to other nodes
398 							send_frame(dataSize);
399 						}
400 
401 						// consider it done
402 						osd_printf_verbose("M1COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount);
403 						m_linkalive = 0x01;
404 						m_zfg = 0x01;
405 
406 						// write to shared mem
407 						m_shared[0] = 0x01;
408 						m_shared[2] = m_linkid;
409 						m_shared[3] = m_linkcount;
410 					}
411 
412 
413 					if (m_linkalive == 0x00)
414 						recv = read_frame(dataSize);
415 					else
416 						recv = 0;
417 				}
418 
419 				// if we are master and link is not yet established
420 				if (isMaster && (m_linkalive == 0x00))
421 				{
422 					// send first packet
423 					if (m_linktimer == 0x01)
424 					{
425 						m_buffer0[0] = 0xff;
426 						m_buffer0[1] = 0x01;
427 						send_frame(dataSize);
428 					}
429 
430 					// send second packet
431 					else if (m_linktimer == 0x00)
432 					{
433 						m_buffer0[0] = 0xfe;
434 						m_buffer0[1] = m_linkcount;
435 						send_frame(dataSize);
436 
437 						// consider it done
438 						osd_printf_verbose("M1COMM: link established - id %02x of %02x\n", m_linkid, m_linkcount);
439 						m_linkalive = 0x01;
440 						m_zfg = 0x01;
441 
442 						// write to shared mem
443 						m_shared[0] = 0x01;
444 						m_shared[2] = m_linkid;
445 						m_shared[3] = m_linkcount;
446 					}
447 
448 					else if (m_linktimer > 0x01)
449 					{
450 						// decrease delay timer
451 						m_linktimer--;
452 					}
453 				}
454 			}
455 		}
456 
457 		if (m_linkalive == 0x01)
458 		{
459 			// link established
460 			do
461 			{
462 				// try to read a message
463 				recv = read_frame(dataSize);
464 				while (recv > 0)
465 				{
466 					// check if valid id
467 					idx = m_buffer0[0];
468 					if (idx > 0 && idx <= m_linkcount)
469 					{
470 						// if not own message
471 						if (idx != m_linkid)
472 						{
473 							// save message to "ring buffer"
474 							frameOffset = frameStart + (idx * frameSize);
475 							for (int j = 0x00 ; j < frameSize ; j++)
476 							{
477 								m_shared[frameOffset + j] = m_buffer0[1 + j];
478 							}
479 
480 							// forward message to other nodes
481 							send_frame(dataSize);
482 						}
483 					}
484 					else
485 					{
486 						if (idx == 0xfc)
487 						{
488 							// 0xFC - VSYNC
489 							m_linktimer = 0x00;
490 							if (!isMaster)
491 								// forward message to other nodes
492 								send_frame(dataSize);
493 						}
494 						if (idx == 0xfd)
495 						{
496 							// 0xFD - master addional bytes
497 							if (!isMaster)
498 							{
499 								// save message to "ring buffer"
500 								frameOffset = 0x06;
501 								for (int j = 0x00 ; j < 0x0a ; j++)
502 								{
503 									m_shared[frameOffset + j] = m_buffer0[1 + j];
504 								}
505 
506 								// forward message to other nodes
507 								send_frame(dataSize);
508 							}
509 						}
510 					}
511 
512 					// try to read another message
513 					recv = read_frame(dataSize);
514 				}
515 			}
516 			while (m_linktimer == 0x01);
517 
518 			// enable wait for vsync
519 			m_linktimer = m_framesync;
520 
521 			// update "ring buffer" if link established
522 			// live relay does not send data
523 			if (m_linkid != 0x00)
524 			{
525 				// check ready-to-send flag
526 				if (m_shared[4] != 0x00)
527 				{
528 					send_data(m_linkid, frameStart, frameSize, dataSize);
529 
530 					// save message to "ring buffer"
531 					frameOffset = frameStart + (m_linkid * frameSize);
532 					for (int j = 0x00 ; j < frameSize ; j++)
533 					{
534 						m_shared[frameOffset + j] = m_buffer0[1 + j];
535 					}
536 				}
537 
538 				if (isMaster)
539 				{
540 					// master sends additional status bytes
541 					send_data(0xfd, 0x06, 0x0a, dataSize);
542 
543 					// send vsync
544 					m_buffer0[0] = 0xfc;
545 					m_buffer0[1] = 0x01;
546 					send_frame(dataSize);
547 				}
548 			}
549 
550 			// clear 05
551 			m_shared[5] = 0x00;
552 		}
553 	}
554 }
555 
read_frame(int dataSize)556 int m1comm_device::read_frame(int dataSize)
557 {
558 	if (!m_line_rx)
559 		return 0;
560 
561 	// try to read a message
562 	std::uint32_t recv = 0;
563 	osd_file::error filerr = m_line_rx->read(m_buffer0, 0, dataSize, recv);
564 	if (recv > 0)
565 	{
566 		// check if message complete
567 		if (recv != dataSize)
568 		{
569 			// only part of a message - read on
570 			std::uint32_t togo = dataSize - recv;
571 			int offset = recv;
572 			while (togo > 0)
573 			{
574 				filerr = m_line_rx->read(m_buffer1, 0, togo, recv);
575 				if (recv > 0)
576 				{
577 					for (int i = 0 ; i < recv ; i++)
578 					{
579 						m_buffer0[offset + i] = m_buffer1[i];
580 					}
581 					togo -= recv;
582 					offset += recv;
583 				}
584 				else if (filerr == osd_file::error::NONE && recv == 0)
585 				{
586 					togo = 0;
587 				}
588 			}
589 		}
590 	}
591 	else if (filerr == osd_file::error::NONE && recv == 0)
592 	{
593 		if (m_linkalive == 0x01)
594 		{
595 			osd_printf_verbose("M1COMM: rx connection lost\n");
596 			m_linkalive = 0x02;
597 			m_linktimer = 0x00;
598 
599 			m_shared[0] = 0xff;
600 
601 			m_line_rx.reset();
602 			m_line_tx.reset();
603 		}
604 	}
605 	return recv;
606 }
607 
send_data(uint8_t frameType,int frameStart,int frameSize,int dataSize)608 void m1comm_device::send_data(uint8_t frameType, int frameStart, int frameSize, int dataSize)
609 {
610 	m_buffer0[0] = frameType;
611 	for (int i = 0x00 ; i < frameSize ; i++)
612 	{
613 		m_buffer0[1 + i] = m_shared[frameStart + i];
614 	}
615 	send_frame(dataSize);
616 }
617 
send_frame(int dataSize)618 void m1comm_device::send_frame(int dataSize){
619 	if (!m_line_tx)
620 		return;
621 
622 	osd_file::error filerr;
623 	std::uint32_t written;
624 
625 	filerr = m_line_tx->write(&m_buffer0, 0, dataSize, written);
626 	if (filerr != osd_file::error::NONE)
627 	{
628 		if (m_linkalive == 0x01)
629 		{
630 			osd_printf_verbose("M1COMM: tx connection lost\n");
631 			m_linkalive = 0x02;
632 			m_linktimer = 0x00;
633 
634 			m_shared[0] = 0xff;
635 
636 			m_line_rx.reset();
637 			m_line_tx.reset();
638 		}
639 	}
640 }
641 #endif
642