#include "insignia.h"
#include "host_def.h"

/*
 * INSIGNIA (SUB)MODULE SPECIFICATION -----------------------------
 * 
 * 
 * THIS PROGRAM SOURCE FILE  IS  SUPPLIED IN CONFIDENCE TO THE CUSTOMER, THE
 * CONTENTS  OR  DETAILS  OF  ITS OPERATION MUST NOT BE DISCLOSED TO ANY
 * OTHER PARTIES  WITHOUT THE EXPRESS AUTHORISATION FROM THE DIRECTORS OF
 * INSIGNIA SOLUTIONS LTD.
 * 
 * 
 * DOCUMENT 		:
 * 
 * RELATED DOCS		: WD2010-05 datasheet WD11C00C-22 (RMAC) eng.spec
 * (Intel 82062 ... very similar to WD2010) IBM PC XT286 tech.ref
 * 
 * DESIGNER		: Jerry Kramskoy
 * 
 * REVISION HISTORY	: First version		: 14-Sep-88.
 * 
 * SUBMODULE NAME		: fdisk
 * 
 * SOURCE FILE NAME	: fdisk.c
 * 
 * PURPOSE			: emulate fixed disk controller and sector buffer
 * manager components of AT dual card.
 * 
 * SccsID = @(#)fdisk.c	1.35 08/31/93 Copyright Insignia Solutions Ltd.
 * 
 * 
 * [1.INTERMODULE INTERFACE SPECIFICATION]
 * 
 * [1.0 INCLUDE FILE NEEDED TO ACCESS THIS INTERFACE FROM OTHER SUBMODULES]
 * 
 * INCLUDE FILE : fdisk.gi
 * 
 * [1.1    INTERMODULE EXPORTS]
 * 
 * PROCEDURES() :	fdisk_inb((io_addr)port, (unsigned char *)value)
 * (uchar)fdisk_read_dir((io_addr)port, (unsigned char *)value) (void)
 * fdisk_outb((io_addr)port, (unsigned char )value)
 * fdisk_inw((io_addr)port, (unsigned short *)value) (void)
 * fdisk_outw((io_addr)port,(unsigned short)value) (void)
 * (void) fdisk_reset() (int)  fdisk_physattach((int)driveno, (char
 * *)name) (void) fdisk_physdetach((int)driveno) (void) fdisk_ioattach()
 * (void) fdisk_iodetach()
 * 
 * DATA 	     :	none
 * 
 * -------------------------------------------------------------------------
 * [1.2 DATATYPES FOR [1.1] (if not basic C types)]
 * 
 * STRUCTURES/TYPEDEFS/ENUMS:
 * 
 * -------------------------------------------------------------------------
 * [1.3 INTERMODULE IMPORTS] (not o/s objects or standard libs)
 * 
 * PROCEDURES() : 	none
 * 
 * DATA 	     : 	none
 * 
 * -------------------------------------------------------------------------
 * 
 * [1.4 DESCRIPTION OF INTERMODULE INTERFACE]
 * 
 * [1.4.1 IMPORTED OBJECTS]
 * 
 * FILES ACCESSED    :	disk image file(s) for C: (and D:)
 * 
 * DEVICES ACCESSED  :	none
 * 
 * SIGNALS CAUGHT	  :	none
 * 
 * SIGNALS ISSUED	  :	none
 * 
 * 
 * [1.4.2 EXPORTED OBJECTS]
 * =========================================================================
 * PROCEDURE	  : 	fdisk_inb((io_addr)port, (unsigned char *)value)
 * fdisk_outb((io_addr)port, (unsigned char)value)
 * 
 * PURPOSE		  : 	i/o space read (write) of a taskfile register these
 * should be byte accesses.
 * 
 * fdisk_inw((io_addr)port, (unsigned short *)value) fdisk_outw((io_addr)port,
 * (unsigned short)value)
 * 
 * PURPOSE		  : 	i/o space read (write) of next index into sector
 * buffer. (for port 1f0). Not normal usage for accessing taskfile.
 * 
 * 
 * PARAMETERS
 * 
 * port	  : 	the i/o address to read. Taskfile addresses are (hex): (1f0)
 * -	data register (see fdisk_inw) 1f1	-	error register 1f2
 * -	sector count register 1f3	-	sector number register 1f4
 * -	cylinder low register 1f5	-	cylinder high register 1f6
 * -	drive/head register 1f7	-	status register
 * 
 * value	  :	(pointer to) byte (short) to receive the value contained in
 * the specified register.
 * 
 * GLOBALS		  :	none.
 * 
 * RETURNED VALUE	  : 	copy of returned value (inb, inw)
 * 
 * DESCRIPTION	  : 	this procedure returns the contents of the specified
 * register, having called any active state machine (which may update the
 * status). Each disk command has a corresponding state machine, which gets
 * initialised when the command is issued, and subsequently called as the
 * command progresses. Only those commands involving data transfer between
 * the host (cpu) and the adaptor can cause multiple calls to the state
 * machine
 * 
 * ERROR INDICATIONS :	none
 * 
 * ERROR RECOVERY	  :	none
 * =========================================================================
 * PROCEDURE	  : 	(unsigned char) fdisk_read_dir((io_addr)port,
 * (unsigned char *)value)
 * 
 * PURPOSE		  : 	return the value of the 7 bits in the fixed disk
 * register pertinent to the fixed disk. (cooperates with floppy)
 * 
 * PARAMETERS	  :	as per fdisk_inb()
 * 
 * GLOBALS		  :	the taskfile
 * 
 * RETURNED VALUE	  : 	value of digital input register (at port 0x3f7)
 * 
 * DESCRIPTION	  : 	obvious
 * 
 * ERROR INDICATIONS :	none
 * 
 * ERROR RECOVERY	  :	none
 * 
 * =========================================================================
 * PROCEDURE	  : 	fdisk_ioattach() fdisk_iodetach()
 * 
 * PURPOSE		  : 	attach/detach the fixed disk components to the
 * io-subsytem. plug/unplug the configured drives to the disk controller (if
 * any). Patches drive parameter block table entries 0 and 1 in system ROM.
 * 
 * PARAMETERS	  :	none
 * 
 * GLOBALS		  :	the drive structure [fd, wiredup members]
 * 
 * RETURNED VALUE	  : 	none
 * 
 * DESCRIPTION	  : 	attaches/detaches to ios as usual. For the drive(s),
 * the drive structure is used to see whether the disk image(s) are opened or
 * non-existent. Based on existence, drive structure indicates plugged
 * in/unplugged. On attach, disk parameter block table entries 1 and 2 (entry
 * 1 for drive type 1 is used for the C drive (drive 0), and entry 2 for
 * drive type 2 is used for the D drive (drive 1)) are edited to reflect the
 * #.of cylinders available.
 * 
 * ERROR INDICATIONS :	none
 * 
 * ERROR RECOVERY	  :	none
 * =========================================================================
 * PROCEDURE	  : 	fdisk_physattach(driveno, name)
 * fdisk_physdetach(driveno)
 * 
 * PURPOSE		  : 	validate and attach host resource(s) for drive(s)
 * /detach host resource(s).
 * 
 * PARAMETERS	  : driveno		-	0 (drive 0 (C:)) or	1
 * (drive 1 (D:)) name		-	pointer to string for hard disk file
 * name. If null string, then the indicated drive is taken to not exist.
 * 
 * GLOBALS		  :	the drive structure [fd, maxcyl members]
 * 
 * RETURNED VALUE	  : 	none
 * 
 * DESCRIPTION	  : 	for attaching, use host validation procedure to
 * validate and open the specified drive image (if not the null string, else
 * mark as no drive available). Use the #.of cylinders returned by the
 * validation to set the max.cylinder number (= #.cyls - 1) into the drive
 * structure for the drive.
 * 
 * ERROR INDICATIONS :	none
 * 
 * ERROR RECOVERY	  :	none
 * 
 * =========================================================================
 * PROCEDURE	  : 	fdisk_reset()
 * 
 * PURPOSE		  : 	power up the disk subsystem.
 * 
 * PARAMETERS	  :	none
 * 
 * GLOBALS		  :	the taskfile
 * 
 * RETURNED VALUE	  : 	none
 * 
 * DESCRIPTION	  : 	sets registers etc. to their powered-up, pre-POST
 * values.
 * 
 * ERROR INDICATIONS :	none
 * 
 * ERROR RECOVERY	  :	none
 * 
 * 
 * =========================================================================
 * [3.INTERMODULE INTERFACE DECLARATIONS]
 * =========================================================================
 * 
 * [3.1 INTERMODULE IMPORTS]
 */
IMPORT	int	soft_reset;

/* [3.1.1 #INCLUDES]                                                    */

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_HDA.seg"
#endif

#include <stdio.h>
#include TypesH
#include StringH
#include "xt.h"
#include "trace.h"
#include CpuH
#include "error.h"
#include "config.h"
#include "ios.h"
#include "ica.h"
#include "dsktrace.h"
#include "debug.h"
#include "sas.h"
#include "quick_ev.h"

/* [3.1.2 DECLARATIONS]                                                 */

/* [3.2 INTERMODULE EXPORTS]						 */
#include "fdisk.h"


/*
 * 5.MODULE INTERNALS   :   (not visible externally, global internally)]
 * 
 * [5.1 LOCAL DECLARATIONS]
 */

/* [5.1.1 #DEFINES]							 */

/*
 * task file ports (9 registers accessed via 7 ports)
 */
#define TFBASE		0x1f0

#define	TFERROR		0x1f1
#define	TFPCMP		0x1f1
#define	TFSCOUNT	0x1f2
#define	TFSNUM		0x1f3
#define	TFCYLLO		0x1f4
#define	TFCYLHI		0x1f5
#define	TFDRVHD		0x1f6
#define	TFSTATUS	0x1f7
#define	TFCMD		0x1f7

/*
 * task file indices ... the 7 ports map out to 9 registers (by using the
 * read/write io signal) arbitrarily set the write precomp register and the
 * command registers at indices 8 and 9
 */
#define	WDERROR		1
#define	WDSCOUNT	2
#define	WDSNUM		3
#define	WDCYLLO		4
#define	WDCYLHI		5
#define	WDDRVHD		6
#define	WDSTAT		7
#define	WDPCMP		8
#define	WDCMD		9

/*
 * wd2010 command register bits.
 */
#define WDCMD_I 0x08
#define WDCMD_M 0x04
#define WDCMD_L 0x02
#define WDCMD_T 0x01

/*
 * command decoding constants, where bits 7-4 of command are non-unique
 */
#define	DIAG_OR_PARMS	9
#define	DIAG		0
#define	PARMS		1

/*
 * wd2010 status register bits
 */
#define	WDSTATBUSY		0x80
#define	WDSTATDRDY		0x40
#define	WDSTATWFAULT		0x20
#define	WDSTATSEEKCOMPLETE	0x10
#define	WDSTATDRQ		0x08
#define	WDSTATDC		0x04
#define	WDSTATCIP		0x02
#define	WDSTATERROR		0x01

/*
 * wd2010 error register bits (bits 0,1,6,7 never get set for this emulation
 * ... since these reflect bad ECC etc.)
 */
#define	WDERRNOID		0x10
#define	WDERRABORT		0x04

/*
 * io signal
 */
#define	IOREAD		0
#define	IOWRITE		~IOREAD

/*
 * interrupt line value
 */
#define	IRQDEASSERT	0
#define	IRQCLEAR	1
#define 	IRQASSERT	~IRQDEASSERT

/*
 * state control for disk commands
 */
#define	START		1	/* new command		 */
#define	CONTINUE	2	/* command in progress	 */
#define	BRDY		3	/* sector buffer ready 	 */
#define	BCR		4	/* clear sector buffer counter */

#define	IDMISMATCH	~0	/* bad seek		 */

/* [5.1.2 TYPEDEF, STRUCTURE, ENUM DECLARATIONS]			 */

/*
 * following describes the disk geometry for a drive, and the file descriptor
 * for the disk image.
 */
typedef struct _drvinfo
{
	long            driveid;
	long            physattached;
	long            maxhead;
	long            maxcyl;
	long            maxsect;
	long            nsecspertrack;
	long            nbytespercyl;
	long            nbytespertrack;
	long            wiredup;
	long            curoffset;
}               drvinfo_;


/* [5.1.3 PROCEDURE() DECLARATIONS]					 */

#ifdef ANSI
static void     restore(int);
static void     seek(int);
static void     rsector(int);
static void     wsector(int);
static void     format(int);
static void     rverify(int);
static void     diagnose(int);
static void     setparams(int);
static void     bad(int);
static void     irq(int);
static void     rmac(unsigned short *, int);
static long     dosearchid(void);
static void     doseek(void);
static int      disktobuffer(long);
static int      buffertodisk(long);
#else
static void     restore();
static void     seek();
static void     rsector();
static void     wsector();
static void     format();
static void     rverify();
static void     diagnose();
static void     setparams();
static void     bad();
static void     irq();
static void     rmac();
static long     dosearchid();
static void     doseek();
static int      disktobuffer();
static int      buffertodisk();
#endif				/* ANSI */

/*
 * -----------------------------------------------------------------------
 * [5.2 LOCAL DEFINITIONS]
 * 
 * [5.2.1 INTERNAL DATA DEFINITIONS
 */

/*
 * dual card can support 2 drives max.
 */
static drvinfo_ drives[2];

/*
 * pointer to the currently selected drive. (gets set up whenever the
 * drive/head register in the taskfile gets written to)
 */
static drvinfo_ *pseldrv;

/*
 * the fixed disk register (write only, 0x3f6)
 */
static unsigned char fixeddiskreg;

/*
 * the digital input register .. reflects state of head and drive selected
 */
static unsigned char digipreg;

/*
 * the wd2010 taskfile (element [0] is pad byte) ... 9 registers + dummy to
 * allow direct table indexing
 */
LOCAL unsigned char taskfile[10];

/*
 * command dispatch ... based on bits 7-4 of command register as index.
 */
LOCAL void     (*dispatch[]) IPT1(int, state) =
{
	bad,
	restore,
	rsector,
	wsector,
	rverify,
	format,
	bad,
	seek,
	bad,
	bad,			/* further decoded	 */
	bad,
	bad,
	bad,
	bad,
	bad,
	bad,
};

/*
 * if a command is in progress, this points to the state machine for that
 * command
 */
LOCAL void     (*activecmd) IPT1(int, state);

/*
 * sector buffer variables
 */
static unsigned char sectindx;
static unsigned short sectbuffer[256];

/* [5.2.2 INTERNAL PROCEDURE DEFINITIONS]				 */

/*
 * ==========================================================================
 * FUNCTION	:	dosearchid() PURPOSE		:	simulate
 * searching for <cyl,hd,sec> id field. If out of range for configured
 * geometry, return failure, else return appropriate file offset into disk
 * image. N.B This assumes that ALL TRACKS have been FORMATTED WITH SECTOR
 * ID's 1-#.sectors per track). EXTERNAL OBJECTS:	drive info for
 * selected drive; taskfile. RETURN VALUE	:	-1 if bad parameters
 * for command else file offset (from byte 0) into hard disk image. INPUT
 * PARAMS	: RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL long 
dosearchid IFN0()
{
	long            head;
	long            cylinder;
	long            sector;

	/*
	 * head ok? (heads numbered from 0 - maxhead)
	 */
	head = taskfile[WDDRVHD] & 0xf;

	if (head > 7 && !(fixeddiskreg & 0x8))
		return IDMISMATCH;

	if (head > pseldrv->maxhead)
		return IDMISMATCH;

	/*
	 * sector ok? (assumes all tracks have been formatted with sector ids
	 * 1 - nsecspertrack which is DOS standard)
	 */
	sector = taskfile[WDSNUM];

	if (sector > pseldrv->nsecspertrack || sector <= 0)
		return IDMISMATCH;

	/*
	 * cylinder ok? (we've imposed an artificial limit on the maximum
	 * cylinder number based upon the file size)
	 */
	cylinder = ((unsigned long) taskfile[WDCYLHI] << 8) +
		(unsigned long) taskfile[WDCYLLO];
	if (cylinder > pseldrv->maxcyl)
		return IDMISMATCH;

	return (cylinder * pseldrv->nbytespercyl + head *
		pseldrv->nbytespertrack + (sector - 1L) * 512L);
}

/*
 * ==========================================================================
 * FUNCTION	:	updateposregs() PURPOSE		:	update the
 * sector count and number registers. if this produces a track or cylinder
 * transfer, then update the head/cylinder registers. This is a guestimate of
 * what the WD1015 micro is doing when it handles these situations, and
 * programs the WD2010. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:
 * RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
updateposregs IFN0()
{
	(taskfile[WDSCOUNT])--;
	if (++taskfile[WDSNUM] > pseldrv->nsecspertrack)
	{
		int             head;
		int             cylinder;

		/*
		 * start at sector 1 of next track
		 */
		taskfile[WDSNUM] = 1;
		head = taskfile[WDDRVHD] & 0xf;
		if (++head > pseldrv->maxhead)
		{

			/*
			 * need to select next cylinder and use head 0.
			 */
			taskfile[WDDRVHD] &= 0xf0;

			cylinder = ((unsigned int) taskfile[WDCYLHI] << 8) + taskfile[WDCYLLO];
			taskfile[WDCYLLO] = (++cylinder) & 0xff;
			taskfile[WDCYLHI] = cylinder >> 8;
		} else
		{

			/*
			 * select next head to read next track
			 */
			taskfile[WDDRVHD] &= 0xf0;
			taskfile[WDDRVHD] |= head;
			if (head > 7)
				fixeddiskreg |= 8;
		}
	}
}

/*
 * ==========================================================================
 * FUNCTION	:	doseek() PURPOSE		:	'seek' to
 * requested cylinder. if requested drive not wired up, then drive will not
 * be ready. (and seek will be incomplete). If cylinder exceeds max. get
 * drive ready and seek complete, but will get 'id not found' when the track
 * gets accessed. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:
 * RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
doseek IFN0()
{

	/*
	 * the drive ready status will be set up when the drive/head register
	 * gets written to. here, just set the seek complete bit in the
	 * status register appropriately
	 */
	if (taskfile[WDSTAT] & WDSTATDRDY)
	{
		dt0(DHW | HWXINFO, 0, "\t\t+SC\n")
		taskfile[WDSTAT] |= WDSTATSEEKCOMPLETE;
	} else
	{
		dt0(DHW | HWXINFO, 0, "\t\t-SC\n")
			taskfile[WDSTAT] &= ~WDSTATSEEKCOMPLETE;
	}
}

/*
 * ==========================================================================
 * FUNCTION	:	restore() PURPOSE		: EXTERNAL OBJECTS:
 * RETURN VALUE	: INPUT  PARAMS	:	state	-	START or CONTINUE
 * RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
restore IFN1(int, state)
{
	UNUSED(state);
	dt0(DHW | HWXINFO, 0, "\tRESTORE cmd\n")
		doseek();
	if (!(taskfile[WDSTAT] & WDSTATDRDY))
	{
		taskfile[WDSTAT] |= WDSTATERROR;
		taskfile[WDERROR] = WDERRABORT;
		dt0(DHW | HWXINFO, 0, "\t\t+ERROR,err=abort\n")
	}
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP\n")
		taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	seek() PURPOSE		:	emulate seek command.
 * Just sets up appropriate status changes (error condition possibly)
 * EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:	state	-
 * START or CONTINUE RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
seek IFN1(int, state)
{
	UNUSED(state);
	dt0(DHW | HWXINFO, 0, "\tSEEK cmd\n")

		doseek();

	/*
	 * if drive not ready, error
	 */
	if (!(taskfile[WDSTAT] & WDSTATDRDY))
	{
		dt0(DHW | HWXINFO, 0, "\t\t+ERROR,err=abort\n")
			taskfile[WDSTAT] |= WDSTATERROR;
		taskfile[WDERROR] = WDERRABORT;
	}
	/*
	 * deassert 'busy' and 'command in progress'; reset the sector buffer
	 * counter; issue interrupt
	 */
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP\n")
		taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	rsector() PURPOSE		:	emulate read
 * command. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:	state
 * -	START or CONTINUE or BRDY RETURN PARAMS   :
 * ==========================================================================
 */

LOCAL void 
fdisk_pause IFN1(long, junk)
{
	UNUSED(junk);
	(*activecmd) (CONTINUE);
}

LOCAL void 
rsector IFN1(int, state)
{
	static int      s;
	long            offset;

	if (state == START)
	{
		dt0(DHW | HWXINFO, 0, "\tREAD cmd\n")
			s = 0;
	}
	while (1)
		switch (s)
		{
		case 0:
			doseek();
			if (!(taskfile[WDSTAT] & WDSTATDRDY))
			{
				dt0(DHW | HWXINFO, 0, "\t\t(drv not ready)set err=abort\n")
					taskfile[WDERROR] |= WDERRABORT;
				s = 3;
				continue;
			}
			s = 1;
		case 1:
			if ((offset = dosearchid()) == IDMISMATCH)
			{
				dt0(DHW | HWXINFO, 0, "\t\tset err=abort,noid\n")
					taskfile[WDERROR] = WDERRNOID | WDERRABORT;
				s = 3;
				continue;

			}
			s = 2;
		case 2:
			rmac((unsigned short *) 0, BCR);

			/*
			 * disk controller transfers disk data to sector
			 * buffer
			 */
			if (!disktobuffer(offset))
			{
				dt0(DHW | HWXINFO, 0, "\t\tset err=abort,noid\n")
					taskfile[WDERROR] = WDERRNOID | WDERRABORT;
				s = 3;
				continue;

			}
			rmac((unsigned short *) 0, BCR);
			updateposregs();

			/*
			 * at this point, tell the host to unload the sector
			 * buffer, and wait for BRDY signal from sector
			 * buffer when unloaded. (this will occur once host
			 * has done 256 inw's or equivalent)
			 */
			taskfile[WDSTAT] |= WDSTATDRQ;
			taskfile[WDSTAT] &= ~WDSTATBUSY;
			dt0(DHW | HWXINFO, 0, "\t\t+DRQ -BUSY\n")

				if (taskfile[WDSCOUNT]
			/* && (taskfile[WDCMD] & WDCMD_M) ??? M=1 case ??? */
				)
			{
				/* more sectors to come */
				irq(IRQASSERT);
				s = 30;
			} else
			{
				/* last sector done */
				irq(IRQASSERT);
				s = 31;
			}
			return;
		case 3:

			/*
			 * flag error in status register
			 */
			taskfile[WDSTAT] |= WDSTATERROR;
			taskfile[WDSTAT] |= WDSTATDRQ;
			taskfile[WDSTAT] &= ~WDSTATBUSY;
			dt0(DHW | HWXINFO, 0, "\t\t+DRQ +ERROR -BUSY\n")
				irq(IRQASSERT);
			s = 31;
			return;
		case 30:

			/*
			 * wait for buffer ready signal from sector buffer
			 */
			if (state == BRDY)
			{
				taskfile[WDSTAT] &= ~WDSTATDRQ;
				taskfile[WDSTAT] |= WDSTATBUSY;
				dt0(DHW | HWXINFO, 0, "\t\t-DRQ +BUSY\n")
					irq(IRQDEASSERT);
				/*
				 * Give CPU chance to run while we find the
				 * next disk sector. In particular the BIOS
				 * can read the disk status register before
				 * we raise the next interrupt. This is
				 * crucial as reading the disk status
				 * register deasserts the interrupt.
				 */
				add_q_event_i(fdisk_pause, HOST_FDISK_DELAY_1, 0);
				s = 32;
			}
			return;
		case 31:
			if (state == BRDY)
			{
				taskfile[WDSTAT] &= ~WDSTATDRQ;
				taskfile[WDSTAT] &= ~WDSTATCIP;
				dt0(DHW | HWXINFO, 0, "\t\t-DRQ -CIP\n")
					irq(IRQDEASSERT);
				rmac((unsigned short *) 0, BCR);
			}
			return;
		case 32:
			s = 1;
			continue;
		}
}

/*
 * ==========================================================================
 * FUNCTION	:	wsector() PURPOSE		:	handle the
 * 'write' command. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:
 * state	-	START or CONTINUE or BRDY RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
wsector IFN1(int, state)
{
	static int      s;
	long            offset;

	if (state == START)
	{
		dt0(DHW | HWXINFO, 0, "\tWRITE cmd\n")
			s = 0;
	}
	while (1)
		switch (s)
		{
		case 0:

			/*
			 * tell host to fill sector buffer
			 */
			dt0(DHW | HWXINFO, 0, "\t\t+DRQ\n")
				taskfile[WDSTAT] |= WDSTATDRQ;
			s = 1;
			return;
		case 1:

			/*
			 * wait for sector buffer to be filled by host (don't
			 * issue IRQ first time around)
			 */
			if (state == BRDY)
			{
				dt0(DHW | HWXINFO, 0, "\t\t-DRQ\n")
					taskfile[WDSTAT] &= ~WDSTATDRQ;
				s = 2;
				continue;
			}
			return;
		case 2:

			/*
			 * check drive is ready, and can find sector to write
			 * to.
			 */
			doseek();
			if (!(taskfile[WDSTAT] & WDSTATDRDY))
			{
				dt0(DHW | HWXINFO, 0, "\t\t(drv not ready)err=abort\n")
					taskfile[WDERROR] |= WDERRABORT;
				s = 10;
				continue;
			} else
			{
				if ((offset = dosearchid()) == IDMISMATCH)
				{
					dt0(DHW | HWXINFO, 0, "\t\terr=abort,noid\n")
						taskfile[WDERROR] = WDERRNOID | WDERRABORT;
					s = 10;
					continue;

				}
				s = 3;
				continue;
			}
		case 3:
			rmac((unsigned short *) 0, BCR);

			/*
			 * disk controller transfers sector buffer to disk
			 */
			if (!buffertodisk(offset))
			{

				/*
				 * any errors we report as missing ID
				 */
				dt0(DHW | HWXINFO, 0, "\t\terr=abort,noid\n")
					taskfile[WDERROR] = WDERRNOID | WDERRABORT;
				s = 10;
				continue;

			}
			rmac((unsigned short *) 0, BCR);
			updateposregs();
			if (taskfile[WDSCOUNT])
			{

				/*
				 * more to go ... tell host to fill sector
				 * buffer again
				 */
				dt0(DHW | HWXINFO, 0, "\t\t+DRQ\n")
					taskfile[WDSTAT] |= WDSTATDRQ;
				irq(IRQASSERT);
				s = 4;
				return;
			} else
				/*
				 * all done
				 */
				s = 11;
			continue;
		case 4:

			/*
			 * wait for sector buffer to be filled by host
			 */
			if (state == BRDY)
			{
				dt0(DHW | HWXINFO, 0, "\t\t-DRQ\n")
					taskfile[WDSTAT] &= ~WDSTATDRQ;

				/*
				 * prepare for next sector
				 */
				s = 2;
				continue;
			}
			return;
		case 10:
			dt0(DHW | HWXINFO, 0, "\t\tset ERROR\n")
				taskfile[WDSTAT] |= WDSTATERROR;
		case 11:
			dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP\n")
			taskfile[WDSTAT] &= ~WDSTATBUSY;
			taskfile[WDSTAT] &= ~WDSTATCIP;
			irq(IRQASSERT);
			rmac((unsigned short *) 0, BCR);
			return;
		}
}

/*
 * ==========================================================================
 * FUNCTION	:	format() PURPOSE		:	handle the
 * 'format' command. almost a 'dummy' command, but needs to make host write
 * to sector buffer. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:
 * state	-	START or CONTINUE or BRDY RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
format IFN1(int, state)
{
	static int      s;

	if (state == START)
	{
		dt0(DHW | HWXINFO, 0, "\tFORMAT cmd\n")
			s = 0;
	}
	while (1)
		switch (s)
		{
		case 0:

			/*
			 * initialise sector buffer tell application to write
			 * to sector buffer
			 */
			rmac((unsigned short *) 0, BCR);
			dt0(DHW | HWXINFO, 0, "\t\t+DRQ\n")
				taskfile[WDSTAT] |= WDSTATDRQ;
			s = 1;
			return;
		case 1:

			/*
			 * wait for sector buffer to fill
			 */
			if (state == BRDY)
			{

				/*
				 * no more data, thanks.
				 */
				doseek();
				dt0(DHW | HWXINFO, 0, "\t\t-DRQ\n")
					taskfile[WDSTAT] &= ~WDSTATDRQ;
				if (!(taskfile[WDSTAT] & WDSTATDRDY))
				{

					/*
					 * formatting thin air!
					 */
					s = 2;
					continue;
				} else
				{
					int             cylinder;

					cylinder = ((unsigned int) taskfile[WDCYLHI] << 8)
						+ taskfile[WDCYLLO];
					if (cylinder > pseldrv->maxcyl)

						/*
						 * formatting the spindle!
						 */
						s = 2;
					else
						s = 3;
					continue;
				}
			}
			return;
		case 2:
			dt0(DHW | HWXINFO, 0, "\t\t+ERROR,err=abort")
				taskfile[WDERROR] |= WDERRABORT;
			taskfile[WDSTAT] |= WDSTATERROR;
			s = 3;
		case 3:
			irq(IRQASSERT);
			dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP")
				taskfile[WDSTAT] &= ~WDSTATBUSY;
			taskfile[WDSTAT] &= ~WDSTATCIP;
			rmac((unsigned short *) 0, BCR);
			return;
		}
}

/*
 * ==========================================================================
 * FUNCTION	:	rverify() PURPOSE		:	handle the
 * 'read verify' command. .. basically a dummy command. EXTERNAL OBJECTS:
 * RETURN VALUE	: INPUT  PARAMS	:	state	-	START or CONTINUE
 * RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
rverify IFN1(int, state)
{

	UNUSED(state);
	/*
	 * if drive not ready, error
	 */
	dt0(DHW | HWXINFO, 0, "\tREAD VERIFY cmd\n")
		doseek();
	if (!(taskfile[WDSTAT] & WDSTATDRDY))
	{
		dt0(DHW | HWXINFO, 0, "\t\t+ERROR,err=abort\n")
			taskfile[WDSTAT] |= WDSTATERROR;
		taskfile[WDERROR] = WDERRABORT;
	}
	/*
	 * deassert 'busy' and 'command in progress'; reset the sector buffer
	 * counter; issue interrupt
	 */
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP\n")
		taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	diagnose() PURPOSE		:	handle the
 * 'diagnostics' command. EXTERNAL OBJECTS: RETURN VALUE	: INPUT
 * PARAMS	:	state	-	START or CONTINUE RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
diagnose IFN1(int, state)
{
	UNUSED(state);
	dt0(DHW | HWXINFO, 0, "\tDIAGNOSTICS cmd\n")

	/*
	 * flag diagnostics as successful
	 */
		dt0(DHW | HWXINFO, 0, "\t\terr=1\n")
		taskfile[WDERROR] = 1;

	/*
	 * deassert 'busy' and 'command in progress'; reset the sector buffer
	 * counter; issue interrupt
	 */
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP\n")
		taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	setparams() PURPOSE		:	handle the
 * 'set parameters' command. EXTERNAL OBJECTS:	task file. drives. RETURN
 * VALUE	: INPUT  PARAMS	:	state	-	START or CONTINUE
 * RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
setparams IFN1(int, state)
{

	UNUSED(state);
	
	dt0(DHW | HWXINFO, 0, "\tSET PARAMETERS cmd\n")

	/*
	 * legal head values are 0 to (nheads-1)
	 */
		pseldrv->maxhead = taskfile[WDDRVHD] & 0xf;

	/*
	 * legal sector ids are 1 to nsecspertrack
	 */
	pseldrv->nsecspertrack = taskfile[WDSCOUNT];

	/*
	 * calculate some geometry constants ... the dual card is set up for
	 * 512 byte sectors.
	 */
	pseldrv->nbytespertrack = pseldrv->nsecspertrack * 512L;
	pseldrv->nbytespercyl = pseldrv->nbytespertrack * (pseldrv->maxhead + 1L);


	/*
	 * deassert 'busy' and 'command in progress'; reset the sector buffer
	 * counter; issue interrupt
	 */
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY,-CIP,+SC\n")
		taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	taskfile[WDSTAT] |= WDSTATSEEKCOMPLETE;
	rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	bad() PURPOSE		:	handle unrecognised
 * disk commands EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:	state
 * -	START or CONTINUE RETURN PARAMS   :
 * ==========================================================================
 */
LOCAL void 
bad IFN1(int, state)
{
	UNUSED(state);
	
	dt0(DHW | HWXINFO, 0, "\tBAD cmd\n")

		taskfile[WDSTAT] |= WDSTATERROR;
	taskfile[WDSTAT] &= ~WDSTATBUSY;
	taskfile[WDSTAT] &= ~WDSTATCIP;
	taskfile[WDERROR] = WDERRABORT;
	dt0(DHW | HWXINFO, 0, "\t\t-BUSY -CIP +ERROR;err=abort\n")
		rmac((unsigned short *) 0, BCR);
	irq(IRQASSERT);
}

/*
 * ==========================================================================
 * FUNCTION	:	wd2010() PURPOSE		:	main
 * procedure for the disk controller component of the dual card. This does
 * NOT handle sector buffer accesses. EXTERNAL OBJECTS:	taskfile,sectbuf.
 * RETURN VALUE	: INPUT  PARAMS	:	taskindx	-	index into
 * taskfile value		-	pointer to byte value io
 * 	IOREAD or IOWRITE RETURN PARAMS   :	*value		-	set
 * if IOWRITE
 * ==========================================================================
 */
LOCAL void 
wd2010 IFN3(int, taskindx, half_word *, value, int, io)
{

	/*
	 * ignore any task file writes if command in progress (unless new
	 * command)
	 */
	if ((taskfile[WDSTAT] & WDSTATCIP) &&
	    io == IOWRITE && taskindx != WDCMD)
	{
		dt0(DHW | HWXINFO, 0, "\tcommand in progress .. write ignored\n")
			return;
	}
	if (io == IOREAD)
	{

		/*
		 * deassert the interrupt request line.
		 */
		if (taskindx == WDSTAT)
			irq(IRQDEASSERT);

		/*
		 * if a command is in progress, then use this poll as an
		 * excuse to kick the controller again.
		 */
		if (taskfile[WDSTAT] & WDSTATCIP)
			(*activecmd) (CONTINUE);



		/*
		 * for the status register, return the WD2010 status. The
		 * real maccoy actually returns the RMAC's status which
		 * differs from the WD2010 in usage of bit 1. For the WD2010,
		 * this is the 'command in progress' status. For the RMAC,
		 * this is derived from the Index signal returned from the
		 * drive. Since this gives the appearance of being randomish,
		 * (unless being explicitly examined over a long period of
		 * time), just use the WD2010 value.
		 */
		*value = taskfile[taskindx];

		return;
	}
	/*
	 * must be writing to task file and no command is in progress.
	 */
	taskfile[taskindx] = *value;
	if (taskindx == WDCMD)
	{
		int             cmd, drdy;

		/*
		 * deassert interrupt request, clear error register, set
		 * 'command in progress' and busy. maintain same status of
		 * drive ready signal.
		 */
		irq(IRQDEASSERT);
		taskfile[WDERROR] = 0;
		drdy = taskfile[WDSTAT] & WDSTATDRDY;
		if (drdy)
		{
			dt0(DHW | HWXINFO, 0, "\t\t+CIP -WF -SC -DRQ -DC -ERROR +BUSY +DRDY\n")
		} else
		{
			dt0(DHW | HWXINFO, 0, "\t\t+CIP -WF -SC -DRQ -DC -ERROR +BUSY -DRDY\n")
		}
		taskfile[WDSTAT] = WDSTATCIP | WDSTATBUSY | drdy;

		/*
		 * decode the command
		 */
		cmd = *value >> 4;
		if (cmd == DIAG_OR_PARMS)
		{
			if ((*value & 0xf) == DIAG)
				activecmd = diagnose;
			else if ((*value & 0xf) == PARMS)
				activecmd = setparams;
			else
				activecmd = bad;
		} else
			activecmd = dispatch[cmd];

		/*
		 * and start the command off
		 */
		(*activecmd) (START);
	} else if (taskindx == WDDRVHD)
	{

		/*
		 * digital input register reflects head select lines
		 */
		digipreg = (taskfile[WDDRVHD] & 0xf) >> 2;

		if (taskfile[WDDRVHD] & 0x10)
		{

			/*
			 * drive 1 selected
			 */
			digipreg |= 2;

			/*
			 * second drive selected. does it exist?
			 */
			if (!drives[1].wiredup)
			{
				/* no */
				pseldrv = (drvinfo_ *) 0;
				taskfile[WDSTAT] &= ~WDSTATDRDY;
				dt0(DHW | HWXINFO, 0, "\t\t-DRDY\n")
			} else
			{
				/* yes */
				pseldrv = &drives[1];
				taskfile[WDSTAT] |= WDSTATDRDY;
				dt0(DHW | HWXINFO, 0, "\t\t+DRDY\n")
			}
		} else
		{

			/*
			 * first drive selected
			 */

			/*
			 * drive 1 selected
			 */
			digipreg |= 1;

			if (!drives[0].wiredup)
			{
				/* no */
				pseldrv = (drvinfo_ *) 0;
				taskfile[WDSTAT] &= ~WDSTATDRDY;
				dt0(DHW | HWXINFO, 0, "\t\t-DRDY\n")
			} else
			{
				/* yes */
				pseldrv = drives;
				taskfile[WDSTAT] |= WDSTATDRDY;
				dt0(DHW | HWXINFO, 0, "\t\t+DRDY\n")
			}
		}
	}
}

/*
 * ==========================================================================
 * FUNCTION	:	irq() PURPOSE		:	assert/deassert the
 * fixed disk interrupt line EXTERNAL OBJECTS: RETURN VALUE	: INPUT
 * PARAMS	:	line	-	IRQCLEAR,IRQASSERT or IRQDEASSERT
 * RETURN PARAMS   :
 * ==========================================================================
 */

void disk_int_call_back IFN1(long, junk)
{
	UNUSED(junk);
	ica_hw_interrupt (1,6,1);
}

LOCAL void 
irq IFN1(int, line)
{
	static int      intrq = IRQDEASSERT;
	switch (line)
	{
	case IRQCLEAR:
		dt0(DHW | INTRUPT, 0, "\t\t** -IRQ\n")
			intrq = IRQDEASSERT;
		break;
	case IRQASSERT:
		if (intrq != IRQASSERT)
		{
			dt0(DHW | INTRUPT, 0, "\t\t** +IRQ\n")

				intrq = IRQASSERT;

			/*
			 * check interrupts not masked out
			 */
			if (!(fixeddiskreg & 2))
				add_q_event_i (disk_int_call_back, HOST_FDISK_DELAY_2, 0);
		}
		break;
	case IRQDEASSERT:
		if (intrq == IRQASSERT)
		{
			dt0(DHW | INTRUPT, 0, "\t\t** -IRQ\n")

				intrq = IRQDEASSERT;

			/* JOKER always wants to deassert the real ICA line */
#ifndef	JOKER
			if (!(fixeddiskreg & 2))
#endif
				ica_clear_int(1, 6);
		}
	}
}

/*
 * ==========================================================================
 * FUNCTION	:	rmac() PURPOSE		:	emulate the buffer
 * manager and controller (WD11C00C-22 (RMAC)) which looks after the sector
 * buffer. EXTERNAL OBJECTS: RETURN VALUE	: INPUT  PARAMS	:	value
 * -	pointer to short to write/read with buffer. sig	-	READ,WRITE or
 * BCR (BCR resets the buffer index .. simulates the BCR pulse) RETURN PARAMS
 * :
 * ==========================================================================
 */
LOCAL void 
rmac IFN2(USHORT *, value, int, sig)
{

	switch (sig)
	{
	case BCR:
		dt0(DHW | HWXINFO, 0, "\t\tBCR raised to sector buffer\n")
			sectindx = 0;
		return;
	case IOWRITE:
		sectbuffer[sectindx] = *value;
		break;
	case IOREAD:
		*value = sectbuffer[sectindx];
	}
	if (!++sectindx)
	{
		if (activecmd)
		{
			dt0(DHW | HWXINFO, 0, "\t\t++sector buffer raises BRDY\n")
				(*activecmd) (BRDY);
		}
	}
}

/*
 * ==========================================================================
 * FUNCTION	:	disktobuffer() PURPOSE		:	read a sector
 * from disk image into sector buffer EXTERNAL OBJECTS:	drive structure
 * RETURN VALUE    :	0 	-	error reading file ~0	-	ok.
 * INPUT  PARAMS	:	offset	-	file offset (from 0)
 * ==========================================================================
 */
#define ONESECTOR	1

LOCAL int 
disktobuffer IFN1(long, offset)
{
	dt1(DHW | HWXINFO,0, 
	    "\t\tdisk data(offset %lx(hex)) -> sector buffer\n", offset)
		return host_fdisk_rd(pseldrv->driveid, offset, ONESECTOR, (char *) sectbuffer);
}

/*
 * ==========================================================================
 * FUNCTION	:	buffertodisk() PURPOSE		:	write the
 * sector buffer to disk image EXTERNAL OBJECTS:	drive structure
 * RETURN VALUE    :	0 	-	error reading file ~0	-	ok.
 * INPUT  PARAMS	:	offset	-	file offset (from 0)
 * ==========================================================================
 */
LOCAL int 
buffertodisk IFN1(long, offset)
{
	dt1(DHW | HWXINFO,0, 
	    "\t\tsector buffer -> disk (offset %lx(hex))\n", offset)

		return host_fdisk_wt(pseldrv->driveid, offset, ONESECTOR, (char *) sectbuffer);
}

/*
 * 7.INTERMODULE INTERFACE IMPLEMENTATION :
 * 
 * [7.1 INTERMODULE DATA DEFINITIONS]
 */
/*
 * [7.2 INTERMODULE PROCEDURE DEFINITIONS]
 */

GLOBAL UTINY fdisk_read_dir IFN2(io_addr, port, UTINY *,value)
{
		switch (port)
	{
	case 0x3f7:
		*value = digipreg;
		dt1(DHW | PORTIO, 0, "read of DIR returns %x(hex)\n", (unsigned) *value)
			break;
	default:
		break;
	}
		return *value;
}

GLOBAL VOID fdisk_inb IFN2(io_addr, port, UTINY *,value)
{
	dt0(DHW, 0, "(\n")

	if (taskfile[WDSTAT] & WDSTATBUSY)
	{
		/* Controller is busy. Return status register contents. */
		*value = taskfile[WDSTAT];
		dt1(DHW | PORTIO, 0, "inb on port %x(hex) - controller busy\n", port)

		/*
		 * deassert the interrupt request line.
		 */
		if ((port - TFBASE) == WDSTAT)
			irq(IRQDEASSERT);

	}
	else
		switch (port)
		{
			case TFBASE:

				/*
		 		 * sector buffer must be accessed only in 16 bit quantities
		 		 */
#ifndef PROD
				printf("(fdisk_inb()) inb on sector buffer ignored!\n");
#endif
				break;
			default:
				dt1(DHW | PORTIO, 0, "inb on port %x(hex)\n", port)

				wd2010(port - TFBASE, value, IOREAD);

				dt1(DHW | PORTIO, 0, "returns %x(hex)\n", (unsigned) *value)
		}
	dt0(DHW, 0, ")\n")
}

GLOBAL VOID fdisk_inw IFN2(io_addr, port, USHORT *, value)
{
#ifdef	JOKER
	/* This is the only way JOKER talks to disks, since
	** Ade's fast disk bios is currently infeasible.
	** Hence, speed is of interest here!
	*/
	if (port == TFBASE)
	{
		/* For speed, JOKER doesn't bother with calling rmac(IOREAD). */

		*value = HostWordToIntelWord(sectbuffer[sectindx]);

		if (!++sectindx)	/* sectindx is a byte value -- wraparound? */
		{
			if (activecmd)
			{
				(*activecmd) (BRDY);
			}
		}
	}

#else	/* JOKER */

#ifdef BIGEND
	unsigned short  temp;
#endif
		switch (port)
	{
	case TFBASE:

#ifdef LITTLEND
			rmac(value, IOREAD);
#endif

#ifdef BIGEND
		rmac(&temp, IOREAD);
		*value = ((temp << 8) & 0xff00) | (temp >> 8);
#endif

#if 0
		dt1(DHW | PORTIO, 0, "inw on sector buffer returns %x(hex)\n", *value)
#else
		dt1(DHW | PORTIO, INW_TRACE_HNDL, "inw on sector buffer", *value)
#endif
			break;
	default:
		/*
		 * task file registers must be accessed as byte quantities
		 */
		dt1(DHW | PORTIO, 0, "inw on port %x(hex) ignored!\n",
		    (unsigned) port)
			break;
	}
#endif	/* JOKER */
}


GLOBAL VOID fdisk_outb IFN2(io_addr, port, UTINY, value)
{
	dt0(DHW, 0, "(\n")
		switch (port)
	{
	case TFBASE:

		/*
		 * byte access to sector buffer is not a good idea
		 */
#ifndef PROD
		printf("(disk_outb()) outb to sector buffer ignored\n!");
#endif
		break;
	case TFPCMP:
		dt2(DHW | PORTIO, 0, "outb to port %x(hex),val %x(hex)\n",
		    (unsigned) port, (unsigned) value)

			wd2010(WDPCMP, &value, IOWRITE);

		dt0(DHW | PORTIO, 0, "\n")
			break;
	case TFCMD:
		dt2(DHW | PORTIO, 0, "outb to port %x(hex),val %x(hex)\n",
		    (unsigned) port, (unsigned) value)

			wd2010(WDCMD, &value, IOWRITE);

		dt0(DHW | PORTIO, 0, "\n")
			break;
	case DISKETTE_FDISK_REG:

		/*
		 * Fixed disk register
		 */
		dt2(DHW | PORTIO, 0, "outb to port %x(hex),val %x(hex)\n",
		    (unsigned) port, (unsigned) value)

			if ((fixeddiskreg & 0x4) && !(value & 0x4))
			{
				/* Turn reset off */
				taskfile[WDSTAT] = WDSTATDRDY | WDSTATSEEKCOMPLETE;
				/* Error register in diagnostic mode */
				taskfile[WDERROR] = 0x01; /* No errors */
				/* Reset rest of taskfile */
				taskfile[WDSCOUNT] = taskfile[WDSNUM] = 0x01;
				taskfile[WDCYLLO] = taskfile[WDCYLHI] = taskfile[WDDRVHD] = 0;
			}

			fixeddiskreg = value;

			if (fixeddiskreg & 0x4)
			{
				/* Enable reset fixed disk function */
				taskfile[WDSTAT] = WDSTATBUSY | WDSTATDC | WDSTATERROR; 
			}
		break;
	default:
		dt2(DHW | PORTIO, 0, "outb to port %x(hex),val %x(hex)\n",
		    (unsigned) port, (unsigned) value)

			wd2010(port - TFBASE, &value, IOWRITE);
		dt0(DHW | PORTIO, 0, "\n")
	}
	dt0(DHW, 0, ")\n")
}

GLOBAL VOID fdisk_outw IFN2(io_addr, port, USHORT, value)
{
#ifdef BIGEND
	unsigned short  temp;
#endif
		switch (port)
	{
	case TFBASE:
#if 0
		dt1(DHW | PORTIO, 0, "outw to sector buffer, val %x(hex)\n",
		    (unsigned) value)
#else
		dt1(DHW | PORTIO, OUTW_TRACE_HNDL, "outw to sector buffer",
		    (unsigned) value)
#endif

#ifdef LITTLEND
			rmac(&value, IOWRITE);
#endif

#ifdef BIGEND
		temp = ((value << 8) & 0xff00) | (value >> 8);
		rmac(&temp, IOWRITE);
#endif

			break;
	default:
		/*
		 * task file registers must be accessed as byte quantities
		 */
		dt1(DHW | PORTIO, 0, "outw to port %x(hex) ignored!\n",
		    (unsigned) port)
			break;
	}
}

GLOBAL VOID fdisk_ioattach IFN0()
{
	unsigned short  ncyls;
	unsigned char   nheads;
	unsigned char   nsects;
	io_addr         p;

	/*
	 * attach to io subsystem
	 */
	io_define_in_routines (HDA_ADAPTOR, fdisk_inb,  fdisk_inw,  0, 0);
	io_define_out_routines(HDA_ADAPTOR, fdisk_outb, fdisk_outw, 0, 0);
	/*
	 * attach taskfile
	 */
	for (p = DISK_PORT_START; p <= DISK_PORT_END; p++)
		io_connect_port(p, HDA_ADAPTOR, IO_READ_WRITE);

	/*
	 * attach Fixed disk register
	 */
	io_connect_port(DISKETTE_FDISK_REG, HDA_ADAPTOR, IO_WRITE);

	if (drives[0].physattached)
	{

		/*
		 * indicate drive is wired up to controller
		 */
		drives[0].wiredup = ~0;

		/*
		 * patch 'ROM' table for drive type 0 with appropriate number
		 * of cylinders
		 */
		ncyls = drives[0].maxcyl + 1;
		nheads = drives[0].maxhead + 1;
		nsects = drives[0].maxsect + 1;
#ifdef REAL_ROM
		host_write_enable((DPB0 & (~0xfff)), (DPB0 & (~0xfff)) + 0x1000);
#endif
		patch_rom(DPB0, (unsigned char) (ncyls & 0xff));
		patch_rom(DPB0+1, (unsigned char) (ncyls >> 8));
		patch_rom(DPB0+2, nheads);
		patch_rom(DPB0+0xe, nsects);
#ifdef REAL_ROM
		host_write_protect((DPB0 & (~0xfff)), (DPB0 & (~0xfff)) + 0x1000);
#endif
		dt1(DHW | IOAD, 0, "drive 0 wiredup, total cyls %d\n",
		    (unsigned) ncyls)
	}
	if (drives[1].physattached)
	{

		/*
		 * indicate drive is wired up to controller
		 */
		drives[1].wiredup = ~0;

		/*
		 * patch 'ROM' table for drive type 0 with appropriate number
		 * of cylinders
		 */
		ncyls = drives[1].maxcyl + 1;
		nheads = drives[1].maxhead + 1;
		nsects = drives[1].maxsect + 1;
#ifdef REAL_ROM
		host_write_enable((DPB1 & (~0xfff)), (DPB1 & (~0xfff)) + 0x1000);
#endif
		patch_rom(DPB1, ncyls & 0xff);
		patch_rom(DPB1 + 1, (ncyls >> 8));
		patch_rom(DPB1+2, nheads);
		patch_rom(DPB1+0xe, nsects);
#ifdef REAL_ROM
		host_write_protect((DPB1 & (~0xfff)), (DPB1 & (~0xfff)) + 0x1000);
#endif
		dt1(DHW | IOAD, 0, "drive 1 wiredup, total cyls %d\n",
		    (unsigned) ncyls)
	}
}

GLOBAL VOID fdisk_iodetach IFN0()
{
	io_addr         p;

	/*
	 * detach from io subsystem
	 */
	for (p = DISK_PORT_START; p <= DISK_PORT_END; p++)
		io_disconnect_port(p, HDA_ADAPTOR);

	io_disconnect_port(DISKETTE_FDISK_REG, HDA_ADAPTOR);

	if (drives[0].physattached)
	{

		/*
		 * indicate not wired up ... reset table in ROM to indicate
		 * 'bad drive type'
		 */
		drives[0].wiredup = 0;
#ifdef REAL_ROM
		host_write_enable((DPB0 & (~0xfff)), (DPB0 & (~0xfff)) + 0x1000);
#endif
		patch_rom(DPB0, 0);
		patch_rom(DPB0 + 1, 0);
		patch_rom(DPB0 + 2, 0);
		patch_rom(DPB0 + 0xe, 0);
#ifdef REAL_ROM
		host_write_protect((DPB0 & (~0xfff)), (DPB0 & (~0xfff)) + 0x1000);
#endif
		dt0(DHW | IOAD, 0, "drive 0 unplugged\n")
	}
	if (drives[1].physattached)
	{

		/*
		 * indicate not wired up ... reset table in ROM to indicate
		 * 'bad drive type'
		 */
		drives[1].wiredup = 0;
#ifdef REAL_ROM
		host_write_enable((DPB1 & (~0xfff)), (DPB1 & (~0xfff)) + 0x1000);
#endif
		patch_rom(DPB1, 0);
		patch_rom(DPB1 + 1, 0);
		patch_rom(DPB1 + 2, 0);
		patch_rom(DPB1 + 0xe, 0);
#ifdef REAL_ROM
		host_write_protect((DPB1 & (~0xfff)), (DPB1 & (~0xfff)) + 0x1000);
#endif
		dt0(DHW | IOAD, 0, "drive 1 unplugged\n")
	}
}

GLOBAL VOID fdisk_physattach IFN1(int,driveno)
{
	int             invalid = 0;
	int             ncyls;
	int             nheads;
	int             nsects;

	drives[driveno].driveid = driveno;
	drives[driveno].physattached = 0;

	if (!*((CHAR *) config_inquire(C_HARD_DISK1_NAME + driveno, NULL)))
		return;

	/*
	 * configuration specifies a disk image file ... validate it
	 */
	 host_fdisk_get_params(driveno, &ncyls, &nheads, &nsects);

	/*
	 * set maximum available cylinder value (this is
	 * artificial. The real controller on an AT does not
	 * have this knowledge ... it fails on ID matching if
	 * it seeks to a non-existent cylinder
	 */
	drives[driveno].nsecspertrack = nsects;
	drives[driveno].nbytespertrack = nsects * 512L;
	drives[driveno].nbytespercyl = drives[driveno].nbytespertrack * nheads;
	drives[driveno].maxcyl = --ncyls;
	drives[driveno].maxhead = --nheads;
	drives[driveno].maxsect = --nsects;
	drives[driveno].physattached = ~0;

	fast_disk_bios_attach(driveno);
}

#ifdef macintosh

GLOBAL VOID fdisk_physdetach IFN1(int,driveno)
{
	if (drives[driveno].physattached)
	{
		host_fdisk_close(driveno);

		drives[driveno].physattached = 0;

		fast_disk_bios_detach (driveno);

		dt0(DHW|PAD, 0, "drive %d closed\n")
	}
}
#endif /* macintosh */

GLOBAL VOID fdisk_reset IFN0()
{
	taskfile[WDERROR] = 0;
	taskfile[WDSCOUNT] = 0;
	taskfile[WDSNUM] = 0;
	taskfile[WDCYLLO] = 0;
	taskfile[WDCYLHI] = 0;
	taskfile[WDDRVHD] = 0;

	/*
	 * show drive 1 selected ... head 0 selected
	 */
	digipreg = 1;

	/*
	 * show drive is ready if available
	 */
	if (drives[0].wiredup)
	{
		taskfile[WDSTAT] = WDSTATDRDY | WDSTATSEEKCOMPLETE;

		/*
		 * reposition file pointer for drive 0 at beginning of file
		 */
		host_fdisk_seek0(0);
	} else
		taskfile[WDSTAT] = 0;

	if (drives[1].wiredup)

		/*
		 * reposition file pointer for drive 1 at beginning of file
		 */
	host_fdisk_seek0(1);

	/*
	 * initialise sector buffer
	 */
	rmac((unsigned short *) 0, BCR);
}


GLOBAL VOID hda_init IFN0()
{
	fdisk_iodetach();
	fast_disk_bios_detach(0);
	fast_disk_bios_detach(1);

	fdisk_physattach(0);
	fdisk_physattach(1);
	fdisk_ioattach();
	fdisk_reset();
}





