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