38 #ifdef CONFIG_EPNET_SUPPORT
45 #define direct_mode_epnet_shift 0
46 #define IS_DIRECT_MODE() (!(mr1&1))
49 int w5300_does_work = 0;
51 static Uint16 wmem[0x10000];
52 static Uint8 wregs[0x400];
53 static Uint8 idm_ar0, idm_ar1;
55 static Uint8 mr0, mr1;
57 static void (*interrupt_cb)(int);
61 struct w5300_fifo_st {
63 int readpos, storepos;
68 struct w5300_socket_st {
69 struct w5300_fifo_st rx_fifo;
70 struct w5300_fifo_st tx_fifo;
72 struct sockaddr_in servaddr;
73 volatile xemusock_socket_t sock;
78 static struct w5300_socket_st wsockets[8];
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
91 static void fifo_fill (
struct w5300_fifo_st *f,
Uint16 data )
98 f->w[f->storepos] =
data;
99 f->storepos =
XEMU_UNLIKELY(f->storepos == f->size - 1) ? 0 : f->storepos + 1;
101 f->free = f->size - f->stored;
106 static Uint16 fifo_consume (
struct w5300_fifo_st *f )
110 f->readpos =
XEMU_UNLIKELY(f->readpos == f->size - 1) ? 0 : f->readpos + 1;
112 f->free = f->size - f->stored;
120 static void fifo_reset (
struct w5300_fifo_st *f )
128 static void default_w5300_config (
void )
133 for (
int sn = 0; sn < 8; sn++) {
134 wsockets[sn].rx_fifo.w = w;
136 wsockets[sn].tx_fifo.w = w;
138 wsockets[sn].rx_fifo.size = 4096;
139 wsockets[sn].tx_fifo.size = 4096;
140 wsockets[sn].mode = 0;
147 static SDL_Thread *thread = NULL;
148 static SDL_atomic_t thread_can_go;
149 struct net_task_data_st {
154 volatile xemusock_socket_t sock;
156 volatile int response;
164 Uint8 rx_buf[0x20000];
165 Uint8 tx_buf[0x20000];
166 struct net_task_data_st
data;
167 } net_thread_tasks[8];
173 static int net_thread (
void *ptr )
176 SDL_AtomicSet(&thread_can_go, 2);
177 while (SDL_AtomicGet(&thread_can_go) != 0) {
179 for (
int sn = 0; sn < 8; sn++) {
180 Uint8 buffer[0x20000];
181 struct net_task_data_st
data;
183 if (SDL_AtomicTryLock(&net_thread_tasks[sn].lock) == SDL_FALSE) {
187 memcpy(&
data, &net_thread_tasks[sn].
data,
sizeof(
data));
188 SDL_AtomicUnlock(&net_thread_tasks[sn].lock);
199 SDL_AtomicLock(&net_thread_tasks[sn].lock);
200 memcpy(&net_thread_tasks[sn].
data, &
data,
sizeof(
data));
201 SDL_AtomicUnlock(&net_thread_tasks[sn].lock);
216 static void init_net_thread_task_list (
void )
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;
225 static int add_net_thread_task (
int sn,
Uint8 *
buf )
230 int start_net_thread (
void )
232 SDL_AtomicSet(&thread_can_go, 1);
233 thread = SDL_CreateThread(net_thread,
"Xep128 EPNET socket thread", (
void*)NULL);
235 ERROR_WINDOW(
"EPNET: cloud not create EPNET thread: %s", SDL_GetError());
238 Uint32 timeout = SDL_GetTicks();
239 while (SDL_AtomicGet(&thread_can_go) == 1) {
241 if (SDL_GetTicks() - timeout > 1000) {
242 SDL_AtomicSet(&thread_can_go, 0);
243 ERROR_WINDOW(
"EPNET: timeout for starting EPNET thread");
247 DEBUGPRINT(
"EPNET: THREAD: thread seems to be started finely :)" NL);
253 static void update_interrupts (
void )
255 int v = ((wregs[2] & wregs[4] & 0xF0) | (wregs[3] & wregs[5])) > 0;
256 if (v != w5300_int) {
269 int sn = (
addr - 0x200) >> 6;
270 int sn_base =
addr & ~0x3F;
271 int sn_reg =
addr & 0x3F;
307 data = (TX_FIFO_GET_FREE(sn) >> 16) & 0x01;
310 data = (TX_FIFO_GET_FREE(sn) >> 8) & 0xFF;
313 data = TX_FIFO_GET_FREE(sn) & 0xFF;
316 {
Uint16 data16 = TX_FIFO_CONSUME(sn);
318 wregs[
addr + 1] = data16 & 0xFF; }
324 {
Uint16 data16 = RX_FIFO_CONSUME(sn);
326 wregs[
addr + 1] = data16 & 0xFF; }
332 DEBUGPRINT(
"EPNET: W5300: reading unemulated SOCKET register $%03X S-%d/$%02X" NL,
addr, sn, sn_reg);
349 int sn = (
addr - 0x200) >> 6;
350 int sn_base =
addr & ~0x3F;
351 int sn_reg =
addr & 0x3F;
371 switch (wregs[sn_base + 1] & 15) {
373 if (wsockets[sn].sock != XS_INVALID_SOCKET) {
374 xemusock_close(wsockets[sn].sock, NULL);
375 wsockets[sn].sock = XS_INVALID_SOCKET;
377 wsockets[sn].mode = 1;
378 wregs[sn_base + 9] = 0x13;
379 xemusock_fill_servaddr_for_inet_ip_netlong(
380 &wsockets[sn].servaddr,
381 xemusock_ipv4_netoctetarray_to_netlong(wregs + sn_base + 0x14),
382 (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13]
385 DEBUGPRINT(
"EPNET: W5300: OPEN: socket %d is open in TCP mode now towards %d.%d.%d.%d:%d" NL,
387 wregs[sn_base + 0x14], wregs[sn_base + 0x15], wregs[sn_base + 0x16], wregs[sn_base + 0x17],
388 (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13]
392 if (wsockets[sn].sock != XS_INVALID_SOCKET) {
393 xemusock_close(wsockets[sn].sock, NULL);
394 wsockets[sn].sock = XS_INVALID_SOCKET;
396 xemusock_fill_servaddr_for_inet_ip_netlong(
397 &wsockets[sn].servaddr,
398 xemusock_ipv4_netoctetarray_to_netlong(wregs + sn_base + 0x14),
399 (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13]
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;
405 DEBUGPRINT(
"EPNET: W5300: OPEN: socket %d is open in UDP mode now towards %d.%d.%d.%d:%d" NL,
407 wregs[sn_base + 0x14], wregs[sn_base + 0x15], wregs[sn_base + 0x16], wregs[sn_base + 0x17],
408 (wregs[sn_base + 0x12] << 8) | wregs[sn_base + 0x13]
411 DEBUGPRINT(
"EPNET: EMU: network problem: %s" NL, xemusock_strerror(xerrno));
412 wsockets[sn].mode = 0;
413 wregs[sn_base + 9] = 0;
417 if (wsockets[sn].sock != XS_INVALID_SOCKET) {
418 xemusock_close(wsockets[sn].sock, NULL);
419 wsockets[sn].sock = XS_INVALID_SOCKET;
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);
428 wsockets[sn].todo = 0;
429 wsockets[sn].mode = 0;
430 wregs[sn_base + 9] = 0;
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;
438 DEBUGPRINT(
"EPNET: W5300: SEND command on SOCKET %d" NL, sn);
439 if (wregs[sn_base + 9] == 0x22) {
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]
452 while (TX_FIFO_GET_USED(sn)) {
454 debug[b++] =
data >> 8;
455 debug[b++] =
data & 0xFF;
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] :
'?');
470 DEBUGPRINT(
"EPNET: W5300: command *UNKNOWN* ($%02X) on SOCKET %d" NL,
data, sn);
509 TX_FIFO_FILL(sn, (wregs[
addr - 1] << 8) |
data);
526 RX_FIFO_FILL(sn, (wregs[
addr - 1] << 8) |
data);
529 DEBUGPRINT(
"EPNET: W5300: writing unemulated SOCKET register $%03X S-%d/$%02X" NL,
addr, sn, sn_reg);
533 }
else switch (
addr) {
554 static void default_interrupt_callback (
int level ) {
562 void epnet_reset (
void )
564 memset(wregs, 0,
sizeof wregs);
565 mr0 = 0x38; mr1 = 0x00;
567 idm_ar0 = 0; idm_ar1 = 0; idm_ar = 0;
569 wregs[0x1C] = 0x07; wregs[0x1D] = 0xD0;
571 memset(wregs + 0x20, 8, 16);
575 DEBUGPRINT(
"EPNET: reset, direct_mode = %s" NL, IS_DIRECT_MODE() ?
"yes" :
"no");
581 int name_len = strlen(
name);
582 int value_len = strlen(
value);
584 memcpy(p,
name, name_len);
587 memcpy(p,
value, value_len);
594 static int patch_rom (
void )
599 if (!memcmp(p,
"EXOS_ROM", 8) && !memcmp(p + 0xD,
"EPNET", 5)) {
601 ERROR_WINDOW(
"Multiple instances of EPNET ROM in memory?\nIt won't work too well!");
607 int segment = (int)(epnet -
memory) >> 14;
608 DEBUGPRINT(
"EPNET: found ROM in segment %03Xh" NL, segment);
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!");
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");
628 epnet[0x18] = EPNET_IO_BASE;
630 static const Uint8 mac_address[] = {0x00,0x00,0xF6,0x42,0x42,0x76};
631 memcpy(epnet + 0x20, mac_address,
sizeof(mac_address));
634 DEBUGPRINT(
"EPNET: cannot found EPNET ROM! Maybe it's not loaded?! EPNET emulation will not work!" NL);
640 void epnet_init (
void (*cb)(
int) )
644 const char *init_status = xemusock_init();
646 ERROR_WINDOW(
"Cannot intiailize socket API:\n%s", init_status);
648 }
else if (!start_net_thread()) {
650 interrupt_cb = cb ? cb : default_interrupt_callback;
653 memset(wmem, 0,
sizeof wmem);
654 default_w5300_config();
655 init_net_thread_task_list();
660 static void epnet_shutdown (
int restart )
662 DEBUGPRINT(
"EPNET: shutdown pending connections (if any)" NL);
665 SDL_AtomicSet(&thread_can_go, 0);
666 SDL_WaitThread(thread, &retval);
667 DEBUGPRINT(
"EPNET: THREAD: %p exited with code %d" NL, thread, retval);
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;
676 if (restart && thread) {
681 void epnet_uninit (
void )
683 if (w5300_does_work) {
692 Uint8 epnet_read_cpu_port (
unsigned int port )
696 port += direct_mode_epnet_shift;
698 data = read_reg(port);
699 DEBUGPRINT(
"EPNET: IO: reading in *DIRECT-MODE* W5300 register $%03X, data: $%02X @ PC=$%04X" NL, port,
data,
Z80_PC);
721 data = read_reg(idm_ar + 0);
722 DEBUGPRINT(
"EPNET: IO: reading W5300 reg#$%03X through IDM_DR0: $%02X @ PC=$%04X" NL, idm_ar + 0,
data,
Z80_PC);
725 data = read_reg(idm_ar + 1);
726 DEBUGPRINT(
"EPNET: IO: reading W5300 reg#$%03X through IDM_DR1: $%02X @ PC=$%04X" NL, idm_ar + 1,
data,
Z80_PC);
729 DEBUGPRINT(
"EPNET: IO: reading *UNDECODED* W5300 port in indirect mode: $%03X @ PC=$%04X" NL, port,
Z80_PC);
736 void epnet_write_cpu_port (
unsigned int port,
Uint8 data )
739 port += direct_mode_epnet_shift;
741 DEBUGPRINT(
"EPNET: IO: writing in *DIRECT-MODE* W5300 register $%03X, data: $%02X @ PC=$%04X" NL, port,
data,
Z80_PC);
742 write_reg(port,
data);
749 if (
data & 1)
ERROR_WINDOW(
"EPNET: FIFO byte-order swap feature is not emulated");
750 mr0 = (mr0 & 0xC0) | (
data & 0x3F);
759 DEBUGPRINT(
"EPNET: memory-test mode by 0x20 on MR1" NL);
761 if (
data & 4)
ERROR_WINDOW(
"EPNET: data bus byte-order swap feature is not emulated");
763 if (((mr1 ^
data) & 1)) {
764 DEBUGPRINT(
"EPNET: w5300 access mode change: %s -> %s\n",
765 (mr1 & 1) ?
"indirect" :
"direct",
766 (
data & 1) ?
"indirect" :
"direct"
773 idm_ar0 =
data & 0x3F;
774 idm_ar = (idm_ar0 << 8) | idm_ar1;
778 idm_ar1 =
data & 0xFE;
779 idm_ar = (idm_ar0 << 8) | idm_ar1;
783 DEBUGPRINT(
"EPNET: IO: writing W5300 reg#$%03X through IDM_DR0: $%02X @ PC=$%04X" NL, idm_ar + 0,
data,
Z80_PC);
784 write_reg(idm_ar + 0,
data);
787 DEBUGPRINT(
"EPNET: IO: writing W5300 reg#$%03X through IDM_DR1: $%02X @ PC=$%04X" NL, idm_ar + 1,
data,
Z80_PC);
788 write_reg(idm_ar + 1,
data);
791 DEBUGPRINT(
"EPNET: IO: writing *UNDECODED* W5300 port in indirect mode: $%03X @ PC=$%04X" NL, port,
Z80_PC);