/*
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 *
 * chip_gen_8390_crc taken from the bochs project, originally licensed
 * as LGPL-2+, but we relicensed it here under GPL-2+ according to 
 * section 3 of the LGPL, version 2.
 *
 * Copyright (C) 2002 MandrakeSoft S.A.
 *
 *    MandrakeSoft S.A.
 *    43, rue d'Aboukir
 *    75002 Paris - France
 *    http://www.linux-mandrake.com/
 *    http://www.mandrakesoft.com/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include "config.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "glue-main.h"
#include "glue-shm.h"
#include "glue-suspend.h"

#include "chip_gen_8390.h"

#define CHIP_(x) chip_gen_8390_ ## x
#define CHIP "chip_gen_8390"

struct cpssp {
	/*
	 * Signals
	 */
	unsigned int state_power;
	struct sig_isa_bus *port_bus;
	struct sig_isa_dma *port_dma;
	struct sig_std_logic *port_irq;
	struct sig_eth *port_eth;

	/*
	 * State
	 */
	uint8_t fifo[0x10000];

	uint8_t bnry;	/* Boundary */
	uint16_t clda;	/* Current Local DMA Address */
	uint8_t cntr0;	/* Tally Counter 0 (Frame Alignment Errors) */
	uint8_t cntr1;	/* Tally Counter 1 (CRC Errors) */
	uint8_t cntr2;	/* Tally Counter 2 (Missed Packet Errors) */
	uint8_t cr;	/* Command */
	uint8_t cr_ps;	/* Command: Page Select */
	uint8_t curr;	/* Current Page */
	uint8_t dcr;	/* Data Configuration */
	uint8_t imr;	/* Interrupt Mask */
	uint8_t isr;	/* Interrupt Status */
	uint8_t mar[8];	/* Multicast Address */
	uint8_t ncr;	/* Number of Collisions */
	uint8_t par[6];	/* Physical Address */
	uint8_t pstart;	/* Page Start */
	uint8_t pstop;	/* Page Stop */
	uint16_t rbcr;	/* Remove Byte Count */
	uint8_t rcr;	/* Receive Configuration */
	uint16_t rsar;	/* Remote Start Address */
	uint8_t rsr;	/* Receiver Status */
	uint16_t tbcr;	/* Transmit Byte Count */
	uint8_t tcr;	/* Transmit Configuration */
	uint8_t tpsr;	/* Transmit Page Start */
	uint8_t tsr;	/* Transmit Status */
};

/* Some generic ethernet register configurations. */
#define SHORTPKT    0x02 /* Accept packets with length fewer than 64 byts */
#define BROADCAST   0x04 /* Accept broadcast packets */
#define MULTICAST   0x08 /* Accept mutlcast packets */
#define PROMISCUOUS 0x10 /* Accept all physical. that is not multi/broad cast */

/*  Register accessed at 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 */

/* Bits in 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 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, 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. */

struct isa_ne2000_e8390_pkt_hdr {
	unsigned char  status;
	unsigned char  next;
	unsigned short count;
};

static uint8_t
CHIP_(mem_readb)(struct cpssp *cpssp, uint16_t addr)
{
	uint8_t val;

	sig_isa_bus_readb(cpssp->port_bus, cpssp, addr, &val);

	return val;
}

static void
CHIP_(mem_writeb)(struct cpssp *cpssp, uint16_t addr, uint8_t val)
{
	sig_isa_bus_writeb(cpssp->port_bus, cpssp, addr, val);
}

/*
 * The Interrupt Status Regoster (ISR) is accessed by the host processor to
 * determine the cause of an interrupt. Any interrupt can be masked in the
 * Interrupt Mask Register (IMR). Individual interrupt bits are cleared by
 * writing a ``1'' into the corresponding bit of the ISR. The INT signal is
 * active as long as any unmasked signal is set, and will not go low until all
 * unmasked bits in this register have been cleared. The ISR must be cleared
 * after power up by writing it with all 1's. So the following functions gets
 * called when IMR or ISR is touched. 
 * The IMR is used to mask interrupts. Each interrupt mask bit corresponds to a
 * bit in the Interrupt Status Register (ISR). If an interrupt mask bit is set
 * an interrupt will be issued whenever the corresponding bit in the ISR is
 * set. If any bit in the IMR is set low, an interrupt will not occur when the
 * bit in the ISR is set. The IMR powers up all zeroes.
 */
static void
CHIP_(handle_irq)(struct cpssp *cpssp)
{
	/*
	 * bit 7 in ISR is mereley a status bit and *not* an interrupt
	 * reaons. So even if this bit gets ONE, it won't raise an interrupt.
	 * Same for the interrupt mask register: bit 7 is reserved and as such
	 * has no effect.
	 */
	if (cpssp->isr & 0x7F & cpssp->imr) { 
		sig_std_logic_or_set(cpssp->port_irq, cpssp, 1);
	} else {
		sig_std_logic_or_set(cpssp->port_irq, cpssp, 0);
	}
}

/*
 * mcast_index() - return the 6-bit index into the multicast
 * table. Stolen unashamedly from Bochs
 */
static unsigned char
CHIP_(crc)(unsigned char *dst)
{
#define POLYNOMIAL 0x04c11db6
	unsigned long crc = 0xffffffffL;
	int carry, i, j;
	unsigned char b;

	for (i = 6; --i >= 0;) {
		b = *dst++;
		for (j = 8; --j >= 0;) {
			carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
			crc <<= 1;
			b >>= 1;
			if (carry)
				crc = ((crc ^ POLYNOMIAL) | carry);
		}
	}
	return (crc >> 26);
#undef POLYNOMIAL
}

/*
 * This function decides whatever this packet should be dropped or given to the
 * kernel. First it checks if it is a BROADCAST address and if BROADCAST
 * packets should be received. Second it checks if the packet is MULTICAST
 * packet, if MULTICAST packets should be received and if they match the hash
 * algorithm / filter. Third it checks if packet is for my MAC address. After
 * that is has a look if the PROMISCIOUS mode is enabled. This is *not& a bug!
 * If you really want to receive all packets you have to turn on BROADCAST,
 * MULTICAST, PROMISCIOUS and set the MULTICAST filter bits to all 1. See Note:
 * 8390 pdf page 25.
 */
static unsigned char
CHIP_(packet_for_me)(struct cpssp *cpssp, unsigned char *buf)
{
	static const unsigned char broadcast[6] = {
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
	};

	if (memcmp(buf, &broadcast, sizeof(broadcast)) == 0) {
		return (cpssp->rcr & BROADCAST);
	}

	if (buf[0] & 0x1) {
		if (! (cpssp->rcr & MULTICAST)) {
			return 0;

		} else {
			/* adapted from bochs */
			unsigned int idx = CHIP_(crc)(buf);
			assert(/* 0 <= (idx >> 3) && */ (idx >> 3) <= 7);
			return (cpssp->mar[idx >> 3] & (1 << (idx & 0x7)));
		}
	}

	if (memcmp(buf, cpssp->par, 6) == 0) {
		return 1;
	}

	return (cpssp->rcr & PROMISCUOUS);

}

/*
 * This functions takes packages from the bridge and stores them in the
 * ringbuffer, if possible.
 */
static void
CHIP_(recv)(void *_cpssp, const void *_buf, unsigned int buflen)
{
	struct cpssp *cpssp = _cpssp;
	const uint8_t *buf = _buf;
	uint8_t pkt_recv[2048]; /* tmp  buffer ethernet frame 3*256 +
					  + some bytes for ne2000 headers */
	char *pkt;
	int i;
	unsigned short starting_index;

	if (! cpssp->state_power) {
		/* Power-off */
		return;
	}

	if (cpssp->cr & E8390_STOP) {
		/* Receiver disabled. */
		return;
	}

	if (buflen < 60
	 && ! (cpssp->rcr & SHORTPKT)) {
		/* Short packet. */
		return;
	}

	assert(buflen <= sizeof(pkt_recv)
			- sizeof(struct isa_ne2000_e8390_pkt_hdr));
	memcpy(pkt_recv + sizeof(struct isa_ne2000_e8390_pkt_hdr),
			buf, buflen);

	pkt = pkt_recv + sizeof(struct isa_ne2000_e8390_pkt_hdr);
	if (! CHIP_(packet_for_me)(cpssp, pkt)) {
		/* Packet not for us. */
		return;
	}

	/* clear ne2000 header */

	pkt = (char *) pkt_recv;
	pkt[0] = 0;
	pkt[1] = 0;
	pkt[2] = 0;
	pkt[3] = 0;

	/* remember start of packet in NE2000 buffer */

	starting_index = (cpssp->curr << 8);

	/* copy incoming packet with heading header into NE2000 buffer */

	buflen += sizeof(struct isa_ne2000_e8390_pkt_hdr);
	i = buflen;
	while (0 < i) {
		unsigned short dst;
		unsigned short j;

		if (cpssp->curr == cpssp->bnry) {
			/* FIXME: overrun */
			fprintf(stderr, "NE2000 overrun, dropping packet\n");
			cpssp->curr = starting_index >> 8;
			return;
		}
		dst = (cpssp->curr << 8) & 0x7fff;
		for (j = 0; j < 256; j++) {
			CHIP_(mem_writeb)(cpssp, dst + j, pkt[j]);
		}
		pkt += 256;
		i -= 256;
		cpssp->curr++;
		if (cpssp->curr == cpssp->pstop) {
			cpssp->curr = cpssp->pstart;
		}
	}

	/* Status */
	CHIP_(mem_writeb)(cpssp, starting_index + 0, ENRSR_RXOK);
	/* Next Pointer */
	CHIP_(mem_writeb)(cpssp, starting_index + 1, cpssp->curr);
	/* Count */
	CHIP_(mem_writeb)(cpssp, starting_index + 2, (buflen >> 0) & 0xff);
	CHIP_(mem_writeb)(cpssp, starting_index + 3, (buflen >> 8) & 0xff);

	cpssp->isr |= ENISR_RX;
	cpssp->rsr = ENRSR_RXOK; /* no errors */

	CHIP_(handle_irq)(cpssp);
}

static void
CHIP_(send)(struct cpssp *cpssp)
{
	unsigned int i;

	if (cpssp->tbcr == 0) {
		/* Nothing to send. */
		return;
	}

	/*
	 * Move bytes to FIFO.
	 */
	for (i = 0; i < cpssp->tbcr; i++) {
		cpssp->fifo[i] = CHIP_(mem_readb)(cpssp,
				(cpssp->tpsr << 8) + i);
	}

	cpssp->isr |= ENISR_TX;  /* intr reason xmit packet w.o. error */
	cpssp->tsr  = ENTSR_PTX; /* Packet transmitted without error */
	CHIP_(handle_irq)(cpssp);

	sig_eth_send(cpssp->port_eth, cpssp, cpssp->fifo, cpssp->tbcr);
}

static int
CHIP_(inb)(void *_cpssp, uint8_t *valp, uint32_t port)
{
	struct cpssp *cpssp = _cpssp;
	uint8_t value;

	switch ((cpssp->cr_ps << 8) | port) {
	case (0 << 8) | 0x00:
	case (1 << 8) | 0x00:
	case (2 << 8) | 0x00:
	case (3 << 8) | 0x00:
		/* Command Register */
		value = (cpssp->cr_ps << 6)
			| cpssp->cr;
		break;

	/* Page 0 */
	case (0 << 8) | 0x01:
		/* Current Local DMA Address 0 */
		value = (cpssp->clda >> 0) & 0xff;
		break;
	case (0 << 8) | 0x02:
		/* Current Local DMA Address 1 */
		value = (cpssp->clda >> 8) & 0xff;
		break;
	case (0 << 8) | 0x03:
		/* Boundary Pointer */
		value = cpssp->bnry;
		break;
	case (0 << 8) | 0x04:
		/* Transmit status reg RD */
		value = cpssp->tsr;
		break;
	case (0 << 8) | 0x05:
		/* Number of Collisions */
		value = cpssp->ncr;
		break;
	case (0 << 8) | 0x06:
		/* FIFO */
		value = 0; /* FIXME */
		break;
	case (0 << 8) | 0x07:
		/* Interrupt Status Register */
		value = cpssp->isr;
		break;
	case (0 << 8) | 0x08:
		/* Current Remote DMA Address 0 */
		value = cpssp->rsar & 0xff;
		break;
	case (0 << 8) | 0x09:
		/* Current Remote DMA Address 1 */
		value = cpssp->rsar >> 8;
		break;
	case (0 << 8) | 0x0a:
		/* Reserved */
		value = 0; /* FIXME */
		break;
	case (0 << 8) | 0x0b:
		/* Reserved */
		value = 0; /* FIXME */
		break;
	case (0 << 8) | 0x0c:
		/* Receiver Status Register */
		value = cpssp->rsr;
		break;
	case (0 << 8) | 0x0d:
		/* Tally Counter 0 (Frame Alignment Errors) */
		value = cpssp->cntr0;
		cpssp->cntr0 = 0;
		break;
	case (0 << 8) | 0x0e:
		/* Tally Counter 1 (CRC Errors) */
		value = cpssp->cntr1;
		cpssp->cntr1 = 0;
		break;
	case (0 << 8) | 0x0f:
		/* Tally Counter 2 (Missed Packet Errors) */
		value = cpssp->cntr2;
		cpssp->cntr2 = 0;
		break;

	/* Page 1 */
	case (1 << 8) | 0x01:
		/* Physical Address 0 */
		value = cpssp->par[0];
		break;
	case (1 << 8) | 0x02:
		/* Physical Address 1 */
		value = cpssp->par[1];
		break;
	case (1 << 8) | 0x03:
		/* Physical Address 2 */
		value = cpssp->par[2];
		break;
	case (1 << 8) | 0x04:
		/* Physical Address 3 */
		value = cpssp->par[3];
		break;
	case (1 << 8) | 0x05:
		/* Physical Address 4 */
		value = cpssp->par[4];
		break;
	case (1 << 8) | 0x06:
		/* Physical Address 5 */
		value = cpssp->par[5];
		break;
	case (1 << 8) | 0x07:
		/* Current Page Register */
		value = cpssp->curr;
		break;
	case (1 << 8) | 0x08:
		/* Multicast Address 0 */
		value = cpssp->mar[0];
		break;
	case (1 << 8) | 0x09:
		/* Multicast Address 1 */
		value = cpssp->mar[1];
		break;
	case (1 << 8) | 0x0a:
		/* Multicast Address 2 */
		value = cpssp->mar[2];
		break;
	case (1 << 8) | 0x0b:
		/* Multicast Address 3 */
		value = cpssp->mar[3];
		break;
	case (1 << 8) | 0x0c:
		/* Multicast Address 4 */
		value = cpssp->mar[4];
		break;
	case (1 << 8) | 0x0d:
		/* Multicast Address 5 */
		value = cpssp->mar[5];
		break;
	case (1 << 8) | 0x0e:
		/* Multicast Address 6 */
		value = cpssp->mar[6];
		break;
	case (1 << 8) | 0x0f:
		/* Multicast Address 7 */
		value = cpssp->mar[7];
		break;

	/* Page 2 */
	case (2 << 8) | 0x01:
		/* Page Start Register */
		value = cpssp->pstart;
		break;
	case (2 << 8) | 0x02:
		/* Page Stop Register */
		value = cpssp->pstop;
		break;
	case (2 << 8) | 0x03:
		/* Remote Next Packet Pointer */
		value = 0; /* FIXME */
		break;
	case (2 << 8) | 0x04:
		/* Transmit Page Start Address */
		value = cpssp->tpsr;
		break;
	case (2 << 8) | 0x05:
		/* Local Next Packet Pointer */
		value = 0; /* FIXME */
		break;
	case (2 << 8) | 0x06:
		/* Address Counter (Upper) */
		value = 0; /* FIXME */
		break;
	case (2 << 8) | 0x07:
		/* Address Counter (Lower) */
		value = 0; /* FIXME */
		break;
	case (2 << 8) | 0x08:
	case (2 << 8) | 0x09:
	case (2 << 8) | 0x0a:
	case (2 << 8) | 0x0b:
		/* Reserved */
		value = 0; /* FIXME */
		break;
	case (2 << 8) | 0x0c:
		/* Receive Configuration */
		value = cpssp->rcr;
		break;
	case (2 << 8) | 0x0d:
		/* Transmit Configuration */
		value = cpssp->tcr;
		break;
	case (2 << 8) | 0x0e:
		/* Data Configuration */
		value = cpssp->dcr;
		break;
	case (2 << 8) | 0x0f:
		/* Interrupt Mask */
		value = cpssp->imr;
		break;

	/* Page 3 */
	case (3 << 8) | 0x01:
	case (3 << 8) | 0x02:
	case (3 << 8) | 0x03:
	case (3 << 8) | 0x04:
	case (3 << 8) | 0x05:
	case (3 << 8) | 0x06:
	case (3 << 8) | 0x07:
	case (3 << 8) | 0x08:
	case (3 << 8) | 0x09:
	case (3 << 8) | 0x0a:
	case (3 << 8) | 0x0b:
	case (3 << 8) | 0x0c:
	case (3 << 8) | 0x0d:
	case (3 << 8) | 0x0e:
	case (3 << 8) | 0x0f:
		value = 0; /* FIXME */
		break;

	default:
		assert(0); /* Cannot happen. */
	}

	*valp = value;
	return 0;
}

static int
CHIP_(outb)(void *_cpssp, uint8_t value, uint32_t port)
{
	struct cpssp *cpssp = _cpssp;

	switch ((cpssp->cr_ps << 8) | port) {
	case (0 << 8) | 0x00:
	case (1 << 8) | 0x00:
	case (2 << 8) | 0x00:
	case (3 << 8) | 0x00:
		/* Command Register */
		cpssp->cr = value & 0x3f;
		cpssp->cr_ps = (value >> 6) & 0x3;

		if (value & E8390_STOP) {
			cpssp->isr |= ENISR_RESET;
			cpssp->rsr = 0;

		} else if (value & E8390_START) {
			cpssp->isr &= ~ENISR_RESET;
			if (value & E8390_RREAD) {
				if (! cpssp->rbcr) {
					cpssp->isr |= ENISR_RDC;
					CHIP_(handle_irq)(cpssp);
				}
			}
			if (value & E8390_TRANS) {
				CHIP_(send)(cpssp);
			}
		}
		break;

	/* Page 0 */
	case (0 << 8) | 0x01:
		/* Page Start Register */
		cpssp->pstart = value;
		break;
	case (0 << 8) | 0x02:
		/* Page Stop Register */
		cpssp->pstop = value;
		break;
	case (0 << 8) | 0x03:
		/* Boundary Register */
		cpssp->bnry = value;
		break;
	case (0 << 8) | 0x04:
		/* Transmit Page Start Address */
		cpssp->tpsr = value;
		break;
	case (0 << 8) | 0x05:
		/* Transmit Byte Count 0 */
		cpssp->tbcr = (cpssp->tbcr & 0xff00) | value;
		break;
	case (0 << 8) | 0x06:
		/* Transmit Byte Count 1 */
		cpssp->tbcr = (value << 8) | (cpssp->tbcr & 0xff);
		break;
	case (0 << 8) | 0x07:
		/* Interrupt Status Register */
		cpssp->isr &= ~(value & 0x7F); /* Don't clr RST that way */
		CHIP_(handle_irq)(cpssp);
		break;
	case (0 << 8) | 0x08:
		/* Remote Start Address Register 0 */
		cpssp->rsar = (cpssp->rsar & 0xff00) | value;
		break;
	case (0 << 8) | 0x09:
		/* Remote Start Address Register 1 */
		cpssp->rsar = (value << 8) | (cpssp->rsar & 0xff);
		break;
	case (0 << 8) | 0x0a:
		/* Remote Byte Count Register 0 */
		cpssp->rbcr = (cpssp->rbcr & 0xff00) | value;
		break;
	case (0 << 8) | 0x0b:
		/* Remote Byte Count Register 1 */
		cpssp->rbcr = (value << 8) | (cpssp->rbcr & 0xff);
		break;
	case (0 << 8) | 0x0c:
		/* Receive Configuration Register */
		cpssp->rcr = value;
		break;
	case (0 << 8) | 0x0d:
		/* Transmit Configuration Register */
		cpssp->tcr = value;
		break;
	case (0 << 8) | 0x0e:
		/* Data Configuration Register */
		cpssp->dcr = value;
		break;
	case (0 << 8) | 0x0f:
		/* Interrupt Mask Register */
		/* Bit 7 is reserved and has absolut no effect. */
		cpssp->imr = value & 0x7f;
		CHIP_(handle_irq)(cpssp);
		break;

	/* Page 1 */
	case (1 << 8) | 0x01:
		/* Physical Address Register 0 */
		cpssp->par[0] = value;
		break;
	case (1 << 8) | 0x02:
		/* Physical Address Register 1 */
		cpssp->par[1] = value;
		break;
	case (1 << 8) | 0x03:
		/* Physical Address Register 2 */
		cpssp->par[2] = value;
		break;
	case (1 << 8) | 0x04:
		/* Physical Address Register 3 */
		cpssp->par[3] = value;
		break;
	case (1 << 8) | 0x05:
		/* Physical Address Register 4 */
		cpssp->par[4] = value;
		break;
	case (1 << 8) | 0x06:
		/* Physical Address Register 5 */
		cpssp->par[5] = value;
		break;
	case (1 << 8) | 0x07:
		/* Current Page Register */
		cpssp->curr = value;
		break;
	case (1 << 8) | 0x08:
		/* Multicast Address Register 0 */
		cpssp->mar[0] = value;
		break;
	case (1 << 8) | 0x09:
		/* Multicast Address Register 1 */
		cpssp->mar[1] = value;
		break;
	case (1 << 8) | 0x0a:
		/* Multicast Address Register 2 */
		cpssp->mar[2] = value;
		break;
	case (1 << 8) | 0x0b:
		/* Multicast Address Register 3 */
		cpssp->mar[3] = value;
		break;
	case (1 << 8) | 0x0c:
		/* Multicast Address Register 4 */
		cpssp->mar[4] = value;
		break;
	case (1 << 8) | 0x0d:
		/* Multicast Address Register 5 */
		cpssp->mar[5] = value;
		break;
	case (1 << 8) | 0x0e:
		/* Multicast Address Register 6 */
		cpssp->mar[6] = value;
		break;
	case (1 << 8) | 0x0f:
		/* Multicast Address Register 7 */
		cpssp->mar[7] = value;
		break;

	/* Page 2 */
	case (2 << 8) | 0x01:
		/* Current Local DMA Address */
		cpssp->clda &= 0xff00;
		cpssp->clda |= value << 0;
		break;
	case (2 << 8) | 0x02:
		/* Current Local DMA Address */
		cpssp->clda &= 0x00ff;
		cpssp->clda |= value << 8;
		break;
	case (2 << 8) | 0x03:
		/* Remote Next Packet Pointer */
		/* FIXME */
		break;
	case (2 << 8) | 0x04:
		/* Reserved */
		/* FIXME */
		break;
	case (2 << 8) | 0x05:
		/* Local Next Packet Pointer */
		/* FIXME */
		break;
	case (2 << 8) | 0x06:
		/* Address Counter (Upper) */
		/* FIXME */
		break;
	case (2 << 8) | 0x07:
		/* Address Counter (Lower) */
		/* FIXME */
		break;
	case (2 << 8) | 0x08:
	case (2 << 8) | 0x09:
	case (2 << 8) | 0x0a:
	case (2 << 8) | 0x0b:
	case (2 << 8) | 0x0c:
	case (2 << 8) | 0x0d:
	case (2 << 8) | 0x0e:
	case (2 << 8) | 0x0f:
		/* Reserved */
		/* FIXME */
		break;

	/* Page 3 */
	case (3 << 8) | 0x01:
	case (3 << 8) | 0x02:
	case (3 << 8) | 0x03:
	case (3 << 8) | 0x04:
	case (3 << 8) | 0x05:
	case (3 << 8) | 0x06:
	case (3 << 8) | 0x07:
	case (3 << 8) | 0x08:
	case (3 << 8) | 0x09:
	case (3 << 8) | 0x0a:
	case (3 << 8) | 0x0b:
	case (3 << 8) | 0x0c:
	case (3 << 8) | 0x0d:
	case (3 << 8) | 0x0e:
	case (3 << 8) | 0x0f:
		/* Reserved */
		/* FIXME */
		break;

	default:
		assert(0); /* Cannot happen. */
	}

	return 0;
}

static int
CHIP_(ack_outb)(void *_cpssp, unsigned int tc, uint8_t value)
{
	struct cpssp *cpssp = _cpssp;

	if (cpssp->rbcr == 0) {
		fprintf(stderr, "write access to NE2000 buffer, but not in DMA mode\n");
		return -1;
	}

	CHIP_(mem_writeb)(cpssp, cpssp->rsar, value);

	cpssp->rsar++;
	if (cpssp->rsar == (cpssp->pstop << 8)) {
		cpssp->rsar = cpssp->pstart << 8;
	}
	cpssp->rbcr--;

	if (! cpssp->rbcr) { 
		/* FIXME check for interrupt or kick all RDC intr */
		cpssp->isr |= ENISR_RDC; /* remote dma complete */
		CHIP_(handle_irq)(cpssp);
	}

	return 0;
}

static int
CHIP_(ack_inb)(void *_cpssp, unsigned int tc, uint8_t *valp)
{
	struct cpssp *cpssp = _cpssp;

	if (cpssp->rbcr == 0) {
		fprintf(stderr, "read access to NE2000 buffer, but not in DMA mode\n");
		return 0xff;
	}

	*valp = CHIP_(mem_readb)(cpssp, cpssp->rsar);

	cpssp->rsar++;
	if (cpssp->rsar == cpssp->pstop << 8) {
		cpssp->rsar = cpssp->pstart << 8;
	}
	cpssp->rbcr--;

	if (cpssp->rbcr == 0) {
		cpssp->isr |= ENISR_RDC;
		CHIP_(handle_irq)(cpssp);
	}

	return 0;
}

static void
CHIP_(power_set)(void *_cpssp, unsigned int val)
{
	struct cpssp *cpssp = (struct cpssp *) _cpssp;

	cpssp->state_power = val;

	if (val) {
		/* Power On Event */
		/* Nothing to do (yet). */

	} else {
		/* Power Off Event */
		/* Nothing to do (yet). */
	}
}

static void
CHIP_(reset_set)(void *_cpssp, unsigned int val)
{
	struct cpssp *cpssp = _cpssp;

	if (! val) {
		cpssp->isr |= ENISR_RESET;
	}
}

void *
CHIP_(create)(
	const char *name,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_boolean *port_reset_hash_,
	struct sig_isa_bus *port_bus,
	struct sig_isa_dma *port_dma,
	struct sig_std_logic *port_irq,
	struct sig_eth *port_eth
)
{
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = CHIP_(power_set),
	};
	static const struct sig_boolean_funcs reset_funcs = {
		.set = CHIP_(reset_set),
	};
	static const struct sig_isa_bus_funcs bus_funcs = {
		.inb = CHIP_(inb),
		.outb = CHIP_(outb),
	};
	static const struct sig_isa_dma_funcs dma_funcs = {
		.ack_inb = CHIP_(ack_inb),
		.ack_outb = CHIP_(ack_outb),
	};
	static const struct sig_eth_funcs eth_funcs = {
		.recv = CHIP_(recv),
	};
	struct cpssp *cpssp;

	cpssp = shm_alloc(sizeof(*cpssp));
	assert(cpssp);

	/* Call */
	cpssp->port_bus = port_bus;
	sig_isa_bus_connect(port_bus, cpssp, &bus_funcs);
	sig_isa_dma_connect(port_dma, cpssp, &dma_funcs);
	cpssp->port_eth = port_eth;
	sig_eth_connect(cpssp->port_eth, cpssp, &eth_funcs);

	/* Out */
	cpssp->port_irq = port_irq;
	sig_std_logic_connect_out(cpssp->port_irq, cpssp, SIG_STD_LOGIC_L);

	/* In */
	sig_std_logic_connect_in(port_power, cpssp, &power_funcs);
	sig_boolean_connect_in(port_reset_hash_, cpssp, &reset_funcs);

	return cpssp;
}

void
CHIP_(destroy)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	shm_free(cpssp);
}

void
CHIP_(suspend)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;
	
	generic_suspend(cpssp, sizeof(*cpssp), fp);
}

void
CHIP_(resume)(void *_cpssp, FILE *fp)
{
	struct cpssp *cpssp = _cpssp;
	
	generic_resume(cpssp, sizeof(*cpssp), fp);
}
