diff options
Diffstat (limited to 'src/emu/machine/dp8390.c')
-rw-r--r-- | src/emu/machine/dp8390.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/src/emu/machine/dp8390.c b/src/emu/machine/dp8390.c new file mode 100644 index 00000000000..a1ac879b12c --- /dev/null +++ b/src/emu/machine/dp8390.c @@ -0,0 +1,485 @@ +#include "emu.h" +#include "dp8390.h" + +#define DP8390_BYTE_ORDER(w) ((m_regs.dcr & 3) == 3 ? ((data << 8) | (data >> 8)) : data) +#define LOOPBACK (!(m_regs.dcr & 8) && (m_regs.tcr & 6)) + +const device_type DP8390D = &device_creator<dp8390d_device>; +const device_type RTL8019A = &device_creator<rtl8019a_device>; + +void dp8390_device::device_config_complete() { + const dp8390_interface *intf = reinterpret_cast<const dp8390_interface *>(static_config()); + if(intf != NULL) + *static_cast<dp8390_interface *>(this) = *intf; + else { + memset(&irq_cb, 0, sizeof(irq_cb)); + memset(&breq_cb, 0, sizeof(breq_cb)); + memset(&mem_read_cb, 0, sizeof(mem_read_cb)); + memset(&mem_write_cb, 0, sizeof(mem_write_cb)); + } +} + +dp8390d_device::dp8390d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : dp8390_device(mconfig, DP8390D, "DP8390D", tag, owner, clock, 10.0f, "dp8390d", __FILE__) { + m_type = TYPE_DP8390D; +} + +rtl8019a_device::rtl8019a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : dp8390_device(mconfig, RTL8019A, "RTL8019A", tag, owner, clock, 10.0f, "rtl8019a", __FILE__) { + m_type = TYPE_RTL8019A; +} + +dp8390_device::dp8390_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, float bandwidth, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_network_interface(mconfig, *this, bandwidth) { +} + +void dp8390_device::device_start() { + irq_func.resolve(irq_cb, *this); + breq_func.resolve(breq_cb, *this); + mem_read.resolve(mem_read_cb, *this); + mem_write.resolve(mem_write_cb, *this); +} + +void dp8390_device::stop() { + m_regs.isr = 0x80; // is this right? + m_regs.cr |= 1; + irq_func(CLEAR_LINE); + m_reset = 1; +} + +void dp8390_device::device_reset() { + memset(&m_regs, 0, sizeof(m_regs)); + m_regs.cr = 0x21; + m_regs.isr = 0x80; + m_regs.dcr = 0x04; + memset(&m_8019regs, 0, sizeof(m_8019regs)); + m_8019regs.config1 = 0x80; + m_8019regs.config3 = 0x01; + irq_func(CLEAR_LINE); + + m_reset = 1; +} + +void dp8390_device::check_dma_complete() { + if(m_regs.rbcr) return; + m_regs.isr |= 0x40; + check_irq(); + m_rdma_active = 0; +} + +void dp8390_device::do_tx() { + UINT8 *buf; + int i; + UINT32 high16 = (m_regs.dcr & 4)?m_regs.rsar<<16:0; + if(m_reset) return; + if(LOOPBACK) return; // TODO: loopback + m_regs.tsr = 0; + if(m_regs.tbcr > 1518) logerror("dp8390: trying to send overlong frame\n"); + if(!m_regs.tbcr) { // ? Bochs says solaris actually does this + m_regs.tsr = 1; + m_regs.cr &= ~4; + return; + } + + buf = global_alloc_array(UINT8, m_regs.tbcr); + for(i = 0; i < m_regs.tbcr; i++) buf[i] = mem_read(high16 + (m_regs.tpsr << 8) + i); + + if(send(buf, m_regs.tbcr)) { + m_regs.tsr = 1; + m_regs.isr |= 2; + } else { + m_regs.tsr = 8; // not quite right but there isn't a generic "tx failed" + m_regs.isr |= 8; + } + m_regs.cr &= ~4; + check_irq(); + global_free(buf); +} + +void dp8390_device::set_cr(UINT8 newcr) { + int ostate = ((m_regs.cr & 3) == 2); + m_regs.cr = newcr; + if((newcr & 1) && (ostate == 1)) return stop(); + if((newcr & 3) == 2) { + m_reset = 0; + m_regs.isr &= ~0x80; + } + if(newcr & 0x20) m_rdma_active = 0; + if(m_reset) return; + if(newcr & 4) do_tx(); + if((newcr & 0x38) == 8) { + m_rdma_active = 1; + check_dma_complete(); + } + if((newcr & 0x38) == 0x10) m_rdma_active = 2; +} + +void dp8390_device::recv_overflow() { + m_regs.rsr = 0x10; + m_regs.isr |= 0x10; + check_irq(); + m_regs.cntr2++; + return; +} + +void dp8390_device::recv(UINT8 *buf, int len) { + int i; + UINT16 start = (m_regs.curr << 8), offset; + UINT32 high16; + if(m_reset) return; + if(m_regs.curr == m_regs.pstop) start = m_regs.pstart << 8; + offset = start + 4; + high16 = (m_regs.dcr & 4)?m_regs.rsar<<16:0; + if(buf[0] & 1) { + if(!memcmp((const char *)buf, "\xff\xff\xff\xff\xff\xff", 6)) { + if(!(m_regs.rcr & 4)) return; + } else return; // multicast + m_regs.rsr = 0x20; + } else m_regs.rsr = 0; + len &= 0xffff; + + for(i = 0; i < len; i++) { + mem_write(high16 + offset, buf[i]); + offset++; + if(!(offset & 0xff)) { + if((offset >> 8) == m_regs.pstop) offset = m_regs.pstart << 8; + if((offset >> 8) == m_regs.bnry) return recv_overflow(); + } + } + if(len < 60) { + // this can't pass to the next page + for(; i < 60; i++) { + mem_write(high16 + offset, 0); + offset++; + } + len = 60; + } + + m_regs.rsr |= 1; + m_regs.isr |= 1; + m_regs.curr = (offset >> 8) + ((offset & 0xff)?1:0); + if(m_regs.curr == m_regs.pstop) m_regs.curr = m_regs.pstart; + len += 4; + mem_write(start, m_regs.rsr); + mem_write(start+1, m_regs.curr); + mem_write(start+2, len & 0xff); + mem_write(start+3, len >> 8); + check_irq(); +} + +void dp8390_device::recv_cb(UINT8 *buf, int len) { + if(!LOOPBACK) recv(buf, len); +} + +WRITE_LINE_MEMBER(dp8390_device::dp8390_cs) { + m_cs = state; +} + +WRITE_LINE_MEMBER(dp8390_device::dp8390_reset) { + if(!state) device_reset(); +} + +READ16_MEMBER(dp8390_device::dp8390_r) { + UINT16 data; + if(m_cs) { + UINT32 high16 = (m_regs.dcr & 4)?m_regs.rsar<<16:0; + if(m_regs.dcr & 1) { + m_regs.crda &= ~1; + data = mem_read(high16 + m_regs.crda++); + data |= mem_read(high16 + m_regs.crda++) << 8; + m_regs.rbcr -= (m_regs.rbcr < 2)?m_regs.rbcr:2; + check_dma_complete(); + return DP8390_BYTE_ORDER(data); + } else { + m_regs.rbcr -= (m_regs.rbcr)?1:0; + data = mem_read(high16 + m_regs.crda++); + check_dma_complete(); + return data; + } + } + + switch((offset & 0x0f)|(m_regs.cr & 0xc0)) { + case 0x00: + case 0x40: + case 0x80: + data = m_regs.cr; + break; + case 0x01: + data = m_regs.clda & 0xff; + break; + case 0x02: + data = m_regs.clda >> 8; + break; + case 0x03: + data = m_regs.bnry; + break; + case 0x04: + data = m_regs.tsr; + break; + case 0x05: + data = m_regs.ncr; + break; + case 0x06: + data = m_regs.fifo; + break; + case 0x07: + data = m_regs.isr; + break; + case 0x08: + data = m_regs.crda & 0xff; + break; + case 0x09: + data = m_regs.crda >> 8; + break; + case 0x0c: + data = m_regs.rsr; + break; + case 0x0d: + data = m_regs.cntr0; + break; + case 0x0e: + data = m_regs.cntr1; + break; + case 0x0f: + data = m_regs.cntr2; + break; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + data = m_regs.par[(offset & 0x7)-1]; + break; + case 0x47: + data = m_regs.curr; + break; + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + data = m_regs.mar[offset & 0x7]; + break; + case 0x81: + data = m_regs.pstart; + break; + case 0x82: + data = m_regs.pstop; + break; + case 0x83: + data = m_regs.rnpp; + break; + case 0x84: + data = m_regs.tpsr; + break; + case 0x85: + data = m_regs.lnpp; + break; + case 0x86: + data = m_regs.ac >> 8; + break; + case 0x87: + data = m_regs.ac & 0xff; + break; + case 0x8c: + data = m_regs.rcr; + break; + case 0x8d: + data = m_regs.tcr; + break; + case 0x8e: + data = m_regs.dcr; + break; + case 0x8f: + data = m_regs.imr; + break; + case 0xc0: + data = m_regs.cr; + break; + default: + if(m_type == TYPE_RTL8019A) { + switch((offset & 0x0f)|(m_regs.cr & 0xc0)) { + case 0x0a: + data = 'P'; + break; + case 0x0b: + data = 'p'; + break; + + case 0xc1: + data = m_8019regs.cr9346; + break; + case 0xc2: + data = m_8019regs.bpage; + break; + case 0xc3: + data = m_8019regs.config0; + break; + case 0xc4: + data = m_8019regs.config1; + break; + case 0xc5: + data = m_8019regs.config2; + break; + case 0xc6: + data = m_8019regs.config3; + break; + case 0xcd: + data = m_8019regs.config4; + break; + case 0xc8: + data = m_8019regs.csnsav; + break; + case 0xcb: + data = m_8019regs.intr; + break; + default: + logerror("rtl8019: invalid read page %01X reg %02X\n", (m_regs.cr & 0xc0) >> 6, offset & 0x0f); + return 0; + } + } else { + logerror("dp8390: invalid read page %01X reg %02X\n", (m_regs.cr & 0xc0) >> 6, offset & 0x0f); + return 0; + } + } + return data; +} + +WRITE16_MEMBER(dp8390_device::dp8390_w) { + if(m_cs) { + UINT32 high16 = (m_regs.dcr & 4)?m_regs.rsar<<16:0; + if(m_regs.dcr & 1) { + data = DP8390_BYTE_ORDER(data); + m_regs.crda &= ~1; + mem_write(high16 + m_regs.crda++, data & 0xff); + mem_write(high16 + m_regs.crda++, data >> 8); + m_regs.rbcr -= (m_regs.rbcr < 2)?m_regs.rbcr:2; + check_dma_complete(); + } else { + data &= 0xff; + mem_write(high16 + m_regs.crda++, data); + m_regs.rbcr -= (m_regs.rbcr)?1:0; + check_dma_complete(); + } + return; + } + + data &= 0xff; + switch((offset & 0x0f)|(m_regs.cr & 0xc0)) { + case 0x00: + case 0x40: + case 0x80: + set_cr(data); + break; + case 0x01: + m_regs.pstart = data; + break; + case 0x02: + m_regs.pstop = data; + break; + case 0x03: + m_regs.bnry = data; + break; + case 0x04: + m_regs.tpsr = data; + break; + case 0x05: + m_regs.tbcr = (m_regs.tbcr & 0xff00) | data; + break; + case 0x06: + m_regs.tbcr = (m_regs.tbcr & 0xff) | (data << 8); + break; + case 0x07: + m_regs.isr &= ~data; + check_irq(); + break; + case 0x08: + m_regs.rsar = (m_regs.rsar & 0xff00) | data; + m_regs.crda = m_regs.rsar; + break; + case 0x09: + m_regs.rsar = (m_regs.rsar & 0xff) | (data << 8); + m_regs.crda = m_regs.rsar; + break; + case 0x0a: + m_regs.rbcr = (m_regs.rbcr & 0xff00) | data; + break; + case 0x0b: + m_regs.rbcr = (m_regs.rbcr & 0xff) | (data << 8); + break; + case 0x0c: + m_regs.rcr = data; + set_promisc((data & 0x10)?true:false); + break; + case 0x0d: + m_regs.tcr = data; + break; + case 0x0e: + m_regs.dcr = data; + break; + case 0x0f: + m_regs.imr = data; + check_irq(); + break; + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + m_regs.par[(offset & 0x7)-1] = data; + set_mac((const char *)m_regs.par); + break; + case 0x47: + m_regs.curr = data; + break; + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + m_regs.mar[offset & 0x7] = data; + break; + case 0x81: + m_regs.clda = (m_regs.clda & 0xff00) | data; + break; + case 0x82: + m_regs.clda = (m_regs.clda & 0xff) | (data << 8); + break; + case 0x83: + m_regs.rnpp = data; + break; + case 0x85: + m_regs.lnpp = data; + break; + case 0x86: + m_regs.ac = (m_regs.ac & 0xff) | (data << 8); + break; + case 0x87: + m_regs.ac = (m_regs.ac & 0xff00) | data; + break; + case 0xc0: + set_cr(data); + break; + default: + if(m_type == TYPE_RTL8019A) { + switch((offset & 0x0f)|(m_regs.cr & 0xc0)) { + // XXX: rest of the regs + default: + logerror("rtl8019: invalid write page %01X reg %02X data %04X\n", (m_regs.cr & 0xc0) >> 6, offset & 0x0f, data); + return; + } + } else { + logerror("dp8390: invalid write page %01X reg %02X data %04X\n", (m_regs.cr & 0xc0) >> 6, offset & 0x0f, data); + return; + } + } +} |