OSDN Git Service

[VM][DEVICE] Enable to build with new state framework.
[csp-qt/common_source_project-fm7.git] / source / src / vm / w3100a.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.12.06 -
6
7         [ WIZnet W3100A ]
8 */
9
10 #include "w3100a.h"
11
12 void W3100A::initialize()
13 {
14         DEVICE::initialize();
15         idm_or = idm_ar0 = idm_ar1 = 0;
16         memset(regs, 0, sizeof(regs));
17         memset(cx_rw_pr, 0, sizeof(cx_rw_pr));
18         memset(cx_rr_pr, 0, sizeof(cx_rr_pr));
19         memset(cx_ta_pr, 0, sizeof(cx_ta_pr));
20         memset(cx_tw_pr, 0, sizeof(cx_tw_pr));
21         memset(cx_tr_pr, 0, sizeof(cx_tr_pr));
22 }
23
24 #define GET_ADDR() { \
25         if(idm_or & 2) { \
26                 raddr = idm_ar0 | (idm_ar1 << 8); \
27         } else { \
28                 raddr = (idm_ar0 << 8) | idm_ar1; \
29         } \
30 }
31
32 #define INC_ADDR() { \
33         if(idm_or & 1) { \
34                 raddr++; \
35                 idm_ar0 = (idm_or & 2) ? (raddr & 0xff) : (raddr >> 8); \
36                 idm_ar1 = (idm_or & 2) ? (raddr >> 8) : (raddr & 0xff); \
37         } \
38 }
39
40 void W3100A::write_io8(uint32_t addr, uint32_t data)
41 {
42         uint16_t raddr;
43         
44         switch(addr & 3) {
45         case 0:
46                 idm_or = data;
47                 break;
48         case 1:
49                 idm_ar0 = data;
50                 break;
51         case 2:
52                 idm_ar1 = data;
53                 break;
54         case 3:
55                 GET_ADDR();
56                 regs[raddr] = data;
57                 process_cmd(raddr, data);
58                 INC_ADDR();
59                 break;
60         }
61 }
62
63 uint32_t W3100A::read_io8(uint32_t addr)
64 {
65         uint16_t raddr;
66         uint32_t val;
67         
68         switch(addr & 3) {
69         case 3:
70                 GET_ADDR();
71                 val = regs[raddr];
72                 process_status(raddr);
73                 INC_ADDR();
74                 return val;
75         }
76         return 0xff;
77 }
78
79 void W3100A::process_cmd(uint16_t raddr, uint8_t data)
80 {
81         uint16_t bufsz[4] = { 1024, 2048, 4096, 8192 };
82         int ch;
83         
84         switch(raddr) {
85         case 0x00:      // c0_cr
86                 // sys init (ch.0 only)
87                 if(data & 1) {
88                         regs[4] = 1;    // always success
89                 }
90         case 0x01:      // c1_cr
91         case 0x02:      // c2_cr
92         case 0x03:      // c3_cr
93                 ch = raddr & 3;
94                 if(data & 2) {
95                         // init sock
96                         uint8_t mode = regs[0xa1 + 24 * ch] & 7;
97                         if(mode == 0) {
98                                 emu->disconnect_socket(ch);
99                         } else {
100                                 bool result = false, connect = false;
101                                 regs[0xa0 + 24 * ch] = 0;       // SOCK_CLOSE
102                                 if(mode == 1) {
103                                         result = connect = emu->initialize_socket_tcp(ch);
104                                         is_tcp[ch] = true;
105                                 } else if(mode == 2) {
106                                         result = connect = emu->initialize_socket_udp(ch);
107                                         if(result) {
108                                                 regs[0xa0 + 24 * ch] = 0xf;     // SOCK_UDP
109                                         }
110                                         is_tcp[ch] = false;
111                                 }
112                                 regs[4 + ch] &= ~(2 | 4 | 8);
113                                 regs[4 + ch] |= (result ? 2 : 0) | (connect ? 0 : 8);
114                                 // patch to force buffer is empty
115                                 cx_ta_pr[ch] = cx_tr_pr[ch] = cx_tw_pr[ch];
116                                 cx_rw_pr[ch] = cx_rr_pr[ch] = 0;
117                         }
118                 }
119                 if(data & 4) {
120                         // connect
121                         regs[0xa0 + 24 * ch] = 3;       // SOCK_SYNSENT
122                         uint32_t ipaddr = regs[0xa8 + 24 * ch];
123                         ipaddr |= regs[0xa9 + 24 * ch] << 8;
124                         ipaddr |= regs[0xaa + 24 * ch] << 16;
125                         ipaddr |= regs[0xab + 24 * ch] << 24;
126                         int port = (regs[0xac + 24 * ch] << 8) | regs[0xad + 24 * ch];
127                         if(!emu->connect_socket(ch, ipaddr, port)) {
128                                 regs[4 + ch] &= ~4;             // established = false
129                                 regs[4 + ch] |= 8;              // closed = true
130                                 regs[0xa0 + 24 * ch] = 0;       // SOCK_CLOSED
131                         }
132                 }
133                 if(data & 8) {
134                         // listen
135                         regs[0xa0 + 24 * ch] = 2;       // SOCK_LISTEN
136                         if(!emu->listen_socket(ch)) {
137                                 regs[4 + ch] &= ~4;             // established = false
138                                 regs[4 + ch] |= 8;              // closed = true
139                                 regs[0xa0 + 24 * ch] = 0;       // SOCK_CLOSED
140                         }
141                 }
142                 if(data & 0x10) {
143                         // close
144                         emu->disconnect_socket(ch);
145                 }
146                 if(data & 0x20) {
147                         // send
148                         send_dst_ptr[ch] = cx_tw_pr[ch];
149                         uint32_t rd_ptr = is_tcp[ch] ? cx_ta_pr[ch] : cx_tr_pr[ch];
150                         if(rd_ptr != send_dst_ptr[ch]) {
151                                 regs[ch + 4] &= ~0x20;
152                                 if(is_tcp[ch]) {
153                                         emu->send_socket_data_tcp(ch);
154                                 } else {
155                                         uint32_t ipaddr = regs[0xa8 + 24 * ch];
156                                         ipaddr |= regs[0xa9 + 24 * ch] << 8;
157                                         ipaddr |= regs[0xaa + 24 * ch] << 16;
158                                         ipaddr |= regs[0xab + 24 * ch] << 24;
159                                         int port = (regs[0xac + 24 * ch] << 8) | regs[0xad + 24 * ch];
160                                         emu->send_socket_data_udp(ch, ipaddr, port);
161                                 }
162                         } else {
163                                 regs[ch + 4] |= 0x20;   // send ok
164                         }
165                 }
166                 if(data & 0x40) {
167                         // recv
168                         recv_dst_ptr[ch] = cx_rr_pr[ch];
169                         regs[ch + 4] &= ~0x40;  // recv ok
170                 }
171                 break;
172         case 0x10: case 0x11: case 0x12: case 0x13: ch = 0; goto lbl_cx_rw_pr;
173         case 0x1c: case 0x1d: case 0x1e: case 0x1f: ch = 1; goto lbl_cx_rw_pr;
174         case 0x28: case 0x29: case 0x2a: case 0x2b: ch = 2; goto lbl_cx_rw_pr;
175         case 0x34: case 0x35: case 0x36: case 0x37: ch = 3;
176 lbl_cx_rw_pr:
177                 cx_rw_pr[ch]  = regs[0x10 + 12 * ch] << 24;
178                 cx_rw_pr[ch] |= regs[0x11 + 12 * ch] << 16;
179                 cx_rw_pr[ch] |= regs[0x12 + 12 * ch] <<  8;
180                 cx_rw_pr[ch] |= regs[0x13 + 12 * ch] <<  0;
181                 break;
182         case 0x14: case 0x15: case 0x16: case 0x17: ch = 0; goto lbl_cx_rr_pr;
183         case 0x20: case 0x21: case 0x22: case 0x23: ch = 1; goto lbl_cx_rr_pr;
184         case 0x2c: case 0x2d: case 0x2e: case 0x2f: ch = 2; goto lbl_cx_rr_pr;
185         case 0x38: case 0x39: case 0x3a: case 0x3b: ch = 3;
186 lbl_cx_rr_pr:
187                 cx_rr_pr[ch]  = regs[0x14 + 12 * ch] << 24;
188                 cx_rr_pr[ch] |= regs[0x15 + 12 * ch] << 16;
189                 cx_rr_pr[ch] |= regs[0x16 + 12 * ch] <<  8;
190                 cx_rr_pr[ch] |= regs[0x17 + 12 * ch] <<  0;
191                 break;
192         case 0x18: case 0x19: case 0x1a: case 0x1b: ch = 0; goto lbl_cx_ta_pr;
193         case 0x24: case 0x25: case 0x26: case 0x27: ch = 1; goto lbl_cx_ta_pr;
194         case 0x30: case 0x31: case 0x32: case 0x33: ch = 2; goto lbl_cx_ta_pr;
195         case 0x3c: case 0x3d: case 0x3e: case 0x3f: ch = 3;
196 lbl_cx_ta_pr:
197                 cx_ta_pr[ch]  = regs[0x18 + 12 * ch] << 24;
198                 cx_ta_pr[ch] |= regs[0x19 + 12 * ch] << 16;
199                 cx_ta_pr[ch] |= regs[0x1a + 12 * ch] <<  8;
200                 cx_ta_pr[ch] |= regs[0x1b + 12 * ch] <<  0;
201                 break;
202         case 0x40: case 0x41: case 0x42: case 0x43: ch = 0; goto lbl_cx_tw_pr;
203         case 0x4c: case 0x4d: case 0x4e: case 0x4f: ch = 1; goto lbl_cx_tw_pr;
204         case 0x58: case 0x59: case 0x5a: case 0x5b: ch = 2; goto lbl_cx_tw_pr;
205         case 0x64: case 0x65: case 0x66: case 0x67: ch = 3;
206 lbl_cx_tw_pr:
207                 cx_tw_pr[ch]  = regs[0x40 + 12 * ch] << 24;
208                 cx_tw_pr[ch] |= regs[0x41 + 12 * ch] << 16;
209                 cx_tw_pr[ch] |= regs[0x42 + 12 * ch] <<  8;
210                 cx_tw_pr[ch] |= regs[0x43 + 12 * ch] <<  0;
211                 break;
212         case 0x44: case 0x45: case 0x46: case 0x47: ch = 0; goto lbl_cx_tr_pr;
213         case 0x50: case 0x51: case 0x52: case 0x53: ch = 1; goto lbl_cx_tr_pr;
214         case 0x5c: case 0x5d: case 0x5e: case 0x5f: ch = 2; goto lbl_cx_tr_pr;
215         case 0x68: case 0x69: case 0x6a: case 0x6b: ch = 3;
216 lbl_cx_tr_pr:
217                 cx_tr_pr[ch]  = regs[0x44 + 12 * ch] << 24;
218                 cx_tr_pr[ch] |= regs[0x45 + 12 * ch] << 16;
219                 cx_tr_pr[ch] |= regs[0x46 + 12 * ch] <<  8;
220                 cx_tr_pr[ch] |= regs[0x47 + 12 * ch] <<  0;
221                 break;
222         case 0x95:      // rmsr
223                 rx_bufsz[0] = bufsz[(data >> 0) & 3];
224                 rx_bufsz[1] = bufsz[(data >> 2) & 3];
225                 rx_bufsz[2] = bufsz[(data >> 4) & 3];
226                 rx_bufsz[3] = bufsz[(data >> 6) & 3];
227                 break;
228         case 0x96:      // tmsr
229                 tx_bufsz[0] = bufsz[(data >> 0) & 3];
230                 tx_bufsz[1] = bufsz[(data >> 2) & 3];
231                 tx_bufsz[2] = bufsz[(data >> 4) & 3];
232                 tx_bufsz[3] = bufsz[(data >> 6) & 3];
233                 break;
234         }
235 }
236
237 void W3100A::process_status(uint16_t raddr)
238 {
239         int ch;
240         
241         switch(raddr) {
242         case 0x1e0: ch = 0; goto lbl_cx_srw_pr;
243         case 0x1e3: ch = 1; goto lbl_cx_srw_pr;
244         case 0x1e6: ch = 2; goto lbl_cx_srw_pr;
245         case 0x1e9: ch = 3;
246 lbl_cx_srw_pr:
247                 regs[0x10 + 12 * ch] = cx_rw_pr[ch] >> 24;
248                 regs[0x11 + 12 * ch] = cx_rw_pr[ch] >> 16;
249                 regs[0x12 + 12 * ch] = cx_rw_pr[ch] >>  8;
250                 regs[0x13 + 12 * ch] = cx_rw_pr[ch] >>  0;
251                 break;
252         case 0x1e1: ch = 0; goto lbl_cx_srr_pr;
253         case 0x1e4: ch = 1; goto lbl_cx_srr_pr;
254         case 0x1e7: ch = 2; goto lbl_cx_srr_pr;
255         case 0x1ea: ch = 3;
256 lbl_cx_srr_pr:
257                 regs[0x14 + 12 * ch] = cx_rr_pr[ch] >> 24;
258                 regs[0x15 + 12 * ch] = cx_rr_pr[ch] >> 16;
259                 regs[0x16 + 12 * ch] = cx_rr_pr[ch] >>  8;
260                 regs[0x17 + 12 * ch] = cx_rr_pr[ch] >>  0;
261                 break;
262         case 0x1e2: ch = 0; goto lbl_cx_sta_pr;
263         case 0x1e5: ch = 1; goto lbl_cx_sta_pr;
264         case 0x1e8: ch = 2; goto lbl_cx_sta_pr;
265         case 0x1eb: ch = 3;
266 lbl_cx_sta_pr:
267                 regs[0x18 + 12 * ch] = cx_ta_pr[ch] >> 24;
268                 regs[0x19 + 12 * ch] = cx_ta_pr[ch] >> 16;
269                 regs[0x1a + 12 * ch] = cx_ta_pr[ch] >>  8;
270                 regs[0x1b + 12 * ch] = cx_ta_pr[ch] >>  0;
271                 break;
272         case 0x1f0: ch = 0; goto lbl_cx_stw_pr;
273         case 0x1f3: ch = 1; goto lbl_cx_stw_pr;
274         case 0x1f6: ch = 2; goto lbl_cx_stw_pr;
275         case 0x1f9: ch = 3;
276 lbl_cx_stw_pr:
277                 regs[0x40 + 12 * ch] = cx_tw_pr[ch] >> 24;
278                 regs[0x41 + 12 * ch] = cx_tw_pr[ch] >> 16;
279                 regs[0x42 + 12 * ch] = cx_tw_pr[ch] >>  8;
280                 regs[0x43 + 12 * ch] = cx_tw_pr[ch] >>  0;
281                 break;
282         case 0x1f1: ch = 0; goto lbl_cx_str_pr;
283         case 0x1f4: ch = 1; goto lbl_cx_str_pr;
284         case 0x1f7: ch = 2; goto lbl_cx_str_pr;
285         case 0x1fa: ch = 3;
286 lbl_cx_str_pr:
287                 regs[0x44 + 12 * ch] = cx_tr_pr[ch] >> 24;
288                 regs[0x45 + 12 * ch] = cx_tr_pr[ch] >> 16;
289                 regs[0x46 + 12 * ch] = cx_tr_pr[ch] >>  8;
290                 regs[0x47 + 12 * ch] = cx_tr_pr[ch] >>  0;
291                 break;
292         }
293 }
294
295 void W3100A::notify_connected(int ch)
296 {
297         regs[4 + ch] |= 4;              // established = true
298         regs[4 + ch] &= ~8;             // closed = false
299         regs[0xa0 + 24 * ch] = 6;       // SOCK_ESTABLISHED
300 }
301
302 void W3100A::notify_disconnected(int ch)
303 {
304         regs[4 + ch] &= ~4;             // established = false
305         regs[4 + ch] |= 8;              // closed = true
306         if(regs[ch] & 0x40) {           // recv_ok = true
307                 regs[4 + ch] |= 0x40;
308         }
309         regs[0xa0 + 24 * ch] = 0;       // SOCK_CLOSED
310 }
311
312 uint8_t* W3100A::get_send_buffer(int ch, int* size)
313 {
314         uint32_t cx_ta_tr = is_tcp[ch] ? cx_ta_pr[ch] : cx_tr_pr[ch];
315         uint32_t rd_ptr = cx_ta_tr & (tx_bufsz[ch] - 1);
316         uint32_t wr_ptr = send_dst_ptr[ch] & (tx_bufsz[ch] - 1);
317         
318         if(!(regs[ch] & 0x20)) {
319                 *size = 0;
320         } else if(rd_ptr == wr_ptr && cx_ta_tr != send_dst_ptr[ch]) {
321                 *size = 0;
322         } else if(rd_ptr <= wr_ptr) {
323                 *size = wr_ptr - rd_ptr;
324         } else {
325                 *size = tx_bufsz[ch] - rd_ptr;
326         }
327         uint32_t ofs = 0x4000;
328         for(int i = 0; i < ch; i++) {
329                 ofs += tx_bufsz[i];
330         }
331         return &regs[ofs + rd_ptr];
332 }
333
334 void W3100A::inc_send_buffer_ptr(int ch, int size)
335 {
336         if(is_tcp[ch]) {
337                 cx_ta_pr[ch] += size;
338                 if(cx_ta_pr[ch] == send_dst_ptr[ch]) {
339                         regs[ch + 4] |= 0x20;   // send ok
340                 }
341         } else {
342                 cx_tr_pr[ch] += size;
343                 if(cx_tr_pr[ch] == send_dst_ptr[ch]) {
344                         regs[ch + 4] |= 0x20;   // send ok
345                 }
346         }
347 }
348
349 uint8_t* W3100A::get_recv_buffer0(int ch, int* size0, int* size1)
350 {
351         uint32_t wr_ptr = cx_rw_pr[ch] & (rx_bufsz[ch] - 1);
352         uint32_t rr_ptr = recv_dst_ptr[ch] & (rx_bufsz[ch] - 1);
353         
354         if(!(regs[ch] & 0x40)) {
355                 *size0 = *size1 = 0;
356         } else if(rr_ptr == wr_ptr && cx_rw_pr[ch] != recv_dst_ptr[ch]) {
357                 *size0 = *size1 = 0;
358         } else if(rr_ptr <= wr_ptr) {
359                 *size0 = rx_bufsz[ch] - wr_ptr;
360                 *size1 = rr_ptr;
361         } else {
362                 *size0 = rr_ptr - wr_ptr;
363                 *size1 = 0;
364         }
365         uint32_t ofs = 0x6000;
366         for(int i = 0; i < ch; i++) {
367                 ofs += rx_bufsz[i];
368         }
369         return &regs[ofs + wr_ptr];
370 }
371
372 uint8_t* W3100A::get_recv_buffer1(int ch)
373 {
374         uint32_t ofs = 0x6000;
375         for(int i = 0; i < ch; i++) {
376                 ofs += rx_bufsz[i];
377         }
378         return &regs[ofs];
379 }
380
381 void W3100A::inc_recv_buffer_ptr(int ch, int size)
382 {
383 //      uint32_t wr_ptr = cx_rw_pr[ch];
384 //      wr_ptr = (wr_ptr + size) & (rx_bufsz[ch] - 1);
385 //      cx_rw_pr[ch] = (cx_rw_pr[ch] & ~(rx_bufsz[ch] - 1)) | wr_ptr;
386         cx_rw_pr[ch] += size;
387 //      regs[ch + 4] |= 0x40;   // recv ok
388 }
389
390 #define STATE_VERSION   1
391
392 bool W3100A::process_state(FILEIO* state_fio, bool loading)
393 {
394         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
395                 return false;
396         }
397         if(!state_fio->StateCheckInt32(this_device_id)) {
398                 return false;
399         }
400         state_fio->StateValue(idm_or);
401         state_fio->StateValue(idm_ar0);
402         state_fio->StateValue(idm_ar1);
403         state_fio->StateArray(regs, sizeof(regs), 1);
404         state_fio->StateArray(is_tcp, sizeof(is_tcp), 1);
405         state_fio->StateArray(rx_bufsz, sizeof(rx_bufsz), 1);
406         state_fio->StateArray(tx_bufsz, sizeof(tx_bufsz), 1);
407         state_fio->StateArray(cx_rw_pr, sizeof(cx_rw_pr), 1);
408         state_fio->StateArray(cx_rr_pr, sizeof(cx_rr_pr), 1);
409         state_fio->StateArray(cx_ta_pr, sizeof(cx_ta_pr), 1);
410         state_fio->StateArray(cx_tw_pr, sizeof(cx_tw_pr), 1);
411         state_fio->StateArray(cx_tr_pr, sizeof(cx_tr_pr), 1);
412         state_fio->StateArray(send_dst_ptr, sizeof(send_dst_ptr), 1);
413         state_fio->StateArray(recv_dst_ptr, sizeof(recv_dst_ptr), 1);
414         return true;
415 }