Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
epnet.c
Go to the documentation of this file.
1 /* Xep128: Minimalistic Enterprise-128 emulator with focus on "exotic" hardware
2  Copyright (C)2015,2016,2020 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
3  http://xep128.lgb.hu/
4 
5  Partial Wiznet W5300 emulation, using the host OS (which runs the emulator)
6  TCP/IP API. Thus, many of the W5300 features won't work, or limited, like:
7  RAW mode, ICMP sockets, listening mode (it would be possible but eg in case
8  of UNIX there would be a need for privilege, which is not so nice to
9  run an emulator as "root" user), no IP/MAC setting (always uses the OS IP
10  and MAC). DHCP and DNS is planned to "faked" so w5300 softwares trying to
11  get IP address via DHCP or wanting resolve a DNS name would get an answer
12  from this w5300 emulator instead of from the "real" network.
13 
14  BIG-FAT-WARNING: this does NOT emulate w5300 just how EPNET and EP software
15  needs. Also DIRECT memory access mode is WRONG, but this is by will: on
16  EPNET this is just an "artifact" to be used as a dirty way to check link
17  status ;-P On EPNET only the first 8 addresses can be
18  accessed, so direct mode is really a trick here only!!! Also, it implements
19  only 8 bit access.
20 
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
25 
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
30 
31 You should have received a copy of the GNU General Public License
32 along with this program; if not, write to the Free Software
33 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
34 
35 #include "xep128.h"
36 
37 #ifdef CONFIG_EPNET_SUPPORT
38 
39 #include "epnet.h"
40 #include "cpu.h"
42 #include <SDL.h>
43 #include <unistd.h>
44 
45 #define direct_mode_epnet_shift 0
46 #define IS_DIRECT_MODE() (!(mr1&1))
47 
48 int w5300_int;
49 int w5300_does_work = 0;
50 
51 static Uint16 wmem[0x10000]; // 128K of internal RAM of w5300
52 static Uint8 wregs[0x400]; // W5300 registers
53 static Uint8 idm_ar0, idm_ar1;
54 static int idm_ar;
55 static Uint8 mr0, mr1;
56 //static int direct_mode;
57 static void (*interrupt_cb)(int);
58 
59 
60 
61 struct w5300_fifo_st {
62  Uint16 *w;
63  int readpos, storepos;
64  int free, stored;
65  int size;
66 };
67 
68 struct w5300_socket_st {
69  struct w5300_fifo_st rx_fifo;
70  struct w5300_fifo_st tx_fifo;
71  volatile int mode;
72  struct sockaddr_in servaddr;
73  volatile xemusock_socket_t sock;
74  volatile int todo;
75  volatile int resp;
76 };
77 
78 static struct w5300_socket_st wsockets[8];
79 
80 #define RX_FIFO_FILL(sn,data) fifo_fill(&wsockets[sn].rx_fifo,data)
81 #define RX_FIFO_CONSUME(sn) fifo_consume(&wsockets[sn].rx_fifo)
82 #define RX_FIFO_RESET(sn) fifo_reset(&wsockets[sn].rx_fifo)
83 #define RX_FIFO_GET_FREE(sn) wsockets[sn].rx_fifo.free
84 #define RX_FIFO_GET_USED(sn) wsockets[sn].rx_fifo.stored
85 #define TX_FIFO_FILL(sn,data) fifo_fill(&wsockets[sn].tx_fifo,data)
86 #define TX_FIFO_CONSUME(sn) fifo_consume(&wsockets[sn].tx_fifo)
87 #define TX_FIFO_RESET(sn) fifo_reset(&wsockets[sn].tx_fifo)
88 #define TX_FIFO_GET_FREE(sn) wsockets[sn].tx_fifo.free
89 #define TX_FIFO_GET_USED(sn) wsockets[sn].tx_fifo.stored
90 
91 static void fifo_fill ( struct w5300_fifo_st *f, Uint16 data )
92 {
93  if (XEMU_LIKELY(f->free)) {
94  if (f->stored == 0) {
95  f->storepos = 0;
96  f->readpos = 0;
97  }
98  f->w[f->storepos] = data;
99  f->storepos = XEMU_UNLIKELY(f->storepos == f->size - 1) ? 0 : f->storepos + 1;
100  f->stored++;
101  f->free = f->size - f->stored;
102  } else
103  DEBUGPRINT("EPNET: W5300: FIFO: fifo is full!!" NL);
104 }
105 
106 static Uint16 fifo_consume ( struct w5300_fifo_st *f )
107 {
108  if (XEMU_LIKELY(f->stored)) {
109  Uint16 data = f->w[f->readpos];
110  f->readpos = XEMU_UNLIKELY(f->readpos == f->size - 1) ? 0 : f->readpos + 1;
111  f->stored--;
112  f->free = f->size - f->stored;
113  return data;
114  } else {
115  DEBUGPRINT("EPNET: W5300: FIFO: fifo is empty!!" NL);
116  return 0xFFFF; // some answer ...
117  }
118 }
119 
120 static void fifo_reset ( struct w5300_fifo_st *f )
121 {
122  f->stored = 0;
123  f->free = f->size;
124  f->readpos = 0;
125  f->storepos = 0;
126 }
127 
128 static void default_w5300_config ( void )
129 {
130  Uint16 *w = wmem;
131  // FIXME!!! This only knows about the default memory layout!
132  // FIXME!!! Currently not emulated other modification of this via w5300 registers!
133  for (int sn = 0; sn < 8; sn++) {
134  wsockets[sn].rx_fifo.w = w;
135  w += 4096;
136  wsockets[sn].tx_fifo.w = w;
137  w += 4096;
138  wsockets[sn].rx_fifo.size = 4096;
139  wsockets[sn].tx_fifo.size = 4096;
140  wsockets[sn].mode = 0;
141  RX_FIFO_RESET(sn);
142  TX_FIFO_RESET(sn);
143  }
144 }
145 
146 
147 static SDL_Thread *thread = NULL;
148 static SDL_atomic_t thread_can_go;
149 struct net_task_data_st {
150  volatile int task;
151 };
152 static struct {
153  SDL_SpinLock lock; // this lock MUST be held in case of accessing fields I mark with
154  volatile xemusock_socket_t sock; // other than init, it's the net thread only stuff, no need to lock
155  //volatile int task; // LOCK!
156  volatile int response; // LOCK!
157  Uint8 source_ip[4]; // LOCK!
158  Uint8 target_ip[4]; // LOCK!
159  int source_port; // LOCK!
160  int target_port; // LOCK!
161  int proto; // LOCK!
162  int tx_size; // LOCK!
163  int rx_size; // LOCK!
164  Uint8 rx_buf[0x20000];// LOCK!
165  Uint8 tx_buf[0x20000];// LOCK!
166  struct net_task_data_st data;
167 } net_thread_tasks[8];
168 
169 
170 // WARNING: this code runs as a _THREAD_!!
171 // Must be very careful what data is touched an in what way, because of the probability of race-condition with the main thread!
172 // SURELY the same warning applies for the main thread want to access "shared" structures!
173 static int net_thread ( void *ptr )
174 {
175  int delay = 0;
176  SDL_AtomicSet(&thread_can_go, 2);
177  while (SDL_AtomicGet(&thread_can_go) != 0) {
178  int activity = 0;
179  for (int sn = 0; sn < 8; sn++) {
180  Uint8 buffer[0x20000];
181  struct net_task_data_st data;
182  /* LOCK BEGINS */
183  if (SDL_AtomicTryLock(&net_thread_tasks[sn].lock) == SDL_FALSE) {
184  activity = 1;
185  continue; // if could not locked, let's continue the check on w5300 sockets, maybe we have better luck next time ...
186  }
187  memcpy(&data, &net_thread_tasks[sn].data, sizeof(data));
188  SDL_AtomicUnlock(&net_thread_tasks[sn].lock);
189  /* LOCK ENDS */
190  // We don't want to held lock while doing things, like socket operations!
191  switch (data.task) {
192  case 0: // there was no task ... no need to answer, etc
193  continue;
194  case 1: // UDP send request
195  break;
196  }
197  // the response should be done while locking too!
198  /* LOCK BEGINS */
199  SDL_AtomicLock(&net_thread_tasks[sn].lock); // we can't use "TryLock" here, as we MUST write the response before checking the next w5300 soket!!
200  memcpy(&net_thread_tasks[sn].data, &data, sizeof(data));
201  SDL_AtomicUnlock(&net_thread_tasks[sn].lock);
202  /* LOCK ENDS */
203  }
204  if (activity) {
205  delay = 0;
206  } else {
207  if (delay < 50)
208  delay++;
209  SDL_Delay(delay);
210  }
211  }
212  return 1976;
213 }
214 
215 
216 static void init_net_thread_task_list ( void )
217 {
218  for (int sn = 0; sn < 8; sn++) {
219  net_thread_tasks[sn].sock = XS_INVALID_SOCKET;
220  net_thread_tasks[sn].data.task = 0;
221  }
222 }
223 
224 
225 static int add_net_thread_task ( int sn, Uint8 *buf )
226 {
227 }
228 
229 
230 int start_net_thread ( void )
231 {
232  SDL_AtomicSet(&thread_can_go, 1);
233  thread = SDL_CreateThread(net_thread, "Xep128 EPNET socket thread", (void*)NULL);
234  if (!thread) {
235  ERROR_WINDOW("EPNET: cloud not create EPNET thread: %s", SDL_GetError());
236  return -1;
237  }
238  Uint32 timeout = SDL_GetTicks();
239  while (SDL_AtomicGet(&thread_can_go) == 1) {
240  SDL_Delay(10);
241  if (SDL_GetTicks() - timeout > 1000) {
242  SDL_AtomicSet(&thread_can_go, 0);
243  ERROR_WINDOW("EPNET: timeout for starting EPNET thread");
244  return -1;
245  }
246  }
247  DEBUGPRINT("EPNET: THREAD: thread seems to be started finely :)" NL);
248  return 0;
249 }
250 
251 
252 
253 static void update_interrupts ( void )
254 {
255  int v = ((wregs[2] & wregs[4] & 0xF0) | (wregs[3] & wregs[5])) > 0;
256  if (v != w5300_int) {
257  w5300_int = v;
258  interrupt_cb(v);
259  }
260 }
261 
262 
263 
264 static Uint8 read_reg ( int addr )
265 {
266  Uint8 data;
267  addr &= 0x3FF;
268  if (addr >= 0x200) {
269  int sn = (addr - 0x200) >> 6; // sn: socket number (0-7)
270  int sn_base = addr & ~0x3F;
271  int sn_reg = addr & 0x3F;
272  switch (sn_reg) {
273  case 0x00:
274  case 0x01:
275  case 0x02:
276  case 0x03:
277  case 0x06: // Sn_IR0
278  case 0x07: // Sn_IR1
279  case 0x08: // Sn_SSR0 [reserved]
280  case 0x09: // Sn_SSR1
281  case 0x0A: // Sn_PORTR0, source port register
282  case 0x0B: // Sn_PORTR1, source port register
283  case 0x14: // Sn_DIPR0, Destination IP Address Register
284  case 0x15: // Sn_DIPR1, Destination IP Address Register
285  case 0x16: // Sn_DIPR2, Destination IP Address Register
286  case 0x17: // Sn_DIPR3, Destination IP Address Register
287  case 0x20: // Sn_TX_WRSR0, TX Write Size Register, but all bits are unused so ...
288  // for these registers are KNOWN it's OK to pass the value in "wregs" as answer
289  data = wregs[addr];
290  break;
291  case 0x12: // Sn_DPORTR0, destination port register, !!WRITE-ONLY-REGISTER!!
292  data = 0xFF;
293  break;
294  case 0x13: // Sn_DPORTR1, destination port register, !!WRITE-ONLY-REGISTER!!
295  data = 0xFF;
296  break;
297  case 0x21: // Sn_TX_WRSR1, TX Write Size Register
298  data = wregs[addr] & 1; // only LSB one bit is valid!
299  break;
300  case 0x22: // Sn_TX_WRSR2, TX Write Size Register
301  data = wregs[addr];
302  break;
303  case 0x23: // Sn_TX_WRSR3, TX Write Size Register
304  data = wregs[addr];
305  break;
306  case 0x25: // Sn_TX_FSR1 (Sn_TX_FSR0 is not written, since the value cannot be so big, even this FSR1 only 1 bit LSB is used!)
307  data = (TX_FIFO_GET_FREE(sn) >> 16) & 0x01;
308  break;
309  case 0x26: // Sn_TX_FSR2
310  data = (TX_FIFO_GET_FREE(sn) >> 8) & 0xFF;
311  break;
312  case 0x27: // Sn_TX_FSR3
313  data = TX_FIFO_GET_FREE(sn) & 0xFF;
314  break;
315  case 0x2E: // Sn_TX_FIFOR0, for real TX FIFO can only be read in memory test mode ... FIXME
316  { Uint16 data16 = TX_FIFO_CONSUME(sn);
317  data = data16 >> 8;
318  wregs[addr + 1] = data16 & 0xFF; }
319  break;
320  case 0x2F: // Sn_TX_FIFOR1, for real TX FIFO can only be read in memory test mode ... FIXME
321  data = wregs[addr]; // access of reg Sn_BASE + 0x2E stored the needed value here!
322  break;
323  case 0x30: // Sn_RX_FIFOR0
324  { Uint16 data16 = RX_FIFO_CONSUME(sn);
325  data = data16 >> 8;
326  wregs[addr + 1] = data16 & 0xFF; }
327  break;
328  case 0x31: // Sn_RX_FIFOR1
329  data = wregs[addr]; // access of reg Sn_BASE + 0x30 stored the needed value here!
330  break;
331  default:
332  DEBUGPRINT("EPNET: W5300: reading unemulated SOCKET register $%03X S-%d/$%02X" NL, addr, sn, sn_reg);
333  data = wregs[addr]; // no idea, just pass back the value ...
334  break;
335  }
336  } else {
337  data = wregs[addr];
338  }
339  //DEBUGPRINT("EPNET: W5300-REG: reading register $%03X with data $%02X" NL, addr, data);
340  return data;
341 }
342 
343 
344 static void write_reg ( int addr, Uint8 data )
345 {
346  addr &= 0x3FF;
347  //DEBUGPRINT("EPNET: W5300-REG: writing register $%03X with data $%02X" NL, addr, data);
348  if (addr >= 0x200) {
349  int sn = (addr - 0x200) >> 6; // sn: socket number (0-7)
350  int sn_base = addr & ~0x3F;
351  int sn_reg = addr & 0x3F;
352  int xerrno;
353  switch (sn_reg) {
354  case 0x00: // Sn_MR0, socket mode register
355  case 0x01: // Sn_MR1
356  wregs[addr] = data;
357  break;
358  case 0x02: // Sn_CR0 [reserved], command register, this byte cannot be written
359  case 0x08: // SSR0 [reserved] cannot be written by the USER
360  case 0x09: // SSR1 cannot be written by the USER
361  break;
362  case 0x03: // Sn_CR1, command register
363  // "When W5300 detects any command, Sn_CR is automatically cleared to 0x00. Even though Sn_CR is
364  // cleared to 0x00, the command can be still performing. It can be checked by Sn_IR or Sn_SSR if
365  // command is completed or not."
366  wregs[addr] = 0;
367  DEBUGPRINT("EPNET: W5300: got command $%02X on SOCKET %d" NL, data, sn);
368  switch (data) {
369  case 0x01: // OPEN
370  // See the protocol the socket wanted to be open with. Currently supporting only TCP and UDP, no raw or anything ...
371  switch (wregs[sn_base + 1] & 15) {
372  case 1: // TCP
373  if (wsockets[sn].sock != XS_INVALID_SOCKET) {
374  xemusock_close(wsockets[sn].sock, NULL);
375  wsockets[sn].sock = XS_INVALID_SOCKET;
376  }
377  wsockets[sn].mode = 1;
378  wregs[sn_base + 9] = 0x13; // SOCK_INIT status, telling that socket is open in TCP mode
379  xemusock_fill_servaddr_for_inet_ip_netlong(
380  &wsockets[sn].servaddr,
381  xemusock_ipv4_netoctetarray_to_netlong(wregs + sn_base + 0x14), // pointer to IP address bytes
382  (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13] // port number
383  );
384  //wsockets[sn].todo = TODO_TCP_CREATE;
385  DEBUGPRINT("EPNET: W5300: OPEN: socket %d is open in TCP mode now towards %d.%d.%d.%d:%d" NL,
386  sn,
387  wregs[sn_base + 0x14], wregs[sn_base + 0x15], wregs[sn_base + 0x16], wregs[sn_base + 0x17], // IP
388  (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13] // port number
389  );
390  break;
391  case 2: // UDP
392  if (wsockets[sn].sock != XS_INVALID_SOCKET) {
393  xemusock_close(wsockets[sn].sock, NULL);
394  wsockets[sn].sock = XS_INVALID_SOCKET;
395  }
396  xemusock_fill_servaddr_for_inet_ip_netlong(
397  &wsockets[sn].servaddr,
398  xemusock_ipv4_netoctetarray_to_netlong(wregs + sn_base + 0x14), // pointer to IP address bytes
399  (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13] // port number
400  );
401  wsockets[sn].sock = xemusock_create_for_inet(XEMUSOCK_UDP, XEMUSOCK_NONBLOCKING, &xerrno);
402  if (wsockets[sn].sock != XS_INVALID_SOCKET) {
403  wsockets[sn].mode = 2;
404  wregs[sn_base + 9] = 0x22; // SOCK_UDP status, telling that socket is open in UDP mode
405  DEBUGPRINT("EPNET: W5300: OPEN: socket %d is open in UDP mode now towards %d.%d.%d.%d:%d" NL,
406  sn,
407  wregs[sn_base + 0x14], wregs[sn_base + 0x15], wregs[sn_base + 0x16], wregs[sn_base + 0x17], // IP
408  (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13] // port number
409  );
410  } else {
411  DEBUGPRINT("EPNET: EMU: network problem: %s" NL, xemusock_strerror(xerrno));
412  wsockets[sn].mode = 0;
413  wregs[sn_base + 9] = 0;
414  }
415  break;
416  default:
417  if (wsockets[sn].sock != XS_INVALID_SOCKET) {
418  xemusock_close(wsockets[sn].sock, NULL);
419  wsockets[sn].sock = XS_INVALID_SOCKET;
420  }
421  wsockets[sn].mode = 0; // ???
422  wregs[sn_base + 9] = 0;
423  DEBUGPRINT("EPNET: W5300: OPEN: unknown protocol on SOCKET %d: %d" NL, sn, wregs[sn_base + 1] & 15);
424  break;
425  }
426  break;
427  case 0x10: // CLOSE
428  wsockets[sn].todo = 0;
429  wsockets[sn].mode = 0;
430  wregs[sn_base + 9] = 0; // "Sn_SSR is changed to SOCK_CLOSED."
431  DEBUGPRINT("EPNET: W5300: command CLOSE on SOCKET %d" NL, sn);
432  if (wsockets[sn].sock != XS_INVALID_SOCKET) {
433  xemusock_close(wsockets[sn].sock, NULL);
434  wsockets[sn].sock = XS_INVALID_SOCKET;
435  }
436  break;
437  case 0x20: // SEND
438  DEBUGPRINT("EPNET: W5300: SEND command on SOCKET %d" NL, sn);
439  if (wregs[sn_base + 9] == 0x22) {
440  // UDP-SEND!!
441  DEBUGPRINT("Target is %d.%d.%d.%d:%d UDP, source port: %d TXLEN=%d" NL,
442  wregs[sn_base + 0x14],
443  wregs[sn_base + 0x15],
444  wregs[sn_base + 0x16],
445  wregs[sn_base + 0x17],
446  (wregs[sn_base + 0x12] << 8) + wregs[sn_base + 0x13],
447  (wregs[sn_base + 0x0A] << 8) + wregs[sn_base + 0x0B],
448  (wregs[sn_base + 0x21] << 16) + (wregs[sn_base + 0x22] << 8) + wregs[sn_base + 0x23]
449  );
450  int b = 0;
451  Uint8 debug[1024];
452  while (TX_FIFO_GET_USED(sn)) {
453  Uint16 data = TX_FIFO_CONSUME(sn);
454  debug[b++] = data >> 8;
455  debug[b++] = data & 0xFF;
456  }
457  for (int a = 0; a < b; a++) {
458  printf("HEXDUMP @ %04X %02X [%c]\n", a, debug[a], debug[a] >= 0x20 && debug[a] < 127 ? debug[a] : '?');
459  }
460  //add_net_thread_tx(sn, sn_base);
461  }
462  break;
463  case 0x40: // RECV
464  case 0x08: // DISCON only valid in TCP mode
465  case 0x04: // CONNECT only valid in TCP mode, operates as "client" mode
466  case 0x02: // LISTEN only valid in TCP mode, operates as "server" mode
467  case 0x21: // SEND_MAC
468  case 0x22: // SEND_KEEP
469  default:
470  DEBUGPRINT("EPNET: W5300: command *UNKNOWN* ($%02X) on SOCKET %d" NL, data, sn);
471  break;
472  }
473  break;
474  case 0x06: // Sn_IR0: not used for too much (but see Sn_IR1)
475  break;
476  case 0x07: // Sn_IR1: Socket interrupt register (Sn_IR0 part is not so much used)
477  wregs[addr] &= ~data; // All bits written to '1' will cause that bit to be CLEARED!
478  // TODO: if we have all zero bits now, no interrupt pending, and IR for the given socket should be cleared!
479  //update_socket_interrupts(sn, wregs[addr]);
480  break;
481  case 0x0A: // Sn_PORTR0, source port register [should be set before OPEN]
482  wregs[addr] = data;
483  break;
484  case 0x0B: // Sn_PORTR1, source port register [should be set before OPEN]
485  wregs[addr] = data;
486  break;
487  case 0x12: // Sn_DPORTR0, destination port register
488  wregs[addr] = data;
489  break;
490  case 0x13: // Sn_DPORTR1, destination port register
491  wregs[addr] = data;
492  break;
493  case 0x14: // Sn_DIPR0, Destination IP Address Register
494  wregs[addr] = data;
495  break;
496  case 0x15: // Sn_DIPR1, Destination IP Address Register
497  wregs[addr] = data;
498  break;
499  case 0x16: // Sn_DIPR3, Destination IP Address Register
500  wregs[addr] = data;
501  break;
502  case 0x17: // Sn_DIPR4, Destination IP Address Register
503  wregs[addr] = data;
504  break;
505  case 0x2E: // Sn_TX_FIFOR0
506  wregs[addr] = data;
507  break;
508  case 0x2F: // Sn_TX_FIFOR1
509  TX_FIFO_FILL(sn, (wregs[addr - 1] << 8) | data); // access of reg Sn_BASE + 0x2E stored the needed value we're referencing here!
510  break;
511  case 0x20: // Sn_TX_WRSR0, TX Write Size Register, but all bits are unused so ...
512  break;
513  case 0x21: // Sn_TX_WRSR1, TX Write Size Register
514  wregs[addr] = data & 1; // only LSB one bit is valid!
515  break;
516  case 0x22: // Sn_TX_WRSR2, TX Write Size Register
517  wregs[addr] = data;
518  break;
519  case 0x23: // Sn_TX_WRSR3, TX Write Size Register
520  wregs[addr] = data;
521  break;
522  case 0x30: // Sn_RX_FIFOR0, for real RX FIFO can only be written in memory test mode ... FIXME
523  wregs[addr] = data;
524  break;
525  case 0x31: // Sn_RX_FIFOR1, for real RX FIFO can only be written in memory test mode ... FIXME
526  RX_FIFO_FILL(sn, (wregs[addr - 1] << 8) | data); // access of reg Sn_BASE + 0x30 stored the needed value we're referencing here!
527  break;
528  default:
529  DEBUGPRINT("EPNET: W5300: writing unemulated SOCKET register $%03X S-%d/$%02X" NL, addr, sn, sn_reg);
530  wregs[addr] = data;
531  break;
532  }
533  } else switch (addr) {
534  case 2: // IR0
535  case 3: // IR1
536  wregs[addr] &= 255 - data;
537  update_interrupts();
538  break;
539  case 4: // IMR0
540  case 5: // IMR1
541  wregs[addr] = data;
542  update_interrupts();
543  break;
544  case 0xFE: // IDR (ID register)
545  case 0xFF: // IDR (ID register)
546  break; // these registers are not writable, skip writing!
547  default:
548  wregs[addr] = data;
549  break;
550  }
551 }
552 
553 
554 static void default_interrupt_callback ( int level ) {
555  DEBUGPRINT("EPNET: INTERRUPT -> %d" NL, level);
556 }
557 
558 
559 /* --- Interface functions --- */
560 
561 
562 void epnet_reset ( void )
563 {
564  memset(wregs, 0, sizeof wregs);
565  mr0 = 0x38; mr1 = 0x00;
566  //direct_mode = (mr1 & 1) ? 0 : 1;
567  idm_ar0 = 0; idm_ar1 = 0; idm_ar = 0;
568  w5300_int = 0;
569  wregs[0x1C] = 0x07; wregs[0x1D] = 0xD0; // RTR retransmission timeout-period register
570  wregs[0x1F] = 8; // RCR retransmission retry-count register
571  memset(wregs + 0x20, 8, 16); // TX and RX mem size conf
572  wregs[0x31] = 0xFF; // MTYPER1
573  wregs[0xFE] = 0x53; // IDR: ID register
574  wregs[0xFF] = 0x00; // IDR: ID register
575  DEBUGPRINT("EPNET: reset, direct_mode = %s" NL, IS_DIRECT_MODE() ? "yes" : "no");
576 }
577 
578 
579 static Uint8 *patch_rom_add_config_pair ( Uint8 *p, const char *name, const char *value )
580 {
581  int name_len = strlen(name);
582  int value_len = strlen(value);
583  *p++ = name_len;
584  memcpy(p, name, name_len);
585  p += name_len;
586  *p++ = value_len;
587  memcpy(p, value, value_len);
588  p += value_len;
589  *p++ = 0;
590  *p = 0; // this will be overwritten if there is more call of this func, otherwise it will close the list of var=val pair list
591  return p;
592 }
593 
594 static int patch_rom ( void )
595 {
596  Uint8 *epnet = NULL;
597  // Search for our ROM ...
598  for (Uint8 *p = memory; p < memory + sizeof(memory); p += 0x4000)
599  if (!memcmp(p, "EXOS_ROM", 8) && !memcmp(p + 0xD, "EPNET", 5)) {
600  if (epnet) {
601  ERROR_WINDOW("Multiple instances of EPNET ROM in memory?\nIt won't work too well!");
602  return -1;
603  } else
604  epnet = p;
605  }
606  if (epnet) {
607  int segment = (int)(epnet - memory) >> 14;
608  DEBUGPRINT("EPNET: found ROM in segment %03Xh" NL, segment);
609  // Found our EPNET ROM :-) Now, it's time to patch things up!
610  /*if (epnet[0x18] == EPNET_IO_BASE) {
611  ERROR_WINDOW("EPNET ROM has been already patched?!");
612  return -1;
613  }*/
614  Uint8 *q = epnet + 0x4000 + (epnet[0x19] | (epnet[0x1A] << 8));
615  if (q > epnet + 0x7F00) {
616  ERROR_WINDOW("Bad EPNET ROM, invalid position of settings area!");
617  //return -1;
618  } else {
619  //DEBUGPRINT("EPNET: byte at set area: %02Xh" NL, *q);
620  q = patch_rom_add_config_pair(q, "DHCP", "n");
621  q = patch_rom_add_config_pair(q, "NTP", "n");
622  q = patch_rom_add_config_pair(q, "IP", "192.168.192.168");
623  q = patch_rom_add_config_pair(q, "SUBNET", "255.255.255.0");
624  q = patch_rom_add_config_pair(q, "GATEWAY", "192.168.192.169");
625  q = patch_rom_add_config_pair(q, "DNS", "8.8.8.8");
626  q = patch_rom_add_config_pair(q, "XEP128", "this-is-an-easter-egg-from-lgb");
627  }
628  epnet[0x18] = EPNET_IO_BASE; // using fixed port (instead of EPNET's decoding based on the ROM position), which matches our emulation of course.
629  // MAC address setup :) According to Bruce's suggestion, though it does not matter in the emulation too much (we don't use eth level stuff anyway, but ask our host OS to do TCP/IP things ...)
630  static const Uint8 mac_address[] = {0x00,0x00,0xF6,0x42,0x42,0x76};
631  memcpy(epnet + 0x20, mac_address, sizeof(mac_address));
632  return 0;
633  } else {
634  DEBUGPRINT("EPNET: cannot found EPNET ROM! Maybe it's not loaded?! EPNET emulation will not work!" NL);
635  return -1;
636  }
637 }
638 
639 
640 void epnet_init ( void (*cb)(int) )
641 {
642  w5300_does_work = 0;
643  patch_rom();
644  char sockapi_error_msg[256];
645  if (xemusock_init(sockapi_error_msg)) {
646  ERROR_WINDOW("Cannot intiailize socket API:\n%s", sockapi_error_msg);
647  w5300_does_work = 0;
648  } else if (!start_net_thread()) {
649  w5300_does_work = 1;
650  interrupt_cb = cb ? cb : default_interrupt_callback;
651  DEBUGPRINT("EPNET: init seems to be OK." NL);
652  epnet_reset();
653  memset(wmem, 0, sizeof wmem);
654  default_w5300_config();
655  init_net_thread_task_list();
656  }
657 }
658 
659 
660 static void epnet_shutdown ( int restart )
661 {
662  DEBUGPRINT("EPNET: shutdown pending connections (if any)" NL);
663  if (thread) {
664  int retval;
665  SDL_AtomicSet(&thread_can_go, 0);
666  SDL_WaitThread(thread, &retval);
667  DEBUGPRINT("EPNET: THREAD: %p exited with code %d" NL, thread, retval);
668  }
669  // since thread does not run here, it's safe to "play" with net_thread_tasks[] stuffs without any lock!
670  for (int sn = 0; sn < 8; sn++) {
671  if (net_thread_tasks[sn].sock != XS_INVALID_SOCKET) {
672  xemusock_close(net_thread_tasks[sn].sock, NULL);
673  net_thread_tasks[sn].sock = XS_INVALID_SOCKET;
674  }
675  }
676  if (restart && thread) {
677  start_net_thread();
678  }
679 }
680 
681 void epnet_uninit ( void )
682 {
683  epnet_shutdown(0);
684  //xemu_free_sockapi();
685  xemusock_uninit();
686  w5300_does_work = 0;
687  DEBUGPRINT("EPNET: uninit" NL);
688 }
689 
690 Uint8 epnet_read_cpu_port ( unsigned int port )
691 {
692  Uint8 data;
693  if (XEMU_UNLIKELY(IS_DIRECT_MODE())) {
694  port += direct_mode_epnet_shift;
695  if (port >= 2) {
696  data = read_reg(port);
697  DEBUGPRINT("EPNET: IO: reading in *DIRECT-MODE* W5300 register $%03X, data: $%02X @ PC=$%04X" NL, port, data, Z80_PC);
698  return data;
699  }
700  }
701  switch (port) {
702  case 0:
703  data = mr0;
704  DEBUGPRINT("EPNET: IO: reading MR0: $%02X @ PC=$%04X" NL, data, Z80_PC);
705  return data;
706  case 1:
707  data = mr1;
708  DEBUGPRINT("EPNET: IO: reading MR1: $%02X @ PC=$%04X" NL, data, Z80_PC);
709  return data;
710  case 2:
711  data = idm_ar0;
712  DEBUGPRINT("EPNET: IO: reading IDM_AR0: $%02X @ PC=$%04X" NL, data, Z80_PC);
713  return data;
714  case 3:
715  data = idm_ar1;
716  DEBUGPRINT("EPNET: IO: reading IDM_AR1: $%02X @ PC=$%04X" NL, data, Z80_PC);
717  return data;
718  case 4:
719  data = read_reg(idm_ar + 0);
720  DEBUGPRINT("EPNET: IO: reading W5300 reg#$%03X through IDM_DR0: $%02X @ PC=$%04X" NL, idm_ar + 0, data, Z80_PC);
721  return data;
722  case 5:
723  data = read_reg(idm_ar + 1);
724  DEBUGPRINT("EPNET: IO: reading W5300 reg#$%03X through IDM_DR1: $%02X @ PC=$%04X" NL, idm_ar + 1, data, Z80_PC);
725  return data;
726  default:
727  DEBUGPRINT("EPNET: IO: reading *UNDECODED* W5300 port in indirect mode: $%03X @ PC=$%04X" NL, port, Z80_PC);
728  return 0xFF;
729  }
730 }
731 
732 
733 
734 void epnet_write_cpu_port ( unsigned int port, Uint8 data )
735 {
736  if (XEMU_UNLIKELY(IS_DIRECT_MODE())) {
737  port += direct_mode_epnet_shift;
738  if (port >= 2) {
739  DEBUGPRINT("EPNET: IO: writing in *DIRECT-MODE* W5300 register $%03X, data: $%02X @ PC=$%04X" NL, port, data, Z80_PC);
740  write_reg(port, data);
741  return;
742  }
743  }
744  switch (port) {
745  case 0:
746  DEBUGPRINT("EPNET: IO: writing MR0: $%02X @ PC=$%04X" NL, data, Z80_PC);
747  if (data & 1) ERROR_WINDOW("EPNET: FIFO byte-order swap feature is not emulated");
748  mr0 = (mr0 & 0xC0) | (data & 0x3F); // DBW and MPF bits cannot be overwritten by user
749  break;
750  case 1:
751  DEBUGPRINT("EPNET: IO: writing MR1: $%02X @ PC=$%04X" NL, data, Z80_PC);
752  if (data & 128) { // software reset?
753  epnet_reset();
754  epnet_shutdown(1);
755  } else {
756  if (data & 0x20)
757  DEBUGPRINT("EPNET: memory-test mode by 0x20 on MR1" NL);
758  if (data & 8) ERROR_WINDOW("EPNET: PPPoE mode is not emulated");
759  if (data & 4) ERROR_WINDOW("EPNET: data bus byte-order swap feature is not emulated");
760  //if ((data & 1) == 0) ERROR_WINDOW("EPNET: direct mode is NOT emulated, only indirect");
761  if (((mr1 ^ data) & 1)) {
762  DEBUGPRINT("EPNET: w5300 access mode change: %s -> %s\n",
763  (mr1 & 1) ? "indirect" : "direct",
764  (data & 1) ? "indirect" : "direct"
765  );
766  }
767  mr1 = data;
768  }
769  break;
770  case 2:
771  idm_ar0 = data & 0x3F;
772  idm_ar = (idm_ar0 << 8) | idm_ar1;
773  //DEBUGPRINT("EPNET: IO: IDM: setting AR0 to $%02X, AR now: $%03X" NL, idm_ar0, idm_ar);
774  break;
775  case 3:
776  idm_ar1 = data & 0xFE; // LSB is chopped off, since the fact reading/writing IDM_DR0 _OR_ 1 will tell that ...
777  idm_ar = (idm_ar0 << 8) | idm_ar1;
778  //DEBUGPRINT("EPNET: IO: IDM: setting AR1 to $%02X, AR now: $%03X" NL, idm_ar1, idm_ar);
779  break;
780  case 4:
781  DEBUGPRINT("EPNET: IO: writing W5300 reg#$%03X through IDM_DR0: $%02X @ PC=$%04X" NL, idm_ar + 0, data, Z80_PC);
782  write_reg(idm_ar + 0, data);
783  break;
784  case 5:
785  DEBUGPRINT("EPNET: IO: writing W5300 reg#$%03X through IDM_DR1: $%02X @ PC=$%04X" NL, idm_ar + 1, data, Z80_PC);
786  write_reg(idm_ar + 1, data);
787  break;
788  default:
789  DEBUGPRINT("EPNET: IO: writing *UNDECODED* W5300 port in indirect mode: $%03X @ PC=$%04X" NL, port, Z80_PC);
790  break;
791  }
792 }
793 
794 // CONIG_EPNET_SUPPORT
795 #endif
tx_size
int tx_size
Definition: ethernet65.c:78
addr
int addr
Definition: dma65.c:81
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
Uint32
uint32_t Uint32
Definition: fat32.c:49
Uint8
uint8_t Uint8
Definition: fat32.c:51
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
emutools_socketapi.h
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
mode
int mode
Definition: vera.c:61
NL
#define NL
Definition: fat32.c:37
memory
Uint8 memory[0x100000]
Definition: commodore_65.c:43
xep128.h
size
int size
Definition: inject.c:37
Z80_PC
#define Z80_PC
Definition: z80ex.h:121
value
int value
Definition: dma65.c:90
Uint16
uint16_t Uint16
Definition: fat32.c:50
name
const char * name
Definition: joystick.c:46
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
buf
Uint8 buf[512]
Definition: fat32.c:155