From 0a6637dd4115fdc946f0b802205c8eb66e213a62 Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Thu, 18 Jun 2015 20:31:36 +0300 Subject: [PATCH] Preliminary PCI bridge board emulation, NE2000 PCI NIC emulation. --- devices.cpp | 14 + include/pci.h | 9 + include/pci_hw.h | 108 +++ pci.cpp | 1673 +++++++++++++++++++++++++++++++++++++++++ qemuvga/ne2000.cpp | 1060 ++++++++++++++++++++++++++ qemuvga/ne2000.h | 42 ++ qemuvga/qemuuaeglue.h | 25 +- 7 files changed, 2930 insertions(+), 1 deletion(-) create mode 100644 include/pci.h create mode 100644 include/pci_hw.h create mode 100644 pci.cpp create mode 100644 qemuvga/ne2000.cpp create mode 100644 qemuvga/ne2000.h diff --git a/devices.cpp b/devices.cpp index 4b708753..60cb50c9 100644 --- a/devices.cpp +++ b/devices.cpp @@ -54,6 +54,8 @@ #include "tabletlibrary.h" #include "luascript.h" #include "driveclick.h" +#include "pci.h" +#include "pci_hw.h" #ifdef RETROPLATFORM #include "rp.h" #endif @@ -83,6 +85,9 @@ void devices_reset(int hardreset) #ifdef NCR9X ncr9x_reset(); #endif +#ifdef WITH_PCI + pci_reset(); +#endif #ifdef JIT compemu_reset (); #endif @@ -188,6 +193,9 @@ void devices_rethink(void) ncr9x_rethink(); #endif ncr80_rethink(); +#ifdef WITH_PCI + pci_rethink(); +#endif #ifdef WITH_TOCCATA sndboard_rethink(); #endif @@ -231,6 +239,9 @@ void reset_all_systems (void) netdev_reset (); netdev_start_threads (); #endif +#ifdef WITH_PCI + pci_reset(); +#endif #ifdef FILESYS filesys_prepare_reset (); filesys_reset (); @@ -295,6 +306,9 @@ void do_leave_program (void) #ifdef AUTOCONFIG expansion_cleanup (); #endif +#ifdef WITH_PCI + pci_free(); +#endif #ifdef FILESYS filesys_cleanup (); #endif diff --git a/include/pci.h b/include/pci.h new file mode 100644 index 00000000..585f6989 --- /dev/null +++ b/include/pci.h @@ -0,0 +1,9 @@ + +extern void pci_free(void); +extern void pci_reset(void); +extern void pci_rethink(void); +extern void pci_dump(int); +extern addrbank *dkb_wildfire_pci_init(struct romconfig *rc); + +extern addrbank *pcibridge_init(struct romconfig *rc); +extern addrbank *pcibridge_init2(struct romconfig *rc); diff --git a/include/pci_hw.h b/include/pci_hw.h new file mode 100644 index 00000000..e445f7a1 --- /dev/null +++ b/include/pci_hw.h @@ -0,0 +1,108 @@ + + +#define MAX_PCI_BOARDS 6 +#define MAX_PCI_BARS 7 + +typedef uae_u32(REGPARAM3 *pci_get_func)(struct pci_board_state*,uaecptr) REGPARAM; +typedef void (REGPARAM3 *pci_put_func)(struct pci_board_state*,uaecptr,uae_u32) REGPARAM; +typedef void (*pci_dev_irq)(struct pci_board_state*,bool); +typedef bool(*pci_dev_init)(struct pci_board_state*); +typedef void(*pci_dev_reset)(struct pci_board_state*); +typedef void(*pci_dev_free)(struct pci_board_state*); + +typedef struct +{ + pci_get_func lget, wget, bget; + pci_put_func lput, wput, bput; +} pci_addrbank; + +typedef int(*pci_slot_index)(uaecptr); + +struct pci_config +{ + uae_u16 vendor; + uae_u16 device; + uae_u16 command; + uae_u16 status; + uae_u8 revision; + uae_u32 deviceclass; + uae_u8 header; + uae_u16 subsystenvendor; + uae_u16 subsystem; + uae_u8 interruptpin; + uae_u32 bars[MAX_PCI_BARS]; +}; + +struct pci_board +{ + const TCHAR *label; + const struct pci_config *config; + pci_dev_init init; + pci_dev_free free; + pci_dev_reset reset; + pci_dev_irq irq; + pci_addrbank bars[MAX_PCI_BARS]; +}; + +struct pci_board_state +{ + uae_u8 config_data[256 + 3]; + uaecptr bar[MAX_PCI_BARS]; + uaecptr bar_old[MAX_PCI_BARS]; + bool bar_enabled[MAX_PCI_BARS]; + uaecptr bar_start[MAX_PCI_BARS]; + uaecptr bar_end[MAX_PCI_BARS]; + uae_u32 bar_size[MAX_PCI_BARS]; + int selected_bar; + const struct pci_board *board; + int index; + bool memory_map_active; + bool io_map_active; + struct pci_bridge *bridge; +}; + +struct pci_bridge +{ + const TCHAR *label; + int type; + int endian_swap_config; + uae_u32 io_offset; + int endian_swap_io; + uae_u32 memory_offset; + int endian_swap_memory; + uae_u8 intena; + bool irq; + uae_u16 intreq_mask; + pci_slot_index get_index; + struct pci_board_state boards[MAX_PCI_BOARDS]; + uae_u8 config[16]; + uae_u8 *data; + int configured; + int configured_2; + int bank_zorro; + int bank_2_zorro; + addrbank *bank; + addrbank *bank_2; + int board_size; + int board_size_2; + uaecptr baseaddress; + uaecptr baseaddress_end; + uaecptr baseaddress_offset; + uaecptr baseaddress_2; + uaecptr baseaddress_end_2; + uaecptr baseaddress_offset_2; + uae_u8 acmemory[128]; + uae_u8 acmemory_2[128]; + struct romconfig *rc; + uae_u16 window; +}; + +extern void pci_free(void); +extern void pci_reset(void); +extern void pci_rethink(void); + +extern addrbank *dkb_wildfire_pci_init(struct romconfig *rc); + +extern void pci_irq_callback(struct pci_board_state *pcibs, bool irq); + +extern const struct pci_board ne2000_pci_board; diff --git a/pci.cpp b/pci.cpp new file mode 100644 index 00000000..3ff5041c --- /dev/null +++ b/pci.cpp @@ -0,0 +1,1673 @@ +/* +* UAE - The Un*x Amiga Emulator +* +* PCI Bridge board emulation +* +* Copyright 2015 Toni Wilen +* Hardware information by Radoslaw Kujawa +* +*/ +#include "sysconfig.h" +#include "sysdeps.h" + +#include "options.h" +#include "custom.h" +#include "memory.h" +#include "debug.h" +#include "pci_hw.h" +#include "pci.h" +#include "ncr_scsi.h" +#include "newcpu.h" + +#include "qemuvga/qemuuaeglue.h" +#include "qemuvga/queue.h" +#include "qemuvga/scsi/scsi.h" + +#define PCI_BRIDGE_WILDFIRE 0 +#define PCI_BRIDGE_GREX (PCI_BRIDGE_WILDFIRE + 1) +#define PCI_BRIDGE_XVISION (PCI_BRIDGE_GREX + 1) +#define PCI_BRIDGE_PROMETHEUS (PCI_BRIDGE_XVISION + 1) +#define PCI_BRIDGE_MEDIATOR (PCI_BRIDGE_PROMETHEUS * MAX_DUPLICATE_EXPANSION_BOARDS) +#define PCI_BRIDGE_MAX (PCI_BRIDGE_MEDIATOR * MAX_DUPLICATE_EXPANSION_BOARDS + 1) + +static struct pci_bridge *bridges[PCI_BRIDGE_MAX + 1]; +static int last_bridge_index; + +extern addrbank pci_config_bank, pci_io_bank, pci_mem_bank, pci_bridge_bank; + +static void pci_board_free(struct pci_board_state *pcibs) +{ + if (!pcibs || !pcibs->board) + return; + if (pcibs->board->free) + pcibs->board->free(pcibs); +} + +static struct pci_bridge *pci_bridge_alloc(void) +{ + struct pci_bridge *pcib = xcalloc(struct pci_bridge, 1); + last_bridge_index = 0; + return pcib; +}; + +static struct pci_bridge *pci_bridge_get_zorro(struct romconfig *rc) +{ + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + if (bridges[i] && bridges[i]->rc == rc) { + return bridges[i]; + } + } + return NULL; +} + +static struct pci_bridge *pci_bridge_alloc_zorro(int offset, struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc(); + for (int i = 0; i < MAX_DUPLICATE_EXPANSION_BOARDS; i++) { + if (bridges[i + offset] == NULL) { + bridges[i + offset] = pcib; + pcib->rc = rc; + pcib->type = offset; + return pcib; + } + } + return NULL; +} + + +static void pci_bridge_free(struct pci_bridge *pcib) +{ + if (!pcib) + return; + for (int i = 0; i < MAX_PCI_BOARDS; i++) { + pci_board_free(&pcib->boards[i]); + } + last_bridge_index = 0; + xfree(pcib->data); + xfree(pcib); +} + +static struct pci_board *pci_board_alloc(struct pci_config *config) +{ + struct pci_board *pci = xcalloc(struct pci_board, 1); + pci->config = config; + return pci; +} + +static void pci_board_add(struct pci_bridge *pcib, const struct pci_board *pci, int index) +{ + struct pci_board_state *pcibs = &pcib->boards[index]; + pcibs->board = pci; + pcibs->index = index; + pcibs->bridge = pcib; + memset(pcibs->config_data, 0, sizeof pcibs->config_data); + for (int i = 0; i < MAX_PCI_BARS; i++) { + pcibs->bar_size[i] = pci->config->bars[i]; + } + if (pci->init) + pci->init(pcibs); +} + +void pci_free(void) +{ + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + pci_bridge_free(bridges[i]); + bridges[i] = NULL; + } +} + +void pci_reset(void) +{ + pci_free(); +} + +void pci_rethink(void) +{ + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + struct pci_bridge *pcib = bridges[i]; + if (!pcib) + continue; + pcib->irq = false; + for (int j = 0; j < MAX_PCI_BOARDS; j++) { + struct pci_board_state *pcibs = &pcib->boards[j]; + if (pcibs->board) { + const struct pci_config *c = pcibs->board->config; + if (c->interruptpin && (1 << (pcibs->board->config->interruptpin - 1)) & pcib->intena) { + if ((pcibs->config_data[5] & (1 << 3)) && !(pcibs->config_data[6] & (1 << (10 - 8)))) { + pcib->irq = true; + } + } + } + } + if (pcib->irq) + INTREQ_0(0x8000 | pcib->intreq_mask); + } +} + +static void set_pci_irq(struct pci_bridge *pcib, struct pci_board_state *pcibs, bool active) +{ + pcibs->config_data[5] &= ~(1 << 3); + if (active) + pcibs->config_data[5] |= (1 << 3); + pci_rethink(); +} + +static void create_config_data(struct pci_board_state *s) +{ + uae_u8 *d = s->config_data; + const struct pci_config *c = s->board->config; + + // big endian, get/put functions will swap if needed. + d[0] = c->device >> 8; + d[1] = c->device; + d[2] = c->vendor >> 8; + d[3] = c->vendor; + + d[8] = c->deviceclass >> 16; + d[9] = c->deviceclass >> 8; + d[10] = c->deviceclass; + d[11] = c->revision; + + d[13] = c->header; + + for (int i = 0; i < MAX_PCI_BARS; i++) { + int off = i == MAX_PCI_BARS - 1 ? 0x30 : 0x10 + i * 4; + d[off + 0] = s->bar[i] >> 24; + d[off + 1] = s->bar[i] >> 16; + d[off + 2] = s->bar[i] >> 8; + d[off + 3] = s->bar[i] >> 0; + } + + d[0x2c] = c->subsystem >> 8; + d[0x2d] = c->subsystem; + d[0x2e] = c->subsystenvendor >> 8; + d[0x2f] = c->subsystenvendor; + + d[0x3e] = c->interruptpin; +} + +static struct pci_bridge *get_pci_bridge(uaecptr addr) +{ + if (addr < 0x10000 || (addr & 0xffff0000) == 0xe80000 || (addr & 0xff000000) == 0xff000000) { + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + struct pci_bridge *pcib = bridges[i]; + if (pcib && pcib->configured == 0) { + return pcib; + } + } + } + struct pci_bridge *pcib = bridges[last_bridge_index]; + if (pcib) { + if (addr >= pcib->baseaddress && addr < pcib->baseaddress_end) + return pcib; + if (pcib->configured_2 && addr >= pcib->baseaddress_2 && addr < pcib->baseaddress_end_2) + return pcib; + } + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + struct pci_bridge *pcib = bridges[i]; + if (pcib) { + if ((addr >= pcib->baseaddress && addr < pcib->baseaddress_end) || + (pcib->configured_2 && addr >= pcib->baseaddress_2 && addr < pcib->baseaddress_end_2)) { + last_bridge_index = i; + return pcib; + } + } + } + return NULL; +} + +static struct pci_bridge *get_pci_bridge_2(uaecptr addr) +{ + if (addr < 0x10000 || (addr & 0xffff0000) == 0xe80000 || (addr & 0xff000000) == 0xff000000) { + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + struct pci_bridge *pcib = bridges[i]; + if (pcib && pcib->configured_2 == 0) { + return pcib; + } + } + } + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + struct pci_bridge *pcib = bridges[i]; + if (pcib && pcib->configured_2) { + if (addr >= pcib->baseaddress_2 && addr < pcib->baseaddress_end_2) { + last_bridge_index = i; + return pcib; + } + } + } + return NULL; +} + +static struct pci_board_state *get_pci_board_state_config(struct pci_bridge *pcib, uaecptr addr) +{ + if (!pcib) + return NULL; + // get slot + int idx = pcib->get_index(addr); + if (idx < 0) + return NULL; + struct pci_board_state *pcibs = &pcib->boards[idx]; + if (!pcibs->board) { + write_log(_T("- Empty slot %d\n"), idx); + return NULL; + } + return pcibs; +} + +static int stored_board, stored_bar; + +static struct pci_board_state *get_pci_board_state(struct pci_bridge *pcib, uaecptr addr, int *bar) +{ + uaecptr addr2 = addr - pcib->io_offset; + struct pci_board_state *pcibs2 = &pcib->boards[stored_board]; + if (pcibs2) { + if (pcibs2->bar_enabled[stored_bar] && addr2 >= pcibs2->bar_start[stored_bar] && addr2 <= pcibs2->bar_end[stored_bar]) { + *bar = stored_bar; + return pcibs2; + } + } + for (int i = 0; i < MAX_PCI_BOARDS; i++) { + struct pci_board_state *pcibs = &pcib->boards[i]; + for (int j = 0; j < MAX_PCI_BARS; j++) { + if (pcibs2->bar_enabled[j] && addr2 >= pcibs->bar_start[j] && addr2 <= pcibs->bar_end[j]) { + *bar = j; + stored_board = i; + stored_bar = j; + return pcibs; + } + } + } + return NULL; +} + +static const pci_addrbank *get_pci_io(uaecptr *addrp, struct pci_board_state **pcibsp, int *endianswap) +{ + uaecptr addr = *addrp; + int bar; + write_log(_T("get_pci_io %08x %08x\n"), addr, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return NULL; + struct pci_board_state *pcibs = get_pci_board_state(pcib, addr, &bar); + if (!pcibs) + return NULL; + *pcibsp = pcibs; + pcibs->selected_bar = bar; + *endianswap = pcib->endian_swap_io; + addr -= pcib->io_offset; + addr &= (pcibs->bar_size[bar] & ~1) - 1; + *addrp = addr; + return &pcibs->board->bars[bar]; +} + +static const pci_addrbank *get_pci_mem(uaecptr *addrp, struct pci_board_state **pcibsp, int *endianswap) +{ + uaecptr addr = *addrp; + int bar; + write_log(_T("get_pci_mem %08x %08x\n"), addr, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return NULL; + struct pci_board_state *pcibs = get_pci_board_state(pcib, addr, &bar); + if (!pcibs) + return NULL; + *pcibsp = pcibs; + pcibs->selected_bar = bar; + *endianswap = pcib->endian_swap_memory; + addr &= pcibs->bar_size[bar] - 1; + addr -= pcib->memory_offset; + *addrp = addr; + return &pcibs->board->bars[bar]; +} + +static uae_u8 *get_pci_config(uaecptr addr, int size, uae_u32 v, int *endianswap) +{ + if (size < 0) { + size = -size; + write_log(_T("PCI Config Space %s READ %08x PC=%08x\n"), + size == 4 ? _T("LONG") : (size == 2 ? _T("WORD") : _T("BYTE")), addr, M68K_GETPC); + } else { + write_log(_T("PCI Config Space %s WRITE %08x = %08x PC=%08x\n"), + size == 4 ? _T("LONG") : (size == 2 ? _T("WORD") : _T("BYTE")), addr, v, M68K_GETPC); + } + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return NULL; + struct pci_board_state *pcibs = get_pci_board_state_config(pcib, addr); + if (!pcibs) + return NULL; + *endianswap = pcib->endian_swap_config; + write_log(_T("- Board %d (%s)\n"), pcibs->index, pcibs->board->label); + create_config_data(pcibs); + return pcibs->config_data; +} + +static void map_pci_banks(struct pci_board_state *pcibs, int type, bool enable) +{ + const struct pci_board *pci = pcibs->board; + uae_u32 mask = type ? 3 : 15; + for (int i = 0; i < MAX_PCI_BARS; i++) { + if (pcibs->bar_size[i] == 0) + continue; + if ((pcibs->bar_size[i] & 1) != type) + continue; + pcibs->bar_start[i] = (pcibs->bar[i] & ~mask) + pcibs->bridge->baseaddress_offset; + pcibs->bar_end[i] = pcibs->bar_start[i] + (pcibs->bar_size[i] & ~1) - 1; + if (enable && pcibs->bar[i] < 0xffff0000) { + pcibs->bar_enabled[i] = true; + if (pcibs->bar_old[i] != pcibs->bar_start[i]) { + write_log(_T("Board %d ('%s') BAR%d: %08x-%08x\n"), pcibs->index, pci->label, i, pcibs->bar_start[i], pcibs->bar_end[i]); + } + } else { + pcibs->bar_enabled[i] = false; + if (pcibs->bar_old[i] != pcibs->bar_start[i]) { + write_log(_T("Board %d ('%s') BAR%d: %08x-%08x\n"), pcibs->index, pci->label, i, pcibs->bar_start[i], pcibs->bar_end[i]); + } + } + pcibs->bar_old[i] = pcibs->bar_start[i]; + } +} + +static void update_pci_config(uaecptr addr) +{ + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return; + struct pci_board_state *pcibs = get_pci_board_state_config(pcib, addr); + if (!pcibs) + return; + uae_u8 *d = pcibs->config_data; + const struct pci_config *c = pcibs->board->config; + for (int i = 0; i < MAX_PCI_BARS; i++) { + int off = i == MAX_PCI_BARS - 1 ? 0x30 : 0x10 + i * 4; + if (pcibs->bar_size[i]) { + pcibs->bar[i] = d[off + 0] << 24; + pcibs->bar[i] |= d[off + 1] << 16; + pcibs->bar[i] |= d[off + 2] << 8; + pcibs->bar[i] |= d[off + 3] << 0; + pcibs->bar[i] &= ~((pcibs->bar_size[i] & ~1) - 1); + pcibs->bar[i] |= (pcibs->bar_size[i] & 1); + } else { + pcibs->bar[i] = 0; + } + } + create_config_data(pcibs); + pcibs->io_map_active = (d[7] & 1) != 0; + pcibs->memory_map_active = (d[7] & 2) != 0; + map_pci_banks(pcibs, 1, pcibs->io_map_active); + map_pci_banks(pcibs, 0, pcibs->memory_map_active); +} + + +static uaecptr beswap(int endianswap, uaecptr addr) +{ + if (endianswap > 0) + return (addr & ~3) | (3 - (addr & 3)); + return addr; +} + +static uae_u32 REGPARAM2 pci_config_lget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffffffff; + int endianswap; + uae_u8 *config = get_pci_config(addr, -4, 0, &endianswap); + if (config) { + uae_u32 offset = addr & 0xff; + if (!endianswap) { + v = config[offset + 0] << 24; + v |= config[offset + 1] << 16; + v |= config[offset + 2] << 8; + v |= config[offset + 3] << 0; + } else { + v = config[offset + 3] << 24; + v |= config[offset + 2] << 16; + v |= config[offset + 1] << 8; + v |= config[offset + 0] << 0; + } + write_log(_T("- %08x\n"), v); + } + return v; +} +static uae_u32 REGPARAM2 pci_config_wget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffff; + int endianswap; + uae_u8 *config = get_pci_config(addr, -2, 0, &endianswap); + if (config) { + uae_u32 offset = addr & 0xff; + if (!endianswap) { + v = config[offset + 0] << 8; + v |= config[offset + 1] << 0; + } else { + v = config[(offset ^ (endianswap > 0 ? 2 : 0)) + 1] << 8; + v |= config[(offset ^ (endianswap > 0 ? 2 : 0)) + 0] << 0; + } + write_log(_T("- %04x\n"), v); + } + return v; +} +static uae_u32 REGPARAM2 pci_config_bget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u8 v = 0xff; + int endianswap; + uae_u8 *config = get_pci_config(addr, -1, 0, &endianswap); + if (config) { + uae_u32 v; + uae_u32 offset = addr & 0xff; + if (!endianswap) { + v = config[offset + 0]; + } else { + v = config[beswap(endianswap, offset)]; + } + write_log(_T("- %02x\n"), v); + } + return v; +} +static void REGPARAM2 pci_config_lput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + uae_u8 *config = get_pci_config(addr, 4, b, &endianswap); + if (config) { + uae_u32 offset = addr & 0xff; + if (!endianswap) { + config[offset + 0] = b >> 24; + config[offset + 1] = b >> 16; + config[offset + 2] = b >> 8; + config[offset + 3] = b >> 0; + } else { + config[offset + 3] = b >> 24; + config[offset + 2] = b >> 16; + config[offset + 1] = b >> 8; + config[offset + 0] = b >> 0; + } + update_pci_config(addr); + } +} +static void REGPARAM2 pci_config_wput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + uae_u8 *config = get_pci_config(addr, 2, b, &endianswap); + if (config) { + uae_u32 offset = addr & 0xff; + if (!endianswap) { + config[offset + 0] = b >> 8; + config[offset + 1] = b >> 0; + } else { + config[(offset ^ (endianswap > 0 ? 2 : 0)) + 1] = b >> 8; + config[(offset ^ (endianswap > 0 ? 2 : 0)) + 0] = b >> 0; + } + update_pci_config(addr); + } +} +static void REGPARAM2 pci_config_bput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + uae_u8 *config = get_pci_config(addr, 1, b, &endianswap); + if (config) { + uae_u32 offset = addr & 0xff; + if (!endianswap) { + config[offset] = b; + } else { + config[beswap(endianswap, offset)] = b; + } + update_pci_config(addr); + } +} + +static uae_u32 endianswap_long(uae_u32 v) +{ + v = (v >> 24) | ((v >> 8) & 0x0000ff00) | ((v << 8) & 0x00ff0000) | (v << 24); + return v; +} +static uae_u16 endianswap_word(uae_u16 v) +{ + v = (v >> 8) | (v << 8); + return v; +} + +static uae_u32 REGPARAM2 pci_io_lget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffffffff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->lget) { + v = a->lget(pcibs, addr); + if (endianswap) + v = endianswap_long(v); + } + return v; +} +static uae_u32 REGPARAM2 pci_io_wget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->wget) { + if (endianswap) { + v = a->wget(pcibs, addr ^ (endianswap > 0 ? 2 : 0)); + v = endianswap_word(v); + } else { + v = a->wget(pcibs, addr); + } + } + return v; +} +static uae_u32 REGPARAM2 pci_io_bget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->bget) { + if (endianswap) { + v = a->bget(pcibs, beswap(endianswap, addr)); + } else { + v = a->bget(pcibs, addr); + } + } + return v; +} +static void REGPARAM2 pci_io_lput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->lput) { + if (endianswap) + b = endianswap_long(b); + a->lput(pcibs, addr, b); + } +} +static void REGPARAM2 pci_io_wput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->wput) { + if (endianswap) { + b = endianswap_word(b); + a->wput(pcibs, addr ^ (endianswap > 0 ? 2 : 0), b); + } else { + a->wput(pcibs, addr, b); + } + } +} +static void REGPARAM2 pci_io_bput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_io(&addr, &pcibs, &endianswap); + if (a && a->bput) { + if (endianswap) { + a->bput(pcibs, beswap(endianswap, addr), b); + } else { + a->bput(pcibs, addr, b); + } + } +} + +static uae_u32 REGPARAM2 pci_mem_lget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffffffff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->lget) { + v = a->lget(pcibs, addr); + if (endianswap) + v = endianswap_long(v); + } + return v; +} +static uae_u32 REGPARAM2 pci_mem_wget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xffff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->wget) { + if (endianswap) { + v = a->wget(pcibs, addr ^ (endianswap > 0 ? 2 : 0)); + v = endianswap_word(v); + } else { + v = a->wget(pcibs, addr); + } + } + return v; +} +static uae_u32 REGPARAM2 pci_mem_bget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0xff; + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->bget) { + if (endianswap) { + v = a->bget(pcibs, beswap(endianswap, addr)); + } else { + v = a->bget(pcibs, addr); + } + } + return v; +} +static void REGPARAM2 pci_mem_lput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->lput) { + if (endianswap) + b = endianswap_long(b); + a->lput(pcibs, addr, b); + } +} +static void REGPARAM2 pci_mem_wput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->wput) { + if (endianswap) { + b = endianswap_word(b); + a->wput(pcibs, addr ^ (endianswap > 0 ? 2 : 0), b); + } else { + a->wput(pcibs, addr, b); + } + } +} +static void REGPARAM2 pci_mem_bput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + int endianswap; + struct pci_board_state *pcibs; + const pci_addrbank *a = get_pci_mem(&addr, &pcibs, &endianswap); + if (a && a->bput) { + if (endianswap) { + a->bput(pcibs, beswap(endianswap, addr), b); + } else { + a->bput(pcibs, addr, b); + } + } +} + +static uae_u32 REGPARAM2 pci_bridge_lget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0; + write_log(_T("pci_bridge_lget %08x PC=%08x\n"), addr, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return v; + if (pcib == bridges[PCI_BRIDGE_GREX] || pcib == bridges[PCI_BRIDGE_XVISION]) { + int reg = (addr & 0xf0) >> 4; + switch(reg) + { + case 0: + v = pcib->endian_swap_io ? 2 : 0; + v |= pcib->config[0]; + if (pcib == bridges[PCI_BRIDGE_GREX]) + v |= 0x02000000; + break; + case 1: + v = pcib->intena ? 1 : 0; + break; + case 2: + break; + case 3: + break; + case 4: + break; + } + } + write_log(_T("=%08x\n"), v); + return v; +} +static uae_u32 REGPARAM2 pci_bridge_wget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u16 v = 0; + write_log(_T("pci_bridge_wget %08x PC=%08x\n"), addr, M68K_GETPC); + return v; +} +static uae_u32 REGPARAM2 pci_bridge_bget(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u8 v = 0; + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return v; + if (!pcib->configured) { + uaecptr offset = addr & 65535; + if (offset >= sizeof pcib->acmemory) + return 0; + return pcib->acmemory[offset]; + + } else if (pcib == bridges[PCI_BRIDGE_WILDFIRE]) { + int offset = addr & 15; + v = pcib->config[offset / 4]; + } + write_log(_T("pci_bridge_bget %08x %02x PC=%08x\n"), addr, v, M68K_GETPC); + return v; +} +static void REGPARAM2 pci_bridge_lput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + write_log(_T("pci_bridge_lput %08x %08x PC=%08x\n"), addr, b, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return; + if (pcib == bridges[PCI_BRIDGE_GREX] || pcib == bridges[PCI_BRIDGE_XVISION]) { + int reg = (addr & 0xf0) >> 4; + switch (reg) + { + case 0: + pcib->endian_swap_memory = pcib->endian_swap_io = (b & 2) != 0; + break; + case 1: + pcib->intena = (b & 1) ? 0xff : 0x00; + break; + case 3: + pcib->config[0] = b & 1; + pcib->endian_swap_memory = pcib->endian_swap_io = (b & 2) != 0; + break; + } + } +} +static void REGPARAM2 pci_bridge_wput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return; + if (!pcib->configured) { + uaecptr offset = addr & 65535; + if (pcib->bank_zorro == 3) { + switch (offset) + { + case 0x44: + if (pcib->type == PCI_BRIDGE_PROMETHEUS) { + map_banks(&pci_io_bank, (expamem_z3_pointer) >> 16, 0xf0000 >> 16, 0); + map_banks(&pci_mem_bank, (expamem_z3_pointer + 0x100000) >> 16, (511 * 1024 * 1024) >> 16, 0); + map_banks(&pci_config_bank, (expamem_z3_pointer + 0xf0000) >> 16, 0x10000 >> 16, 0); + pcib->baseaddress_offset = pcib->baseaddress; + } else if (pcib->type == PCI_BRIDGE_MEDIATOR) { + map_banks(&pci_mem_bank, expamem_z3_pointer >> 16, expamem_z3_size >> 16, 0); + pcib->baseaddress_offset = 0; + } + pcib->baseaddress = expamem_z3_pointer; + pcib->board_size = expamem_z3_size; + pcib->baseaddress_end = pcib->baseaddress + pcib->board_size; + pcib->configured = 1; + expamem_next(pcib->bank, NULL); + break; + } + } + } + write_log(_T("pci_bridge_wput %08x %04x PC=%08x\n"), addr, b & 0xffff, M68K_GETPC); +} +static void REGPARAM2 pci_bridge_bput(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return; + write_log(_T("pci_bridge_bput %08x %02x PC=%08x\n"), addr, b & 0xff, M68K_GETPC); + if (!pcib->configured) { + uaecptr offset = addr & 65535; + if (pcib->bank_zorro == 2) { + switch (offset) + { + case 0x48: + // Mediator 1200 + map_banks_z2(&pci_mem_bank, b, expamem_z2_size >> 16); + pcib->baseaddress = b << 16; + pcib->board_size = expamem_z2_size; + pcib->baseaddress_end = pcib->baseaddress + pcib->board_size; + pcib->configured = 1; + expamem_next(pcib->bank, NULL); + break; + case 0x4c: + pcib->configured = -1; + expamem_shutup(pcib->bank); + break; + } + } + } + if (pcib == bridges[PCI_BRIDGE_WILDFIRE]) { + addr &= 15; + if (addr == 8) { + pcib->config[2] = b; + if (b & 1) { + write_log(_T("Wildfire 68000 mode!\n")); + cpu_halt(CPU_HALT_ACCELERATOR_CPU_FALLBACK); + } + } + } +} + + +static void mediator_set_window_offset(struct pci_bridge *pcib, uae_u16 v) +{ + uae_u32 offset = pcib->memory_offset; + if (pcib->bank_2_zorro == 3) { + // 4000 + uae_u8 mask = pcib->board_size == 256 * 1024 * 1024 ? 0xf0 : 0xe0; + pcib->window = v & mask; + pcib->memory_offset = pcib->window << 18; + } else { + // 1200 + uae_u16 mask = pcib->board_size == 4 * 1024 * 1024 ? 0xffc0 : 0xff80; + pcib->window = v & mask; + pcib->memory_offset = pcib->window << 16; + } + pcib->memory_offset -= pcib->baseaddress; + if (pcib->memory_offset != offset) { + write_log(_T("Mediator window: %08x offset: %08x\n"), + pcib->memory_offset + pcib->baseaddress, pcib->memory_offset); + } +} + +static uae_u32 REGPARAM2 pci_bridge_bget_2(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u8 v = 0; + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return v; + if (!pcib->configured_2) { + uaecptr offset = addr & 65535; + if (offset >= sizeof pcib->acmemory_2) + return 0; + return pcib->acmemory_2[offset]; + } else { + if (pcib->bank_2_zorro == 3) { + int offset = addr & 0x7fffff; + if (offset == 0) { + v = pcib->window; + } + if (offset == 4) { + v = pcib->irq ? 0xff : 0x00; + } + } + } + write_log(_T("pci_bridge_bget_2 %08x %02x PC=%08x\n"), addr, v, M68K_GETPC); + return v; +} +static uae_u32 REGPARAM2 pci_bridge_wget_2(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u16 v = 0; + write_log(_T("pci_bridge_wget_2 %08x PC=%08x\n"), addr, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return v; + if (pcib->configured_2) { + if (pcib->bank_2_zorro == 2) { + int offset = addr & 0xffff; + if (offset == 2) { + v = pcib->window; + } + } + } + return v; +} +static uae_u32 REGPARAM2 pci_bridge_lget_2(uaecptr addr) +{ +#ifdef JIT + special_mem |= S_READ; +#endif + uae_u32 v = 0; + write_log(_T("pci_bridge_lget_2 %08x PC=%08x\n"), addr, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return v; + v = pci_bridge_wget_2(addr + 0) << 16; + v |= pci_bridge_wget_2(addr + 2); + return v; +} + +static void REGPARAM2 pci_bridge_bput_2(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return; + write_log(_T("pci_bridge_bput_2 %08x %02x PC=%08x\n"), addr, b & 0xff, M68K_GETPC); + if (!pcib->configured_2) { + uaecptr offset = addr & 65535; + if (pcib->bank_2_zorro == 2) { + switch (offset) + { + case 0x48: + // Mediator 1200 IO + pcib->baseaddress_2 = b << 16; + pcib->baseaddress_end_2 = (b << 16) + expamem_z2_size; + map_banks_z2(pcib->bank_2, pcib->baseaddress_2 >> 16, 0x10000 >> 16); + map_banks_z2(&dummy_bank, (pcib->baseaddress_2 + 0x10000) >> 16, (expamem_z2_size - 0x10000) >> 16); + pcib->configured_2 = 1; + expamem_next(pcib->bank_2, NULL); + break; + case 0x4c: + pcib->configured_2 = -1; + expamem_shutup(pcib->bank_2); + break; + } + } + } else { + if (pcib->bank_2_zorro == 2) { + // Mediator 1200 + int offset = addr & 0xffff; + if (offset == 7) { + // config/io mapping + if (b & 0x20) { + if (b & 0x80) { + map_banks_z2(&pci_config_bank, (pcib->baseaddress_2 + 0x10000) >> 16, 0x10000 >> 16); + } else { + map_banks_z2(&pci_io_bank, (pcib->baseaddress_2 + 0x10000) >> 16, 0x10000 >> 16); + } + } else { + map_banks_z2(&dummy_bank, (pcib->baseaddress_2 + 0x10000) >> 16, 0x10000 >> 16); + } + } else if (offset == 11) { + pcib->intena = b >> 4; + } + } + if (pcib->bank_2_zorro == 3) { + // Mediator 4000 window + int offset = addr & 0x7fffff; + if (offset == 0) { + mediator_set_window_offset(pcib, b); + } else if (offset == 4) { + pcib->intena = b >> 4; + } + } + } +} +static void REGPARAM2 pci_bridge_wput_2(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return; + if (!pcib->configured_2) { + uaecptr offset = addr & 65535; + if (pcib->bank_2_zorro == 3) { + switch (offset) + { + case 0x44: + // Mediator 4000 IO + map_banks(pcib->bank_2, expamem_z3_pointer >> 16, 0x800000 >> 16, 0); + map_banks(&pci_config_bank, (expamem_z3_pointer + 0x800000) >> 16, 0x400000 >> 16, 0); + map_banks(&pci_io_bank, (expamem_z3_pointer + 0xc00000) >> 16, 0x400000 >> 16, 0); + pcib->baseaddress_2 = expamem_z3_pointer; + pcib->baseaddress_end_2 = expamem_z3_pointer + expamem_z3_size; + pcib->board_size_2 = expamem_z3_size; + pcib->configured_2 = 1; + pcib->io_offset = (expamem_z3_pointer + 0xc00000); + expamem_next(pcib->bank, NULL); + break; + } + } + } else { + if (pcib->bank_2_zorro == 2) { + // Mediator 1200 window + int offset = addr & 0xffff; + if (offset == 2) { + mediator_set_window_offset(pcib, b); + } + } + } + write_log(_T("pci_bridge_wput_2 %08x %04x PC=%08x\n"), addr, b & 0xffff, M68K_GETPC); +} +static void REGPARAM2 pci_bridge_lput_2(uaecptr addr, uae_u32 b) +{ +#ifdef JIT + special_mem |= S_WRITE; +#endif + write_log(_T("pci_bridge_lput_2 %08x %08x PC=%08x\n"), addr, b, M68K_GETPC); + struct pci_bridge *pcib = get_pci_bridge_2(addr); + if (!pcib) + return; + pci_bridge_wput_2(addr + 0, b >> 16); + pci_bridge_wput_2(addr + 2, b >> 0); +} + + +addrbank pci_config_bank = { + pci_config_lget, pci_config_wget, pci_config_bget, + pci_config_lput, pci_config_wput, pci_config_bput, + default_xlate, default_check, NULL, NULL, _T("PCI CONFIG"), + pci_config_lget, pci_config_wget, ABFLAG_IO | ABFLAG_SAFE +}; +addrbank pci_io_bank = { + pci_io_lget, pci_io_wget, pci_io_bget, + pci_io_lput, pci_io_wput, pci_io_bput, + default_xlate, default_check, NULL, NULL, _T("PCI IO"), + pci_io_lget, pci_io_wget, ABFLAG_IO | ABFLAG_SAFE +}; +addrbank pci_mem_bank = { + pci_mem_lget, pci_mem_wget, pci_mem_bget, + pci_mem_lput, pci_mem_wput, pci_mem_bput, + default_xlate, default_check, NULL, NULL, _T("PCI MEMORY"), + pci_mem_lget, pci_mem_wget, ABFLAG_IO | ABFLAG_SAFE +}; +addrbank pci_bridge_bank = { + pci_bridge_lget, pci_bridge_wget, pci_bridge_bget, + pci_bridge_lput, pci_bridge_wput, pci_bridge_bput, + default_xlate, default_check, NULL, NULL, _T("PCI BRIDGE"), + pci_bridge_lget, pci_bridge_wget, ABFLAG_IO | ABFLAG_SAFE +}; +addrbank pci_bridge_bank_2 = { + pci_bridge_lget_2, pci_bridge_wget_2, pci_bridge_bget_2, + pci_bridge_lput_2, pci_bridge_wput_2, pci_bridge_bput_2, + default_xlate, default_check, NULL, NULL, _T("PCI BRIDGE #2"), + pci_bridge_lget_2, pci_bridge_wget_2, ABFLAG_IO | ABFLAG_SAFE +}; + +static void pci_dump_out(const TCHAR *txt, int log) +{ + if (log > 0) + write_log(txt); + else if (log == 0) + console_out(txt); +} + +static void pci_dump_memio_region(struct pci_bridge *pcib, uaecptr start, uaecptr end, int type, int log) +{ + for (int i = 0; i < MAX_PCI_BOARDS; i++) { + struct pci_board_state *pcibs = &pcib->boards[i]; + for (int j = 0; j < MAX_PCI_BARS; j++) { + if (pcibs->bar_size[i] && (pcibs->bar_start[j] || pcibs->bar_end[j]) && (pcibs->bar_size[j] & 1) == type) { + TCHAR txt[256]; + _stprintf(txt, _T(" - %08X - %08X: BAR%d %s\n"), pcibs->bar_start[j], pcibs->bar_end[j], j, pcibs->board->label); + pci_dump_out(txt, log); + } + } + } +} + +static void pci_dump_region(addrbank *bank, uaecptr *start, uaecptr *end) +{ + *start = 0; + *end = 0; + for (int i = 0; i < 65536 + 1; i++) { + addrbank *a = mem_banks[i]; + if (*start == 0 && a == bank) + *start = i << 16; + if (*start && a != bank) { + *end = i << 16; + return; + } + } +} + +void pci_dump(int log) +{ + for (int i = 0; i < PCI_BRIDGE_MAX; i++) { + TCHAR txt[256]; + uae_u8 slots[MAX_PCI_BOARDS] = { 0 }; + uaecptr start, end; + struct pci_bridge *pcib = bridges[i]; + if (!pcib) + continue; + _stprintf(txt, _T("PCI bridge '%s'\n"), pcib->label); + pci_dump_out(txt, log); + pci_dump_region(&pci_config_bank, &start, &end); + if (start) { + int previdx = -1; + _stprintf(txt, _T("%08X - %08X: Configuration space\n"), start, end - 1); + pci_dump_out(txt, log); + while (start < end) { + int idx = pcib->get_index(start); + if (idx >= 0 && idx != previdx && slots[idx] == 0) { + struct pci_board_state *pcibs = &pcib->boards[idx]; + const struct pci_board *pci = pcibs->board; + if (pcibs->board) { + _stprintf(txt, _T(" - Slot %d: [%04X/%04X] %s IO=%d MEM=%d\n"), + idx, pci->config->vendor, pci->config->device, pci->label, + pcibs->io_map_active, pcibs->memory_map_active); + } else { + _stprintf(txt, _T(" - Slot %d: \n"), idx); + } + pci_dump_out(txt, log); + previdx = idx; + slots[idx] = 1; + } + start += 256; + } + } + pci_dump_region(&pci_io_bank, &start, &end); + if (start) { + _stprintf(txt, _T("%08X - %08X: IO space\n"), start, end - 1); + pci_dump_out(txt, log); + pci_dump_memio_region(pcib, start, end, 1, log); + } + pci_dump_region(&pci_mem_bank, &start, &end); + if (start) { + _stprintf(txt, _T("%08X - %08X: Memory space\n"), start, end - 1); + pci_dump_out(txt, log); + pci_dump_memio_region(pcib, start, end, 0, log); + } + } +} + +static int countbit(int mask) +{ + int found = -1; + for (int i = 0; i < 15; i++) { + if (mask & (1 << i)) { + if (found >= 0) + return -1; + found = i; + } + } + return found; +} + +/* DKB Wildfire */ + +#define WILDFIRE_CONFIG_MASK 32767 + +static void REGPARAM2 wildfire_bput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + // BAR6 = "ROM" + if (pcibs->selected_bar == 6) { + bridges[PCI_BRIDGE_WILDFIRE]->data[addr & WILDFIRE_CONFIG_MASK] = b; + } else { + ncr815_io_bput_wildfire(addr, b); + } +} +static void REGPARAM2 wildfire_wput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + if (pcibs->selected_bar == 6) { + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 0) & WILDFIRE_CONFIG_MASK] = b >> 8; + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 1) & WILDFIRE_CONFIG_MASK] = b; + } else { + ncr815_io_bput_wildfire(addr + 1, b >> 0); + ncr815_io_bput_wildfire(addr + 0, b >> 8); + } +} +static void REGPARAM2 wildfire_lput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + if (pcibs->selected_bar == 6) { + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 0) & WILDFIRE_CONFIG_MASK] = b >> 24; + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 1) & WILDFIRE_CONFIG_MASK] = b >> 16; + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 2) & WILDFIRE_CONFIG_MASK] = b >> 8; + bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 3) & WILDFIRE_CONFIG_MASK] = b >> 0; + } else { + ncr815_io_bput_wildfire(addr + 3, b >> 0); + ncr815_io_bput_wildfire(addr + 2, b >> 8); + ncr815_io_bput_wildfire(addr + 1, b >> 16); + ncr815_io_bput_wildfire(addr + 0, b >> 24); + } +} +static uae_u32 REGPARAM2 wildfire_bget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = 0; + if (pcibs->selected_bar == 6) { + v = bridges[PCI_BRIDGE_WILDFIRE]->data[addr & WILDFIRE_CONFIG_MASK]; + } else { + v = ncr815_io_bget_wildfire(addr); + } + return v; +} +static uae_u32 REGPARAM2 wildfire_wget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = 0; + if (pcibs->selected_bar == 6) { + v = bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 0) & WILDFIRE_CONFIG_MASK] << 8; + v |= bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 1) & WILDFIRE_CONFIG_MASK]; + } else { + v = ncr815_io_bget_wildfire(addr + 1) << 0; + v |= ncr815_io_bget_wildfire(addr + 0) << 8; + } + return v; +} +static uae_u32 REGPARAM2 wildfire_lget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = 0; + if (pcibs->selected_bar == 6) { + v = bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 0) & WILDFIRE_CONFIG_MASK] << 24; + v |= bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 1) & WILDFIRE_CONFIG_MASK] << 16; + v |= bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 2) & WILDFIRE_CONFIG_MASK] << 8; + v |= bridges[PCI_BRIDGE_WILDFIRE]->data[(addr + 3) & WILDFIRE_CONFIG_MASK]; + } else { + v = ncr815_io_bget_wildfire(addr + 3) << 0; + v |= ncr815_io_bget_wildfire(addr + 2) << 8; + v |= ncr815_io_bget_wildfire(addr + 1) << 16; + v |= ncr815_io_bget_wildfire(addr + 0) << 24; + } + return v; +} + +static int dkb_wildfire_get_index(uaecptr addr) +{ + int idx = 0; + int slot = -1; + for (int i = 0x0800; i <= 0x10000000; i <<= 1, idx++) { + if (addr & i) { + if (slot >= 0) + return -1; + slot = idx; + } + } + if (slot > 5) + slot = -1; + return slot; +} + +void pci_irq_callback(struct pci_board_state *pcibs, bool irq) +{ + set_pci_irq(pcibs->bridge, pcibs, irq); +} + +static const struct pci_config ncr_53c815_pci_config = +{ + 0x1000, 0x0004, 0, 0, 0, 0x010000, 0, 0, 0, 1, { 256 | 1, 1024 | 0, 0, 0, 0, 0, 32768 | 0 } +}; +static const struct pci_board ncr_53c815_pci_board = +{ + _T("NCR53C815"), + &ncr_53c815_pci_config, NULL, NULL, NULL, pci_irq_callback, + { + { wildfire_lget, wildfire_wget, wildfire_bget, wildfire_lput, wildfire_wput, wildfire_bput }, + { wildfire_lget, wildfire_wget, wildfire_bget, wildfire_lput, wildfire_wput, wildfire_bput }, + { NULL }, + { NULL }, + { NULL }, + { NULL }, + { wildfire_lget, wildfire_wget, wildfire_bget, wildfire_lput, wildfire_wput, wildfire_bput } + } +}; + +static void add_pci_devices(struct pci_bridge *pcib) +{ + if (currprefs.ne2000pciname[0]) + pci_board_add(pcib, &ne2000_pci_board, 0); + + //pci_board_add(pcib, &ncr_53c815_pci_board, 1); +} + +// Wildfire + +void wildfire_ncr815_irq(int v) +{ + struct pci_board_state *pcibs = &bridges[PCI_BRIDGE_WILDFIRE]->boards[0]; + set_pci_irq(bridges[PCI_BRIDGE_WILDFIRE], pcibs, v != 0); +} + +addrbank *dkb_wildfire_pci_init(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc(); + + bridges[PCI_BRIDGE_WILDFIRE] = pcib; + pcib->label = _T("Wildfire"); + pcib->endian_swap_config = 0; + pcib->endian_swap_io = 0; + pcib->endian_swap_memory = 0; + pcib->intena = 0xff; // controlled by bridge config bits, bit unknown. + pcib->intreq_mask = 0x2000; + pcib->get_index = dkb_wildfire_get_index; + pcib->baseaddress = 0x80000000; + pcib->baseaddress_end = 0xffffffff; + pcib->configured = -1; + pci_board_add(pcib, &ncr_53c815_pci_board, 0); + map_banks(&pci_config_bank, 0x80000000 >> 16, 0x10000000 >> 16, 0); + map_banks(&pci_mem_bank, 0x90000000 >> 16, 0x30000000 >> 16, 0); + map_banks(&pci_io_bank, 0xc0000000 >> 16, 0x30000000 >> 16, 0); + map_banks(&pci_bridge_bank, 0xffff0000 >> 16, 0x10000 >> 16, 0); + pcib->data = xcalloc(uae_u8, 32768); + return &expamem_null; +} + +// Prometheus: 44359/1 + +static const uae_u8 prometheus_autoconfig[16] = { 0x85, 0x01, 0x30, 0x00, 0xad, 0x47, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static void ew(uae_u8 *acmemory, int addr, uae_u8 value) +{ + if (addr == 00 || addr == 02 || addr == 0x40 || addr == 0x42) { + acmemory[addr] = (value & 0xf0); + acmemory[addr + 2] = (value & 0x0f) << 4; + } else { + acmemory[addr] = ~(value & 0xf0); + acmemory[addr + 2] = ~((value & 0x0f) << 4); + } +} + +static int prometheus_get_index(uaecptr addr) +{ + struct pci_bridge *pcib = get_pci_bridge(addr); + + addr -= pcib->baseaddress; + if ((addr & 0xffff0000) != 0x000f0000) + return -1; + int slot = (addr & 0xf000) >> 13; + if (slot > 3) + slot = -1; + return slot; +} + +static addrbank *prometheus_pci_init(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc_zorro(PCI_BRIDGE_PROMETHEUS, rc); + if (!pcib) + return &expamem_null; + pcib->label = _T("Prometheus"); + pcib->endian_swap_config = 1; + pcib->endian_swap_io = 1; + pcib->endian_swap_memory = 1; + pcib->intena = 0xff; + pcib->intreq_mask = 0x0008; + pcib->get_index = prometheus_get_index; + pcib->bank = &pci_bridge_bank; + pcib->bank_zorro = 3; + + add_pci_devices(pcib); + + memset(pcib->acmemory, 0xff, sizeof pcib->acmemory); + for (int i = 0; i < sizeof prometheus_autoconfig; i++) { + ew(pcib->acmemory, i * 4, prometheus_autoconfig[i]); + } + return pcib->bank; +} + +// G-REX + +static int grex_get_index(uaecptr addr) +{ + int slot = -1; + struct pci_bridge *pcib = get_pci_bridge(addr); + + if ((addr & 0xfffc0700) == 0xfffc0000) { + int v = (addr & 0x3f000) >> 13; + slot = countbit(v); + } + return slot; +} + +static addrbank *grex_pci_init(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc(); + + bridges[PCI_BRIDGE_GREX] = pcib; + pcib->label = _T("G-REX"); + pcib->intena = 0; + pcib->intreq_mask = 0x0008; + pcib->get_index = grex_get_index; + pcib->baseaddress = 0x80000000; + pcib->baseaddress_end = 0xffffffff; + pcib->configured = -1; + + add_pci_devices(pcib); + + map_banks(&pci_config_bank, 0xfffc0000 >> 16, 0x20000 >> 16, 0); + map_banks(&pci_mem_bank, 0x80000000 >> 16, 0x78000000 >> 16, 0); + map_banks(&pci_io_bank, 0xfffa0000 >> 16, 0x20000 >> 16, 0); + map_banks(&pci_bridge_bank, 0xfffe0000 >> 16, 0x10000 >> 16, 0); + pcib->io_offset = 0xfffa0000; + return &expamem_null; +} + +// CyberVision/BlizzardVision without VGA chip... + +static int xvision_get_index(uaecptr addr) +{ + struct pci_bridge *pcib = get_pci_bridge(addr); + if ((addr & 0xfffcf700) == 0xfffc0000) + return 0; + return -1; +} + +static addrbank *cbvision(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc(); + + bridges[PCI_BRIDGE_XVISION] = pcib; + pcib->label = _T("CBVision"); + pcib->intena = 0; + pcib->intreq_mask = 0x0008; + pcib->get_index = xvision_get_index; + pcib->baseaddress = 0xe0000000; + pcib->baseaddress_end = 0xffffffff; + pcib->configured = -1; + + map_banks(&pci_config_bank, 0xfffc0000 >> 16, 0x20000 >> 16, 0); + map_banks(&pci_mem_bank, 0xe0000000 >> 16, 0x10000000 >> 16, 0); + map_banks(&pci_io_bank, 0xfffa0000 >> 16, 0x20000 >> 16, 0); + map_banks(&pci_bridge_bank, 0xfffe0000 >> 16, 0x10000 >> 16, 0); + pcib->io_offset = 0xfffa0000; + return &expamem_null; +} + +// Mediator + +static const uae_u8 autoconfig_mediator_4000mk2_256m[16] = { 0x84,0xa1,0x20,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; +static const uae_u8 autoconfig_mediator_4000mk2_512m[16] = { 0x85,0xa1,0x20,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; +static const uae_u8 autoconfig_mediator_4000mk2_2[16] = { 0x88,0x21,0x20,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; + +static const uae_u8 autoconfig_mediator_1200tx_1[16] = { 0xca,0x3c,0x00,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; +static const uae_u8 autoconfig_mediator_1200tx_2_4m[16] = { 0xc7,0xbc,0x00,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; +static const uae_u8 autoconfig_mediator_1200tx_2_8m[16] = { 0xc0,0xbc,0x00,0x00,0x08,0x9e,0x00,0x00,0x00,0x00,0x00,0x00 }; + +static int mediator_get_index_1200(uaecptr addr) +{ + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return -1; + uae_u32 offset = addr - pcib->baseaddress_2; + if (offset < 0x10000) + return -1; + offset -= 0x10000; + int slot = offset / 0x800; + if (slot >= 6) + slot = -1; + return slot; +} + +static int mediator_get_index_4000(uaecptr addr) +{ + struct pci_bridge *pcib = get_pci_bridge(addr); + if (!pcib) + return -1; + uae_u32 offset = addr - pcib->baseaddress_2; + if (offset < 0x800000 || offset >= 0xc00000) + return -1; + offset -= 0x800000; + int slot = offset / 0x800; + if (slot >= 6) + slot = -1; + return slot; +} + +static addrbank *mediator_pci_init_1200(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_alloc_zorro(PCI_BRIDGE_MEDIATOR, rc); + if (!pcib) + return &expamem_null; + pcib->label = _T("Mediator 1200"); + pcib->endian_swap_config = 1; + pcib->endian_swap_io = 1; + pcib->endian_swap_memory = 1; + pcib->intena = 0; + pcib->intreq_mask = 0x0008; + pcib->get_index = mediator_get_index_1200; + pcib->bank = &pci_bridge_bank; + pcib->bank_2 = &pci_bridge_bank_2; + pcib->bank_zorro = 2; + pcib->bank_2_zorro = 2; + mediator_set_window_offset(pcib, 0); + + add_pci_devices(pcib); + + memset(pcib->acmemory_2, 0xff, sizeof pcib->acmemory_2); + for (int i = 0; i < sizeof autoconfig_mediator_1200tx_1; i++) { + ew(pcib->acmemory_2, i * 4, autoconfig_mediator_1200tx_1[i]); + } + return &pci_bridge_bank_2; +} +static addrbank *mediator_pci_init_1200_2(struct romconfig *rc, int size) +{ + struct pci_bridge *pcib = pci_bridge_get_zorro(rc); + if (!pcib) + return &expamem_null; + + memset(pcib->acmemory, 0xff, sizeof pcib->acmemory); + const uae_u8 *ac = size ? autoconfig_mediator_1200tx_2_8m : autoconfig_mediator_1200tx_2_4m; + for (int i = 0; i < 16; i++) { + ew(pcib->acmemory, i * 4, ac[i]); + } + return &pci_bridge_bank; +} + +static addrbank *mediator_pci_init_4000(struct romconfig *rc, int size) +{ + struct pci_bridge *pcib = pci_bridge_alloc_zorro(PCI_BRIDGE_MEDIATOR, rc); + if (!pcib) + return &expamem_null; + pcib->label = _T("Mediator 4000"); + pcib->endian_swap_config = -1; + pcib->endian_swap_io = -1; + pcib->endian_swap_memory = -1; + pcib->intena = 0; + pcib->intreq_mask = 0x0008; + pcib->get_index = mediator_get_index_4000; + pcib->bank = &pci_bridge_bank; + pcib->bank_2 = &pci_bridge_bank_2; + pcib->bank_zorro = 3; + pcib->bank_2_zorro = 3; + mediator_set_window_offset(pcib, 0); + + add_pci_devices(pcib); + + memset(pcib->acmemory, 0xff, sizeof pcib->acmemory); + const uae_u8 *ac = size ? autoconfig_mediator_4000mk2_512m : autoconfig_mediator_4000mk2_256m; + for (int i = 0; i < 16; i++) { + ew(pcib->acmemory, i * 4, ac[i]); + } + return pcib->bank; +} +static addrbank *mediator_pci_init_4000_2(struct romconfig *rc) +{ + struct pci_bridge *pcib = pci_bridge_get_zorro(rc); + if (!pcib) + return &expamem_null; + + memset(pcib->acmemory_2, 0xff, sizeof pcib->acmemory_2); + for (int i = 0; i < sizeof autoconfig_mediator_4000mk2_2; i++) { + ew(pcib->acmemory_2, i * 4, autoconfig_mediator_4000mk2_2[i]); + } + return pcib->bank_2; +} + +addrbank *pcibridge_init(struct romconfig *rc) +{ + switch (rc->subtype) + { + case 0: + return prometheus_pci_init(rc); + case 1: + return grex_pci_init(rc); + case 2: + return mediator_pci_init_1200(rc); + case 3: + return mediator_pci_init_1200(rc); + case 4: + return mediator_pci_init_4000(rc, 0); + case 5: + return mediator_pci_init_4000(rc, 1); + case 6: + return cbvision(rc); + } + return &expamem_null; +} +addrbank *pcibridge_init2(struct romconfig *rc) +{ + switch (rc->subtype) + { + case 2: + return mediator_pci_init_1200_2(rc, 0); + case 3: + return mediator_pci_init_1200_2(rc, 1); + case 4: + return mediator_pci_init_4000_2(rc); + case 5: + return mediator_pci_init_4000_2(rc); + } + return &expamem_null; +} diff --git a/qemuvga/ne2000.cpp b/qemuvga/ne2000.cpp new file mode 100644 index 00000000..b4a14afc --- /dev/null +++ b/qemuvga/ne2000.cpp @@ -0,0 +1,1060 @@ +/* + * QEMU NE2000 emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysconfig.h" +#include "sysdeps.h" + +#include "options.h" +#include "crc32.h" + +#include "qemuuaeglue.h" +#include "queue.h" + +struct NetClientState +{ + struct NE2000State *ne2000state; + struct pci_board_state *pcistate; + const struct pci_board *device; +}; + +struct MACAddr { + uint8_t a[6]; +}; + +typedef struct NICPeers { + //NetClientState *ncs[MAX_QUEUE_NUM]; + int32_t queues; +} NICPeers; + +typedef struct NICConf { + MACAddr macaddr; + NICPeers peers; + int32_t bootindex; +} NICConf; + +#include "ethernet.h" +#include "memory.h" +#include "pci_hw.h" + +#define qemu_get_nic_opaque(x) ((x)->ne2000state) + +static struct netdriverdata *td; +static void *sysdata; +static uae_u8 *transmitbuffer; +static volatile int transmitlen; +static volatile int transmitnow; + +static void ne2000_receive_check(void); + +static int getfunc(void *devv, uae_u8 *d, int *len) +{ + struct s2devstruct *dev = (struct s2devstruct*)devv; + + if (transmitlen <= 0) + return 0; + if (transmitlen > *len) { + write_log(_T("NE2000: too large packet transmission attempt %d > %d\n"), transmitlen, *len); + transmitlen = 0; + return 0; + } + memcpy(d, transmitbuffer, transmitlen); + *len = transmitlen; + transmitlen = 0; + transmitnow = 1; + return 1; +} + +#define POLYNOMIAL 0x04c11db6 +static unsigned compute_mcast_idx(const uint8_t *ep) +{ + uint32_t crc; + int carry, i, j; + uint8_t b; + + crc = 0xffffffff; + for (i = 0; i < 6; i++) { + b = *ep++; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) { + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + } + return crc >> 26; +} + +//#include "hw/hw.h" +//#include "hw/pci/pci.h" +//#include "net/net.h" +#include "ne2000.h" +//#include "hw/loader.h" +//#include "sysemu/sysemu.h" + +/* debug NE2000 card */ +#define DEBUG_NE2000 + +static NetClientState ncs; +static NE2000State ne2000state; + +#define MAX_ETH_FRAME_SIZE 1514 + +#define E8390_CMD 0x00 /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ +#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ +#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ +#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ +#define EN0_TSR 0x04 /* Transmit status reg RD */ +#define EN0_TPSR 0x04 /* Transmit starting page WR */ +#define EN0_NCR 0x05 /* Number of collision reg RD */ +#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ +#define EN0_FIFO 0x06 /* FIFO RD */ +#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ +#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ +#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ +#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ +#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ +#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ +#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ +#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ +#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ +#define EN0_RSR 0x0c /* rx status reg RD */ +#define EN0_RXCR 0x0c /* RX configuration reg WR */ +#define EN0_TXCR 0x0d /* TX configuration reg WR */ +#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ +#define EN0_DCFG 0x0e /* Data configuration reg WR */ +#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ +#define EN0_IMR 0x0f /* Interrupt mask reg WR */ +#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ + +#define EN1_PHYS 0x11 +#define EN1_CURPAG 0x17 +#define EN1_MULT 0x18 + +#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ +#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ + +#define EN3_CONFIG0 0x33 +#define EN3_CONFIG1 0x34 +#define EN3_CONFIG2 0x35 +#define EN3_CONFIG3 0x36 + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +static void ne2000_reset2(NE2000State *s) +{ + int i; + + s->isr = ENISR_RESET; + s->cmd = 0; + memcpy(s->mem, &s->c.macaddr, 6); + s->mem[14] = 0x57; + s->mem[15] = 0x57; + + /* duplicate prom data */ + for(i = 15;i >= 0; i--) { + s->mem[2 * i] = s->mem[i]; + s->mem[2 * i + 1] = s->mem[i]; + } +} + +static void ne2000_update_irq(NE2000State *s) +{ + int isr; + isr = (s->isr & s->imr) & 0x7f; +#if defined(DEBUG_NE2000) + write_log("NE2000: Set IRQ to %d (%02x %02x)\n", + isr ? 1 : 0, s->isr, s->imr); +#endif + ncs.device->irq(ncs.pcistate, isr != 0); +// qemu_set_irq(s->irq, (isr != 0)); +} + +static int ne2000_buffer_full(NE2000State *s) +{ + int avail, index, boundary; + + index = s->curpag << 8; + boundary = s->boundary << 8; + if (index < boundary) + avail = boundary - index; + else + avail = (s->stop - s->start) - (index - boundary); + if (avail < (MAX_ETH_FRAME_SIZE + 4)) + return 1; + return 0; +} + +static int ne2000_can_receive(NetClientState *nc) +{ + NE2000State *s = qemu_get_nic_opaque(nc); + + if (s->cmd & E8390_STOP) + return 1; + return !ne2000_buffer_full(s); +} + +#define MIN_BUF_SIZE 60 + +static ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) +{ + NE2000State *s = qemu_get_nic_opaque(nc); + int size = size_; + uint8_t *p; + unsigned int total_len, next, avail, len, index, mcast_idx; + uint8_t buf1[60]; + static const uint8_t broadcast_macaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +#if defined(DEBUG_NE2000) + write_log("NE2000: received len=%d\n", size); +#endif + + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) + return -1; + + /* XXX: check this */ + if (s->rxcr & 0x10) { + /* promiscuous: receive all */ + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (!(s->rxcr & 0x04)) + return size; + } else if (buf[0] & 0x01) { + /* multicast */ + if (!(s->rxcr & 0x08)) + return size; + mcast_idx = compute_mcast_idx(buf); + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) + return size; + } else if (s->mem[0] == buf[0] && + s->mem[2] == buf[1] && + s->mem[4] == buf[2] && + s->mem[6] == buf[3] && + s->mem[8] == buf[4] && + s->mem[10] == buf[5]) { + /* match */ + } else { + return size; + } + } + + + /* if too small buffer, then expand it */ + if (size < MIN_BUF_SIZE) { + memcpy(buf1, buf, size); + memset(buf1 + size, 0, MIN_BUF_SIZE - size); + buf = buf1; + size = MIN_BUF_SIZE; + } + + index = s->curpag << 8; + /* 4 bytes for header */ + total_len = size + 4; + /* address for next packet (4 bytes for CRC) */ + next = index + ((total_len + 4 + 255) & ~0xff); + if (next >= s->stop) + next -= (s->stop - s->start); + /* prepare packet header */ + p = s->mem + index; + s->rsr = ENRSR_RXOK; /* receive status */ + /* XXX: check this */ + if (buf[0] & 0x01) + s->rsr |= ENRSR_PHY; + p[0] = s->rsr; + p[1] = next >> 8; + p[2] = total_len; + p[3] = total_len >> 8; + index += 4; + + /* write packet data */ + while (size > 0) { + if (index <= s->stop) + avail = s->stop - index; + else + avail = 0; + len = size; + if (len > avail) + len = avail; + memcpy(s->mem + index, buf, len); + buf += len; + index += len; + if (index == s->stop) + index = s->start; + size -= len; + } + s->curpag = next >> 8; + + /* now we can signal we have received something */ + s->isr |= ENISR_RX; + ne2000_update_irq(s); + + return size_; +} + +static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = (NE2000State*)opaque; + int offset, page, index; + + addr &= 0xf; +#ifdef DEBUG_NE2000 + write_log("NE2000: write addr=0x%x val=0x%02x\n", addr, val); +#endif + if (addr == E8390_CMD) { + /* control register */ + s->cmd = val; + if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ + s->isr &= ~ENISR_RESET; + /* test specific case: zero length transfer */ + if ((val & (E8390_RREAD | E8390_RWRITE)) && + s->rcnt == 0) { + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } + if (val & E8390_TRANS) { + index = (s->tpsr << 8); + /* XXX: next 2 lines are a hack to make netware 3.11 work */ + if (index >= NE2000_PMEM_END) + index -= NE2000_PMEM_SIZE; + // must be before ethernet_trigger + s->tsr = ENTSR_PTX; + s->isr |= ENISR_TX; + s->cmd &= ~E8390_TRANS; + /* fail safe: check range on the transmitted length */ + if (index + s->tcnt <= NE2000_PMEM_END) { + transmitbuffer = s->mem + index; + transmitlen = s->tcnt; + ethernet_trigger(td, sysdata); +#if 0 + qemu_send_packet(qemu_get_queue(s->nic), s->mem + index, s->tcnt); +#endif + } + /* signal end of transfer */ + ne2000_update_irq(s); + } + } + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_STARTPG: + s->start = val << 8; + break; + case EN0_STOPPG: + s->stop = val << 8; + break; + case EN0_BOUNDARY: + s->boundary = val; + break; + case EN0_IMR: + s->imr = val; + ne2000_update_irq(s); + break; + case EN0_TPSR: + s->tpsr = val; + break; + case EN0_TCNTLO: + s->tcnt = (s->tcnt & 0xff00) | val; + break; + case EN0_TCNTHI: + s->tcnt = (s->tcnt & 0x00ff) | (val << 8); + break; + case EN0_RSARLO: + s->rsar = (s->rsar & 0xff00) | val; + break; + case EN0_RSARHI: + s->rsar = (s->rsar & 0x00ff) | (val << 8); + break; + case EN0_RCNTLO: + s->rcnt = (s->rcnt & 0xff00) | val; + break; + case EN0_RCNTHI: + s->rcnt = (s->rcnt & 0x00ff) | (val << 8); + break; + case EN0_RXCR: + s->rxcr = val; + break; + case EN0_DCFG: + s->dcfg = val; + break; + case EN0_ISR: + s->isr &= ~(val & 0x7f); + ne2000_update_irq(s); + break; + case EN1_PHYS: + case EN1_PHYS + 1: + case EN1_PHYS + 2: + case EN1_PHYS + 3: + case EN1_PHYS + 4: + case EN1_PHYS + 5: + s->phys[offset - EN1_PHYS] = val; + break; + case EN1_CURPAG: + s->curpag = val; + break; + case EN1_MULT: + case EN1_MULT + 1: + case EN1_MULT + 2: + case EN1_MULT + 3: + case EN1_MULT + 4: + case EN1_MULT + 5: + case EN1_MULT + 6: + case EN1_MULT + 7: + s->mult[offset - EN1_MULT] = val; + break; + } + } + ne2000_receive_check(); +} + +static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = (NE2000State*)opaque; + int offset, page, ret; + + addr &= 0xf; + if (addr == E8390_CMD) { + ret = s->cmd; + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_TSR: + ret = s->tsr; + break; + case EN0_BOUNDARY: + ret = s->boundary; + break; + case EN0_ISR: + ret = s->isr; + break; + case EN0_RSARLO: + ret = s->rsar & 0x00ff; + break; + case EN0_RSARHI: + ret = s->rsar >> 8; + break; + case EN1_PHYS: + case EN1_PHYS + 1: + case EN1_PHYS + 2: + case EN1_PHYS + 3: + case EN1_PHYS + 4: + case EN1_PHYS + 5: + ret = s->phys[offset - EN1_PHYS]; + break; + case EN1_CURPAG: + ret = s->curpag; + break; + case EN1_MULT: + case EN1_MULT + 1: + case EN1_MULT + 2: + case EN1_MULT + 3: + case EN1_MULT + 4: + case EN1_MULT + 5: + case EN1_MULT + 6: + case EN1_MULT + 7: + ret = s->mult[offset - EN1_MULT]; + break; + case EN0_RSR: + ret = s->rsr; + break; + case EN2_STARTPG: + ret = s->start >> 8; + break; + case EN2_STOPPG: + ret = s->stop >> 8; + break; + case EN0_RTL8029ID0: + ret = 0x50; + break; + case EN0_RTL8029ID1: + ret = 0x43; + break; + case EN3_CONFIG0: + ret = 0; /* 10baseT media */ + break; + case EN3_CONFIG2: + ret = 0x40; /* 10baseT active */ + break; + case EN3_CONFIG3: + ret = 0x40; /* Full duplex */ + break; + default: + ret = 0x00; + break; + } + } + ne2000_receive_check(); +#ifdef DEBUG_NE2000 + write_log("NE2000: read addr=0x%x val=%02x\n", addr, ret); +#endif + return ret; +} + +static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, + uint32_t val) +{ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + s->mem[addr] = val; + } +} + +static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, + uint32_t val) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); + } +} + +static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, + uint32_t val) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + stl_le_p(s->mem + addr, val); + } +} + +static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) +{ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return s->mem[addr]; + } else { + return 0xff; + } +} + +static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le16_to_cpu(*(uint16_t *)(s->mem + addr)); + } else { + return 0xffff; + } +} + +static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) +{ + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return ldl_le_p(s->mem + addr); + } else { + return 0xffffffff; + } +} + +static inline void ne2000_dma_update(NE2000State *s, int len) +{ + s->rsar += len; + /* wrap */ + /* XXX: check what to do if rsar > stop */ + if (s->rsar == s->stop) + s->rsar = s->start; + + if (s->rcnt <= len) { + s->rcnt = 0; + /* signal end of transfer */ + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } else { + s->rcnt -= len; + } +} + +static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = (NE2000State*)opaque; + +#ifdef DEBUG_NE2000 + write_log("NE2000: asic write val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + if (s->dcfg & 0x01) { + /* 16 bit access */ + ne2000_mem_writew(s, s->rsar, val); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ne2000_mem_writeb(s, s->rsar, val); + ne2000_dma_update(s, 1); + } +} + +static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = (NE2000State*)opaque; + int ret; + + if (s->dcfg & 0x01) { + /* 16 bit access */ + ret = ne2000_mem_readw(s, s->rsar); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ret = ne2000_mem_readb(s, s->rsar); + ne2000_dma_update(s, 1); + } +#ifdef DEBUG_NE2000 + write_log("NE2000: asic read val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + NE2000State *s = (NE2000State*)opaque; + +#ifdef DEBUG_NE2000 + write_log("NE2000: asic writel val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + /* 32 bit access */ + ne2000_mem_writel(s, s->rsar, val); + ne2000_dma_update(s, 4); +} + +static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr) +{ + NE2000State *s = (NE2000State*)opaque; + int ret; + + /* 32 bit access */ + ret = ne2000_mem_readl(s, s->rsar); + ne2000_dma_update(s, 4); +#ifdef DEBUG_NE2000 + write_log("NE2000: asic readl val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + /* nothing to do (end of reset pulse) */ +} + +static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr) +{ + NE2000State *s = (NE2000State*)opaque; + ne2000_reset2(s); + return 0; +} + +static int ne2000_post_load(void* opaque, int version_id) +{ + NE2000State* s = (NE2000State*)opaque; + + if (version_id < 2) { + s->rxcr = 0x0c; + } + return 0; +} + +#if 0 +const VMStateDescription vmstate_ne2000 = { + .name = "ne2000", + .version_id = 2, + .minimum_version_id = 0, + .post_load = ne2000_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8_V(rxcr, NE2000State, 2), + VMSTATE_UINT8(cmd, NE2000State), + VMSTATE_UINT32(start, NE2000State), + VMSTATE_UINT32(stop, NE2000State), + VMSTATE_UINT8(boundary, NE2000State), + VMSTATE_UINT8(tsr, NE2000State), + VMSTATE_UINT8(tpsr, NE2000State), + VMSTATE_UINT16(tcnt, NE2000State), + VMSTATE_UINT16(rcnt, NE2000State), + VMSTATE_UINT32(rsar, NE2000State), + VMSTATE_UINT8(rsr, NE2000State), + VMSTATE_UINT8(isr, NE2000State), + VMSTATE_UINT8(dcfg, NE2000State), + VMSTATE_UINT8(imr, NE2000State), + VMSTATE_BUFFER(phys, NE2000State), + VMSTATE_UINT8(curpag, NE2000State), + VMSTATE_BUFFER(mult, NE2000State), + VMSTATE_UNUSED(4), /* was irq */ + VMSTATE_BUFFER(mem, NE2000State), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_ne2000 = { + .name = "ne2000", + .version_id = 3, + .minimum_version_id = 3, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PCINE2000State), + VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State), + VMSTATE_END_OF_LIST() + } +}; +#endif + +static uint64_t ne2000_read(void *opaque, hwaddr addr, + unsigned size) +{ + uint64_t v; + NE2000State *s = (NE2000State*)opaque; + + if (addr < 0x10 && size == 1) { + v = ne2000_ioport_read(s, addr); + } else if (addr == 0x10) { + if (size <= 2) { + v = ne2000_asic_ioport_read(s, addr); + } else { + v = ne2000_asic_ioport_readl(s, addr); + } + } else if (addr == 0x1f && size == 1) { + v = ne2000_reset_ioport_read(s, addr); + } else { + v = ((uint64_t)1 << (size * 8)) - 1; + } + write_log(_T("NE2000_READ %08x=%08x %d\n"), addr, (uae_u32)v, size); + return v; +} + +static void ne2000_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + NE2000State *s = (NE2000State*)opaque; + + write_log(_T("NE2000_WRITE %08x %08x %d\n"), addr, (uae_u32)data, size); + + if (addr < 0x10 && size == 1) { + ne2000_ioport_write(s, addr, data); + } else if (addr == 0x10) { + if (size <= 2) { + ne2000_asic_ioport_write(s, addr, data); + } else { + ne2000_asic_ioport_writel(s, addr, data); + } + } else if (addr == 0x1f && size == 1) { + ne2000_reset_ioport_write(s, addr, data); + } +} + +#if 0 +static const MemoryRegionOps ne2000_ops = { + .read = ne2000_read, + .write = ne2000_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/***********************************************************/ +/* PCI NE2000 definitions */ + +void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) +{ + memory_region_init_io(&s->io, OBJECT(dev), &ne2000_ops, s, "ne2000", size); +} + +static NetClientInfo net_ne2000_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = ne2000_can_receive, + .receive = ne2000_receive, +}; + +static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp) +{ + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s; + uint8_t *pci_conf; + + pci_conf = d->dev.config; + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ + + s = &d->ne2000; + ne2000_setup_io(s, DEVICE(pci_dev), 0x100); + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); + s->irq = pci_allocate_irq(&d->dev); + + qemu_macaddr_default_if_unset(&s->c.macaddr); + ne2000_reset(s); + + s->nic = qemu_new_nic(&net_ne2000_info, &s->c, + object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); +} + +static void pci_ne2000_exit(PCIDevice *pci_dev) +{ + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s = &d->ne2000; + + qemu_del_nic(s->nic); + qemu_free_irq(s->irq); +} + +static void ne2000_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s = &d->ne2000; + + device_add_bootindex_property(obj, &s->c.bootindex, + "bootindex", "/ethernet-phy@0", + &pci_dev->qdev, NULL); +} + +static Property ne2000_properties[] = { + DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ne2000_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = pci_ne2000_realize; + k->exit = pci_ne2000_exit; + k->romfile = "efi-ne2k_pci.rom", + k->vendor_id = PCI_VENDOR_ID_REALTEK; + k->device_id = PCI_DEVICE_ID_REALTEK_8029; + k->class_id = PCI_CLASS_NETWORK_ETHERNET; + dc->vmsd = &vmstate_pci_ne2000; + dc->props = ne2000_properties; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static const TypeInfo ne2000_info = { + .name = "ne2k_pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCINE2000State), + .class_init = ne2000_class_init, + .instance_init = ne2000_instance_init, +}; + +static void ne2000_register_types(void) +{ + type_register_static(&ne2000_info); +} + +type_init(ne2000_register_types) +#endif + +#define MAX_PACKET_SIZE 2000 +#define RECEIVE_BUFFER_INDEX_MASK 3 +static int receive_buffer_index; +static uae_u8 receive_buffer[4][MAX_PACKET_SIZE]; +static int receive_buffer_read, receive_buffer_write; +static int receive_buffer_size[4]; + +static void ne2000_receive_check(void) +{ + while (receive_buffer_read != receive_buffer_write) { + if (receive_buffer_size[receive_buffer_read]) { + if (ne2000state.isr & (ENISR_RX | ENISR_TX)) + return; + if (ne2000_receive(&ncs, receive_buffer[receive_buffer_read], receive_buffer_size[receive_buffer_read]) < 0) + return; + receive_buffer_size[receive_buffer_read] = 0; + receive_buffer_read++; + receive_buffer_read &= RECEIVE_BUFFER_INDEX_MASK; + } + } +} + +static void gotfunc(void *devv, const uae_u8 *databuf, int len) +{ + ne2000_receive_check(); + if (len > MAX_PACKET_SIZE) + return; + if (((receive_buffer_write + 1) & RECEIVE_BUFFER_INDEX_MASK) == receive_buffer_read) + return; + memcpy(receive_buffer[receive_buffer_write], databuf, len); + receive_buffer_size[receive_buffer_write] = len; + receive_buffer_write++; + receive_buffer_write &= RECEIVE_BUFFER_INDEX_MASK; + ne2000_receive_check(); +} + +static void REGPARAM2 ne2000_bput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + ne2000_write(ncs.ne2000state, addr, b, 1); +} +static void REGPARAM2 ne2000_wput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + ne2000_write(ncs.ne2000state, addr, b, 2); +} +static void REGPARAM2 ne2000_lput(struct pci_board_state *pcibs, uaecptr addr, uae_u32 b) +{ + ne2000_write(ncs.ne2000state, addr, b, 4); +} +static uae_u32 REGPARAM2 ne2000_bget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = ne2000_read(ncs.ne2000state, addr, 1); + return v; +} +static uae_u32 REGPARAM2 ne2000_wget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = ne2000_read(ncs.ne2000state, addr, 2); + return v; +} +static uae_u32 REGPARAM2 ne2000_lget(struct pci_board_state *pcibs, uaecptr addr) +{ + uae_u32 v = ne2000_read(ncs.ne2000state, addr, 4); + return v; +} + +static void ne2000_reset(struct pci_board_state *pcibs) +{ + ne2000_reset2(&ne2000state); + receive_buffer_read = receive_buffer_write = 0; + for (int i = 0; i <= RECEIVE_BUFFER_INDEX_MASK; i++) + receive_buffer_size[i] = 0; +} + +static void ne2000_free(struct pci_board_state *pcibs) +{ + ethernet_close(td, sysdata); + xfree(sysdata); + td = NULL; + sysdata = NULL; +} + +static bool ne2000_init(struct pci_board_state *pcibs) +{ + ne2000_free(pcibs); + ncs.device = &ne2000_pci_board; + ncs.pcistate = pcibs; + ncs.ne2000state = &ne2000state; + memset(&ne2000state, 0, sizeof ne2000state); + + td = NULL; + uae_u8 *m = ncs.ne2000state->c.macaddr.a; + memset(m, 0, 6); + if (ethernet_enumerate(&td, currprefs.ne2000pciname)) { + memcpy(m, td->mac, 6); + if (!m[0] && !m[1] && !m[2]) { + m[0] = 0x52; + m[1] = 0x54; + m[2] = 0x05; + } + write_log(_T("NE2000: '%s' %02X:%02X:%02X:%02X:%02X:%02X\n"), + td->name, td->mac[0], td->mac[1], td->mac[2], td->mac[3], td->mac[4], td->mac[5]); + } else { + m[0] = 0x52; + m[1] = 0x54; + m[2] = 0x05; + m[3] = 4; + m[4] = 3; + m[5] = 2; + write_log(_T("NE2000: Disconnected mode %02X:%02X:%02X:%02X:%02X:%02X\n"), + m[0], m[1], m[2], m[3], m[4], m[5]); + } + + ne2000_reset(pcibs); + + if (td != NULL) { + if (!sysdata) + sysdata = xcalloc(uae_u8, ethernet_getdatalenght(td)); + if (!ethernet_open(td, sysdata, NULL, gotfunc, getfunc, 0)) { + write_log(_T("NE2000: failed to initialize winpcap driver\n")); + return false; + } + } + return true; +} + + +static const struct pci_config ne2000_pci_config = +{ + 0x10ec, 0x8029, 0, 0, 0, 0x020000, 0, 0x10ec, 0x8029, 1, { 0x20 | 1, 0, 0, 0, 0, 0, 0 } +}; + +const struct pci_board ne2000_pci_board = +{ + _T("RTL8029"), + &ne2000_pci_config, ne2000_init, ne2000_free, ne2000_reset, pci_irq_callback, + { + { ne2000_lget, ne2000_wget, ne2000_bget, ne2000_lput, ne2000_wput, ne2000_bput }, + { NULL }, + { NULL }, + { NULL }, + { NULL }, + { NULL }, + { NULL }, + } +}; diff --git a/qemuvga/ne2000.h b/qemuvga/ne2000.h new file mode 100644 index 00000000..fd4fffd0 --- /dev/null +++ b/qemuvga/ne2000.h @@ -0,0 +1,42 @@ +#ifndef HW_NE2000_H +#define HW_NE2000_H 1 + +#define NE2000_PMEM_SIZE (32*1024) +#define NE2000_PMEM_START (16*1024) +#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) +#define NE2000_MEM_SIZE NE2000_PMEM_END + +typedef struct NE2000State { + MemoryRegion io; + uint8_t cmd; + uint32_t start; + uint32_t stop; + uint8_t boundary; + uint8_t tsr; + uint8_t tpsr; + uint16_t tcnt; + uint16_t rcnt; + uint32_t rsar; + uint8_t rsr; + uint8_t rxcr; + uint8_t isr; + uint8_t dcfg; + uint8_t imr; + uint8_t phys[6]; /* mac address */ + uint8_t curpag; + uint8_t mult[8]; /* multicast mask array */ + qemu_irq irq; + //NICState *nic; + NICConf c; + uint8_t mem[NE2000_MEM_SIZE]; +} NE2000State; + +#if 0 +void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size); +extern const VMStateDescription vmstate_ne2000; +void ne2000_reset2(NE2000State *s); +int ne2000_can_receive(NetClientState *nc); +ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_); +#endif + +#endif diff --git a/qemuvga/qemuuaeglue.h b/qemuvga/qemuuaeglue.h index 5a936dde..34c044ba 100644 --- a/qemuvga/qemuuaeglue.h +++ b/qemuvga/qemuuaeglue.h @@ -26,6 +26,8 @@ extern void write_log (const char *, ...); #define unlikely(x) __builtin_expect(!!(x), 0) #endif +typedef int ssize_t; + #ifdef _MSC_VER #include #define container_of(address, type, field) ((type *)( \ @@ -113,7 +115,28 @@ typedef struct DisplaySurface { uint16_t le16_to_cpu(uint16_t v); uint32_t le32_to_cpu(uint32_t v); -static inline void cpu_to_32wu(uint32_t *p, uint32_t v) +#define le_bswap(v, size) (v) +#define cpu_to_le16(x) (x) + +STATIC_INLINE void stl_he_p(void *ptr, uint32_t v) +{ + memcpy(ptr, &v, sizeof(v)); +} +STATIC_INLINE void stl_le_p(void *ptr, uint32_t v) +{ + stl_he_p(ptr, le_bswap(v, 32)); +} +STATIC_INLINE int ldl_he_p(const void *ptr) +{ + int32_t r; + memcpy(&r, ptr, sizeof(r)); + return r; +} +STATIC_INLINE int ldl_le_p(const void *ptr) +{ + return le_bswap(ldl_he_p(ptr), 32); +} +STATIC_INLINE void cpu_to_32wu(uint32_t *p, uint32_t v) { } -- 2.47.3