1 /*****************************************************************************
2 ** $Source: /cygdrive/d/Private/_SVNROOT/bluemsx/blueMSX/Src/IoDevice/I8251.c,v $
3 **
4 ** $Revision: 1.12 $
5 **
6 ** $Date: 2008-03-30 18:38:40 $
7 **
8 ** More info: http://www.bluemsx.com
9 **
10 ** Copyright (C) 2003-2006 Daniel Vik, Tomas Karlsson
11 **
12 ** This program is free software; you can redistribute it and/or modify
13 ** it under the terms of the GNU General Public License as published by
14 ** the Free Software Foundation; either version 2 of the License, or
15 ** (at your option) any later version.
16 **
17 ** This program is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ** GNU General Public License for more details.
21 **
22 ** You should have received a copy of the GNU General Public License
23 ** along with this program; if not, write to the Free Software
24 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 **
26 ******************************************************************************
27 */
28
29 #include "I8251.h"
30 #include "SaveState.h"
31 #include "Board.h"
32 #include "ArchEvent.h"
33 #include <stdlib.h>
34
transmitDummy(void * ref,UInt8 value)35 static int transmitDummy(void* ref, UInt8 value) {
36 return 0;
37 }
38
signalDummy(void * ref)39 static int signalDummy(void* ref) {
40 return 0;
41 }
setDataBitsDummy(void * ref,int value)42 static void setDataBitsDummy(void* ref, int value) {
43 }
setStopBitsDummy(void * ref,int value)44 static void setStopBitsDummy(void* ref, int value) {
45 }
setParityDummy(void * ref,int value)46 static void setParityDummy(void* ref, int value) {
47 }
48
setRxReadyDummy(void * ref,int status)49 static void setRxReadyDummy(void* ref, int status) {
50 }
51
setDtrDummy(void * ref,int status)52 static void setDtrDummy(void* ref, int status) {
53 }
54
setRtsDummy(void * ref,int status)55 static void setRtsDummy(void* ref, int status) {
56 }
57
getDtrDummy(void * ref)58 static int getDtrDummy(void* ref) {
59 return 0;
60 }
61
getRtsDummy(void * ref)62 static int getRtsDummy(void* ref) {
63 return 0;
64 }
65
66 static void onRxPoll(I8251* i8251, UInt32 time);
67
68 #define STAT_TXRDY 0x01
69 #define STAT_RXRDY 0x02
70 #define STAT_TXEMPTY 0x04
71 #define STAT_PE 0x08
72 #define STAT_OE 0x10
73 #define STAT_FE 0x20
74 #define STAT_SYNBRK 0x40
75 #define STAT_DSR 0x80
76
77 #define MODE_BAUDRATE 0x03
78 #define MODE_SYNCHRONOUS 0x00
79 #define MODE_RATE1 0x01
80 #define MODE_RATE16 0x02
81 #define MODE_RATE64 0x03
82 #define MODE_WORDLENGTH 0x0c
83 #define MODE_5BIT 0x00
84 #define MODE_6BIT 0x04
85 #define MODE_7BIT 0x08
86 #define MODE_8BIT 0x0c
87 #define MODE_PARITYEN 0x10
88 #define MODE_PARITODD 0x00
89 #define MODE_PARITEVEN 0x20
90 #define MODE_STOP_BITS 0xc0
91 #define MODE_STOP_INV 0x00
92 #define MODE_STOP_1 0x40
93 #define MODE_STOP_15 0x80
94 #define MODE_STOP_2 0xc0
95 #define MODE_SINGLESYNC 0x80
96
97 #define CMD_TXEN 0x01
98 #define CMD_DTR 0x02
99 #define CMD_RXE 0x04
100 #define CMD_SBRK 0x08
101 #define CMD_RSTERR 0x10
102 #define CMD_RTS 0x20
103 #define CMD_RESET 0x40
104 #define CMD_HUNT 0x80
105
106 #define PHASE_MODE 0
107 #define PHASE_SYNC1 1
108 #define PHASE_SYNC2 2
109 #define PHASE_CMD 3
110
111 #define RX_QUEUE_SIZE 16
112
113 struct I8251
114 {
115 I8251Transmit transmit;
116 I8251Signal signal;
117 I8251Set setDataBits;
118 I8251Set setStopBits;
119 I8251Set setParity;
120 I8251Set setRxReady;
121 I8251Set setDtr;
122 I8251Set setRts;
123 I8251Get getDtr;
124 I8251Get getRts;
125 void* ref;
126
127 BoardTimer* timerRecv;
128 BoardTimer* timerRxPoll;
129 BoardTimer* timerTrans;
130
131 UInt32 timeRecv;
132 UInt32 timeRxPoll;
133 UInt32 timeTrans;
134
135 UInt8 status;
136 UInt8 command;
137 UInt8 mode;
138 UInt8 sync1;
139 UInt8 sync2;
140 int charLength;
141 int cmdFaze;
142
143 int dataBits;
144 int stopBits;
145 int parityEnabled;
146 int parity;
147 UInt8 recvBuf;
148 int recvReady;
149 UInt8 sendByte;
150 UInt8 sendBuffer;
151 int sendBuffered;
152
153 void* semaphore;
154 int rxPending;
155 UInt32 rxHead;
156 UInt8 rxQueue[RX_QUEUE_SIZE];
157 };
158
setMode(I8251 * i8251,UInt8 value)159 static void setMode(I8251* i8251, UInt8 value)
160 {
161 int baudrate = 1;
162 int stopBits = 0;
163 int dataBits = 8;
164 int parityEnable = 1;
165
166 i8251->mode = value;
167
168 switch (value & MODE_WORDLENGTH) {
169 case MODE_5BIT:
170 dataBits = 5;
171 break;
172 case MODE_6BIT:
173 dataBits = 6;
174 break;
175 case MODE_7BIT:
176 dataBits = 7;
177 break;
178 case MODE_8BIT:
179 dataBits = 8;
180 break;
181 }
182
183 i8251->setDataBits(i8251->ref, dataBits);
184
185 switch(value & MODE_STOP_BITS) {
186 case MODE_STOP_INV:
187 stopBits = 0;
188 break;
189 case MODE_STOP_1:
190 stopBits = 2;
191 break;
192 case MODE_STOP_15:
193 stopBits = 3;
194 break;
195 case MODE_STOP_2:
196 stopBits = 4;
197 break;
198 }
199
200 i8251->setStopBits(i8251->ref, stopBits);
201
202 switch(value & (MODE_PARITYEN | MODE_PARITEVEN)) {
203 case MODE_PARITYEN | MODE_PARITEVEN:
204 i8251->setParity(i8251->ref, I8251_PARITY_EVEN);
205 break;
206 case MODE_PARITYEN:
207 i8251->setParity(i8251->ref, I8251_PARITY_ODD);
208 break;
209 default:
210 i8251->setParity(i8251->ref, I8251_PARITY_NONE);
211 }
212
213 switch (value & MODE_BAUDRATE) {
214 case MODE_SYNCHRONOUS:
215 baudrate = 1;
216 break;
217 case MODE_RATE1:
218 baudrate = 1;
219 break;
220 case MODE_RATE16:
221 baudrate = 16;
222 break;
223 case MODE_RATE64:
224 baudrate = 64;
225 break;
226 }
227
228 parityEnable = (value & MODE_PARITYEN) ? 1 : 0;
229 i8251->charLength = (((2 * (1 + dataBits + parityEnable)) + stopBits) * baudrate) / 2;
230 }
231
writeCommand(I8251 * i8251,UInt8 value)232 static void writeCommand(I8251* i8251, UInt8 value)
233 {
234 UInt8 oldCommand = i8251->command;
235 i8251->command = value;
236
237 i8251->setRts(i8251->ref, value & CMD_RTS);
238 i8251->setDtr(i8251->ref, value & CMD_DTR);
239
240 if (!(value & CMD_TXEN)) {
241 boardTimerRemove(i8251->timerTrans);
242 i8251->status |= STAT_TXRDY | STAT_TXEMPTY;
243 }
244 if (value & CMD_RSTERR) {
245 i8251->status &= ~(STAT_PE | STAT_OE | STAT_FE);
246 }
247 if ((value ^ oldCommand) & CMD_RXE) {
248 if (value & CMD_RXE) {
249 i8251->status &= ~(STAT_PE | STAT_OE | STAT_FE);
250 i8251->recvReady = 1;
251 i8251->rxPending = 0;
252 onRxPoll(i8251, boardSystemTime());
253 }
254 else {
255 boardTimerRemove(i8251->timerRecv);
256 boardTimerRemove(i8251->timerRxPoll);
257 i8251->status &= ~(STAT_PE | STAT_OE | STAT_FE);
258 i8251->status &= ~STAT_RXRDY;
259 }
260 i8251->signal(i8251->ref);
261 }
262 }
263
readStatus(I8251 * i8251)264 static UInt8 readStatus(I8251* i8251)
265 {
266 UInt8 result = i8251->status;
267 if (i8251->getDtr(i8251->ref)) {
268 result |= STAT_DSR;
269 }
270 return result;
271 }
272
readTrans(I8251 * i8251)273 static UInt8 readTrans(I8251* i8251)
274 {
275 i8251->status &= ~STAT_RXRDY;
276 i8251->setRxReady(i8251->ref, 0);
277 return i8251->recvBuf;
278 }
279
writeTrans(I8251 * i8251,UInt8 value)280 static void writeTrans(I8251* i8251, UInt8 value)
281 {
282 if (!(i8251->command & CMD_TXEN)) {
283 return;
284 }
285 if (i8251->status & STAT_TXEMPTY) {
286 i8251->status &= ~STAT_TXEMPTY;
287 i8251->sendByte = value;
288
289 i8251->timeTrans = (UInt32)(boardSystemTime() + (UInt64)i8251->charLength * boardFrequency() / 4000000);
290 boardTimerAdd(i8251->timerTrans, i8251->timeTrans);
291 }
292 else {
293 i8251->sendBuffer = value;
294 i8251->status &= ~STAT_TXRDY;
295 }
296 }
297
onTrans(I8251 * i8251,UInt32 time)298 static void onTrans(I8251* i8251, UInt32 time)
299 {
300 i8251->timeTrans = 0;
301
302 i8251->transmit(i8251->ref, i8251->sendByte);
303 if (i8251->status & STAT_TXRDY) {
304 i8251->status |= STAT_TXEMPTY;
305 }
306 else {
307 i8251->status |= STAT_TXRDY;
308 i8251->status &= ~STAT_TXEMPTY;
309 i8251->sendByte = i8251->sendBuffer;
310
311 i8251->timeTrans = (UInt32)(boardSystemTime() + (UInt64)i8251->charLength * boardFrequency() / 4000000);
312 boardTimerAdd(i8251->timerTrans, i8251->timeTrans);
313 }
314 }
315
i8251Peek(I8251 * i8251,UInt16 port)316 UInt8 i8251Peek(I8251* i8251, UInt16 port)
317 {
318 switch (port & 1) {
319 case 0:
320 return i8251->recvBuf;
321 case 1:
322 return readStatus(i8251);
323 }
324 return 0xff;
325 }
326
i8251Read(I8251 * i8251,UInt16 port)327 UInt8 i8251Read(I8251* i8251, UInt16 port)
328 {
329 switch (port & 1) {
330 case 0:
331 return readTrans(i8251);
332 case 1:
333 return readStatus(i8251);
334 }
335 return 0xff;
336 }
337
i8251Write(I8251 * i8251,UInt16 port,UInt8 value)338 void i8251Write(I8251* i8251, UInt16 port, UInt8 value)
339 {
340 switch (port & 1) {
341 case 0:
342 writeTrans(i8251, value);
343 break;
344 case 1:
345 switch (i8251->cmdFaze) {
346 case PHASE_MODE:
347 setMode(i8251, value);
348 if ((i8251->mode & MODE_BAUDRATE) == MODE_SYNCHRONOUS) {
349 i8251->cmdFaze = PHASE_SYNC1;
350 }
351 else {
352 i8251->cmdFaze = PHASE_CMD;
353 }
354 break;
355 case PHASE_SYNC1:
356 i8251->sync1 = value;
357 if (i8251->mode & MODE_SINGLESYNC) {
358 i8251->cmdFaze = PHASE_CMD;
359 }
360 else {
361 i8251->cmdFaze = PHASE_SYNC2;
362 }
363 break;
364 case PHASE_SYNC2:
365 i8251->sync2 = value;
366 i8251->cmdFaze = PHASE_CMD;
367 break;
368 case PHASE_CMD:
369 if (value & CMD_RESET) {
370 i8251->cmdFaze = PHASE_MODE;
371 }
372 else {
373 writeCommand(i8251, value);
374 }
375 break;
376 }
377 break;
378 }
379 }
380
i8251LoadState(I8251 * i8251)381 void i8251LoadState(I8251* i8251)
382 {
383 SaveState* state = saveStateOpenForRead("i8251");
384
385 i8251->timeRecv = saveStateGet(state, "timeRecv", 0);
386 i8251->timeRxPoll = saveStateGet(state, "timeRxPoll", 0);
387 i8251->timeTrans = saveStateGet(state, "timeTrans", 0);
388 i8251->status = (UInt8)saveStateGet(state, "status", 0);
389 i8251->command = (UInt8)saveStateGet(state, "command", 0);
390 i8251->mode = (UInt8)saveStateGet(state, "mode", 0);
391 i8251->sync1 = (UInt8)saveStateGet(state, "sync1", 0);
392 i8251->sync2 = (UInt8)saveStateGet(state, "sync2", 0);
393 i8251->charLength = saveStateGet(state, "charLength", 0);
394 i8251->cmdFaze = saveStateGet(state, "cmdFaze", 0);
395 i8251->dataBits = saveStateGet(state, "dataBits", 0);
396 i8251->stopBits = saveStateGet(state, "stopBits", 0);
397 i8251->parityEnabled = saveStateGet(state, "parityEnabled", 0);
398 i8251->parity = saveStateGet(state, "parity", 0);
399 i8251->recvBuf = (UInt8)saveStateGet(state, "recvBuf", 0);
400 i8251->recvReady = saveStateGet(state, "recvReady", 0);
401 i8251->sendByte = (UInt8)saveStateGet(state, "sendByte", 0);
402 i8251->sendBuffer = (UInt8)saveStateGet(state, "sendBuffer", 0);
403 i8251->sendBuffered = saveStateGet(state, "sendBuffered", 0);
404
405 if (i8251->timeRecv != 0) {
406 boardTimerAdd(i8251->timerRecv, i8251->timeRecv);
407 }
408 if (i8251->timeRxPoll != 0) {
409 boardTimerAdd(i8251->timerRxPoll, i8251->timeRxPoll);
410 }
411 if (i8251->timeTrans != 0) {
412 boardTimerAdd(i8251->timerTrans, i8251->timeTrans);
413 }
414 saveStateClose(state);
415 }
416
i8251SaveState(I8251 * i8251)417 void i8251SaveState(I8251* i8251)
418 {
419 SaveState* state = saveStateOpenForWrite("i8251");
420
421 saveStateSet(state, "timeRecv", i8251->timeRecv);
422 saveStateSet(state, "timeRxPoll", i8251->timeRxPoll);
423 saveStateSet(state, "timeTrans", i8251->timeTrans);
424 saveStateSet(state, "status", i8251->status);
425 saveStateSet(state, "command", i8251->command);
426 saveStateSet(state, "mode", i8251->mode);
427 saveStateSet(state, "sync1", i8251->sync1);
428 saveStateSet(state, "sync2", i8251->sync2);
429 saveStateSet(state, "charLength", i8251->charLength);
430 saveStateSet(state, "cmdFaze", i8251->cmdFaze);
431 saveStateSet(state, "dataBits", i8251->dataBits);
432 saveStateSet(state, "stopBits", i8251->stopBits);
433 saveStateSet(state, "parityEnabled", i8251->parityEnabled);
434 saveStateSet(state, "parity", i8251->parity);
435 saveStateSet(state, "recvBuf", i8251->recvBuf);
436 saveStateSet(state, "recvReady", i8251->recvReady);
437 saveStateSet(state, "sendByte", i8251->sendByte);
438 saveStateSet(state, "sendBuffer", i8251->sendBuffer);
439 saveStateSet(state, "sendBuffered", i8251->sendBuffered);
440
441 saveStateClose(state);
442 }
443
i8251Reset(I8251 * i8251)444 void i8251Reset(I8251* i8251)
445 {
446 i8251->charLength = 1024;
447 i8251->rxPending = 0;
448
449 i8251->status = STAT_TXRDY | STAT_TXEMPTY;
450 i8251->command = 0xff;
451 writeCommand(i8251, 0);
452 i8251->cmdFaze = PHASE_MODE;
453 }
454
onRxPoll(I8251 * i8251,UInt32 time)455 static void onRxPoll(I8251* i8251, UInt32 time)
456 {
457 UInt8 value = 0;
458
459 if (i8251->timeRxPoll != 0) {
460 boardTimerRemove(i8251->timerRxPoll);
461 i8251->timeRxPoll = 0;
462 }
463
464 if (i8251->rxPending == 0) {
465 i8251->timeRxPoll = (UInt32)(boardSystemTime() + (UInt64)i8251->charLength * boardFrequency() / 4000000);
466 boardTimerAdd(i8251->timerRxPoll, i8251->timeRxPoll);
467 return;
468 }
469
470 if (i8251->rxPending != 0) {
471 archSemaphoreWait(i8251->semaphore, -1);
472 value = i8251->rxQueue[(i8251->rxHead - i8251->rxPending) & (RX_QUEUE_SIZE - 1)];
473 i8251->rxPending--;
474 archSemaphoreSignal(i8251->semaphore);
475 }
476 i8251->recvBuf = value;
477 i8251->status |= STAT_RXRDY;
478 i8251->setRxReady(i8251->ref, 1);
479 i8251->recvReady = 0;
480
481 i8251->timeRecv = (UInt32)(boardSystemTime() + (UInt64)i8251->charLength * boardFrequency() / 4000000);
482 boardTimerAdd(i8251->timerRecv, i8251->timeRecv);
483 }
484
onRecv(I8251 * i8251,UInt32 time)485 static void onRecv(I8251* i8251, UInt32 time)
486 {
487 i8251->timeRecv = 0;
488 i8251->recvReady = 1;
489 i8251->signal(i8251->ref);
490
491 onRxPoll(i8251, time);
492 }
493
i8251RxData(I8251 * i8251,UInt8 value)494 void i8251RxData(I8251* i8251, UInt8 value)
495 {
496 archSemaphoreWait(i8251->semaphore, -1);
497
498 if (i8251->rxPending < RX_QUEUE_SIZE) {
499 i8251->rxQueue[i8251->rxHead & (RX_QUEUE_SIZE - 1)] = value;
500 i8251->rxHead++;
501 i8251->rxPending++;
502 }
503 else {
504 i8251->status |= STAT_OE;
505 }
506 archSemaphoreSignal(i8251->semaphore);
507 }
508
i8251Destroy(I8251 * i8251)509 void i8251Destroy(I8251* i8251)
510 {
511 boardTimerDestroy(i8251->timerRecv);
512 boardTimerDestroy(i8251->timerRxPoll);
513 boardTimerDestroy(i8251->timerTrans);
514
515 archSemaphoreDestroy(i8251->semaphore);
516
517 free(i8251);
518 }
519
i8251Create(I8251Transmit transmit,I8251Signal signal,I8251Set setDataBits,I8251Set setStopBits,I8251Set setParity,I8251Set setRxReady,I8251Set setDtr,I8251Set setRts,I8251Get getDtr,I8251Get getRts,void * ref)520 I8251* i8251Create(I8251Transmit transmit, I8251Signal signal,
521 I8251Set setDataBits, I8251Set setStopBits,
522 I8251Set setParity, I8251Set setRxReady,
523 I8251Set setDtr, I8251Set setRts,
524 I8251Get getDtr, I8251Get getRts,
525 void* ref)
526 {
527 I8251* i8251 = calloc(1, sizeof(I8251));
528
529 i8251->transmit = transmit ? transmit : transmitDummy;
530 i8251->signal = signal ? signal : signalDummy;
531 i8251->setDataBits = setDataBits ? setDataBits : setDataBitsDummy;
532 i8251->setStopBits = setStopBits ? setStopBits : setStopBitsDummy;
533 i8251->setParity = setParity ? setParity : setParityDummy;
534 i8251->setRxReady = setRxReady ? setRxReady : setRxReadyDummy;
535 i8251->setDtr = setDtr ? setDtr : setDtrDummy;
536 i8251->setRts = setRts ? setRts : setRtsDummy;
537 i8251->getDtr = getDtr ? getDtr : getDtrDummy;
538 i8251->getRts = getRts ? getRts : getRtsDummy;
539
540 i8251->ref = ref;
541
542 i8251->timerRecv = boardTimerCreate(onRecv, i8251);
543 i8251->timerRxPoll = boardTimerCreate(onRxPoll, i8251);
544 i8251->timerTrans = boardTimerCreate(onTrans, i8251);
545
546 i8251->semaphore = archSemaphoreCreate(1);
547
548 i8251Reset(i8251);
549
550 return i8251;
551 }
552