/*
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "config.h"

#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "std-pci.h" /* Should go away - FIXME */
#include "glue-log.h"
#include "glue-main.h"
#include "glue-shm.h"
#include "glue-suspend.h"
#include "umutil.h"

#include "chip_lsi_53C810.h"

#define COMP "chip_lsi_53C810"
#define COMP_(x) chip_lsi_53C810_ ## x

/* Debugging stuff */
#define WARNINGS 0x0

#define SIZE_IO_BUF 4096

#if 0
#define DEBUGMASK 0x03fb /* all except SCRIPTS_ATOMIC */
#else
#define DEBUGMASK 0x0
#endif

/* Add the following values together to select which debug messages you want */
#define DEBUG_CONFSPACE		0x0001
#define DEBUG_SCRIPTS		0x0002
#define DEBUG_SCRIPTS_ATOMIC	0x0004
#define DEBUG_IO		0x0008
#define DEBUG_IRQ		0x0010
#define DEBUG_TIMER		0x0020
#define DEBUG_SCSI		0x0040
#define DEBUG_SCSIBUS		0x0080
#define DEBUG_SCSIBUS_BLOCK	0x0100
#define DEBUG_OTHER		0x0200

/*how deep can ints be stacked... */
#define INT_QUEUE_DEPTH 5

/* SCSI bus phases... */
#define SCSI_PHASE_DATA_OUT	0x00
#define SCSI_PHASE_DATA_IN	0x01
#define SCSI_PHASE_COMMAND	0x02
#define SCSI_PHASE_STATUS	0x03
#define SCSI_PHASE_MESSAGE_OUT	0x06
#define SCSI_PHASE_MESSAGE_IN	0x07
#define SCSI_PHASE_BUSFREE	0x08

/* register-space in mapped-memory */
#define SZ_53C810MEM	0x60
#define SZ_53CROMREQUEST (64*1024)
#define SZ_53CBOOTROM (64*1024)

#define REVISION	1

struct cpssp {
	/* 32-bit-wide */
	uint32_t config_space[64];

	/* boot-rom */
	uint8_t bootrom[SZ_53CBOOTROM];

	/* Signals */
	struct sig_pci_bus *port_pci_bus;
	struct sig_scsi_bus *port_scsi_bus;
	struct sig_std_logic *port_intA;
	unsigned int state_power;
	struct sig_i2c_bus *port_i2c_bus;
	unsigned int state_i2c_data;
	unsigned int state_i2c_clk;

	/* State */
	uint32_t inst_cnt;
	uint32_t inst_limit;
	uint8_t SCSI_phase;
	uint8_t cardregs[96];
	uint8_t scsi_io_buf[SIZE_IO_BUF + 1];
	uint8_t int_queue_sist0[INT_QUEUE_DEPTH];
	uint8_t int_queue_sist1[INT_QUEUE_DEPTH];
	uint8_t int_queue_dstat[INT_QUEUE_DEPTH];
	uint8_t int_sist0_depth;
	uint8_t int_sist1_depth;
	uint8_t int_dstat_depth;
	bool scripts_running;
	bool ALU_CARRYBIT;
	bool delayed_transfer;

	/* the controller has started a transaction as an initiator */
	bool SCSI_initiated;

	/* the SCSI-status of the controller */
	bool SCSI_selected;
	bool SCSI_reselected;

	/*In case the target wnaat to transfer data: */
	unsigned long SCSI_data_to_target;
	unsigned long SCSI_data_from_target;

	/* Timer-clocks */
	unsigned long long timertick_h2h;
	unsigned long long timertick_sel;
	unsigned long long timertick_gen;

	/* Timer-settings */
	unsigned long long timerset_h2h;
	unsigned long long timerset_sel;
	unsigned long long timerset_gen;

	/* CPU is simulated by process. */
	struct process process;
};


/* happy little makros for peaceful castings */
#define REG32(reg)	(*(uint32_t *) &cpssp->cardregs[reg])
#define REG8(reg)	(*(uint8_t *) &cpssp->cardregs[reg])

#define LSI53C810MEMADDR(cpssp) \
	((uint32_t)(cpssp->config_space[PCI_BASE_ADDRESS_1>>2] \
	 & PCI_BASE_ADDRESS_MEM_MASK))

#define LSI53C810IOADDR(cpssp) \
	((uint16_t)((cpssp->config_space[PCI_BASE_ADDRESS_0>>2] \
	 & PCI_BASE_ADDRESS_IO_MASK)&0x0000ffff))


#define LSI53C810ROMADDR(cpssp) \
	((uint32_t)(cpssp->config_space[PCI_ROM_ADDRESS>>2] \
	& PCI_ROM_ADDRESS_MASK))

/* Debugging */
#if (DEBUGMASK > 0)
static unsigned long long old_time;
#define TRACE(lvl, arg...) \
	if ((lvl & DEBUGMASK)) { \
		unsigned long long new_time; \
		\
		new_time = time_virt(); \
		fprintf(stderr, "%15llu: ", new_time - old_time); \
		fprintf(stderr, arg); \
		old_time = new_time; \
	}
#else
#define TRACE(lvl, arg...) while(0) { } /* ; */
#endif /* DEBUGMASK > 0 */

/* Update-Interval of timer */
#define TIMERTICKS (TIME_HZ / 100) /* 10ms */

/* astimated time of a single SCRIPTS-Instruction */
#define SCRIPTS_TIMERTICKS (TIME_HZ / 200000) /* 5us */

/* timer interval for requeueing SCRIPS */
#define TSYNC_INTERVAL_SCRIPTS  (TIME_HZ / 20000) /* 50us*/

/* timer for reselect-wait */
/* This is the interval the controller
 * gets called in idle state to check for reselection */
#define TSYNC_INTERVAL_RESELECT  (TIME_HZ / 200) /*5ms*/

static const char * const reg_names[] = {
	"SCNTL0", "SCNTL1", "SCNTL2", "SCNTL3",
	"SCID", "SXFER", "SDID", "GPREG",
	"SFBR", "SOC", "SSID", "SBCL",
	"DSTAT", "SSTAT0", "SSTAT1", "SSTAT2",
	"DSA[0]", "DSA[1]", "DSA[2]", "DSA[3]",
	"ISTAT", "RESERVED", "RESERVED", "RESERVED",
	"RESERVED", "CTEST1", "CTEST2", "CTEST3",
	"TEMP[0]", "TEMP[1]", "TEMP[2]", "TEMP[3]",
	"DFIFO", "CTEST4", "CTEST5", "CTEST6",
	"DBC[0]", "DBC[1]", "DBC[2]", "DCMD",
	"DNAD[0]", "DNAD[1]", "DNAD[2]", "DNAD[3]",
	"DSP[0]", "DSP[1]", "DSP[2]", "DSP[3]",
	"DSPS[0]", "DSPS[1]", "DSPS[2]", "DSPS[3]",
	"SCRATCH_A[0]", "SCRATCH_A[1]", "SCRATCH_A[2]", "SCRATCH_A[3]",
	"DMODE", "DIEN", "SBR", "DCNTL",
	"ADDER[0]", "ADDER[1]", "ADDER[2]", "ADDER[3]",
	"SIEN0", "SIEN1", "SIST0", "SIST1",
	"SLPAR", "RESERVED", "MACNTL", "GPCNTL",
	"STIME0", "STIME1", "RESPID", "RESERVED",
	"STEST0", "STEST1", "STEST2", "STEST3",
	"SIDL", "RESERVED", "RESERVED", "RESERVED",
	"SODL", "RESERVED", "RESERVED", "RESERVED",
	"SBDL", "RESERVED", "RESERVED", "RESERVED",
	"SCRATCH_B[0]", "SCRATCH_B[1]", "SCRATCH_B[2]", "SCRATCH_B[3]"
};

/* some important bits... */
#define ISTAT_DIP	(1 << 0)
#define ISTAT_SIP	(1 << 1)
#define ISTAT_INTF	(1 << 2)
#define ISTAT_CON	(1 << 3)
#define ISTAT_SEM	(1 << 4)
#define ISTAT_SIGP	(1 << 5)
#define ISTAT_SRST	(1 << 6)
#define ISTAT_ABRT	(1 << 7)

/*Card registers (offsets)*/
#define REG_SCNTL0 0x00
#define REG_SCNTL1 0x01
#define REG_SCNTL2 0x02
#define REG_SCNTL3 0x03

#define REG_SCID 0x04
#define REG_SXFER 0x05
#define REG_SDID 0x06
#define REG_GPREG 0x07

#define REG_SFBR 0x08
#define REG_SOCL 0x09
#define REG_SSID 0x0a
#define REG_SBCL 0x0b

#define REG_DSTAT 0x0c
#define REG_SSTAT0 0x0d
#define REG_SSTAT1 0x0e
#define REG_SSTAT2 0x0f

#define REG_DSA 0x10

#define REG_ISTAT 0x14
#define REG_RESERVED_1 0x15
#define REG_RESERVED_2 0x16
#define REG_RESERVED_3 0x17

#define REG_RESERVED_4 0x18
#define REG_CTEST1 0x19
#define REG_CTEST2 0x1a
#define REG_CTEST3 0x1b

#define REG_TEMP 0x1c

#define REG_DFIFO 0x20
#define REG_CTEST4 0x21
#define REG_CTEST5 0x22
#define REG_CTEST6 0x23

#define REG_DBC 0x24
#define REG_DCMD 0x27

#define REG_DNAD 0x28

#define REG_DSP 0x2c

#define REG_DSPS 0x30

#define REG_SCRATCH_A 0x34

#define REG_DMODE 0x38
#define REG_DIEN 0x39
#define REG_SBR 0x3a
#define REG_DCNTL 0x3b

#define REG_ADDER 0x3c

#define REG_SIEN0 0x40
#define REG_SIEN1 0x41
#define REG_SIST0 0x42
#define REG_SIST1 0x43

#define REG_SLPAR 0x44
#define REG_RESERVED_5 0x45
#define REG_MACNTL 0x46
#define REG_GPCNTL 0x47

#define REG_STIME0 0x48
#define REG_STIME1 0x49
#define REG_RESPID 0x4a
#define REG_RESERVED_6 0x4b

#define REG_STEST0 0x4c
#define REG_STEST1 0x4d
#define REG_STEST2 0x4e
#define REG_STEST3 0x4f

#define REG_SIDL 0x50
#define REG_RESERVED_7 0x51
#define REG_RESERVED_8 0x52
#define REG_RESERVED_9 0x53

#define REG_SODL 0x54
#define REG_RESERVED_10 0x55
#define REG_RESERVED_11 0x56
#define REG_RESERVED_12 0x57

#define REG_SBDL 0x58
#define REG_RESERVED_13 0x59
#define REG_RESERVED_14 0x5a
#define REG_RESERVED_15 0x5b

#define REG_SCRATCH_B 0x5c


/* forward declaration... */
static void
COMP_(readb)(struct cpssp *cpssp, uint16_t port, uint8_t *valp);
static void
COMP_(writeb)(struct cpssp *cpssp, uint16_t port, uint8_t val);

static int32_t
int24(int32_t x)
{
	return (x << 8) >> 8;
}

static void
COMP_(i2c_update)(struct cpssp *cpssp)
{
	if ((cpssp->cardregs[REG_GPCNTL] >> 0) & 1) {
		/* Port is input. */
		sig_i2c_bus_set_data(cpssp->port_i2c_bus, cpssp, 1);
	} else {
		/* Port is output. */
		sig_i2c_bus_set_data(cpssp->port_i2c_bus, cpssp,
				(cpssp->cardregs[REG_GPREG] >> 0) & 1);
	}
	if ((cpssp->cardregs[REG_GPCNTL] >> 1) & 1) {
		/* Port is input. */
		sig_i2c_bus_set_clk(cpssp->port_i2c_bus, cpssp, 1);
	} else {
		/* Port is output. */
		sig_i2c_bus_set_clk(cpssp->port_i2c_bus, cpssp,
				(cpssp->cardregs[REG_GPREG] >> 1) & 1);
	}
}

/*
 * Asserts IRQ-Line depending on DCNTL and the bits in
 * the interrupt-mask-registers.
 * Called for example after int_add and int_del.
 */
static void
COMP_(int_trigger)(struct cpssp *cpssp)
{
	/* Remember: All interrupts indicated in the ISTAT-register
	 * are fatal anyway!
	 * We are NOT dealing with non-fatal ints here any more!
	 * Non-fatal ints are _always_ masked ints, but not every masked
	 * int will be non-fatal :) (but int_add handles this)
	 *
	 * Onliest Exception: Interrupt-on-the-fly in SCRIPTS is
	 * always non-fatal (but cannot be stacked or masked, so this
	 * is not a big problem...) */


	/* easy one:
	 * if DCNTL blocks the INT-line, then there will never be an Interrupt...*/
	if (cpssp->cardregs[REG_DCNTL] & 0x02) {
		TRACE(DEBUG_IRQ, "DCNTL BLOCKED! NO INTERRUPT WILL BE TRIGGERED!\n");
		sig_std_logic_or_set(cpssp->port_intA, cpssp, 0);
		return;
	}

	/* This is also easy and the standard-case:
	 * No (more) interrupt is pending, so there is no need for int (any more)*/
	if ((cpssp->cardregs[REG_ISTAT] & (ISTAT_DIP | ISTAT_SIP | ISTAT_INTF)) == 0){
		TRACE(DEBUG_IRQ, "ALL INT-conditions cleared! Deasserting INT-line!\n");
		sig_std_logic_or_set(cpssp->port_intA, cpssp, 0);
		return;
	}

	/* Was there an Interrupt-on-the-fly?
	 * if so, INT has to be asserted in any case*/
	if (cpssp->cardregs[REG_ISTAT] & ISTAT_INTF){
		TRACE(DEBUG_IRQ, "Asserting INT-line: INTFLY\n");
		sig_std_logic_or_set(cpssp->port_intA, cpssp, 1);
		/*reset STD-bit */
		cpssp->cardregs[REG_DCNTL]&=~0x04;
		return;
	}

	/* Was there at least a DMA type interrupt? */
	if (cpssp->cardregs[REG_ISTAT] & ISTAT_DIP) {
		TRACE(DEBUG_IRQ, "DMA-type INT: %x\n", cpssp->cardregs[REG_DSTAT]);
#if 0
			&&
		(cpssp->cardregs[REG_DSTAT] & cpssp->cardregs[REG_DIEN])){
#endif
		/* not masked? */
		if (cpssp->cardregs[REG_DSTAT] & cpssp->cardregs[REG_DIEN]) {
			TRACE(DEBUG_IRQ, "Asserting INT-line: DMA-type INT: %x\n",
				cpssp->cardregs[REG_DSTAT]);
			sig_std_logic_or_set(cpssp->port_intA, cpssp, 1);
			return;
		}  else {
			TRACE(DEBUG_IRQ, "MASKED, not asserting int: %x\n",
				cpssp->cardregs[REG_DSTAT]);

		}
		/* reset STD-bit */
		cpssp->cardregs[REG_DCNTL] &= ~0x04;
	}

	/* Ok, now was there a SCSI-type interrupt? */
	if (cpssp->cardregs[REG_ISTAT] & ISTAT_SIP){
		/* And again: was it masked or not? */
		if (cpssp->cardregs[REG_SIST0] & cpssp->cardregs[REG_SIEN0]) {
			TRACE(DEBUG_IRQ, "Asserting INT-line: SCSI-type(0) INT: %x\n",
					cpssp->cardregs[REG_SIST0]);
			sig_std_logic_or_set(cpssp->port_intA, cpssp, 1);
			/*reset STD-bit */
			cpssp->cardregs[REG_DCNTL]&=~0x04;
			return;
		} else {
			TRACE(DEBUG_IRQ, "MASKED in SIST0, not asserting int: %x\n",
				cpssp->cardregs[REG_DSTAT]);
		}

		/* the same for SIST1: */
		if (cpssp->cardregs[REG_SIST1] & cpssp->cardregs[REG_SIEN1]) {
			TRACE(DEBUG_IRQ, "Asserting INT-line: SCSI-type(1) INT: %x\n",
					cpssp->cardregs[REG_SIST1]);
			sig_std_logic_or_set(cpssp->port_intA, cpssp, 1);
			/*reset STD-bit */
			cpssp->cardregs[REG_DCNTL]&=~0x04;
			return;
		} else {
			TRACE(DEBUG_IRQ, "MASKED in SIST1, not asserting int: %x\n",
				cpssp->cardregs[REG_DSTAT]);
		}
	}

	TRACE(DEBUG_IRQ, "EVERY POTENTIAL INTERRUPT WAS MASKED!\n");
}

/*
 * int_add:
 * Enqueues (stackable) interrupt-requests in the corresponding
 * register-queues:
 * Checks masking-bits in SIEN0, SIEN1, DIEN:
 * Note: Queueing only for non-masked and masked/fatal ints!
 */
static void
COMP_(int_add)(struct cpssp *cpssp, uint8_t reg, uint8_t bits)
{
	switch (reg){
	case REG_ISTAT:
		assert(bits == ISTAT_INTF);

		TRACE(DEBUG_IRQ, "Setting ISTAT-int-on-fly: %x\n", bits);

		cpssp->cardregs[REG_ISTAT] |= ISTAT_INTF;
		break;

	case REG_DSTAT:
		/*
		 * DMA-type interrupts are simpler:
		 * They are considered fatal in every case!
		 */
		/* Queue sanity check */
		assert(cpssp->int_dstat_depth < INT_QUEUE_DEPTH);

		/*
		 * Is there already a pending DMA-type-interrupt (DIP-bit=1)?
		 */
		if (cpssp->cardregs[REG_ISTAT] & ISTAT_DIP) {
			/* We have to queue this one for now... */
			TRACE(DEBUG_IRQ, "Queueing DSTAT-condition, Istat = %x\n",
					cpssp->cardregs[REG_ISTAT]);

			cpssp->int_queue_dstat[cpssp->int_dstat_depth] = bits;
			cpssp->int_dstat_depth++;

		} else {
			/*
			 * ...otherwise we just set the corresponding bits
			 * in DSTAT and indicate our INT-condition in
			 * ISTAT by setting the DIP-bit to "1"
			 */
			TRACE(DEBUG_IRQ, "Setting DSTAT-int: %x\n", bits);
			cpssp->cardregs[REG_DSTAT] |= bits;
			cpssp->cardregs[REG_ISTAT] |= ISTAT_DIP;
		}

		/* These ints are always fatal -> stop SCRIPTS. */
		cpssp->scripts_running = false;
		break;

	case REG_SIST0:
		/* Queue sanity check */
		assert(cpssp->int_sist0_depth < INT_QUEUE_DEPTH);

		/*
		 * TODO: catch masked non-fatal ints here: these will
		 * set bits directly in SIST0, but will not trigger
		 * further actions and will not stop SCRIPTS!
		 * (--> not setting the SIP-bit in ISTAT)
		 */

		/*
		 * Is there already a pending SCSI-type-interrupt (SIP-bit=1)?
		 */
		if (cpssp->cardregs[REG_ISTAT] & ISTAT_SIP) {
			/* We have to queue this one for now... */
			TRACE(DEBUG_IRQ, "Queueing SIST0-condition %x\n", bits);
			cpssp->int_queue_sist0[cpssp->int_sist0_depth] = bits;
			cpssp->int_sist0_depth++;

		} else {
			/*
			 * ...otherwise just set the corresponding bits
			 * in SIST0 and indicate our INT-condition in
			 * ISTAT (SIP-bit)!
			 */
			TRACE(DEBUG_IRQ, "Setting SIST0-int: %x\n", bits);
			cpssp->cardregs[REG_SIST0] |= bits;
			cpssp->cardregs[REG_ISTAT] |= ISTAT_SIP;
		}

		/*
		 * INTs that are not filtered out above (masked & non-fatal)
		 * are consifered as fatal -> stop SCRIPTS.
		 */
		cpssp->scripts_running = false;
		break;

	case REG_SIST1:
		/* Queue sanity check */
		assert(cpssp->int_sist1_depth < INT_QUEUE_DEPTH);

		/*
		 * TODO: catch masked non-fatal ints here: these will
		 * set bits directly in SIST1, but will not trigger
		 * further actions and will not stop SCRIPTS!
		 */

		/*
		 * Is there already a pending SCSI-type-interrupt (SIP-bit=1)?
		 */
		if (cpssp->cardregs[REG_ISTAT] & ISTAT_SIP) {
			/* We have to queue this one for now... */
			TRACE(DEBUG_IRQ, "Queueing SIST1-condition %x\n", bits);
			cpssp->int_queue_sist1[cpssp->int_sist1_depth] = bits;
			cpssp->int_sist1_depth++;

		} else {
			/*
			 * ...otherwise just set the corresponding bits
			 * in SIST1 and indicate our INT-condition in
			 * ISTAT (SIP-bit)!
			 */
			TRACE(DEBUG_IRQ, "Setting SIST1-int: %x\n", bits);
			cpssp->cardregs[REG_SIST1] |= bits;
			cpssp->cardregs[REG_ISTAT] |= ISTAT_SIP;
		}

		/*
		 * INTs that are not filtered out above (masked & non-fatal)
		 * are consifered as fatal -> stop SCRIPTS.
		 */
		cpssp->scripts_running = false;
		break;
	}

	/* This will take care of (re,de)-asserting the irq-line properly. */
	COMP_(int_trigger)(cpssp);
}

/*
 * int_del:
 * Dequeues an interrupt-request from the corresponding register-queues:
 * Called for example after reading-out corresponding int-status-regs.
 * (IRQ-clearing).
 */
static void
COMP_(int_del)(struct cpssp *cpssp, uint8_t reg)
{
	unsigned int i;

	switch (reg) {
	case REG_ISTAT:
		/*
		 * Clearing Int-On-Fly Bit
		 */
		TRACE(DEBUG_IRQ, "Clearing ISTAT-int-on-fly\n");
		cpssp->cardregs[REG_ISTAT] &= ~ISTAT_INTF;
		break;

	case REG_DSTAT:
		/*
		 * Dequeue one DMA Int
		 */
		if (cpssp->int_dstat_depth == 0) {
			TRACE(DEBUG_IRQ, "ALL DSTAT-int-conds cleared!\n");

			cpssp->cardregs[REG_DSTAT] = 0;
			cpssp->cardregs[REG_ISTAT] &= ~ISTAT_DIP;

			TRACE(DEBUG_IRQ, "ISTAT = %x\n", cpssp->cardregs[REG_ISTAT]);

		} else {
			TRACE(DEBUG_IRQ, "Dequeing DSTAT-int %x\n", cpssp->int_queue_dstat[0]);

			cpssp->cardregs[REG_DSTAT] = cpssp->int_queue_dstat[0];
			for (i = 1; i < cpssp->int_dstat_depth; i++) {
				cpssp->int_queue_dstat[i - 1] = cpssp->int_queue_dstat[i];
			}
			cpssp->int_dstat_depth--;
		}
		break;

	case REG_SIST0:
		/*
		 * Dequeue SCSI Int
		 */
		if (cpssp->int_sist0_depth == 0) {
			TRACE(DEBUG_IRQ, "ALL SIST0-int-conds cleared!\n");
			
			cpssp->cardregs[REG_SIST0] = 0;
			/* If SIST1 is also clear, we can clear SIP in ISTAT! */
			if (cpssp->cardregs[REG_SIST1] == 0) {
				cpssp->cardregs[REG_ISTAT] &= ~ISTAT_SIP;
			}

		} else {
			TRACE(DEBUG_IRQ, "Dequeing SIST0-int %x\n",
					cpssp->int_queue_sist0[0]);

			cpssp->cardregs[REG_SIST0] = cpssp->int_queue_sist0[0];
			for (i = 1; i < cpssp->int_sist0_depth; i++) {
				cpssp->int_queue_sist0[i - 1] = cpssp->int_queue_sist0[i];
			}
			cpssp->int_sist0_depth--;
		}
		break;

	case REG_SIST1:
		/*
		 * Dequeue SCSI Int
		 */
		if (cpssp->int_sist1_depth == 0) {
			TRACE(DEBUG_IRQ, "ALL SIST1-int-conds cleared!\n");

			cpssp->cardregs[REG_SIST1] = 0;
			/* If SIST0 is also clear, we can clear SIP in ISTAT! */
			if (cpssp->cardregs[REG_SIST0] == 0) {
				cpssp->cardregs[REG_ISTAT] &= ~ISTAT_SIP;
			}

		} else {
			TRACE(DEBUG_IRQ, "Dequeing SIST1-int %x\n",
					cpssp->int_queue_sist1[0]);

			cpssp->cardregs[REG_SIST1] = cpssp->int_queue_sist1[0];
			for (i = 1; i < cpssp->int_sist1_depth; i++) {
				cpssp->int_queue_sist1[i - 1] = cpssp->int_queue_sist1[i];
			}
			cpssp->int_sist1_depth--;
		}
		break;
	}

	/* This will take care of (re,de)-asserting the irq-line properly. */
	COMP_(int_trigger)(cpssp);
}

/*
 * Move memory from any to any address
 * (including memory-mapped-register-ranges).
 */
static void
COMP_(mmove)(
	struct cpssp *cpssp,  
	unsigned long from, 
	unsigned long to, 
	uint32_t count
)
{
	uint32_t i;
	uint8_t val;
	uint32_t val32;

	/* FIX ME: I dont know why the hell the driver is using
	 * relative register-addresses?!? */
	if (from <= SZ_53C810MEM) {
		from += LSI53C810MEMADDR(cpssp);
	}

	if (to <= SZ_53C810MEM) { 
		to += LSI53C810MEMADDR(cpssp); 
	}

	/* FIX ME: use something faster than bytewise copying */
	/* FIX ME: use chip read/write for register-access... */
	for (i = 0; i < count; i++) {
		/* Getting source-byte */
		if (LSI53C810MEMADDR(cpssp) <= from + i
		 && from + i <= (LSI53C810MEMADDR(cpssp) + SZ_53C810MEM)) {
			TRACE(DEBUG_SCRIPTS, 
					"warning: direct register read: %lx\n",
					from + i - LSI53C810MEMADDR(cpssp));
			val = REG8(
					from + i - LSI53C810MEMADDR(cpssp));
		} else {
			sig_pci_bus_mr(cpssp->port_pci_bus, cpssp,
				(from + i) & ~3, 1 << ((from + i) & 3),
				&val32);
			val = val32 >> ((from + i) & 3) * 8;
		}
		TRACE(DEBUG_SCRIPTS_ATOMIC, 
				"MMOVE-DMA: read 1 bytes (%0x) from %0lx\n",
				val, from + i);
		/* writing target-byte */
		if (LSI53C810MEMADDR(cpssp) <= to + i
		 && to + i <= (LSI53C810MEMADDR(cpssp) + SZ_53C810MEM)) {
			TRACE(DEBUG_SCRIPTS,
					"warning: direct register write: %lx\n",
					to + i - LSI53C810MEMADDR(cpssp));
			REG8(to + i - LSI53C810MEMADDR(cpssp)) = val;
		} else {
			val32 = val << ((to + i) & 3) * 8;
			sig_pci_bus_mw(cpssp->port_pci_bus, cpssp,
				(to + i) & ~3, 1 << ((to + i) & 3),
				val32);
		}
		TRACE(DEBUG_SCRIPTS_ATOMIC, 
				"MMOVE-DMA: wrote 1 bytes (%0x) to %0lx\n",
				val, to + i);
	}
}

/*
 * This will take care of sending some data
 * in any data-transfer-phase from the target.
 */
static uint32_t
COMP_(data_from_target)(
	struct cpssp *cpssp,
	unsigned int phase,
	uint32_t to_pci,
	unsigned long lsi_count
)
{
	unsigned long i;

	i = 0;

	for (;;) {
		unsigned long m;
		unsigned long count;
		uint32_t buf_word;

		if (! cpssp->SCSI_data_from_target
		 && lsi_count == i) {
			/* Transfer finished. */
			break;

		} else if (! cpssp->SCSI_data_from_target) {
			/* Target stops transfer. */
			cpssp->delayed_transfer = true;
			sched_sleep();
			cpssp->delayed_transfer = false;
			continue;

		} else if (lsi_count == i) {
			/* Controller stops transfer. */
			break;
		}

		if (cpssp->SCSI_phase != phase) {
			/* Phase Mismatch Interrupt */
			COMP_(int_add)(cpssp, REG_SIST0, 0x80);
			break;
		}

		count = SIZE_IO_BUF;
		if (cpssp->SCSI_data_from_target < count) {
			count = cpssp->SCSI_data_from_target;
		}
		if (lsi_count - i < count) {
			count = lsi_count - i;
		}

		/* Get byte(s) from SCSI-bus... */
		sig_scsi_bus_recv(cpssp->port_scsi_bus, cpssp,
				cpssp->scsi_io_buf, count);

		/* Copy the first byte received into the SFBR register. */
		if (i == 0) {
			REG8(REG_SFBR) = cpssp->scsi_io_buf[0];
		}

		/* ...and write it to memory. */
		for (m = 0; m < count; m++) {
			buf_word = cpssp->scsi_io_buf[m] << ((to_pci + m + i) & 3) * 8;
			sig_pci_bus_mw(cpssp->port_pci_bus, cpssp,
					(to_pci + m + i) & ~3, 1 << ((to_pci + m + i) & 3),
					buf_word);
#if 0
			TRACE(DEBUG_SCSIBUS, "WRITE TO PCI-BUS, BS: %0x, MEM: %0lx \n",
				1 << ((to_pci + m + i) & 3), (to_pci + m + i) & ~3);
#endif

		}

		i += count;
		cpssp->SCSI_data_from_target -= count;
	}

	return i;
}


/* This will take care of sending some data
 * in any data-transfer-phase to the target */
static uint32_t
COMP_(data_to_target)(
	struct cpssp *cpssp,
	unsigned int phase,
	uint32_t from_pci,
	unsigned long lsi_count
)
{
	unsigned long i;

	i = 0;

	/* Only relevant in MSG-OUT-PHASE:
	 * ATN-line-triggering */
	/* SCSI-Specs say:
	 * "The initiator shall keep the ATN signal asserted
	 * if more then one byte is to be transferred" */
	if (cpssp->SCSI_phase == SCSI_PHASE_MESSAGE_OUT) {
		if (lsi_count > 1) {
			TRACE(DEBUG_SCSIBUS_BLOCK, "RISING ATN-LINE\n");
			sig_scsi_bus_atn_set(cpssp->port_scsi_bus, cpssp, 1);
		} else {
			TRACE(DEBUG_SCSIBUS_BLOCK, "NO NEED TO RISE ATN-LINE\n");
			sig_scsi_bus_atn_set(cpssp->port_scsi_bus, cpssp, 0);
		}
	}

	for (;;) {
		unsigned long m;
		unsigned long count;
		uint32_t buf_word;

		if (! cpssp->SCSI_data_to_target
		 && lsi_count == i) {
			/* Transfer finished. */
			cpssp->SCSI_data_to_target = 0;
			break;

		} else if (! cpssp->SCSI_data_to_target) {
			/* Target stops transfer. */
			cpssp->delayed_transfer = true;
			sched_sleep();
			cpssp->delayed_transfer = false;
			continue;

		} else if (lsi_count == i) {
			/* Controller stops transfer. */
			break;
		}

		if (cpssp->SCSI_phase != phase) {
			/* Phase Mismatch Interrupt */
			COMP_(int_add)(cpssp, REG_SIST0, 0x80);
			break;
		}

		/* Only in MSG-OUT-phase:
		 * before transferring the last byte,
		 * disable ATN-Line */
		/* (again according to SCSI specs) */
		if (cpssp->SCSI_phase == SCSI_PHASE_MESSAGE_OUT) {
			if (i == (lsi_count - 1)){
				TRACE(DEBUG_SCSIBUS_BLOCK, "SETTING ATN-LINE LOW (transferring last MSG-OUT-BYTE)\n");
				sig_scsi_bus_atn_set(cpssp->port_scsi_bus, cpssp, 0);
			}
		}

		count = SIZE_IO_BUF;
		if (cpssp->SCSI_data_to_target < count) {
			count = cpssp->SCSI_data_to_target;
		}
		if (lsi_count - i < count) {
			count = lsi_count - i;
		}

		/* Get it from memory. */
		for (m = 0; m < count; m++) {
			sig_pci_bus_mr(cpssp->port_pci_bus,
					cpssp,
					(from_pci + m + i) & ~3,
					1 << ((from_pci + m + i) & 3),
					&buf_word);

			/* write byte to buffer */
			cpssp->scsi_io_buf[m] = buf_word >> ((from_pci + m + i) & 3) * 8;
		}

		/* Send buffer to scsibus. */
		sig_scsi_bus_send(cpssp->port_scsi_bus, cpssp,
				cpssp->scsi_io_buf, count);

		i += count;
		cpssp->SCSI_data_to_target -= count;
	}

	return i;
}

/*
 * Checks whether there is a need for triggering a timeout-condition.
 */
static void
COMP_(timeoutcheck)(struct cpssp *cpssp)
{
	/* H2H-timer timeout? */
	if (cpssp->timerset_h2h
	 && cpssp->timerset_h2h <= cpssp->timertick_h2h) {
		cpssp->timerset_h2h = 0;
		cpssp->timertick_h2h = 0;
		TRACE(DEBUG_SCRIPTS, "H2H timer timeout!\n");
		/* signal (potential) interrupt: */
		COMP_(int_add)(cpssp, REG_SIST1, 0x01);
	}

	/* gen-timer timeout? */
	if (cpssp->timerset_gen
	 && cpssp->timerset_gen <= cpssp->timertick_gen) {
		cpssp->timerset_gen = 0;
		cpssp->timertick_gen = 0;
		TRACE(DEBUG_SCRIPTS, "General purpose timer timeout!\n");
		/* signal (potential) interrupt: */
		COMP_(int_add)(cpssp, REG_SIST1, 0x02);
	}

	/* Selection-timer timeout? */
	if (cpssp->timerset_sel
	 && cpssp->timerset_sel <= cpssp->timertick_sel) {
		cpssp->timerset_sel = 0;
		cpssp->timertick_sel = 0;
		TRACE(DEBUG_SCRIPTS, "Selection timer timeout!\n");
		/* signal (potential) interrupt: */
		COMP_(int_add)(cpssp, REG_SIST1, 0x04);

	}
}

/*
 * Increases the chip_internal ticks for the timer-clocks.
 */
static void
COMP_(update_timerticks)(struct cpssp *cpssp, unsigned long long ticks)
{
	/* H2H Timer set? */
	if (cpssp->timerset_h2h) {
		cpssp->timertick_h2h += ticks;
		TRACE(DEBUG_TIMER, "H2H timer tick added: %0llx\n", ticks);
		assert(0);
	}

	/* General purpose timer set? */
	if (cpssp->timerset_gen) {
		cpssp->timertick_gen += ticks;
		TRACE(DEBUG_TIMER, "General purpose timer tick added: %0llx\n",
				ticks);
	}

	/* Selection Timer set? */
	if (cpssp->timerset_sel) {
		cpssp->timertick_sel += ticks;
		TRACE(DEBUG_TIMER, "Selection timer tick added: %llu (%llu)\n",
			ticks, cpssp->timertick_sel);
	}
}

/*
 * Updates the time periodically.
 */
static void
COMP_(timer_tick)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	COMP_(timeoutcheck)(cpssp);

	/*
	 * If there is a minimum of one timer active, reschedule timer!
	 */
	if (cpssp->timerset_sel
	 || cpssp->timerset_h2h
	 || cpssp->timerset_gen) {
		COMP_(update_timerticks)(cpssp, TIMERTICKS);
		time_call_at(time_virt() + TIMERTICKS,
				COMP_(timer_tick), cpssp);
	}
}

/*
 * Calculates the timer-interval in nano-seconds,
 * based on the given bit-parameter.
 */
static long long
COMP_(calc_timer_nano)(uint8_t factor)
{
	const unsigned long long base = TIME_HZ / 8000; /* 125us */

	TRACE(DEBUG_TIMER, "Calculated Timer Interval based on %0x: %0llx\n",
			factor, base << (factor - 1));

	return base << (factor - 1);
}

static void __attribute__((__noreturn__))
COMP_(step)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t iodata_addr;
	uint8_t cmpdata;
	uint8_t phase;
	uint8_t options;
	uint8_t opcode;
	uint8_t mask;
	uint32_t data32;
	uint8_t iodata_config;
	uint8_t iodata_id;
	uint8_t iodata_period;
	uint8_t iodata_null;
	uint8_t reg_lst;
	uint32_t count;
	uint32_t subcount;
	uint32_t i;
	uint32_t addr;
	uint32_t reg_temp;
	uint32_t sel_stat;
	bool jumpflag;
	bool phaseflag;
	bool cond;

again:	;
	while (cpssp->process.inst_cnt < cpssp->process.inst_limit) {
		if (! cpssp->scripts_running) {
			cpssp->process.inst_cnt = cpssp->process.inst_limit;
			break;
		}

		/* For now, we assume that every SCRIPTS instruction 
		 * has a constant execution time...*/
		COMP_(timeoutcheck)(cpssp);
		if (! cpssp->scripts_running) {
			cpssp->process.inst_cnt = cpssp->process.inst_limit;
			break;
		}

		/* dont forget... */
		cpssp->process.inst_cnt++;

		COMP_(update_timerticks)(cpssp, SCRIPTS_TIMERTICKS);

		/*
		 * Fetch instruction from memory:
		 * program-counter is always in DSP,
		 * first 32 bit of command in DBC/DCMD!
		 */
		sig_pci_bus_mr(cpssp->port_pci_bus, cpssp,
				REG32(REG_DSP), 0xf, &REG32(REG_DBC));
		REG32(REG_DSP) += 4;

		/* The second parameter is always in REG_DSPS. */
		sig_pci_bus_mr(cpssp->port_pci_bus, cpssp,
				REG32(REG_DSP), 0xf, &REG32(REG_DSPS));
		REG32(REG_DSP) += 4;

		TRACE(DEBUG_SCRIPTS,
				"SCRIPTS INSTRUCTION (%0x: %08x %08x)\n",
				REG32(REG_DSP) - 8,
				REG32(REG_DBC), REG32(REG_DSPS));

		/* DSPS is copied to DNAD
		 * at the start of every SCRIPTS operation... */
		REG32(REG_DNAD) = REG32(REG_DSPS);

		switch (REG8(REG_DCMD)){
		case 0x00 ... 0x3f:
			/* MOVE: 3-42 */
			/* check for addressing-mode */
			if (cpssp->cardregs[REG_DCMD] & 0x10) {
				/* Table-indirect */
				iodata_addr = REG32(REG_DSA)
					+ int24(REG32(REG_DSPS));
				TRACE(DEBUG_SCRIPTS, "Using table-indirect addressing at: %0x\n",
						iodata_addr);
				TRACE(DEBUG_SCRIPTS, "Offset: %0x\n",
						(REG32(REG_DSPS) & 0x00ffffff));

				/* fetch data structure: */
				sig_pci_bus_mr(cpssp->port_pci_bus, cpssp, iodata_addr, 0xf,
						&data32);
				count = data32;
				sig_pci_bus_mr(cpssp->port_pci_bus, cpssp, iodata_addr + 4, 0xf,
						&data32);
				addr = data32;
				TRACE(DEBUG_SCRIPTS, 
					"Block-move: Count: %0x, Addr: %0x\n",
					count, addr);

				REG32(REG_DBC) = (REG32(REG_DBC) & 0xff000000)
						| (count & 0x00ffffff);
				REG32(REG_DSPS) = addr;

			} else if (cpssp->cardregs[REG_DCMD] & 0x20) {
				/* Indirect-adressing */
				/* implement me! */
				assert(0);
			}

			phase = (cpssp->cardregs[REG_DCMD] >> 0) & 0x07;
			count = (REG32(REG_DBC) >> 0) & 0xffffff;
			addr = REG32(REG_DSPS);
			TRACE(DEBUG_SCRIPTS, "BLOCK MOVE: %0x (phase %0x): Count: %0x, Addr: %0x\n",
					REG32(REG_DBC), phase, count, REG32(REG_DSPS));

			if (phase & 1) {
				/* This will handle the actual data-transfer 
				 * in data_from_target-phase */
				subcount = COMP_(data_from_target)(
					cpssp, phase, addr, count);
			} else{
				/* This will handle the actual data-transfer
				 * in data_to_target-phase */
				subcount = COMP_(data_to_target)(
					cpssp, phase, addr, count);
			}

			/* Decrement DBC, Increment DNAD */
			REG32(REG_DNAD) += subcount;
			REG32(REG_DBC) -= subcount;
			break;

		case 0x40 ... 0x47:
			/* RESELECT: 3-54 */
			/* SELECT: 3-62 */
			TRACE(DEBUG_SCRIPTS, "SELECT 0x%x\n", REG32(REG_DBC));

			iodata_addr = REG32(REG_DSA) + int24(REG32(REG_DBC));
			TRACE(DEBUG_SCRIPTS, "IO-DATA at %0x (DBC: %0x)\n",
					iodata_addr, 
					(REG32(REG_DBC) & 0x00ffffff) );
			/* fetch io-data structure */
			sig_pci_bus_mr(cpssp->port_pci_bus, cpssp, iodata_addr, 0xf,
					&data32);
			iodata_null = data32 >> 0;
			iodata_period = data32 >> 8;
			iodata_id = data32 >> 16;
			iodata_config = data32 >> 24;
			cpssp->cardregs[REG_SDID] = iodata_id;
			cpssp->cardregs[REG_SXFER] = iodata_period;
			cpssp->cardregs[REG_SCNTL3] = iodata_config;
			TRACE(DEBUG_SCRIPTS, 
					"FETCHED IO-DATA: config: %0x, id: %0x, priod/offset: %0x, null: %0x\n",
					iodata_config,
					iodata_id,
					iodata_period,
					iodata_null);

			TRACE(DEBUG_SCRIPTS,
					"Own ID: %0x\n", cpssp->cardregs[REG_SCID]);

			/* start SELECT timer */
			cpssp->timerset_sel = COMP_(calc_timer_nano)(
					cpssp->cardregs[REG_STIME0] & 0x0f);
			TRACE(DEBUG_TIMER, "SELECT-TIMER started: %llu\n", cpssp->timerset_sel);

			/* send SELECT to SCSI bus */
			TRACE(DEBUG_SCSIBUS, "CHANGING SCSI-BUS-PHASE: SELECT: %0x \n", iodata_id);
			sel_stat = sig_scsi_bus_phase_select(
					cpssp->port_scsi_bus,
					cpssp,
					iodata_id);

			/* Success? */
			if (sel_stat) {
				TRACE(DEBUG_SCSIBUS, "SELECT: accepted%0x \n", iodata_id);
				/* stop timer */
				cpssp->timertick_sel = 0;
				cpssp->timerset_sel = 0;

				/* now we are an initiator */
				cpssp->SCSI_initiated = true;

			} else {
				TRACE(DEBUG_SCSIBUS, "SELECT: no answer%0x \n", iodata_id);
				cpssp->SCSI_initiated = false;
			}
			break;

		case 0x48 ... 0x4f:
			/* WAIT DISCONNECT: 3-68 */
			/* (Simply check for busfree) */
			TRACE(DEBUG_SCRIPTS, "WAIT FOR DISCONNECT...\n");

			while (cpssp->SCSI_phase != SCSI_PHASE_BUSFREE
			    && cpssp->scripts_running) {
				/* Retry later... */
				sched_delay(TIMERTICKS);
				COMP_(update_timerticks)(cpssp, TIMERTICKS);
				COMP_(timeoutcheck)(cpssp);
			}
			if (! cpssp->scripts_running) {
				break;
			}
			break;

		case 0x50 ... 0x57:
			/* WAIT SELECT: 3-69 */
			/* WAIT RESELECT: 3-71 */
			TRACE(DEBUG_SCRIPTS, "WAIT (RE)SELECT!\n");
			TRACE(DEBUG_SCRIPTS, "DCMD/DBC: %0x\n", REG32(REG_DBC));

			while (! cpssp->SCSI_reselected
			    && ! cpssp->SCSI_selected
			    && ! (REG8(REG_ISTAT) & ISTAT_SIGP)
			    && cpssp->scripts_running) {
				sched_delay(TSYNC_INTERVAL_RESELECT);
				COMP_(update_timerticks)(cpssp, TSYNC_INTERVAL_RESELECT);
				COMP_(timeoutcheck)(cpssp);
			}
			if (! cpssp->scripts_running) {
				break;
			}

			if (cpssp->SCSI_reselected) {
				/* FIXME */
				assert(0);

			} else if (cpssp->SCSI_selected) {
				/* FIXME */
				assert(0);

			} else { assert(REG8(REG_ISTAT) & ISTAT_SIGP);
				if (REG8(REG_DCMD) & 0x04) {
					REG32(REG_DSP) += int24(REG32(REG_DNAD));
				} else {
					REG32(REG_DSP) = REG32(REG_DNAD);
				}
			}
			break;

		case 0x58 ... 0x5f: {
			/* SET: 3-64 */
			unsigned int set_carry;
			unsigned int set_target_mode;
			unsigned int set_sack;
			unsigned int set_satn;

			set_carry = (REG32(REG_DBC) >> 10) & 1;
			set_target_mode = (REG32(REG_DBC) >> 9) & 1;
			set_sack = (REG32(REG_DBC) >> 6) & 1;
			set_satn = (REG32(REG_DBC) >> 3) & 1;

			TRACE(DEBUG_SCRIPTS, "SET (ignored) %u %u %u %u\n",
					set_carry, set_target_mode,
					set_sack, set_satn);

			/* FIXME */
			break;
		    }
		case 0x60 ... 0x67: {
			/* CLEAR: 3-14 */
			unsigned int clr_carry;
			unsigned int clr_target_mode;
			unsigned int clr_sack;
			unsigned int clr_satn;

			clr_carry = (REG32(REG_DBC) >> 10) & 1;
			clr_target_mode = (REG32(REG_DBC) >> 9) & 1;
			clr_sack = (REG32(REG_DBC) >> 6) & 1;
			clr_satn = (REG32(REG_DBC) >> 3) & 1;

			TRACE(DEBUG_SCRIPTS, "CLEAR (ignored) %u %u %u %u\n",
					clr_carry, clr_target_mode,
					clr_sack, clr_satn);

			/* FIXME */
			break;
		    }
		case 0x68 ... 0x7f: {
			/* MOVE REGISTER: 3-48 */
			uint8_t rwopcode;
			uint8_t rwoperation;
			uint8_t reg;
			uint8_t data8;
			uint8_t val_s;
			uint8_t val_d;
			uint16_t tmp;

			rwopcode = (REG8(REG_DCMD) >> 3) & 0x7;
			rwoperation = (REG8(REG_DCMD) >> 0) & 0x7;
			reg = (REG32(REG_DBC) >> 16) & 0xff;
			data8 = (REG32(REG_DBC) >> 8) & 0xff;

			TRACE(DEBUG_SCRIPTS,
					"MOVE REGISTER type=%x, op=%x,  reg=%x, data=%x\n",
					rwopcode, rwoperation, reg, data8);

			/* Read register. */
			if (rwopcode & 0x2) {
				COMP_(readb)(cpssp, reg, &val_s);
			} else {
				COMP_(readb)(cpssp, REG_SFBR, &val_s);
			}

			/* Do calculation. */
			switch (rwoperation) {
			case 0x0: /* Move data */
				val_d = data8;
				break;
			case 0x1: /* Shift left data */
				tmp = (val_s << 1) | cpssp->ALU_CARRYBIT;
				cpssp->ALU_CARRYBIT = (tmp >> 8) & 1;
				val_d = tmp & 0xff;
				break;
			case 0x2: /* OR data */
				val_d = val_s | data8;
				break;
			case 0x3: /* XOR data */
				val_d = val_s ^ data8;
				break;
			case 0x4: /* AND data */
				val_d = val_s & data8;
				break;
			case 0x5: /* Shift right data */
				tmp = (cpssp->ALU_CARRYBIT << 8) | val_s;
				cpssp->ALU_CARRYBIT = tmp & 1;
				val_d = (tmp >> 1) & 0xff;
				break;
			case 0x6: /* Add data without carry */
				tmp = val_s + data8;
				cpssp->ALU_CARRYBIT = (tmp >> 8) & 1;
				val_d = tmp & 0xff;
				break;
			case 0x7: /* Add data with carry */
				tmp = val_s + data8 + cpssp->ALU_CARRYBIT;
				cpssp->ALU_CARRYBIT = (tmp >> 8) & 1;
				val_d = tmp & 0xff;
				break;
			default:
				assert(0); /* Cannot happen. */
			}

			/* Write register. */
			if (rwopcode & 0x1) {
				COMP_(writeb)(cpssp, reg, val_d);
			} else {
				COMP_(writeb)(cpssp, REG_SFBR, val_d);
			}
			break;
		    }
		case 0x80 ... 0x87:
			/* JUMP: 3-27 */
		case 0x88 ... 0x8f:
			/* CALL: ?-?? */
		case 0x90 ... 0x97:
			/* RETURN: 3-58 */
		case 0x98 ... 0x9f:
			/* INT: 3-17 */
			/* INTFLY: 3-21 */
			TRACE(DEBUG_SCRIPTS,
					"EXECUTING JUMP/CALL/RET/INT/INTFLY: %0x\n",
					REG32(REG_DBC));

			options = (REG32(REG_DBC) >> 16) & 0xff;
			mask = ~((REG32(REG_DBC) >> 8) & 0xff);
			cmpdata = (REG32(REG_DBC) >> 0) & 0xff;
			jumpflag = (REG32(REG_DBC) >> 19) & 0x1;
			phaseflag = (REG32(REG_DBC) >> 16) & 0x1;
			phase = (cpssp->cardregs[REG_DCMD] >> 0) & 0x07;
			opcode = (REG8(REG_DCMD) >> 3) & 0x7;

			/* Do we have to wait for a valid phase? */
			if (phaseflag) {
				while (cpssp->SCSI_data_to_target == 0
				    && cpssp->SCSI_data_from_target == 0
				    && cpssp->scripts_running) {
					/* Retry later... */
					sched_delay(TIMERTICKS);
					COMP_(update_timerticks)(cpssp, TIMERTICKS);
					COMP_(timeoutcheck)(cpssp);
				}
				if (! cpssp->scripts_running) {
					break;
				}
			}

			switch (options & 0x26) {
			case 0x20:
				/* Test carry. */
				cond = cpssp->ALU_CARRYBIT;
				break;

			case 0x04:
				/* Compare SFBR with data/mask. */
				cond = (cmpdata & mask) == (REG8(REG_SFBR) & mask);
				break;

			case 0x02:
				/* Compare phase. */
				cond = phase == cpssp->SCSI_phase;
				break;

			case 0x00:
				/* Unconditional jump/call. */
				cond = 1;
				break;

			default:
				TRACE(DEBUG_SCRIPTS, 
					"UNKNOWN JUMP OPTION:%0x\n",
					(REG32(REG_DBC)));
				assert(0);
				break;
			}

			if (! jumpflag) {
				cond = ! cond;
			}

			if (cond) {
				if (opcode == 0x01) {
					/* CALL */
					/* Save return address. */
					REG32(REG_TEMP) = REG32(REG_DSP);
				}

				if (opcode == 0x02) {
					/* RETURN */
					REG32(REG_DSP) = REG32(REG_TEMP);

				} else if (opcode == 0x03) {
					/* INT/INTFLY */
					if ((options >> 4) & 1) {
						/* INTFLY */
						COMP_(int_add)(cpssp,
							REG_ISTAT, ISTAT_INTF);

						/*
						 * After an interrupt we give
						 * the simulator-core the
						 * chance to react...
						 */
						cpssp->inst_cnt = cpssp->inst_limit;
					} else {
						/* INT */
						COMP_(int_add)(cpssp,
							REG_DSTAT, 0x04);
					}

				} else if ((options >> 7) & 1) {
					/* Relative jump. */
					REG32(REG_DSP) += int24(REG32(REG_DSPS));

				} else {
					/* Absolute jump. */
					REG32(REG_DSP) = REG32(REG_DSPS);
				}
			}
			break;

		case 0xc0 ... 0xc7:
			/* MOVE: 3-46 */
			count = (REG32(REG_DBC) >> 0) & 0xffffff;

			/* source address = REG_DSPS */
			/* destination address -> REG_TEMP:
			 * although the third parameter is stored in
			 * REG_TEMP by the controller,
			 * the content of REG_TEMP has to be preserved
			 * during a MMOVE.
			 * So we use an temporary 'reg_temp' variable instead */
			sig_pci_bus_mr(cpssp->port_pci_bus, cpssp,
					REG32(REG_DSP), 0xf, &reg_temp);
			REG32(REG_DSP) += 4;

			/* execute command: bytewise moving */
			COMP_(mmove)(cpssp,  REG32(REG_DSPS), 
					reg_temp, count);

			TRACE(DEBUG_SCRIPTS, 
				"MMOVE: %0x bytes from %0x to %0x\n", count,
				REG32(REG_DSPS), reg_temp);
			break;

		case 0xe0 ... 0xff: /* Register store (NO DSA offset) */
			/* LOAD: 3-37 */
			/* STORE: 3-66 */
			reg_lst = (REG32(REG_DBC) >> 16) & 0xff;
			count = (REG32(REG_DBC) >> 0) & 0x7;

			if (REG8(REG_DCMD) & 0x10) {
				TRACE(DEBUG_SCRIPTS, "LD/ST (DSA-RELATIVE)\n");

				addr = REG32(REG_DSA) + REG32(REG_DSPS);
			} else {
				TRACE(DEBUG_SCRIPTS, "LD/ST (DIRECT)\n");
				addr = REG32(REG_DSPS);
			}

			TRACE(DEBUG_SCRIPTS, 
				"REGISTER LD/ST: %0x to %0x  count: %0x\n",
				reg_lst, addr, count);

			if ((REG8(REG_DCMD) >> 0) & 1) {
				/* LOAD */
				for (i = 0; i < count; i++){
					sig_pci_bus_mr(cpssp->port_pci_bus,
							cpssp,
							(addr + i) & ~3,
							1 << (i & 3), &data32);
					REG8(reg_lst + i) = data32 >> (i & 3) * 8;
				}
			} else {
				/* STORE */
				for (i = 0; i < count; i++){
					data32 = REG8(reg_lst + i) << (i & 3) * 8;
					sig_pci_bus_mw(cpssp->port_pci_bus,
							cpssp, 
							(addr + i) & ~3,
							1 << (i & 3), data32);
				}
			}
			break;

		default: /* UNKOWN SCSI SCRIPTS OPCODE */
			 /* This should never happen! */
			fprintf(stderr, "UNKNOWN OPCODE: DCMD: %0x\n",
					REG8(REG_DCMD));
			fprintf(stderr, "(DCMD-DBC): %0x\n",
					REG32(REG_DBC));
			assert(0);
		};
	}

	sched_to_scheduler();
	goto again;
}

static void
COMP_(reset_chip)(struct cpssp *cpssp)
{
	cpssp->SCSI_phase = 0;

	memset(cpssp->cardregs, 0, sizeof(cpssp->cardregs));
	cpssp->cardregs[REG_SCNTL0] = 0xc0;

	memset(cpssp->scsi_io_buf, 0, sizeof(cpssp->scsi_io_buf));
	memset(cpssp->int_queue_sist0, 0, sizeof(cpssp->int_queue_sist0));
	memset(cpssp->int_queue_sist1, 0, sizeof(cpssp->int_queue_sist1));
	memset(cpssp->int_queue_dstat, 0, sizeof(cpssp->int_queue_dstat));
	cpssp->int_sist0_depth = 0;
	cpssp->int_sist1_depth = 0;
	cpssp->int_dstat_depth = 0;
	cpssp->scripts_running = false;
	cpssp->ALU_CARRYBIT = 0;
	cpssp->delayed_transfer = false;
	cpssp->SCSI_initiated = false;
	cpssp->SCSI_selected = false;
	cpssp->SCSI_reselected = false;
	cpssp->SCSI_data_to_target = 0;
	cpssp->SCSI_data_from_target = 0;
	cpssp->timertick_h2h = 0;
	cpssp->timertick_sel = 0;
	cpssp->timertick_gen = 0;
	cpssp->timerset_h2h = 0;
	cpssp->timerset_sel = 0;
	cpssp->timerset_gen = 0;

	TRACE(DEBUG_OTHER,  "LSI 53C810 Card resetted!\n");
}

/* ============ PCI-CONFSPACE Section ================ */

static int
COMP_(cread0)(
	void *_cpssp, 
	uint32_t addr, 
	unsigned int bs,
	uint32_t *valp
)
{
	struct cpssp *cpssp = _cpssp;

	addr &= 0x7ff;

	/* allow only valid access to CONF-Space */
	if (255 < addr) {
		TRACE(DEBUG_CONFSPACE, 
			"IGNORED CONFSPACE-ACCESS TO ADDR: %0x\n", addr);
		*valp = 0;
		return -1;
	}

	*valp = 0x00000000;
	if ((bs >> 0) & 1) {
		*valp |= pci_getconfigb(cpssp->config_space, addr + 0) << 0;
	}
	if ((bs >> 1) & 1) {
		*valp |= pci_getconfigb(cpssp->config_space, addr + 1) << 8;
	}
	if ((bs >> 2) & 1) {
		*valp |= pci_getconfigb(cpssp->config_space, addr + 2) << 16;
	}
	if ((bs >> 3) & 1) {
		*valp |= pci_getconfigb(cpssp->config_space, addr + 3) << 24;
	}
	TRACE(DEBUG_CONFSPACE, 
		"CSPACE READ: Addr: %0x Value: %0x BS: %0x\n",
		addr, *valp, bs);

	return 0;
}

static int
COMP_(cwrite0)(
	void *_cpssp,
	uint32_t addr,
	unsigned int bs,
	uint32_t val
)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t val32;
	uint32_t oaddr;
	uint32_t naddr;

	addr &= 0x7ff;

	val32 = cpssp->config_space[addr >> 2];
	if ((bs >> 0) & 1) {
		val32 &= ~(0xff << 0);
		val32 |= val & (0xff << 0);
	}
	if ((bs >> 1) & 1) {
		val32 &= ~(0xff << 8);
		val32 |= val & (0xff << 8);
	}
	if ((bs >> 2) & 1) {
		val32 &= ~(0xff << 16);
		val32 |= val & (0xff << 16);
	}
	if ((bs >> 3) & 1) {
		val32 &= ~(0xff << 24);
		val32 |= val & (0xff << 24);
	}

	TRACE(DEBUG_CONFSPACE, "CSPACE WRITE: Addr: %0x Value:%0x BS:%0x\n",
		addr, val32, bs);

	switch (addr) {
	case PCI_CACHE_LINE_SIZE: /* actually 4 * 8 bit registers */
		val32 &= 0x0000FF18; /* filter the hardwired to 0-bits */
		cpssp->config_space[addr >> 2] = val32;
		TRACE(DEBUG_CONFSPACE,  "WRITE CACHELINESIZE: %0x\n", val32);
		break;
	case PCI_COMMAND:
		/* set default bits */
#if 0
		val32 |= 0x100; /* only bit 8 */
#endif
#if 0
		val32 |= 0x40; /* only bit 6 */
#endif
		/* Just filter the hardwired-to-0/unimplemented bits */
		/* val32 &= 0x157; */
#if 0
		pci_setconfigw(cpssp->config_space, addr & 0xff, val32 & 0xffff);
		/* FIX ME: Statusregister */
#endif
		pci_setconfigl(cpssp->config_space, addr, val32);
		TRACE(DEBUG_CONFSPACE,  "WRITE PCI_COMMAND: %0x\n", val32);
		break;
	case PCI_BASE_ADDRESS_1: /* Request 4 KB Memory space */
		oaddr = ((uint32_t)(cpssp->config_space[PCI_BASE_ADDRESS_1>>2]
				    & PCI_BASE_ADDRESS_MEM_MASK));
		cpssp->config_space[addr >> 2] = pci_requestspace(val32, 4096,
						PCI_BASE_ADDRESS_SPACE_MEMORY
						| PCI_BASE_ADDRESS_MEM_TYPE_32);
		naddr = ((uint32_t)(cpssp->config_space[PCI_BASE_ADDRESS_1>>2]
				    & PCI_BASE_ADDRESS_MEM_MASK));
		if (oaddr != naddr) {
			/* Re-map old/new region. */
			sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp, oaddr, 4096);
			sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp, naddr, 4096);
		}
		TRACE(DEBUG_CONFSPACE, "WRITE BASE-ADDRESS_0:%0x, Using: %0x\n",
		     val32, ((uint32_t)(cpssp->config_space[PCI_BASE_ADDRESS_1>>2]
				        & PCI_BASE_ADDRESS_MEM_MASK)) );
		break;
	case PCI_BASE_ADDRESS_0: /* Request 96 IO Ports */
		cpssp->config_space[addr >> 2] = pci_requestspace(val32, 128,
						PCI_BASE_ADDRESS_SPACE_IO);
		TRACE(DEBUG_CONFSPACE, 
			"WRITE IO-ADDRESS: %0x,  Using: %0x\n",
			val32, ((uint16_t)((cpssp->config_space[PCI_BASE_ADDRESS_0
									>> 2]
			& PCI_BASE_ADDRESS_IO_MASK)&0x0000ffff)));
		break;
	case PCI_BASE_ADDRESS_2: /* used by flash rom (request 1 MB) */
		TRACE(DEBUG_CONFSPACE,  
				"WRITE BASE-ADDRESS 2 (ignoring): %0x\n", val32);
		break;
	case PCI_ROM_ADDRESS: /* ROM address (bootrom) */
		oaddr = cpssp->config_space[PCI_ROM_ADDRESS >> 2]
			& PCI_ROM_ADDRESS_MASK;
		cpssp->config_space[addr>>2] = pci_requestspace(val,
							SZ_53CROMREQUEST, 0);
		cpssp->config_space[addr>>2] &= PCI_ROM_ADDRESS_MASK;
		cpssp->config_space[addr>>2] |= val & PCI_ROM_ADDRESS_ENABLE;
		naddr = cpssp->config_space[PCI_ROM_ADDRESS >> 2]
			& PCI_ROM_ADDRESS_MASK;
		if (oaddr != naddr) {
			/* Re-map new region. */
			sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
					oaddr, SZ_53CROMREQUEST);
			sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
					naddr, SZ_53CROMREQUEST);
		}
		TRACE(DEBUG_CONFSPACE,
			"Now Using ROM-Addr 0x%08x\n",
			(uint32_t) (cpssp->config_space[addr >> 2] ));
		break;
	case PCI_INTERRUPT_LINE: /* actually 4 * 8 bit registers */
		/* only 8 bit of this are writeable */
		pci_setconfigb(cpssp->config_space, addr, val32 & 0xff);
		TRACE(DEBUG_CONFSPACE, "SET INTERRUPTLINE: %0x\n", val32 & 0xff);
		break;
	case PCI_REVISION_ID:
	case PCI_BASE_ADDRESS_3: /* unused */
	case PCI_BASE_ADDRESS_4: /* unused */
	case PCI_BASE_ADDRESS_5: /* unused */
	case 0x28:		 /* reserved */
	case PCI_VENDOR_ID:      /* 16 bit Vendor ID and 16 bit Device ID */
	case PCI_SUBSYSTEM_VENDOR_ID:
	case PCI_CAPABILITY_LIST:/* not supported */
	case 0x38:		 /* reserved */
	case 0x40 ... 0xfc:	 /* unused/reserved */
		/* All the registers above are write-protected or
		 * hard-wired to 0. That's why we ignore the write. */
		break;
	default:
		TRACE(DEBUG_CONFSPACE,  
		 "UNKNOW CSPAVE WRITE: addr: %02x, val: %08x\n",
		 addr, val32);
		break;
	};

	return 0;
}

/* ================= I/O Section ========================= */

/*
 * portread(), portwrite():
 * byteselect-based access to card-registers
 */
static void
COMP_(portread)(
	struct cpssp *cpssp,
	uint32_t port,
	unsigned int bs,
	uint32_t *valp
)
{
	*valp = 0x00000000;

	/*
	 * Little Helper Makro:
	 * reads 8bit-value from bs-specified byte-position.
	 * We use this to just read from registers without
	 * triggering further actions...
	 */
#define _BS_REG_READ(_pos) \
	*valp |= cpssp->cardregs[port + _pos] << (_pos * 8); \
	TRACE(DEBUG_IO, "reading %s-register (%x)\n", \
			reg_names[port + _pos], \
			cpssp->cardregs[port + _pos]);

	switch (port) {
	case 0x04: /* SCID, SXFER, SDID, GPREG */
		if ((bs >> 0) & 1) { _BS_REG_READ(0) }
		if ((bs >> 1) & 1) { _BS_REG_READ(1) }
		if ((bs >> 2) & 1) { _BS_REG_READ(2) }
		if ((bs >> 3) & 1) {
			if ((cpssp->cardregs[REG_GPCNTL] >> 0) & 1) {
				/* Port is input. */
				*valp |= cpssp->state_i2c_data << 24;
			} else {
				/* Port is output. */
				*valp |= (cpssp->cardregs[REG_GPREG] & 0x01) << 24;
			}
			if ((cpssp->cardregs[REG_GPCNTL] >> 1) & 1) {
				/* Port is input. */
				*valp |= cpssp->state_i2c_clk << 25;
			} else {
				/* Port is output. */
				*valp |= (cpssp->cardregs[REG_GPREG] & 0x02) << 25;
			}
			TRACE(DEBUG_IO, "reading %s-register (%x)\n",
					reg_names[port + 3],
					(*valp >> 24) & 0xff);
		}
		break;
	case 0x0c:
		if ((bs >> 0) & 1) {
			/* DSTAT */
			_BS_REG_READ(0)
			COMP_(int_del)(cpssp, REG_DSTAT);
		}
		if ((bs >> 1) & 1) { _BS_REG_READ(1) }
		if ((bs >> 2) & 1) { _BS_REG_READ(2) }
		if ((bs >> 3) & 1) { _BS_REG_READ(3) }
		break;
	case 0x18:
		if ((bs >> 0) & 1) { _BS_REG_READ(0) }
		if ((bs >> 1) & 1) { _BS_REG_READ(1) }
		if ((bs >> 2) & 1) { /* CTEST2 */
			_BS_REG_READ(2)
			/* clear SIGP in ISTAT and CTEST2! */
			cpssp->cardregs[REG_ISTAT]&=~0x20;
			cpssp->cardregs[REG_CTEST2]&=~0x40;
		}
		if ((bs >> 3) & 1) {
			_BS_REG_READ(3)
			*valp |= (REVISION << 4) << 24;
		}
		break;
	case 0x40: /* SIST1, SIST0, SIEN1, SIEN0 */
		if ((bs >> 0) & 1) { _BS_REG_READ(0) }
		if ((bs >> 1) & 1) { _BS_REG_READ(1) }
		if ((bs >> 2) & 1) { /* SIST0 */
			_BS_REG_READ(2) 
			COMP_(int_del)(cpssp, REG_SIST0);
		}
		if ((bs >> 3) & 1) { /*SIST1 */
			_BS_REG_READ(3) 
			COMP_(int_del)(cpssp, REG_SIST1);
		}
		break;

	/* Just read-out register-values... */
	default:
		if ((bs >> 0) & 1) { _BS_REG_READ(0) }
		if ((bs >> 1) & 1) { _BS_REG_READ(1) }
		if ((bs >> 2) & 1) { _BS_REG_READ(2) }
		if ((bs >> 3) & 1) { _BS_REG_READ(3) }
		break;
	}
}

static void
COMP_(portwrite)(
	struct cpssp *cpssp,
	uint32_t port,
	unsigned int bs,
	uint32_t val
)
{
	uint8_t val8 = 0;
	uint8_t backup_reg = 0;

	/* helper vars */
	uint8_t val8_pre = 0;

	/*
	 * Little Helper Makro:
	 * writes 8bit-value to bs-specified byte-position.
	 * We use this to just write into registers without
	 * triggering further actions...
	 */
#define _BS_REG_WRITE(_pos) \
	val8 = (val >> (_pos * 8)) & 0xff; \
	cpssp->cardregs[port + _pos] = val8; \
	TRACE(DEBUG_IO, "writing %s-register (%x)\n", \
			reg_names[port + _pos], \
			cpssp->cardregs[port + _pos] ); 

	switch (port) {
	case 0x00: /* Only SCNTL1-Register (BS 1) needs special treatment: */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }

		if ((bs >> 1) & 1) {
			val8 = (val & 0x0000ff00) >> 8;
			cpssp->cardregs[port + 1] = val8;
			TRACE(DEBUG_IO, "writing %s-register... (%0x) \n",
					reg_names[port + 1],
					cpssp->cardregs[port + 1] );
			if (val8 & 0x8) { /* Trigger scsi-reset: */
				/*for now, only set the scsi-status register! */
				/* Triggers also Interrupt */
				TRACE(DEBUG_IO, 
					"RESETTING SCSIBUS....(%0x) \n", val8);
				cpssp->cardregs[REG_SSTAT0] |= 0x2;
				COMP_(int_add)(cpssp, REG_SIST0, 0x02);
			} else {
				cpssp->cardregs[REG_SSTAT0] &= ~0x2;
				/*
				 * "SCSI reset detected" is edge triggered
				 * interrupt. FIXME
				 */
				// COMP_(int_del)(cpssp, REG_SIST0);
			}
		}

		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { _BS_REG_WRITE(3) }
		break;
	case 0x04: /* SCID, SXFER, SDID, GPREG */
		/* filter out RESERVED bits */
		val &= 0x03ffffff;

		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) {
			_BS_REG_WRITE(3)
			COMP_(i2c_update)(cpssp);
		}
		break;

	case 0x0c: /* DSTAT, STAT0, STAT1, STAT1 are readonly-registers! */
		TRACE(DEBUG_IO, 
				"ignoring portwrite to readonly register: bd: %0x, bs: %0x, val: %0x\n",
				port, bs, val);
		break;

	case 0x14: /* ISTAT and reserved*/
		if ((bs >> 0) & 1) { 
			/* backup INT status-bits */
			backup_reg = cpssp->cardregs[port];

			val8 = (val & 0x000000ff);
			cpssp->cardregs[port] = val8;
			TRACE(DEBUG_IO, "writing %s-register... (%0x) \n", 
					reg_names[port],
					cpssp->cardregs[port] ); 

			/* restore INT status-bits */
			cpssp->cardregs[port] &= ~0x7;
			cpssp->cardregs[port] |= (backup_reg & 0x07);

			/* set SIGP in CTEST2
			 * depending on SIGP in ISTAT */
			if (cpssp->cardregs[REG_ISTAT] & 0x20) {
				cpssp->cardregs[REG_CTEST2] |= 0x40;
			}

			if (val8 & 0x04) {
				/* INT-FLY Reset */
				COMP_(int_del)(cpssp, REG_ISTAT);
			}

			if (val8 & 0x40) {
				/* Reset Chip */
				COMP_(reset_chip)(cpssp);
			}

			if (val8 & 0x80) { /* ABORT */
				TRACE(DEBUG_IO, 
					"PROPAGATING ABORT OPERATION! %0x\n",
					0);
				COMP_(int_add)(cpssp, REG_DSTAT, 0x10);
				/* Stop all timers! */
				cpssp->timertick_sel = 0;
				cpssp->timerset_sel = 0;
				cpssp->timertick_h2h = 0;
				cpssp->timerset_h2h = 0;
				cpssp->timertick_gen = 0;
				cpssp->timerset_gen = 0;
			}
#if 0
			/* the int-status-bits were overwritten at this time,
			 * so reset the int-status-bits 
			 * according to their status-registers...*/
			TRACE(DEBUG_IO, 
				"WRITE ON ISTAT: RECALCULATING INT-BITS! %0x\n",
				0);
			COMP_(int_trigger)(cpssp);
#endif
		}
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { _BS_REG_WRITE(3) }
		break;

	case 0x18: /* CTEST-Register (32-bits) */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) {
			/* CTEST3 */
			/* Bit 7-4 are read-only (REVISION). */
			val &= ~(0xf << (4+24));

			if ((val >> (3+24)) & 1) {
				/* Flush DMA FIFO */
			}
			if ((val >> (2+24)) & 1) {
				/* Clear DMA FIFO */

				/* Bit is self-clearing. */
				val &= ~(1 << (2+24));
			}

			_BS_REG_WRITE(3)
		}
		break;

	case 0x2c: /* DSP-Register (32-bits) */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { _BS_REG_WRITE(3) }
		 /* After DSP-registervalues are set, start SCRIPTS */
		cpssp->scripts_running = true;
		sched_wakeup(&cpssp->process);
		break;

	case 0x38: /* DMODE, DIEN, SBR, DCNTL */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { 
			_BS_REG_WRITE(3)
			/* Restart SCRIPTS? */
			if (val8 & 0x04) {
				TRACE(DEBUG_IO, 
				  "STD set, restarting SCRIPTS: %0x \n", val8);
				cpssp->scripts_running = true;
				sched_wakeup(&cpssp->process);
			}
		}
		break;

	case 0x44: /* SLPAR, RESERVED, MACNTL, GPCNTL */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { 
			_BS_REG_WRITE(3)
			COMP_(i2c_update)(cpssp);
		}
		break;

	case 0x48: /* STIME0, STIME1, RESPID */
		if ((bs >> 0) & 1) {
			/* get the previous value...*/
			val8_pre = cpssp->cardregs[REG_STIME0];

			_BS_REG_WRITE(0)

			/* Note: Timers get only activated if
			 * they were previously resetted (written to 0) */

			/* activate sel-timer? */
			if (((val8_pre & 0x0f) == 0)
			    && (val8 & 0x0f)) {
				TRACE(DEBUG_IO, 
				  "SEL-timer set (not yet running...): %0x \n", val8);
				/* Selection Timer activated! */
				cpssp->timertick_sel = 0;
#if 0
				cpssp->timerset_sel = COMP_(calc_timer_nano)(
						val8 & 0x0f);
#endif

				/* the timer gets started later on a SELECT... */
				cpssp->timerset_sel = 0;
			} else if ( ((val8 & 0x0f) == 0)
				&&  (val8_pre & 0x0f)) {
				TRACE(DEBUG_IO, "SEL-timer deactivated!\n");
				/* deactivate timer */
				cpssp->timertick_sel = 0;
				cpssp->timerset_sel = 0;
			}

			/*activate h2h-timer? */
			if (((val8_pre & 0xf0) == 0)
			    && (val8 & 0xf0)) {
				TRACE(DEBUG_IO, 
				  "H2H-timer set (not yet running...): %0x \n", val8);
				/* Selection Timer activated! */
				cpssp->timertick_h2h = 0;
#if 0
				cpssp->timerset_h2h = COMP_(calc_timer_nano)(
						(val8 & 0xf0) >> 4);
#endif
				/* the timer gets started later (on h2h?)... */
				cpssp->timerset_h2h = 0;
				assert(0);
			} else if ( ((val8 & 0xf0) == 0)
				&&  (val8_pre & 0xf0) ){
				TRACE(DEBUG_IO, "H2H-timer deactivated!\n");
				/* deactivate timer */
				cpssp->timertick_h2h = 0;
				cpssp->timerset_h2h = 0;
			}

		}
		if ((bs >> 1) & 1) { 
			/* get the previous value...*/
			val8_pre = cpssp->cardregs[REG_STIME1];

			_BS_REG_WRITE(1) 
			/* Note: Timers get only activated if
			 * they were previously resetted (written to 0) */

			/* activate gen-timer? */
			if (((val8_pre & 0x0f) == 0)
			    && (val8 & 0x0f)) {
				TRACE(DEBUG_IO, 
				  "GEN-timer activated!: %0x \n", val8);
				cpssp->timertick_gen = 0;
				cpssp->timerset_gen = COMP_(calc_timer_nano)(
						val8 & 0x0f);
				COMP_(timer_tick)(cpssp);
			} else if ( ((val8 & 0x0f) == 0)
				&& (val8_pre & 0x0f) ){
				TRACE(DEBUG_IO, "GEN-timer deactivated!\n");
				/* deactivate timer */
				cpssp->timertick_gen = 0;
				cpssp->timerset_gen = 0;
			}

		}
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { _BS_REG_WRITE(3) }
		break;

	default:
		/*
		 * Default: Just write the 8bit-values into the cardregs
		 * (depending on bs), without triggering further actions...
		 */
		if ((bs >> 0) & 1) { _BS_REG_WRITE(0) }
		if ((bs >> 1) & 1) { _BS_REG_WRITE(1) }
		if ((bs >> 2) & 1) { _BS_REG_WRITE(2) }
		if ((bs >> 3) & 1) { _BS_REG_WRITE(3) }
		break;
	}
}

/*
 * Small wrappers for bytewise register-access.
 * some SCRIPTS functions need this...
 */
static void
COMP_(readb)(struct cpssp *cpssp, uint16_t port, uint8_t *val)
{
	uint32_t val32;

	COMP_(portread)(cpssp, (port >> 2) << 2, 1 << (port & 0x03), &val32);
	*val = val32 >> (port & 0x03) * 8;
}

static void
COMP_(writeb)(struct cpssp *cpssp, uint16_t port, uint8_t val)
{
	uint32_t val32;

	val32 = val << (port & 0x03) * 8;
	COMP_(portwrite)(cpssp, (port >> 2) << 2, 1 << (port & 0x03), val32);
}

/*
 * PCI-Interface functions: ior, iow, read, write
 */
static int
COMP_(iow)(void *_cpssp, uint32_t portaddr, unsigned int bs, uint32_t val)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t iobase;
	uint32_t iolimit;
	uint32_t port;

	/* card IO enabled? */
	if ((pci_getconfigb(cpssp->config_space, PCI_COMMAND) & 0x01) == 0) {
		return -1;
	}

	iobase = LSI53C810IOADDR(cpssp);
	iolimit = LSI53C810IOADDR(cpssp) + SZ_53C810MEM;

	if (iobase <= portaddr && portaddr < iolimit) {
		port = portaddr - iobase;
	} else {
		return -1;
	}

	COMP_(portwrite)(cpssp, port, bs, val);
	return 0;

}

static int
COMP_(ior)(
	void *_cpssp,
	uint32_t portaddr,
	unsigned int bs,
	uint32_t *valp
)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t iobase;
	uint32_t iolimit;
	uint32_t port;

	/* card IO enabled? */
	if ((pci_getconfigb(cpssp->config_space, PCI_COMMAND) & 0x01) == 0) {
		return -1;
	}

	iobase = LSI53C810IOADDR(cpssp);
	iolimit = LSI53C810IOADDR(cpssp) + SZ_53C810MEM;

	if (iobase <= portaddr && portaddr < iolimit){
		port = portaddr - iobase;
	} else {
		return -1;
	}
	
	COMP_(portread)(cpssp, port, bs, valp);
	return 0;
}

/* ====================== READ/WRITE Section ===================== */

static int
COMP_(read)(
	void *_cpssp,
	uint32_t portaddr,
	unsigned int bs,
	uint32_t *valp
)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t membase;
	uint32_t memlimit;
	uint32_t port;

	membase = LSI53C810MEMADDR(cpssp);
	memlimit = LSI53C810MEMADDR(cpssp) + SZ_53C810MEM;

	if (membase <= portaddr && portaddr < memlimit) {
		port = portaddr - membase;
		COMP_(portread)(cpssp, port, bs, valp);

	} else if ((cpssp->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE)
		 && LSI53C810ROMADDR(cpssp) <= portaddr
		 && portaddr < LSI53C810ROMADDR(cpssp) + SZ_53CROMREQUEST) {
		portaddr %= SZ_53CROMREQUEST;
		*valp = 0;
		if ((bs >> 0) & 1) {
			*valp |= cpssp->bootrom[portaddr + 0] << 0;
		}
		if ((bs >> 1) & 1) {
			*valp |= cpssp->bootrom[portaddr + 1] << 8;
		}
		if ((bs >> 2) & 1) {
			*valp |= cpssp->bootrom[portaddr + 2] << 16;
		}
		if ((bs >> 3) & 1) {
			*valp |= cpssp->bootrom[portaddr + 3] << 24;
		}
	} else {
		return -1;
	}

	return 0;
}

static int
COMP_(write)(
	void *_cpssp,
	uint32_t portaddr,
	unsigned int bs,
	uint32_t val
)
{
	struct cpssp *cpssp = _cpssp;
	uint32_t membase;
	uint32_t memlimit;
	uint32_t port;

	membase = LSI53C810MEMADDR(cpssp);
	memlimit = LSI53C810MEMADDR(cpssp) + SZ_53C810MEM;

	if (membase <= portaddr
	 && portaddr < memlimit) {
		port = portaddr - membase;
	} else {
		return -1;
	}

	COMP_(portwrite)(cpssp, port, bs, val);
	return 0;
}

/* ============= MAPPING Section ================= */

static int
COMP_(map_r)(
	void *_cpssp,
	uint32_t pa,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t *),
	void **csp,
	char **haddr_p
)
{
	struct cpssp *cpssp = _cpssp;

	if ((cpssp->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY) == 0) {
		return -1;
	}

	if (LSI53C810MEMADDR(cpssp) != 0x0000
	 && LSI53C810MEMADDR(cpssp) <= pa
	 && pa < LSI53C810MEMADDR(cpssp) + SZ_53C810MEM) {
		/* don't map but simulate access instead. */
		*cfp = COMP_(read);
		*csp = cpssp;
		*haddr_p = NULL;
		return 0;
	}

	if (cpssp->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE
	 && LSI53C810ROMADDR(cpssp) <= pa
	 && pa < LSI53C810ROMADDR(cpssp) + SZ_53CROMREQUEST) {
		/* don't map but simulate access instead. */
		*cfp = COMP_(read);
		*csp = cpssp;
		*haddr_p = NULL;
		return 0;
	}

	return 1;
}

static int
COMP_(map_w)(
	void *_cpssp,
	uint32_t pa,
	int (**cfp)(void *, uint32_t, unsigned int, uint32_t),
	void **csp,
	char **haddr_p
)
{
	struct cpssp *cpssp = _cpssp;

	if ((cpssp->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY) == 0) {
		return -1;
	}

	if (LSI53C810MEMADDR(cpssp) != 0x0000
	 && LSI53C810MEMADDR(cpssp) <= pa
	 && pa < LSI53C810MEMADDR(cpssp) + SZ_53C810MEM) {
		/* don't map but simulate access instead. */
		*cfp = COMP_(write);
		*csp = cpssp;
		*haddr_p = NULL;
		return 0;
	}

	if (cpssp->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE
	 && LSI53C810ROMADDR(cpssp) <= pa
	 && pa < LSI53C810ROMADDR(cpssp) + SZ_53CROMREQUEST) {
		/* don't map but simulate access instead. */
		*cfp = COMP_(write);
		*csp = cpssp;
		*haddr_p = NULL;
		return 0;
	}

	return 1;
}

/* =================== SCSI-Bus ======================== */

static int
COMP_(phase_select)(void *_cpssp, uint32_t id)
{
	struct cpssp *cpssp = _cpssp;

	if (id == cpssp->cardregs[REG_SCID]) {
		/* The controller is always the Initiator! */
		TRACE(DEBUG_SCSIBUS, "Tried to select the Controller!!!%0x \n", id);
		assert(0);
	}
	return 0;
}

static int
COMP_(phase_reselect)(void *_cpssp, uint32_t id)
{
	struct cpssp *cpssp = _cpssp;

	if (id == cpssp->cardregs[REG_SCID]) {
		/* RESELECT not yet supported */
		TRACE(DEBUG_SCSIBUS, "RESELECT not working yet!%0x \n", id);
		assert(0);
		/* cpssp->SCSI_initiated = true */
	}
	return 0;
}

static void
COMP_(phase_msg_out)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_MESSAGE_OUT;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-MSG-OUT!\n");
}

static void
COMP_(phase_msg_in)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_MESSAGE_IN;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-MSG-IN!\n");
}

static void
COMP_(phase_command)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_COMMAND;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-COMMAND!\n");
}

static void
COMP_(phase_data_out)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_DATA_OUT;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-DATA-OUT!\n");
}

static void
COMP_(phase_data_in)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_DATA_IN;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-DATA-IN!\n");
}

static void
COMP_(phase_status)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->SCSI_phase = SCSI_PHASE_STATUS;
	TRACE(DEBUG_SCSIBUS, "LSI detected PHASE-STATUS!\n");
}

static void
COMP_(phase_free)(void *_cpssp)
{
	struct cpssp *cpssp = _cpssp;

	/* There was a bus-free phase, so there is no longer a running transaction! */
	TRACE(DEBUG_SCSIBUS, "SCSIBUS WENT TO BUS-FREE PHASE: TA exited!\n");
	cpssp->SCSI_initiated = false;
	cpssp->SCSI_phase = SCSI_PHASE_BUSFREE;
}

static void
COMP_(atn_set)(void *_cpssp, unsigned int val)
{
	TRACE(DEBUG_SCSIBUS, "ATN-line high!\n");
}

static void
COMP_(scsibus_want_recv)(void *_cpssp, unsigned long count)
{
	struct cpssp *cpssp = _cpssp;

	TRACE(DEBUG_IO, "%s: %lu\n", __FUNCTION__, count);

	cpssp->SCSI_data_to_target = count;

	if (cpssp->delayed_transfer) {
		TRACE(DEBUG_IO, "%s: wakeup process\n", __FUNCTION__);
		sched_wakeup(&cpssp->process);
	}
}

static unsigned long
COMP_(scsibus_send)(void *_cpssp, const uint8_t *buf, unsigned long bufsize)
{
	return 0;
}

static void
COMP_(scsibus_want_send)(void *_cpssp, unsigned long count)
{
	struct cpssp *cpssp = _cpssp;

	TRACE(DEBUG_IO, "%s: %lu\n", __FUNCTION__, count);

	cpssp->SCSI_data_from_target = count;

	if (cpssp->delayed_transfer) {
		TRACE(DEBUG_IO, "%s: wakeup process\n", __FUNCTION__);
		sched_wakeup(&cpssp->process);
	}
}

static unsigned long
COMP_(scsibus_recv)(void *_cpssp, uint8_t *buf, unsigned long bufsize)
{
	return 0;
}

/* =================== I2C Bus ===================== */

static void
COMP_(data_event)(void *_cpssp, bool val)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->state_i2c_data = val;
}

static void
COMP_(clk_event)(void *_cpssp, bool val)
{
	struct cpssp *cpssp = _cpssp;

	cpssp->state_i2c_clk = val;
}

/* =================== Power / Reset ===================== */

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

	TRACE(DEBUG_OTHER,  "LSI 53C810 POWER:%x\n", val);

	cpssp->state_power = val;
}

static void
COMP_(n_reset_set)(void *_cpssp, unsigned int n_val)
{
	struct cpssp *cpssp = _cpssp;

	TRACE(DEBUG_OTHER,  "LSI 53C810 RESETTING\n");

	/* initialize/reset PCI config space */
	memset(cpssp->config_space, 0, 0x100);

	pci_setconfigw(cpssp->config_space, PCI_VENDOR_ID, 0x1000);
	pci_setconfigw(cpssp->config_space, PCI_DEVICE_ID, 0x0001);
	pci_setconfigw(cpssp->config_space, PCI_COMMAND, PCI_COMMAND_MASTER);

	pci_setconfigw(cpssp->config_space,
		PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK);

	pci_setconfigb(cpssp->config_space, PCI_REVISION_ID, 0x10 | REVISION);
	pci_setconfigw(cpssp->config_space, PCI_CLASS_DEVICE, 0x0100);
	pci_setconfigb(cpssp->config_space, PCI_LATENCY_TIMER, 32);

	pci_setconfigl(cpssp->config_space, PCI_BASE_ADDRESS_1, 
		(PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32));
	pci_setconfigl(cpssp->config_space, PCI_BASE_ADDRESS_0,
		PCI_BASE_ADDRESS_SPACE_IO);


	pci_setconfigb(cpssp->config_space, PCI_INTERRUPT_PIN, PCI_INT_A);

	pci_setconfigb(cpssp->config_space, PCI_MIN_GNT, 0x08);
	pci_setconfigb(cpssp->config_space, PCI_MAX_LAT, 0x18);

	COMP_(reset_chip)(cpssp);

	TRACE(DEBUG_OTHER,  "LSI 53C810 Card resetted!\n");
}

/* ====================== global functions ==================== */

void *
COMP_(create)(
	const char *name,
	const char *bios,
	struct sig_manage *port_manage,
	struct sig_std_logic *port_power,
	struct sig_std_logic *port_reset_hash_,
	struct sig_pci_idsel *port_idsel,
	struct sig_pci_bus *port_pci_bus,
	struct sig_std_logic *port_intA,
	struct sig_scsi_bus *port_scsi_bus,
	struct sig_i2c_bus *port_i2c_bus
)
{
	static const struct sig_std_logic_funcs power_funcs = {
		.boolean_or_set = COMP_(power_set),
	};
	static const struct sig_std_logic_funcs reset_hash__funcs = {
		.boolean_or_set = COMP_(n_reset_set),
	};
	static const struct sig_pci_idsel_funcs idsel_funcs = {
		.c0r = COMP_(cread0),
		.c0w = COMP_(cwrite0),
	};
	static const struct sig_pci_bus_funcs pci_bus_funcs = {
		.ior = COMP_(ior),
		.iow = COMP_(iow),
		.mr = COMP_(read),
		.mw = COMP_(write),
		.map_r = COMP_(map_r),
		.map_w = COMP_(map_w),
	};
	static const struct sig_scsi_bus_funcs scsi_bus_funcs = {
		.phase_select = COMP_(phase_select),
		.phase_reselect = COMP_(phase_reselect),
		.phase_msg_out = COMP_(phase_msg_out),
		.phase_msg_in = COMP_(phase_msg_in),
		.phase_command = COMP_(phase_command),
		.phase_data_out = COMP_(phase_data_out),
		.phase_data_in = COMP_(phase_data_in),
		.phase_status = COMP_(phase_status),
		.phase_free = COMP_(phase_free),
		.atn_set = COMP_(atn_set),
		.want_recv = COMP_(scsibus_want_recv),
		.send = COMP_(scsibus_send),
		.want_send = COMP_(scsibus_want_send),
		.recv = COMP_(scsibus_recv),
	};
	static const struct sig_i2c_bus_funcs i2c_bus_funcs = {
		.data_event = COMP_(data_event),
		.clk_event = COMP_(clk_event),
	};
	struct cpssp *cpssp;
	const char *path;
	int fd;
	int ret;

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

	TRACE(DEBUG_OTHER,  "LSI 53C810 INIT START!\n");

	/* Initialize flash ROM. */
	path = buildpath(ROMDIR, bios);
	fd = open(path, O_RDONLY);
	if (0 <= fd) {
		/* Load file */
		ret = read(fd, cpssp->bootrom, SZ_53CBOOTROM);
		assert(ret == SZ_53CBOOTROM);
		ret = close(fd);
		assert(0 <= ret);
	} else {
		memset(cpssp->bootrom, 0, SZ_53CBOOTROM);
	}

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

	/* Call */
	sig_pci_idsel_connect(port_idsel, cpssp, &idsel_funcs);

	cpssp->port_pci_bus = port_pci_bus;
	sig_pci_bus_connect(port_pci_bus, cpssp, &pci_bus_funcs);

	cpssp->port_scsi_bus = port_scsi_bus;
	sig_scsi_bus_connect(port_scsi_bus, cpssp, &scsi_bus_funcs);

	cpssp->port_i2c_bus = port_i2c_bus;
	sig_i2c_bus_connect_raw(port_i2c_bus, cpssp, &i2c_bus_funcs);

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

	sig_std_logic_connect_in(port_reset_hash_, cpssp, &reset_hash__funcs);

	cpssp->process.inst_hz = 33 * 1024 * 1024;
	sched_process_init(&cpssp->process, COMP_(step), cpssp);

	TRACE(DEBUG_OTHER,  "LSI 53C810 INIT END!\n");

	return cpssp;
}

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

	shm_free(cpssp);
}

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

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