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