forked from tonioni/WinUAE
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserial.cpp
413 lines (346 loc) · 8.46 KB
/
serial.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
* UAE - The Un*x Amiga Emulator
*
* Serial Line Emulation
*
* (c) 1996, 1997 Stefan Reinauer <stepan@linux.de>
* (c) 1997 Christian Schmitt <schmitt@freiburg.linux.de>
*
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include "config.h"
#include "options.h"
#include "uae.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "cia.h"
#undef POSIX_SERIAL
/* Some more or less good way to determine whether we can safely compile in
* the serial stuff. I'm certain it breaks compilation on some systems. */
#if defined HAVE_SYS_TERMIOS_H && defined HAVE_POSIX_OPT_H && defined HAVE_SYS_IOCTL_H && defined HAVE_TCGETATTR
#define POSIX_SERIAL
#endif
#ifdef POSIX_SERIAL
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#endif
#if !defined B300 || !defined B1200 || !defined B2400 || !defined B4800 || !defined B9600
#undef POSIX_SERIAL
#endif
#if !defined B19200 || !defined B57600 || !defined B115200 || !defined B230400
#undef POSIX_SERIAL
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
#define SERIALDEBUG 1 /* 0, 1, 2 3 */
#define MODEMTEST 0 /* 0 or 1 */
void serial_open (void);
void serial_close (void);
void serial_init (void);
void serial_exit (void);
void serial_dtr_on (void);
void serial_dtr_off (void);
void serial_flush_buffer (void);
static int serial_read (char *buffer);
int serial_readstatus (void);
uae_u16 serial_writestatus (int, int);
uae_u16 SERDATR (void);
int SERDATS (void);
void SERPER (uae_u16 w);
void SERDAT (uae_u16 w);
static char inbuf[1024], outbuf[1024];
static int inptr, inlast, outlast;
int waitqueue=0,
carrier=0,
serdev=0,
dsr=0,
dtr=0,
isbaeh=0,
doreadser=0,
serstat=-1;
int sd = -1;
#ifdef POSIX_SERIAL
struct termios tios;
#endif
uae_u16 serper=0,serdat;
void SERPER (uae_u16 w)
{
int baud=0, pspeed;
if (!currprefs.use_serial)
return;
#if defined POSIX_SERIAL
if (serper == w) /* don't set baudrate if it's already ok */
return;
serper=w;
if (w&0x8000)
write_log ("SERPER: 9bit transmission not implemented.\n");
switch (w & 0x7fff) {
/* These values should be calculated by the current
* color clock value (NTSC/PAL). But this solution is
* easy and it works.
*/
case 0x2e9b:
case 0x2e14: baud=300; pspeed=B300; break;
case 0x170a:
case 0x0b85: baud=1200; pspeed=B1200; break;
case 0x05c2:
case 0x05b9: baud=2400; pspeed=B2400; break;
case 0x02e9:
case 0x02e1: baud=4800; pspeed=B4800; break;
case 0x0174:
case 0x0170: baud=9600; pspeed=B9600; break;
case 0x00b9:
case 0x00b8: baud=19200; pspeed=B19200; break;
case 0x005c:
case 0x005d: baud=38400; pspeed=B38400; break;
case 0x003d: baud=57600; pspeed=B57600; break;
case 0x001e: baud=115200; pspeed=B115200; break;
case 0x000f: baud=230400; pspeed=B230400; break;
default:
write_log ("SERPER: unsupported baudrate (0x%04x) %d\n",w&0x7fff,
(unsigned int)(3579546.471/(double)((w&0x7fff)+1))); return;
}
/* Only access hardware when we own it */
if (serdev == 1) {
if (tcgetattr (sd, &tios) < 0) {
write_log ("SERPER: TCGETATTR failed\n");
return;
}
if (cfsetispeed (&tios, pspeed) < 0) { /* set serial input speed */
write_log ("SERPER: CFSETISPEED (%d bps) failed\n", baud);
return;
}
if (cfsetospeed (&tios, pspeed) < 0) { /* set serial output speed */
write_log ("SERPER: CFSETOSPEED (%d bps) failed\n", baud);
return;
}
if (tcsetattr (sd, TCSADRAIN, &tios) < 0) {
write_log ("SERPER: TCSETATTR failed\n");
return;
}
}
#endif
#if SERIALDEBUG > 0
if (serdev == 1)
write_log ("SERPER: baudrate set to %d bit/sec\n", baud);
#endif
}
/* Not (fully) implemented yet:
*
* - Something's wrong with the Interrupts.
* (NComm works, TERM does not. TERM switches to a
* blind mode after a connect and wait's for the end
* of an asynchronous read before switching blind
* mode off again. It never gets there on UAE :-< )
*
* - RTS/CTS handshake, this is not really neccessary,
* because you can use RTS/CTS "outside" without
* passing it through to the emulated Amiga
*
* - ADCON-Register ($9e write, $10 read) Bit 11 (UARTBRK)
* (see "Amiga Intern", pg 246)
*/
void SERDAT (uae_u16 w)
{
unsigned char z;
if (!currprefs.use_serial)
return;
z = (unsigned char)(w&0xff);
if (currprefs.serial_demand && !dtr) {
if (!isbaeh) {
write_log ("SERDAT: Baeh.. Your software needs SERIAL_ALWAYS to work properly.\n");
isbaeh=1;
}
return;
} else {
outbuf[outlast++] = z;
if (outlast == sizeof outbuf)
serial_flush_buffer();
}
#if SERIALDEBUG > 2
write_log ("SERDAT: wrote 0x%04x\n", w);
#endif
serdat|=0x2000; /* Set TBE in the SERDATR ... */
intreq|=1; /* ... and in INTREQ register */
return;
}
uae_u16 SERDATR (void)
{
if (!currprefs.use_serial)
return 0;
#if SERIALDEBUG > 2
write_log ("SERDATR: read 0x%04x\n", serdat);
#endif
waitqueue = 0;
return serdat;
}
int SERDATS (void)
{
unsigned char z;
if (!serdev) /* || (serdat&0x4000)) */
return 0;
if (waitqueue == 1) {
intreq |= 0x0800;
return 1;
}
if ((serial_read ((char *)&z)) == 1) {
waitqueue = 1;
serdat = 0x4100; /* RBF and STP set! */
serdat |= ((unsigned int)z) & 0xff;
intreq |= 0x0800; /* Set RBF flag (Receive Buffer full) */
#if SERIALDEBUG > 1
write_log ("SERDATS: received 0x%02x --> serdat==0x%04x\n",
(unsigned int)z, (unsigned int)serdat);
#endif
return 1;
}
return 0;
}
void serial_dtr_on(void)
{
#if SERIALDEBUG > 0
write_log ("DTR on.\n");
#endif
dtr=1;
if (currprefs.serial_demand)
serial_open ();
}
void serial_dtr_off(void)
{
#if SERIALDEBUG > 0
write_log ("DTR off.\n");
#endif
dtr=0;
if (currprefs.serial_demand)
serial_close ();
}
static int serial_read (char *buffer)
{
if (inptr < inlast) {
*buffer = inbuf[inptr++];
return 1;
}
if (serdev == 1) {
inlast = read (sd, inbuf, sizeof inbuf);
inptr = 0;
if (inptr < inlast) {
*buffer = inbuf[inptr++];
return 1;
}
}
return 0;
}
void serial_flush_buffer(void)
{
if (serdev == 1) {
if (outlast) {
if (sd != 0) {
write (sd, outbuf, outlast);
}
}
outlast = 0;
} else {
outlast = 0;
inptr = 0;
inlast = 0;
}
}
int serial_readstatus(void)
{
int status = 0;
#ifdef POSIX_SERIAL
ioctl (sd, TIOCMGET, &status);
if (status & TIOCM_CAR) {
if (!carrier) {
ciabpra |= 0x20; /* Push up Carrier Detect line */
carrier = 1;
#if SERIALDEBUG > 0
write_log ("Carrier detect.\n");
#endif
}
} else {
if (carrier) {
ciabpra &= ~0x20;
carrier = 0;
#if SERIALDEBUG > 0
write_log ("Carrier lost.\n");
#endif
}
}
if (status & TIOCM_DSR) {
if (!dsr) {
ciabpra |= 0x08; /* DSR ON */
dsr = 1;
}
} else {
if (dsr) {
ciabpra &= ~0x08;
dsr = 0;
}
}
#endif
return status;
}
uae_u16 serial_writestatus (int old, int nw)
{
if ((old & 0x80) == 0x80 && (nw & 0x80) == 0x00)
serial_dtr_on();
if ((old & 0x80) == 0x00 && (nw & 0x80) == 0x80)
serial_dtr_off();
if ((old & 0x40) != (nw & 0x40))
write_log ("RTS %s.\n", ((nw & 0x40) == 0x40) ? "set" : "cleared");
if ((old & 0x10) != (nw & 0x10))
write_log ("CTS %s.\n", ((nw & 0x10) == 0x10) ? "set" : "cleared");
return nw; /* This value could also be changed here */
}
void serial_open(void)
{
if (serdev == 1)
return;
if ((sd = open (currprefs.sername, O_RDWR|O_NONBLOCK|O_BINARY, 0)) < 0) {
write_log ("Error: Could not open Device %s\n", currprefs.sername);
return;
}
serdev = 1;
#ifdef POSIX_SERIAL
if (tcgetattr (sd, &tios) < 0) { /* Initialize Serial tty */
write_log ("Serial: TCGETATTR failed\n");
return;
}
cfmakeraw (&tios);
#ifndef MODEMTEST
tios.c_cflag &= ~CRTSCTS; /* Disable RTS/CTS */
#else
tios.c_cflag |= CRTSCTS; /* Enabled for testing modems */
#endif
if (tcsetattr (sd, TCSADRAIN, &tios) < 0) {
write_log ("Serial: TCSETATTR failed\n");
return;
}
#endif
}
void serial_close (void)
{
if (sd >= 0)
close (sd);
serdev = 0;
}
void serial_init (void)
{
if (!currprefs.use_serial)
return;
if (!currprefs.serial_demand)
serial_open ();
serdat = 0x2000;
return;
}
void serial_exit (void)
{
serial_close (); /* serial_close can always be called because it */
dtr = 0; /* just closes *opened* filehandles which is ok */
return; /* when exiting. */
}