OSDN Git Service

[VM][FM7][FM77AV] Set sub clock.
[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         recv_buffer = new FIFO(BUFFER_SIZE);
42         send_buffer = new FIFO(4);
43         status = TXRDY | TXE;
44 }
45
46 void I8251::release()
47 {
48         recv_buffer->release();
49         delete recv_buffer;
50         send_buffer->release();
51         delete send_buffer;
52 }
53
54 void I8251::reset()
55 {
56         mode = MODE_CLEAR;
57         recv = 0xff;
58         // dont reset dsr
59         status &= DSR;
60         status |= TXRDY | TXE;
61         txen = rxen = loopback = false;
62         
63         recv_buffer->clear();
64         send_buffer->clear();
65         recv_id = send_id = -1;
66 }
67
68 void I8251::write_io8(uint32 addr, uint32 data)
69 {
70         if(addr & 1) {
71                 switch(mode) {
72                 case MODE_CLEAR:
73                         if(data & 3) {
74                                 mode = MODE_ASYNC;
75                         } else if(data & 0x80) {
76                                 mode = MODE_SYNC2;      // 1char
77                         } else {
78                                 mode = MODE_SYNC1;      // 2chars
79                         }
80                         break;
81                 case MODE_SYNC1:
82                         mode = MODE_SYNC2;
83                         break;
84                 case MODE_SYNC2:
85                         mode = MODE_SYNC;
86                         break;
87                 case MODE_ASYNC:
88                 case MODE_SYNC:
89                         if(data & 0x40) {
90                                 mode = MODE_CLEAR;
91                                 break;
92                         }
93                         if(data & 0x10) {
94                                 status &= ~(PE | OE | FE);
95                         }
96                         // dtr
97                         write_signals(&outputs_dtr, (data & 2) ? 0xffffffff : 0);
98                         // rst/sbrk
99                         write_signals(&outputs_rst, (data & 8) ? 0xffffffff : 0);
100                         // rxen
101                         rxen = ((data & 4) != 0);
102                         if(rxen && !recv_buffer->empty() && recv_id == -1) {
103                                 register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
104                         }
105                         // txen
106                         txen = ((data & 1) != 0);
107                         if(txen && !send_buffer->empty() && send_id == -1) {
108                                 register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
109                         }
110                         // note: when txen=false, txrdy signal must be low
111                         break;
112                 }
113         } else {
114                 if(status & TXRDY) {
115                         send_buffer->write(data);
116                         // txrdy
117                         if(send_buffer->full()) {
118                                 status &= ~TXRDY;
119                                 write_signals(&outputs_txrdy, 0);
120                         }
121                         // txempty
122                         status &= ~TXE;
123                         write_signals(&outputs_txe, 0);
124                         // register event
125                         if(txen && send_id == -1) {
126                                 register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
127                         }
128                 }
129         }
130 }
131
132 uint32 I8251::read_io8(uint32 addr)
133 {
134         if(addr & 1) {
135                 return status;
136         } else {
137                 if(status & RXRDY) {
138                         status &= ~RXRDY;
139                         write_signals(&outputs_rxrdy, 0);
140                 }
141                 return recv;
142         }
143 }
144
145 void I8251::write_signal(int id, uint32 data, uint32 mask)
146 {
147         if(id == SIG_I8251_RECV) {
148                 recv_buffer->write(data & mask);
149                 // register event
150                 if(rxen && !recv_buffer->empty() && recv_id == -1) {
151                         register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
152                 }
153         } else if(id == SIG_I8251_BREAK) {
154                 if(data & mask) {
155                         recv_buffer->write(RECV_BREAK);
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                 }
161         } else if(id == SIG_I8251_DSR) {
162                 if(data & mask) {
163                         status |= DSR;
164                 } else {
165                         status &= ~DSR;
166                 }
167         } else if(id == SIG_I8251_CLEAR) {
168                 recv_buffer->clear();
169         } else if(id == SIG_I8251_LOOPBACK) {
170                 loopback = ((data & mask) != 0);
171         }
172 }
173
174 void I8251::event_callback(int event_id, int err)
175 {
176         if(event_id == EVENT_RECV) {
177                 if(rxen && !(status & RXRDY)) {
178                         if(!recv_buffer->empty()) {
179                                 int val = recv_buffer->read();
180                                 if(val == RECV_BREAK) {
181                                         // break
182                                         status |= SYNDET;
183                                         write_signals(&outputs_syndet, 0xffffffff);
184                                 } else {
185                                         recv = (uint8)val;
186                                         status |= RXRDY;
187                                         write_signals(&outputs_rxrdy, 0xffffffff);
188                                 }
189                         }
190                 }
191                 // if data is still left in buffer, register event for next data
192                 if(rxen && !recv_buffer->empty()) {
193                         register_event(this, EVENT_RECV, RECV_DELAY, false, &recv_id);
194                 } else {
195                         recv_id = -1;
196                 }
197         } else if(event_id == EVENT_SEND) {
198                 if(txen && !send_buffer->empty()) {
199                         uint8 send = send_buffer->read();
200                         if(loopback) {
201                                 // send to this device
202                                 write_signal(SIG_I8251_RECV, send, 0xff);
203                         } else {
204                                 // send to external devices
205                                 write_signals(&outputs_out, send);
206                         }
207                         // txrdy
208                         status |= TXRDY;
209                         write_signals(&outputs_txrdy, 0xffffffff);
210                         // txe
211                         if(send_buffer->empty()) {
212                                 status |= TXE;
213                                 write_signals(&outputs_txe, 0xffffffff);
214                         }
215                 }
216                 // if data is still left in buffer, register event for next data
217                 if(txen && !send_buffer->empty()) {
218                         register_event(this, EVENT_SEND, SEND_DELAY, false, &send_id);
219                 } else {
220                         send_id = -1;
221                 }
222         }
223 }
224
225 #define STATE_VERSION   1
226
227 void I8251::save_state(FILEIO* state_fio)
228 {
229         state_fio->FputUint32(STATE_VERSION);
230         state_fio->FputInt32(this_device_id);
231         
232         state_fio->FputUint8(recv);
233         state_fio->FputUint8(status);
234         state_fio->FputUint8(mode);
235         state_fio->FputBool(txen);
236         state_fio->FputBool(rxen);
237         state_fio->FputBool(loopback);
238         recv_buffer->save_state((void *)state_fio);
239         send_buffer->save_state((void *)state_fio);
240         state_fio->FputInt32(recv_id);
241         state_fio->FputInt32(send_id);
242 }
243
244 bool I8251::load_state(FILEIO* state_fio)
245 {
246         if(state_fio->FgetUint32() != STATE_VERSION) {
247                 return false;
248         }
249         if(state_fio->FgetInt32() != this_device_id) {
250                 return false;
251         }
252         recv = state_fio->FgetUint8();
253         status = state_fio->FgetUint8();
254         mode = state_fio->FgetUint8();
255         txen = state_fio->FgetBool();
256         rxen = state_fio->FgetBool();
257         loopback = state_fio->FgetBool();
258         if(!recv_buffer->load_state((void *)state_fio)) {
259                 return false;
260         }
261         if(!send_buffer->load_state((void *)state_fio)) {
262                 return false;
263         }
264         recv_id = state_fio->FgetInt32();
265         send_id = state_fio->FgetInt32();
266         return true;
267 }
268