1 /*
2  * Z80SIM  -  a Z80-CPU simulator
3  *
4  * Common I/O devices used by various simulated machines
5  *
6  * Copyright (C) 2014-2018 by Udo Munk
7  *
8  * Emulation of a Cromemco TU-ART S100 board
9  *
10  * History:
11  *    DEC-14 first version
12  *    JAN-15 better subdue of non printable characters in output
13  * 02-FEB-15 implemented the timers and interrupt flag for TBE
14  * 05-FEB-15 implemented interrupt flag for RDA
15  * 14-FEB-15 improvements, so that the Cromix tty driver works
16  * 10-MAR-15 lpt's implemented for CP/M, CDOS and Cromix
17  * 23-MAR-15 drop only null's
18  * 26-MAR-15 tty's implemented for CDOS and Cromix
19  * 25-APR-18 cleanup
20  * 03-MAY-18 improved accuracy
21  * 15-JUL-18 use logging
22  */
23 
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <sys/poll.h>
30 #include "sim.h"
31 #include "simglb.h"
32 #include "log.h"
33 #include "unix_terminal.h"
34 #include "unix_network.h"
35 
36 static const char *TAG = "TU-ART";
37 
38 /************************/
39 /*	Device 0A	*/
40 /************************/
41 
42 int uart0a_int_mask, uart0a_int, uart0a_int_pending, uart0a_rst7;
43 int uart0a_timer1, uart0a_timer2, uart0a_timer3, uart0a_timer4, uart0a_timer5;
44 int uart0a_tbe, uart0a_rda;
45 
46 /*
47  * D7	Transmit Buffer Empty
48  * D6	Read Data Available
49  * D5	Interrupt Pending
50  * D4	Start Bit Detected
51  * D3	Full Bit Detected
52  * D2	Serial Rcv
53  * D1	Overrun Error
54  * D0	Frame Error
55  */
cromemco_tuart_0a_status_in(void)56 BYTE cromemco_tuart_0a_status_in(void)
57 {
58 	BYTE status = 4;
59 
60 	if (uart0a_tbe)
61 		status |= 128;
62 
63 	if (uart0a_rda)
64 		status |= 64;
65 
66 	if (uart0a_int_pending)
67 		status |= 32;
68 
69 	return(status);
70 }
71 
72 /*
73  * D7	Stop Bits	high=1 low=2
74  * D6	9600		A high one of the lower seven bits selects the
75  * D5	4800		corresponding baud rate. If more than one bit is high,
76  * D4	2400		the highest rate selected will result. If none of the
77  * D3	1200		bits are high, the serial transmitter and receiver will
78  * D2	300		be disabled.
79  * D1	150
80  * D0	110
81  */
cromemco_tuart_0a_baud_out(BYTE data)82 void cromemco_tuart_0a_baud_out(BYTE data)
83 {
84 	data = data;	/* to avoid compiler warning */
85 }
86 
cromemco_tuart_0a_data_in(void)87 BYTE cromemco_tuart_0a_data_in(void)
88 {
89 	BYTE data;
90 	static BYTE last;
91 	struct pollfd p[1];
92 
93 	uart0a_rda = 0;
94 
95 again:
96 	/* if no input waiting return last */
97 	p[0].fd = fileno(stdin);
98 	p[0].events = POLLIN;
99 	p[0].revents = 0;
100 	poll(p, 1, 0);
101 	if (!(p[0].revents & POLLIN))
102 		return(last);
103 
104 	if (read(fileno(stdin), &data, 1) == 0) {
105 		/* try to reopen tty, input redirection exhausted */
106 		freopen("/dev/tty", "r", stdin);
107 		set_unix_terminal();
108 		goto again;
109 	}
110 
111 	/* process read data */
112 	last = data;
113 	return(data);
114 }
115 
cromemco_tuart_0a_data_out(BYTE data)116 void cromemco_tuart_0a_data_out(BYTE data)
117 {
118 	data &= 0x7f;
119 	uart0a_tbe = 0;
120 	if (data == 0x00)
121 		return;
122 
123 again:
124 	if (write(fileno(stdout), (char *) &data, 1) != 1) {
125 		if (errno == EINTR) {
126 			goto again;
127 		} else {
128 			LOGE(TAG, "can't write tu-art 0a data");
129 			cpu_error = IOERROR;
130 			cpu_state = STOPPED;
131 		}
132 	}
133 }
134 
135 /*
136  * D7	Not Used
137  * D6	Not Used
138  * D5	Test
139  * D4	High Baud
140  * D3	INTA Enable
141  * D2	RST7 Select
142  * D1	Break
143  * D0	Reset
144  */
cromemco_tuart_0a_command_out(BYTE data)145 void cromemco_tuart_0a_command_out(BYTE data)
146 {
147 	uart0a_rst7 = (data & 4) ? 1 : 0;
148 
149 	if (data & 1) {
150 		uart0a_rda = 0;
151 		uart0a_tbe = 1;
152 		uart0a_timer1 = 0;
153 		uart0a_timer2 = 0;
154 		uart0a_timer3 = 0;
155 		uart0a_timer4 = 0;
156 		uart0a_timer5 = 0;
157 		uart0a_int_pending = 0;
158 	}
159 }
160 
161 /*
162  * C7	Timer 1
163  * CF	Timer 2
164  * D7	!Sens
165  * DF	Timer 3
166  * E7	Receiver Data Available
167  * EF	Transmitter Buffer Supply
168  * F7	Timer 4
169  * FF	Timer 5 or PI7
170  */
cromemco_tuart_0a_interrupt_in(void)171 BYTE cromemco_tuart_0a_interrupt_in(void)
172 {
173 	return((BYTE) uart0a_int);
174 }
175 
176 /*
177  * D7	Timer 5 or PI7
178  * D6	Timer 4
179  * D5	TBE
180  * D4	RDA
181  * D3	Timer 3
182  * D2	!Sens
183  * D1	Timer 2
184  * D0	Timer 1
185  */
cromemco_tuart_0a_interrupt_out(BYTE data)186 void cromemco_tuart_0a_interrupt_out(BYTE data)
187 {
188 	uart0a_int_mask = data;
189 }
190 
191 /*
192  *	The parallel port is used on the FDC's
193  *	for auxiliary disk control/status,
194  *	so don't implement something here.
195  */
196 
cromemco_tuart_0a_parallel_in(void)197 BYTE cromemco_tuart_0a_parallel_in(void)
198 {
199 	return((BYTE) 0);
200 }
201 
cromemco_tuart_0a_parallel_out(BYTE data)202 void cromemco_tuart_0a_parallel_out(BYTE data)
203 {
204 	data = data;	/* to avoid compiler warning */
205 }
206 
cromemco_tuart_0a_timer1_out(BYTE data)207 void cromemco_tuart_0a_timer1_out(BYTE data)
208 {
209 	uart0a_timer1 = data;
210 }
211 
cromemco_tuart_0a_timer2_out(BYTE data)212 void cromemco_tuart_0a_timer2_out(BYTE data)
213 {
214 	uart0a_timer2 = data;
215 }
216 
cromemco_tuart_0a_timer3_out(BYTE data)217 void cromemco_tuart_0a_timer3_out(BYTE data)
218 {
219 	uart0a_timer3 = data;
220 }
221 
cromemco_tuart_0a_timer4_out(BYTE data)222 void cromemco_tuart_0a_timer4_out(BYTE data)
223 {
224 	uart0a_timer4 = data;
225 }
226 
cromemco_tuart_0a_timer5_out(BYTE data)227 void cromemco_tuart_0a_timer5_out(BYTE data)
228 {
229 	uart0a_timer5 = data;
230 }
231 
232 /************************/
233 /*	Device 1A	*/
234 /************************/
235 
236 int uart1a_int_mask;
237 int uart1a_int_mask, uart1a_int, uart1a_int_pending;
238 int uart1a_sense, uart1a_lpt_busy;
239 int uart1a_tbe, uart1a_rda;
240 
cromemco_tuart_1a_status_in(void)241 BYTE cromemco_tuart_1a_status_in(void)
242 {
243 	BYTE status = (ncons[0].ssc) ? 4 : 0;
244 
245 	if (uart1a_tbe)
246 		status |= 128;
247 
248 	if (uart1a_rda)
249 		status |= 64;
250 
251 	if (uart1a_int_pending)
252 		status |= 32;
253 
254 	return(status);
255 }
256 
cromemco_tuart_1a_baud_out(BYTE data)257 void cromemco_tuart_1a_baud_out(BYTE data)
258 {
259 	data = data;	/* to avoid compiler warning */
260 }
261 
cromemco_tuart_1a_data_in(void)262 BYTE cromemco_tuart_1a_data_in(void)
263 {
264 	BYTE data, dummy;
265 	static BYTE last;
266 	struct pollfd p[1];
267 
268 	uart1a_rda = 0;
269 
270 	/* if not connected return last */
271 	if (ncons[0].ssc == 0)
272 		return(last);
273 
274 	/* if no input waiting return last */
275 	p[0].fd = ncons[0].ssc;
276 	p[0].events = POLLIN;
277 	p[0].revents = 0;
278 	poll(p, 1, 0);
279 	if (!(p[0].revents & POLLIN))
280 		return(last);
281 
282 	if (read(ncons[0].ssc, &data, 1) != 1) {
283 		if ((errno == EAGAIN) || (errno == EINTR)) {
284 			/* EOF, close socket and return last */
285 			close(ncons[0].ssc);
286 			ncons[0].ssc = 0;
287 			return(last);
288 		} else {
289 			LOGE(TAG, "can't read tu-art 1a data");
290 			cpu_error = IOERROR;
291 			cpu_state = STOPPED;
292 			return(0);
293 		}
294 	}
295 
296 	/* process read data */
297 	/* telnet client sends \r\n or \r\0, drop second character */
298 	if (ncons[0].telnet && (data == '\r'))
299 		read(ncons[0].ssc, &dummy, 1);
300 	last = data;
301 	return(data);
302 }
303 
cromemco_tuart_1a_data_out(BYTE data)304 void cromemco_tuart_1a_data_out(BYTE data)
305 {
306 	uart1a_tbe = 0;
307 	if (ncons[0].ssc == 0)
308 		return;
309 	data &= 0x7f;
310 	if (data == 0x00)
311 		return;
312 
313 again:
314 	if (write(ncons[0].ssc, (char *) &data, 1) != 1) {
315 		if (errno == EINTR) {
316 			goto again;
317 		} else {
318 			LOGE(TAG, "can't write tu-art 1a data");
319 			cpu_error = IOERROR;
320 			cpu_state = STOPPED;
321 		}
322 	}
323 }
324 
cromemco_tuart_1a_command_out(BYTE data)325 void cromemco_tuart_1a_command_out(BYTE data)
326 {
327 	if (data & 1) {
328 		uart1a_rda = 0;
329 		uart1a_tbe = 1;
330 		uart1a_int_pending = 0;
331 	}
332 }
333 
cromemco_tuart_1a_interrupt_in(void)334 BYTE cromemco_tuart_1a_interrupt_in(void)
335 {
336 	return((BYTE) uart1a_int);
337 }
338 
cromemco_tuart_1a_interrupt_out(BYTE data)339 void cromemco_tuart_1a_interrupt_out(BYTE data)
340 {
341 	uart1a_int_mask = data;
342 }
343 
cromemco_tuart_1a_parallel_in(void)344 BYTE cromemco_tuart_1a_parallel_in(void)
345 {
346 	if (uart1a_lpt_busy == 0)
347 		return((BYTE) ~0x20);
348 	else
349 		return((BYTE) 0xff);
350 }
351 
cromemco_tuart_1a_parallel_out(BYTE data)352 void cromemco_tuart_1a_parallel_out(BYTE data)
353 {
354 	extern int lpt2;
355 
356 	if (lpt2 == 0)
357 		lpt2 = creat("lpt2.txt", 0664);
358 
359 	uart1a_sense = 1;
360 	uart1a_lpt_busy = 1;
361 
362 	/* bit 7 is strobe, every byte is send 3 times. First with
363 	   strobe on, then with strobe off, then with on again.
364 	   Take over data when strobe not set */
365 
366 	if (!(data & 0x80) && (data != '\r')) {
367 again:
368 		if (write(lpt2, (char *) &data, 1) != 1) {
369 			if (errno == EINTR) {
370 				goto again;
371 			} else {
372 				LOGE(TAG, "can't write lpt2.txt");
373 				cpu_error = IOERROR;
374 				cpu_state = STOPPED;
375 			}
376 		}
377 	}
378 }
379 
380 /************************/
381 /*	Device 1B	*/
382 /************************/
383 
384 int uart1b_int_mask, uart1b_int, uart1b_int_pending;
385 int uart1b_sense, uart1b_lpt_busy;
386 int uart1b_tbe, uart1b_rda;
387 
cromemco_tuart_1b_status_in(void)388 BYTE cromemco_tuart_1b_status_in(void)
389 {
390 	BYTE status = (ncons[1].ssc) ? 4 : 0;
391 
392 	if (uart1b_tbe)
393 		status |= 128;
394 
395 	if (uart1b_rda)
396 		status |= 64;
397 
398 	if (uart1b_int_pending)
399 		status |= 32;
400 
401 	return(status);
402 }
403 
cromemco_tuart_1b_baud_out(BYTE data)404 void cromemco_tuart_1b_baud_out(BYTE data)
405 {
406 	data = data;	/* to avoid compiler warning */
407 }
408 
cromemco_tuart_1b_data_in(void)409 BYTE cromemco_tuart_1b_data_in(void)
410 {
411 	BYTE data, dummy;
412 	static BYTE last;
413 	struct pollfd p[1];
414 
415 	uart1b_rda = 0;
416 
417 	/* if not connected return last */
418 	if (ncons[1].ssc == 0)
419 		return(last);
420 
421 	/* if no input waiting return last */
422 	p[0].fd = ncons[1].ssc;
423 	p[0].events = POLLIN;
424 	p[0].revents = 0;
425 	poll(p, 1, 0);
426 	if (!(p[0].revents & POLLIN))
427 		return(last);
428 
429 	if (read(ncons[1].ssc, &data, 1) != 1) {
430 		if ((errno == EAGAIN) || (errno == EINTR)) {
431 			/* EOF, close socket and return last */
432 			close(ncons[1].ssc);
433 			ncons[1].ssc = 0;
434 			return(last);
435 		} else {
436 			LOGE(TAG, "can't read tu-art 1b data");
437 			cpu_error = IOERROR;
438 			cpu_state = STOPPED;
439 			return(0);
440 		}
441 	}
442 
443 	/* process read data */
444 	/* telnet client sends \r\n or \r\0, drop second character */
445 	if (ncons[1].telnet && (data == '\r'))
446 		read(ncons[1].ssc, &dummy, 1);
447 	last = data;
448 	return(data);
449 }
450 
cromemco_tuart_1b_data_out(BYTE data)451 void cromemco_tuart_1b_data_out(BYTE data)
452 {
453 	uart1b_tbe = 0;
454 	if (ncons[1].ssc == 0)
455 		return;
456 	data &= 0x7f;
457 	if (data == 0x00)
458 		return;
459 
460 again:
461 	if (write(ncons[1].ssc, (char *) &data, 1) != 1) {
462 		if (errno == EINTR) {
463 			goto again;
464 		} else {
465 			LOGE(TAG, "can't write tu-art 1b data");
466 			cpu_error = IOERROR;
467 			cpu_state = STOPPED;
468 		}
469 	}
470 }
471 
cromemco_tuart_1b_command_out(BYTE data)472 void cromemco_tuart_1b_command_out(BYTE data)
473 {
474 	if (data & 1) {
475 		uart1b_rda = 0;
476 		uart1b_tbe = 1;
477 		uart1b_int_pending = 0;
478 	}
479 }
480 
cromemco_tuart_1b_interrupt_in(void)481 BYTE cromemco_tuart_1b_interrupt_in(void)
482 {
483 	return((BYTE) uart1b_int);
484 }
485 
cromemco_tuart_1b_interrupt_out(BYTE data)486 void cromemco_tuart_1b_interrupt_out(BYTE data)
487 {
488 	uart1b_int_mask = data;
489 }
490 
cromemco_tuart_1b_parallel_in(void)491 BYTE cromemco_tuart_1b_parallel_in(void)
492 {
493 	if (uart1b_lpt_busy == 0)
494 		return((BYTE) ~0x20);
495 	else
496 		return((BYTE) 0xff);
497 }
498 
cromemco_tuart_1b_parallel_out(BYTE data)499 void cromemco_tuart_1b_parallel_out(BYTE data)
500 {
501 	extern int lpt1;
502 
503 	if (lpt1 == 0)
504 		lpt1 = creat("lpt1.txt", 0664);
505 
506 	uart1b_sense = 1;
507 	uart1b_lpt_busy = 1;
508 
509 	/* bit 7 is strobe, every byte is send 3 times. First with
510 	   strobe on, then with strobe off, then with on again.
511 	   Take over data when strobe not set */
512 
513 	if (!(data & 0x80) && (data != '\r')) {
514 again:
515 		if (write(lpt1, (char *) &data, 1) != 1) {
516 			if (errno == EINTR) {
517 				goto again;
518 			} else {
519 				LOGE(TAG, "can't write lpt1.txt");
520 				cpu_error = IOERROR;
521 				cpu_state = STOPPED;
522 			}
523 		}
524 	}
525 }
526