/*
 * Driver for different AM7990 LANCE based boards.
 *
 * Please notice: The driver sources may only distributed unchanged. You're
 * allowed to modify the driver for your needs but when you want your
 * changes to be released in this driver you *MUST* pass me your changes for
 * including.
 * I just don't want to be flamed for errors I'm not responsible for. And -
 * programming the ethernet boards is sometimes somewhat tricky because
 * of several design errors.
 * If you ignore this notice and redistribute modified versions without my
 * permission there will be no further source distributions of this driver.
 *
 * Currently supported:
 * PAMs EMEGA
 * PAMs VME (untested)
 * RIEBL MEGA (old and new boards)
 * RIEBL MEGA (old and new boards)
 *            modified for compatibility with TOS 2.0x/3.0x/4.0x/5.x
 *            (b15 and b17 of the MEGA bus connector swapped)
 * RIEBL VME (MEGA STe/TT, both untested)
 * To be supported soon:
 * TUM board MEGA
 * Some of these boards don't have their complete hw address in an NV memory,
 * so you need to pass the hw address BEFORE opening the device.
 *
 * This driver has been developed and tested on a 8MHz MEGA ST and a 32MHz
 * 68030 elrad ST with 12MB ST RAM. If you run it on substantially faster
 * machines a few wait()'s may be needed here and there. Especially the RIEBL
 * cards for MEGA STs may cause some trouble due to a severe design error.
 * Please contact me in case of success or failure...
 *
 * My EMail address (may change in near future):
 * Torsten.Lang@ap.physik.uni-giessen.de
 *
 * TO DOs:
 * - Other boards need the full 24 address bits - so handle them correctly!
 * - Better send buffer handling using more than one send buffer.
 * - "Follow" the lance through the receive buffers to guarantee packets to
 *   be passed to the upper layers "in order".
 * - Better error handling: If MERRs or similar severe errors happen the LANCE
 *   has to be reinitialized!
 *
 * On a 32MHz elrad ST the driver reaches the following throughput:
 * ftp send:  ~100 k/sec (Atari <--> Sun Sparc 10)
 * ftp recv:  ~100 k/sec (        - "" -         )
 * tcp send:  untested
 * tcp recv:   - "" -
 * nfs read:   - "" -    (Atari <--> Sun Sparc 10 seem to transmit around 100KB/s)
 * nfs write:  - "" -
 *
 * 01/15/94, Kay Roemer.
 * 09/26/95, Torsten Lang.
 */

#include <string.h>
#include "config.h"
#include "netinfo.h"
#include "kerbind.h"
#include "atarierr.h"
#include "sockios.h"
#include "sockerr.h"
#include "buf.h"
#include "if.h"
#include "ifeth.h"
#include "lancemem.h"
#include "util.h"

#define offsetof(s,e) ((long)(&((s *)0)->e))

/*
 * 200 Hz timer
 */
#define HZ200 (*(volatile long *)0x4baL)

#define MIN_LEN 60

#define TIMEOUTCNT 400 /* 2s */
#define TIMEOUT	1000 /* 5s */

static PKTBUF *pkt_ring[RECVBUFFS];

void (*ihandler) (void) = 0;
void (*lance_probe_c_) (void);
extern void lance_probe_asm(void);
static volatile int found = 0;
static int pkt_recv;
static volatile int pkt_sent = 0;
static volatile TMD *tmd_sent;

static struct netif if_lance;

static long	lance_open	(struct netif *);
static long	lance_close	(struct netif *);
static long	lance_output	(struct netif *, BUF *, char *, short, short);
static long	lance_ioctl	(struct netif *, short, long);
static long	lance_config	(struct netif *, struct ifopt *);

static long	lance_probe	(struct netif *);
static void	lance_probe_c	(void);
static long	lance_reset	(struct netif *);
static void	lance_install_ints (void);
static void	lance_int	(void);
static long	lance_pkt_init	(void);

static long
lance_open (nif)
	struct netif *nif;
{
	return (lance_reset (nif));
}

static long
lance_close (nif)
	struct netif *nif;
{
	register volatile int *rdp;

	rdp = RCP_RDP;
	*(rdp+1) = CSR0;
	*rdp = CSR0_STOP;

	ihandler = NULL;
	lance_pkt_init ();

	return 0;
}

static long
lance_reset (nif)
	struct netif *nif;
{
	register volatile LNCMEM *mem;
	register volatile TMD	*md;
	register long i;
	register volatile int *rdp;

	rdp = RCP_RDP;
	mem = (LNCMEM *) RCP_MEMBOT;
	*(rdp+1) = CSR0;
	*rdp = CSR0_STOP;

	if (!memcmp(nif->hwlocal.addr, "\377\377\377\377\377\377", ETH_ALEN))
		return -1; /* hw address not set! */

	for (i=0 ; i<ETH_ALEN ; i++)
		mem->init.haddr[i ^ 1] = nif->hwlocal.addr[i];

	mem->init.rdrp.drp_lo = (u_short)offsetof(LNCMEM,rmd[0]);
#if 0
	/*
	 * receive ring descr. pointer
	 */
	((u_long)&(PRMD[0]));
#endif
	mem->init.rdrp.drp_hi = 0;
	mem->init.rdrp.len = RECVRLEN;
	
	mem->init.tdrp.drp_lo = (u_short)offsetof(LNCMEM,tmd);
#if 0
	/*
	 * transmit ring descr. pointer
	 */
	((u_long)&PTMD);
#endif
	mem->init.tdrp.drp_hi = 0;
	mem->init.tdrp.len = XMITRLEN;

	md = &mem->tmd;
	/*
	 * don't know address of packet to send
	 */
	md->ladr = (u_short)(PPKT);
	md->tmd1 = OWN_HOST;
	/*
	 * zero pkt size
	 */
	md->tmd2 = 0;
	md->tmd3 = 0;
	for (i=0; i< RECVBUFFS; i++) {
		(RMD *)md = mem->rmd+i;
		((RMD *)md)->ladr = (u_short)(0xFFFFL & (u_long)pkt_ring[i]);
		((RMD *)md)->rmd1 = OWN_CHIP;
		/*
		 * 2's complement (maximum size)
		 */
		((RMD *)md)->rmd2 = -MAXPKTLEN;
		((RMD *)md)->mcnt = 0;
	}
	tmd_sent = &mem->tmd;
	pkt_recv = 0;

	*(rdp+1) = CSR3;
#ifndef PAMs_INTERN
	*rdp = CSR3_BSWP;
#else
	*rdp = CSR3_BSWP | CSR3_ACON;
#endif
	*(rdp+1) = CSR2;
	*rdp = 0;
	*(rdp+1) = CSR1;
	*rdp = 0;
	*(rdp+1) = CSR0;
	*rdp = CSR0_STRT | CSR0_INIT;
	i = HZ200 + TIMEOUTCNT;
	while (!(*rdp & CSR0_IDON) && HZ200 - i < 0)
		;
	if ((*rdp & (CSR0_IDON | CSR0_ERR)) ^ CSR0_IDON) {
		*rdp = CSR0_STOP;
		ihandler = NULL;
		return -1;
	}
	ihandler = lance_int;
	/*
	 * clear idon-bit
	 */
	*rdp = CSR0_IDON;
	/*
	 * enable interrupts
	 */
	*rdp = CSR0_INEA;
	/*
	 * RCP_RAP (=*rdp+1) must not be changed after initialition!
	 * Other routines assume that RCP_RAP is set to CSR0 which is
	 * in fact the only register that can be read when LANCE is running.
	 */

	return 0;
}

static long
lance_ioctl (nif, cmd, arg)
	struct netif *nif;
	short cmd;
	long arg;
{
	struct ifreq *ifr;

	switch (cmd) {
	case SIOCSIFNETMASK:
	case SIOCSIFFLAGS:
	case SIOCSIFADDR:
		return 0;

	case SIOCSIFMTU:
		/*
		 * Limit MTU to 1500 bytes. MintNet has alraedy set nif->mtu
		 * to the new value, we only limit it here.
		 */
		if (nif->mtu > ETH_MAX_DLEN)
			nif->mtu = ETH_MAX_DLEN;
		return 0;

	case SIOCSIFOPT:
		/*
		 * Interface configuration, handled by lance_config()
		 */
		ifr = (struct ifreq *)arg;
		return lance_config (nif, ifr->ifru.data);
	}
	return EINVFN;
}

/*
 * Interface configuration via SIOCSIFOPT. The ioctl is passed a
 * struct ifreq *ifr. ifr->ifru.data points to a struct ifopt, which
 * we get as the second argument here.
 *
 * If the user MUST configure some parameters before the interface
 * can run make sure that lance_open() fails unless all the necessary
 * parameters are set.
 *
 * Return values	meaning
 * EINVFN		option not supported
 * EFILNF		invalid option value
 * 0			Ok
 */
static long
lance_config (nif, ifo)
	struct netif *nif;
	struct ifopt *ifo;
{
#define STRNCMP(s)	(strncmp ((s), ifo->option, sizeof (ifo->option)))

	if (!STRNCMP ("hwaddr")) {
		unsigned char *cp;
		/*
		 * Set hardware address
		 */
		if (ifo->valtype != IFO_HWADDR)
			return EFILNF;
		memcpy (nif->hwlocal.addr, ifo->ifou.v_string, ETH_ALEN);
		cp = nif->hwlocal.addr;
		DEBUG (("LANCE: hwaddr is %x:%x:%x:%x:%x:%x",
			cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]));
	}
	else if (!STRNCMP ("braddr")) {
		unsigned char *cp;
		/*
		 * Set broadcast address
		 */
		if (ifo->valtype != IFO_HWADDR)
			return EFILNF;
		memcpy (nif->hwbrcst.addr, ifo->ifou.v_string, ETH_ALEN);
		cp = nif->hwbrcst.addr;
		DEBUG (("LANCE: braddr is %x:%x:%x:%x:%x:%x",
			cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]));
	}
	else if (!STRNCMP ("debug")) {
		/*
		 * turn debuggin on/off
		 */
		if (ifo->valtype != IFO_INT)
			return EFILNF;
		DEBUG (("LANCE: debug level is %ld", ifo->ifou.v_long));
	}
	else if (!STRNCMP ("log")) {
		/*
		 * set log file
		 */
		if (ifo->valtype != IFO_STRING)
			return EFILNF;
		DEBUG (("LANCE: log file is %s", ifo->ifou.v_string));
	}
	else {
		return EINVFN;
	}
	return 0;
}

long
driver_init (void)
{
	char *lance_type =
#ifdef PAMs_INTERN
	"PAMs EMega/VME"
#else
#ifdef MEGA_STE
	"RIEBL VME (MSTE)"
#else
#ifdef ATARI_TT
	"RIEBL VME (TT)"
#else
#ifdef SPECIAL
	"RIEBL ST (mod.)"
#else
#ifdef MEGA_ST
	"RIEBL ST"
#endif
#endif
#endif
#endif
#endif
	;

	static char message[100];

	strcpy (if_lance.name, "en");
	if_lance.unit = if_getfreeunit ("en");
	if_lance.metric = 0;
	if_lance.flags = IFF_BROADCAST;
	if_lance.mtu = 1500;
	if_lance.timer = 0;
	if_lance.hwtype = HWTYPE_ETH;
	if_lance.hwlocal.len =
	if_lance.hwbrcst.len = ETH_ALEN;

	if_lance.rcv.maxqlen = IF_MAXQ;
	if_lance.snd.maxqlen = IF_MAXQ;

	if_lance.open = lance_open;
	if_lance.close = lance_close;
	if_lance.output = lance_output;
	if_lance.ioctl = lance_ioctl;

	if_lance.timeout = 0;

	if_lance.data = 0;

	/*
	 * Tell upper layers the max. number of packets we are able to
	 * receive in fast succession.
	 */
	if_lance.maxpackets = 0;

	if (lance_probe (&if_lance) < 0) {
		sprintf (message, "LANCE: driver for %s not installed, board not found.\r\n", lance_type);
		c_conws(message);
		return 1;
	}
	lance_pkt_init ();
	lance_install_ints ();

	if_register (&if_lance);

	if (!memcmp(&if_lance.hwlocal.addr, "\377\377\377\377\377\377", ETH_ALEN))
		sprintf (message, "LANCE driver v%s (en%d) for %s (no valid hw address!)\r\n",
			"0.5á",
			if_lance.unit,
			lance_type);
	else
		sprintf (message, "LANCE driver v%s (en%d) for %s (%x:%x:%x:%x:%x:%x)\r\n",
			"0.5á",
			if_lance.unit,
			lance_type,
			if_lance.hwlocal.addr[0],
			if_lance.hwlocal.addr[1],
			if_lance.hwlocal.addr[2],
			if_lance.hwlocal.addr[3],
			if_lance.hwlocal.addr[4],
			if_lance.hwlocal.addr[5]);
	c_conws (message);
	return 0;
}

static long
lance_probe (nif)
	struct netif *nif;
{
	register volatile LNCMEM *mem;
	register long i;
	register volatile int *rdp;
	short sr;
#ifdef PAMs_INTERN
	u_short volatile *eemem;
#endif

	rdp = RCP_RDP;
	mem = (LNCMEM *)RCP_MEMBOT;
	lance_probe_c_ = lance_probe_c;

	sr = spl7 ();

	(&lance_probe_asm)();

	spl (sr);
	
	if (!found)
		return -1;

	*(rdp+1) = CSR0;
	*rdp = CSR0_STOP;
	/*
	 * init data structures
	 */
		
#ifdef PAMs_INTERN
	eemem = (u_short *)RCP_MEMBOT;
	*(volatile char *)LANCE_EEPROM;
	memcpy (nif->hwbrcst.addr, "\377\377\377\377\377\377", ETH_ALEN);
	for (i=0 ; i<ETH_ALEN ; i++)
		nif->hwlocal.addr[i] = 
			((eemem[i<<1] & 0xf) << 4) | (eemem[(i<<1)+1] & 0xf); 
	*(volatile char *)LANCE_MEM;
#endif
	mem->init.mode = 0;		/* mode word */

/* 
 * Please note: This driver awaits the board's ethernet address in the
 * init block if it is a Riebl card. The address does *NOT* conform to the
 * ANS software what is - to my mind - not a real drawback.
 * In case of old boards you have to set the hw address by hand every time
 * before *OPENING* the device, otherwise only once. This is done by IOCTLs.
 */

#ifdef RIEBL                           /* .. <- This is normally 2 for MEGAs */
                                       /* ||    and 4 for TTs(/MEGA STes?)   */
	if ((*(u_long *)mem->init.haddr & 0xFFFFF8FFL) != 0x00000036L)
		/* Set a default hw address that is known as error condition to the */
		/* caller.                                                          */
		memcpy ((u_char *)mem->init.haddr, "\377\377\377\377\377\377", ETH_ALEN);
	memcpy (nif->hwbrcst.addr, "\377\377\377\377\377\377", ETH_ALEN);
	for (i=0 ; i<ETH_ALEN ; i++)
		nif->hwlocal.addr[i ^ 1] = mem->init.haddr[i];
#endif

    	/*
    	 * logical address filter - receive *NO* logical addresses (no multicasting)
    	 */    	
	mem->init.laf[0] = 0x00000000L;
	mem->init.laf[1] = 0x00000000L;

			/*
			 * We return here. The rest of the initialization is done in lance_reset.
			 */
	return 0;
}

static void
lance_probe_c (void)
{
	register volatile LNCMEM *mem;
	register long i;
	register volatile int *rdp;

	rdp = RCP_RDP;
	mem = (LNCMEM *)RCP_MEMBOT;

#ifdef PAMs_INTERN
	*(volatile char *)LANCE_MEM;
#endif

	*(rdp+1) = 0;

	*rdp = CSR0_STOP;
	if (*rdp != CSR0_STOP)
		goto err;
	*rdp = ~(CSR0_INIT | CSR0_STRT);
	if (*rdp != CSR0_STOP)
		goto err;

	*(rdp+1) = 0xFFFF;
	if (*(rdp+1) != 3)
		goto err;

	*(rdp+1) = 1;

/* For some reason the following test does not work :-( */
#if 0
	for (i = 0; i < 0x10000L; i++) {
		*rdp = i;
		if (*rdp != i)
			break;
	}
	if (i != 0x10000L)
		goto err;
#endif
	
	for (i = 0; i < 0x10000L; i++) {
		*(u_short *)mem = (u_short)i;
		if (*(u_short *)mem != (u_short)i)
			break;
		*(u_char *)mem = (u_char)(i & 0xFF);
		if (*(u_char *)mem != (u_char)(i & 0xFF))
			break;
		*(((u_char *)mem)+1) = (u_char)(i & 0xFF);
		if (*(((u_char *)mem)+1) != (u_char)(i & 0xFF))
			break;
	}
	if (i != 0x10000L)
		goto err;

	found = 1;
err:
}

static long
lance_output (nif, buf, hwaddr, hwlen, pktype)
	struct netif *nif;
	BUF *buf;
	char *hwaddr;
	short hwlen, pktype;
{
#ifdef HACK
	short i;
#endif
	short len;
	BUF *nbuf;
	register volatile int *rdp;
	register volatile TMD *tmd;
	register long timeout;

	nbuf = eth_build_hdr (buf, nif, hwaddr, pktype);
	if (nbuf == 0) {
		++nif->out_errors;
		return ENSMEM;
	}
	if (nif->bpf)
		bpf_input (nif, nbuf);

	len = nbuf->dend - nbuf->dstart;

#ifndef HACK
	memcpy (PPKT, nbuf->dstart, len);
#else
	for (i=0 ; i<len ; i++) {
		*(((char *)PPKT)+i) = *(((char *)nbuf->dstart)+i);
		MFP_GPIP;
	}
#endif

	if (len < MIN_LEN)
		len = MIN_LEN;
#ifdef PAMs_INTERN
	len = (len+1) & ~1;
#endif

	tmd = (TMD *)tmd_sent;

#ifdef HACK
	MFP_GPIP;
#endif
	if ((tmd->tmd1 & OWN) == OWN_CHIP) {
		buf_deref (nbuf, BUF_NORMAL);
		++nif->out_errors;
		return EINTRN;
	}

	rdp = RCP_RDP;
	pkt_sent = 0;
	if (!(*rdp & CSR0_TXON)) {
		buf_deref (nbuf, BUF_NORMAL);
		++nif->out_errors;
		return EINTRN;
	}

#ifdef HACK
	MFP_GPIP;
#endif
	tmd->tmd2 = -len;
#ifdef HACK
	MFP_GPIP;
#endif
	tmd->tmd3 = 0;

#ifdef HACK
	MFP_GPIP;
#endif
	tmd->tmd1 = (STP | ENP | OWN_CHIP);	/* start/end of packet */

	*rdp = CSR0_TDMD | CSR0_INEA; /* Force immediate transmit buffer polling */

	timeout = HZ200 + TIMEOUT;
	while (!pkt_sent && !(tmd->tmd1 & ERR) &&
	       !(tmd->tmd3 & ~0x3FF) && HZ200 - timeout < 0)
#ifdef HACK
		MFP_GPIP
#endif
		;   /* wait till packet sent */

	buf_deref (nbuf, BUF_NORMAL);

#ifdef HACK
	MFP_GPIP;
#endif
	if (!pkt_sent && !(tmd->tmd1 & ERR) && !(tmd->tmd3 & ~0x3FF)) {
		++nif->out_errors;
		return ETIMEDOUT;
	}
#ifdef HACK
	MFP_GPIP;
#endif
	if ((tmd->tmd1 & ERR) || (tmd->tmd3 & ~0x3FF) || pkt_sent < 0) {
		++nif->collisions;
		return ETIMEDOUT;
	}
	return 0;	
}

/*
 * LANCE interrupt routine
 */
void
lance_int (void)
{
#ifdef HACK
	register int		j;
#endif
	register int		i;
	register u_short	csr0;
	register int		type;

	register volatile int	*rdp;
	register volatile TMD	*md;
	register          PKTBUF *pkt;

	struct netif *nif = &if_lance;
	BUF *buf;
	int pktlen;

	/*
	 * register data port
	 */
	rdp = RCP_RDP;
	csr0 = *rdp;
	if (!(csr0 & CSR0_INTR))
		return;

again:	
	if (csr0 & CSR0_IDON)
		*(rdp) = CSR0_IDON | CSR0_INEA;
	if (csr0 & CSR0_RINT) {
		*rdp = CSR0_RINT | CSR0_INEA;
		for (i=0; i<RECVBUFFS; i++ ) {
			(RMD *)md = PRMD+i;
#ifdef HACK
			MFP_GPIP;
#endif
			if ((((RMD *)md)->rmd1 & OWN) == OWN_CHIP)
				continue;
			/*
			 * packet ok ?
			 */
#ifdef HACK
			MFP_GPIP;
#endif
			if ((STP|ENP) == (((RMD *)md)->rmd1 & (ERR|STP|ENP))) {
				pkt = pkt_ring[i];
#ifdef HACK
				MFP_GPIP;
#endif
				type = pkt->et_type;

				/*
				 * packet length without checksum
				 */
#ifdef HACK
				MFP_GPIP;
#endif
				pktlen = ((RMD *)md)->mcnt - 4;
				buf = buf_alloc (pktlen+100, 50, BUF_ATOMIC);
				if (buf == 0)
					++nif->in_errors;
				else {
					buf->dend = buf->dstart + pktlen;
#ifndef HACK
					memcpy (buf->dstart, pkt, pktlen);
#else
					for (j=0 ; j<pktlen ; j++) {
						*(((char *)buf->dstart)+j) = *(((char *)pkt)+j);
						MFP_GPIP;
					}
#endif
					if (nif->bpf)
						bpf_input (nif, buf);
					if (!if_input (nif, buf, 0,
						       eth_remove_hdr (buf)))
						++nif->in_packets;
					else
						++nif->in_errors;
				}
			} else {
#ifdef HACK
				MFP_GPIP;
#endif
			  	if (((RMD *)md)->rmd1 & ERR) {
			  		++nif->in_errors;
					/* lance_reset (nif); */
					/* return; */ /* Not yet... */
				}
			}
			/*
			 * free packet
			 */
#ifdef HACK
			MFP_GPIP;
#endif
			((RMD *)md)->mcnt = 0;
			/*
			 * give packet back to lance
			 */
#ifdef HACK
			MFP_GPIP;
#endif
			((RMD *)md)->rmd1 = OWN_CHIP;
		}
	}
	if (csr0 & CSR0_TINT) {
		/*
		 * clear interrupt bit & reenable
		 */ 
		*rdp = CSR0_TINT | CSR0_INEA;
		md = (TMD *)tmd_sent;
		/*
		 * packet given back to host ?
		 */
#ifdef HACK
		MFP_GPIP;
#endif
		if ((md->tmd1 & OWN) == OWN_HOST) {
#ifdef HACK
			MFP_GPIP;
#endif
			if ((md->tmd1) & ERR) {
				pkt_sent = -1;
				++nif->out_errors;
			} else {
				pkt_sent = 1;
				++nif->out_packets;
#ifdef HACK
				MFP_GPIP;
#endif
				if (md->tmd1 & (MORE | ONE | DEF)) 
					++nif->collisions;
			}
		} else
			++nif->out_errors;
	}
	if (csr0 & CSR0_ERR) {
		++nif->in_errors;
		*rdp = CSR0_CERR | CSR0_MERR | CSR0_BABL | CSR0_MISS | CSR0_INEA;
	}
	if ((csr0 = *rdp) & CSR0_INTR)
		goto again;
}

extern void lance_hbi_int (void);
extern void lance_v5_int (void);
extern void lance_vbi_int (void);
extern long old_vbi;

/*
 * Initialize the packet ring. This function also clears the packet buffers
 * for security purposes. Since the RIEBL boards have battery back'd static
 * memory as packet buffers you may find things like passwords etc. in the
 * static memory even after turning off and on the machine.
 * This function is also called by lance_reset() and lance_close for better
 * security.
 */
static long
lance_pkt_init (void)
{
	short i, j;
	
	for (i = 0; i < RECVBUFFS; ++i) {
		pkt_ring[i] = PPKT+(i+XMITBUFFS);
		/* The following loop is just for security purposes (see lance_close). */
		for (j = 0; j < ETH_MAX_DLEN; j++)
			*(((char *)pkt_ring[i])+j) = 0;
		}
	return 0;
}

/*
 * Install interrupt handler
 */
static void
lance_install_ints (void)
{
	long old_v5 
#ifdef PAMs_INTERN
	   , old_hbl
#endif
	   ;
	short sr;

	sr = spl7 ();
	old_v5 = s_etexec ((short)LANCEIVEC, (long)lance_v5_int);
#ifdef PAMs_INTERN
	/*
         * This is a real dirty hack but seems to be necessary for the MiNT
         * environment. Even with a correct hbi handler installed I found an
         * IPL of 3 quite often. This is definitely a misbehaviour of the
         * MiNT environment since the hbi handler works under plain TOS (what
         * it is supposed to do since Atari guarantees an IPL of 0 at start
         * time of a program).
         * Since the PAMs EMEGA boards run at an unchangeable IPL of 3 it is
         * ABSOLUTELY necessary that the OS and the application programs
         * behave correct!
         * So someone with the knowledge and enough time should take a look
         * at the various MiNT sources, filesystems etc. to find the bug...
         * When these bug(s) are fixed the vbi can stay unchanged!
         */

	old_vbi = s_etexec ((short)HBI+2, (long)lance_vbi_int);

	*(char *)LANCEIVECREG = LANCEIVEC;
	old_hbl = s_etexec ((short)HBI, (long)lance_hbi_int);
#endif
#ifdef RIEBL
	*(char *)LANCEIVECREG = LANCEIVEC;
#endif
	spl (sr);
}
