OSDN Git Service

[GENERAL] Merge upstream 2018-03-01.
[csp-qt/common_source_project-fm7.git] / source / src / vm / i8251.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.09.14 -
6
7         [ i8251 ]
8 */
9
10 #include "i8251.h"
11 #include "../fifo.h"
12
13 // max 256kbytes
14 #define BUFFER_SIZE     0x40000
15 // 100usec/byte
16 #define RECV_DELAY      100
17 #define SEND_DELAY      100
18
19 #define EVENT_RECV      0
20 #define EVENT_SEND      1
21
22 #define TXRDY           0x01
23 #define RXRDY           0x02
24 #define TXE             0x04
25 #define PE              0x08
26 #define OE              0x10
27 #define FE              0x20
28 #define SYNDET          0x40
29 #define DSR             0x80
30
31 #define MODE_CLEAR      0
32 #define MODE_SYNC       1
33 #define MODE_ASYNC      2
34 #define MODE_SYNC1      3
35 #define MODE_SYNC2      4
36
37 #define RECV_BREAK      -1
38
39 void I8251::initialize()
40 {
41         DEVICE::initialize();
42         recv_buffer = new FIFO(BUFFER_SIZE);
43         send_buffer = new FIFO(4);
44         status = TXRDY | TXE;
45 }
46
47 void I8251::release()
48 {
49         recv_buffer->release();
50         delete recv_buffer;
51         send_buffer->release();
52         delete send_buffer;
53 }
54
55 void I8251::reset()
56 {
57         mode = MODE_CLEAR;
58         recv = 0x00;    // XM8 version 1.10
59 //      recv = 0xff;
60         
61         // dont reset dsr
62         status &= DSR;
63         status |= TXRDY | TXE;
64         txen = rxen = loopback = false;
65         
66         recv_buffer->clear();
67         send_buffer->clear();
68         recv_id = send_id = -1;
69 }
70
71 void I8251::write_io8(uint32_t addr, uint32_t data)
72 {
73         if(addr & 1) {
74                 switch(mode) {
75                 case MODE_CLEAR:
76                         if(data & 3) {
77                                 mode = MODE_ASYNC;
78                         } else if(data & 0x80) {
79                                 mode = MODE_SYNC2;      // 1char
80                         } else {
81                                 mode = MODE_SYNC1;      // 2chars
82                         }
83                         break;
84                 case MODE_SYNC1:
85                         mode = MODE_SYNC2;
86                         break;
87                 case MODE_SYNC2:
88                         mode = MODE_SYNC;
89                         break;
90                 case MODE_ASYNC:
91                 case MODE_SYNC:
92                         if(data & 0x40) {
93                                 mode = MODE_CLEAR;
94                                 break;
95                         }
96                         if(data & 0x10) {
97                                 status &= ~(PE | OE | FE);
98                         }
99                         // dtr
100                         write_signals(&outputs_dtr, (data & 2) ? 0xffffffff : 0);
101                         // rst/sbrk
102                         write_signals(&outputs_rst, (data & 8) ? 0xffffffff : 0);
103                         // rxen
104                         rxen = ((data & 4) != 0);
105                         if(rxen && !recv_buffer->empty() && recv_id == -1) {
106                                 register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
107                         }
108                         // txen
109                         txen = ((data & 1) != 0);
110                         if(txen && !send_buffer->empty() && send_id == -1) {
111                                 register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
112                         }
113                         // note: when txen=false, txrdy signal must be low
114                         break;
115                 }
116         } else {
117                 if(status & TXRDY) {
118                         send_buffer->write(data);
119                         // txrdy
120                         if(send_buffer->full()) {
121                                 status &= ~TXRDY;
122                                 write_signals(&outputs_txrdy, 0);
123                         }
124                         // txempty
125                         status &= ~TXE;
126                         write_signals(&outputs_txe, 0);
127                         // register event
128                         if(txen && send_id == -1) {
129                                 register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
130                         }
131                 }
132         }
133 }
134
135 uint32_t I8251::read_io8(uint32_t addr)
136 {
137         if(addr & 1) {
138                 // XM8 version 1.10
139                 if(!txen) {
140                         return status & ~(TXRDY | TXE);
141                 }
142                 return status;
143         } else {
144                 if(status & RXRDY) {
145                         status &= ~RXRDY;
146                         write_signals(&outputs_rxrdy, 0);
147                 }
148                 return recv;
149         }
150 }
151
152 void I8251::write_signal(int id, uint32_t data, uint32_t mask)
153 {
154         if(id == SIG_I8251_RECV) {
155                 recv_buffer->write(data & mask);
156                 // register event
157                 if(rxen && !recv_buffer->empty() && recv_id == -1) {
158                         register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
159                 }
160         } else if(id == SIG_I8251_BREAK) {
161                 if(data & mask) {
162                         recv_buffer->write(RECV_BREAK);
163                         // register event
164                         if(rxen && !recv_buffer->empty() && recv_id == -1) {
165                                 register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
166                         }
167                 }
168         } else if(id == SIG_I8251_DSR) {
169                 if(data & mask) {
170                         status |= DSR;
171                 } else {
172                         status &= ~DSR;
173                 }
174         } else if(id == SIG_I8251_CLEAR) {
175                 recv_buffer->clear();
176         } else if(id == SIG_I8251_LOOPBACK) {
177                 loopback = ((data & mask) != 0);
178         }
179 }
180
181 void I8251::event_callback(int event_id, int err)
182 {
183         if(event_id == EVENT_RECV) {
184                 if(rxen && !(status & RXRDY)) {
185                         if(!recv_buffer->empty()) {
186                                 int val = recv_buffer->read();
187                                 if(val == RECV_BREAK) {
188                                         // break
189                                         status |= SYNDET;
190                                         write_signals(&outputs_syndet, 0xffffffff);
191                                 } else {
192                                         recv = (uint8_t)val;
193                                         status |= RXRDY;
194                                         write_signals(&outputs_rxrdy, 0xffffffff);
195                                 }
196                         }
197                 }
198                 // if data is still left in buffer, register event for next data
199                 if(rxen && !recv_buffer->empty()) {
200                         register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
201                 } else {
202                         recv_id = -1;
203                 }
204         } else if(event_id == EVENT_SEND) {
205                 if(txen && !send_buffer->empty()) {
206                         uint8_t send = send_buffer->read();
207                         if(loopback) {
208                                 // send to this device
209                                 write_signal(SIG_I8251_RECV, send, 0xff);
210                         } else {
211                                 // send to external devices
212                                 write_signals(&outputs_out, send);
213                         }
214                         // txrdy
215                         status |= TXRDY;
216                         write_signals(&outputs_txrdy, 0xffffffff);
217                         // txe
218                         if(send_buffer->empty()) {
219                                 status |= TXE;
220                                 write_signals(&outputs_txe, 0xffffffff);
221                         }
222                 }
223                 // if data is still left in buffer, register event for next data
224                 if(txen && !send_buffer->empty()) {
225                         register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
226                 } else {
227                         send_id = -1;
228                 }
229         }
230 }
231
232 #define STATE_VERSION   1
233
234 void I8251::save_state(FILEIO* state_fio)
235 {
236         state_fio->FputUint32(STATE_VERSION);
237         state_fio->FputInt32(this_device_id);
238         
239         state_fio->FputUint8(recv);
240         state_fio->FputUint8(status);
241         state_fio->FputUint8(mode);
242         state_fio->FputBool(txen);
243         state_fio->FputBool(rxen);
244         state_fio->FputBool(loopback);
245         recv_buffer->save_state((void *)state_fio);
246         send_buffer->save_state((void *)state_fio);
247         state_fio->FputInt32(recv_id);
248         state_fio->FputInt32(send_id);
249 }
250
251 bool I8251::load_state(FILEIO* state_fio)
252 {
253         if(state_fio->FgetUint32() != STATE_VERSION) {
254                 return false;
255         }
256         if(state_fio->FgetInt32() != this_device_id) {
257                 return false;
258         }
259         recv = state_fio->FgetUint8();
260         status = state_fio->FgetUint8();
261         mode = state_fio->FgetUint8();
262         txen = state_fio->FgetBool();
263         rxen = state_fio->FgetBool();
264         loopback = state_fio->FgetBool();
265         if(!recv_buffer->load_state((void *)state_fio)) {
266                 return false;
267         }
268         if(!send_buffer->load_state((void *)state_fio)) {
269                 return false;
270         }
271         recv_id = state_fio->FgetInt32();
272         send_id = state_fio->FgetInt32();
273         return true;
274 }
275