]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1995-02-09 11:14:18
authorRichard Burgess <>
Thu, 9 Feb 1995 11:14:18 +0000 (11:14 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:48 +0000 (14:03 +0000)
ossource/floppy.c [new file with mode: 0644]

diff --git a/ossource/floppy.c b/ossource/floppy.c
new file mode 100644 (file)
index 0000000..2d2ea44
--- /dev/null
@@ -0,0 +1,1272 @@
+/* Floppy.C  Floppy Disk Device Driver for MMURTL */\r
+\r
+/*\r
+  MMURTL Operating System Source Code\r
+  Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+  ALL RIGHTS RESERVED   Version 1.0\r
+*/\r
+\r
+#define U32 unsigned long\r
+#define S32 long\r
+#define U16 unsigned int\r
+#define S16 int\r
+#define U8 unsigned char\r
+#define S8 char\r
+\r
+/* MMURTL OS PROTOTYPES */\r
+\r
+extern far SpawnTask(S8  *pEntry,\r
+                            U32 dPriority,\r
+                     U32 fDebug,\r
+                     S8  *pStack,\r
+                            U32 fOSCode);\r
+\r
+extern far U32 AllocExch(U32 *pExchRet);\r
+\r
+extern far U32 AllocDMAPage(U32 nPages, U8 **ppMemRet U32 *pPhyMemRet);\r
+\r
+extern far U32 InitDevDr(U32 dDevNum,\r
+                                         S8  *pDCBs,\r
+                                                 U32 nDevices,\r
+                                                 U32 dfReplace);\r
+\r
+extern far DmaSetUp(S8  *pPhyMem,\r
+                                       U32 sdMem,                      /* size */\r
+                                       U32 dChannel,           /* channel 2 floppy */\r
+                                       U32 dType,                      /* 0=Verfify, 1=IN,  2=OUT */\r
+                                       U32 dMode);             /* FDC uses 1 (single cycle) */\r
+\r
+extern far U32 UnMaskIRQ(U32 IRQNum);\r
+extern far U32 MaskIRQ(U32 IRQNum);\r
+extern far U32 SetIRQVector(U32 IRQNum, S8  *pIRQ);\r
+extern far U32 EndOfIRQ(U32 IRQNum);\r
+extern far U32 SendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 ISendMsg(U32 Exch, U32 msg1, U32 msg2);\r
+extern far U32 WaitMsg(U32 Exch, S8  *pMsgRet);\r
+extern far U32 CheckMsg(U32 Exch, S8  *pMsgRet);\r
+extern far U32 GetTimerTick(U32 *pTickRet);\r
+extern far U32 Alarm(U32 Exch, U32 count);\r
+extern far U32 KillAlarm(U32 Exch);\r
+extern far U32 Sleep(U32 count);\r
+extern far void MicroDelay(U32 us15count);\r
+extern far void OutByte(U8 Byte, U16 wPort);\r
+extern far void OutWord(U16 Word, U16 wPort);\r
+extern far U8 InByte(U16 wPort);\r
+extern far U16 InWord(U16 wPort);\r
+extern far U8 ReadCMOS(U16 Address);\r
+extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
+\r
+\r
+/* THE FLOPPY INTERRUPT FUNCTION PROTOTYPE */\r
+\r
+static void interrupt fdisk_isr(void);\r
+\r
+/*  PROTOTYPE FOR Floppy motor control task and associated stuff */\r
+\r
+static void fdmotor_task(void);\r
+static void fd_select(U32 drive);\r
+static void fd_motoroff(U32 drive);\r
+\r
+/* THE REST OF THE PROTOTYPES */\r
+\r
+U32 fdisk_setup(void);\r
+static U32 RdWrtVerf(U32 op);\r
+static U32 format_track(void);\r
+static U32 Set_Media(U32 drive, U32 type);\r
+static U32 FDC_reset(void);\r
+static U8  cmos_type (U8 drive_nr);\r
+static U32 send_fdc(U8 parm);\r
+static U8  GetParm(U8 index);\r
+static U32 wait_int (void);\r
+static U32 seek(void);\r
+static U32 recal(void);\r
+static U32 read_data(U8 *pDataRet);\r
+static U32 results(U32 expect);\r
+static void purge_fdc (void);\r
+static void wait_for_head(void);\r
+static U32 med_change(void);\r
+static U32 get_fdc_status(void);\r
+\r
+/* The following 3 calls are required in every MMURTL device driver */\r
+\r
+static U32 dev_op(U32 dDevice,\r
+                   U32 dOpNum,\r
+                   U32 dLBA,\r
+                   U32 dnBlocks,\r
+                   S8  *pData);\r
+\r
+static U32 dev_stat(U32 dDevice,\r
+                         S8 * pStatRet,\r
+                         U32 dStatusMax,\r
+                         U32 *pdSatusRet);\r
+\r
+static U32 dev_init(U32 dDevNum,\r
+                         S8  *pInitData,\r
+                         U32  sdInitData);\r
+\r
+/* Near External for troubleshooting */\r
+\r
+extern long xprintf(char *fmt, ...);\r
+\r
+/* LOCAL DEFINITIONS */\r
+\r
+#define ok    0\r
+#define TRUE  1\r
+#define FALSE 0\r
+\r
+#define RATE_500               0x00\r
+#define RATE_300               0x01\r
+#define RATE_250               0x02\r
+#define RATE_1000       0x03\r
+#define INT_FLAG               0x80\r
+\r
+/* Error Codes to return */\r
+\r
+#define ErcNotInstalled 504\r
+#define ErcAddrMark            602\r
+#define ErcReadOnly            603\r
+#define ErcSectNotFound        604\r
+#define ErcNewMedia            605\r
+#define ErcNotMounted  606\r
+#define ErcCRC                 607\r
+#define ErcBadFDC              608\r
+#define ErcBadSeek             609\r
+#define ErcFDCTimeOut  610\r
+#define ErcOverRun             611\r
+#define ErcBadLBA              612\r
+#define ErcDriveType   613\r
+#define ErcBadOp               614\r
+#define ErcBadRecal            615\r
+#define ErcSendFDC             616\r
+#define ErcResults             617\r
+#define ErcBadCmd              618\r
+#define ErcReadyLine   619\r
+\r
+/* Commands accepted by driver */\r
+\r
+#define CmdNull     0\r
+#define CmdRead     1\r
+#define CmdWrite    2\r
+#define CmdVerify   3\r
+#define CmdFmtBlk   4\r
+#define CmdFmtTrk   5\r
+#define CmdSeekTrk  6\r
+\r
+/* FDC port definitions */\r
+\r
+#define DOR_PORT       0x3f2\r
+#define MSR_PORT       0x3f4\r
+#define DATA_PORT      0x3f5\r
+#define DIR_PORT       0x3f7\r
+#define DRR_PORT       0x3f7\r
+\r
+/* FDC Return Status bit definitions */\r
+\r
+#define BUSY           0x10    /* was BIT4 */\r
+#define DSKCHANGE_BIT 0x80\r
+#define BIT7           0x80\r
+#define BIT6           0x40\r
+#define BIT5           0x20\r
+#define BIT4           0x10\r
+#define BIT3           0x08\r
+#define BIT2           0x04\r
+#define BIT1           0x02\r
+#define BIT0           0x01\r
+\r
+#define RQM                    0x80\r
+#define DIO                    0x40\r
+\r
+/* FDC commands */\r
+\r
+#define FDC_READ       0xe6\r
+#define FDC_WRITE      0xc5\r
+#define FDC_FORMAT     0x4d\r
+\r
+/* FDC DOR register bits */\r
+\r
+#define FD_MOTOR0      0x10\r
+#define FD_MOTOR1   0x20\r
+#define FD_INTS                0x08\r
+#define FD_RESET       0x04\r
+#define FD_DRV1SEL     0x01\r
+#define FD_MOTMASK     0xf0            /* mask to see motor bits */\r
+\r
+/* L O C A L   C O N S T A N T S */\r
+\r
+/* The drive table contains parameters for each disk\r
+   type.  The values are:\r
+   0  - FDC SPECIFY Command byte 1\r
+   1  - FDC SPECIFY Command byte 2\r
+   2  - Unused\r
+   3  - Bytes per Sector (coded 0=128, 1=256, 2=512, 3=1024);\r
+   4  - number of sectors per track (Last sector)\r
+   5  - Intersector Gap Size\r
+   6  - Data Length (FFh = 512)\r
+   7  - GAP 3 for Format Command\r
+   8  - Fill Byte for Format command\r
+   9  - Head settle time in milliseconds\r
+   10 - Motor start time in milliseconds/10 (mult value by 10 to use)\r
+   11 -        max cylinder index (number of cyls - 1)\r
+   12 -        Xfer rate Command\r
+   13 - Unused\r
+   14 - Double Step Flag (e.g., 360K in a 1.2Mb drive)\r
+   15 - Unused\r
+*/\r
+\r
+\r
+static U8 fdisk_table[5][16]= {\r
+\r
+ /* 0 = NO DRIVE - first two params set to allow FDC_reset */\r
+  {0x0af,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+\r
+ /* 1 = 360 kb drive */\r
+ {0x0af, 2, 0, 2, 9, 0x2a, -1, 0x50, 0x0f6, 15, 8, 39, RATE_250, 0, 0, 0},\r
+\r
+ /* 2 = 1.2 mb drive */\r
+ {0xaf, 2, 0, 2, 15, 0x1b, -1, 0x54, 0x0f6, 15, 8, 79, RATE_500, 0, 0, 0},\r
+\r
+ /* 3 = 720 type drive */\r
+ {0x0af, 2, 0, 2, 9, 0x2a, -1, 0x50, 0x0f6, 15, 8, 79, RATE_250, 0, 0, 0},\r
+\r
+ /* 4 = 1.44 mb drive */\r
+ {0xaf, 2, 0, 2, 18, 0x1b, -1, 0x6c, 0x0f6, 15, 8, 79, RATE_500, 0, 0, 0},\r
+\r
+};\r
+\r
+static U32 MotorStk[100];  /* 400 byte stack for motor task */\r
+static U32 MotorStkTop;\r
+\r
+static U8 fd_fdc_command;\r
+static U8 fd_drive;\r
+static U8 fd_nr_sectors;\r
+static U8 fd_head;\r
+static U8 fd_sector;\r
+static U8 fd_track;\r
+static U8 seek_status;\r
+static U8 fwrite;\r
+static S8  *fd_pData;\r
+\r
+/* current fdisk_table[x] for drive 0 & 1 */\r
+\r
+static U8 type0\r
+static U8 type1\r
+\r
+/* Record for 64 byte status and init record */\r
+\r
+#define sStatus 64\r
+\r
+static struct statstruct{\r
+  U32 erc;\r
+  U32 blocks_done;\r
+  U32 BlocksMax;\r
+  U8 fNewMedia;\r
+  U8 type_now;         /* current fdisk_table for drive selected */\r
+  U8 resvd1[2];                /* padding for DWord align  */\r
+  U32 nCyl;                    /* total physical cylinders */\r
+  U32 nHead;           /* total heads on device    */\r
+  U32 nSectors;                /* Sectors per track        */\r
+  U32 nBPS;                    /* Number of bytes per sect */\r
+  U8 params[16];       /* begin device specific fields */\r
+  U8 STATUS[8];                /* status returned from FDC (for user status) */\r
+  U32 resvd3;\r
+  U32 resvd4;          /* 64 bytes total */\r
+  };\r
+\r
+static struct statstruct fdstatus;\r
+static struct statstruct FDStatTmp;\r
+\r
+static U8 FDC_STATUS[8];       /* status returned from FDC */\r
+static U8 LAST_TRACK[3];  /* holds last track number */\r
+\r
+static struct dcbtype {\r
+       S8   Name[12];\r
+       S8   sbName;\r
+       S8   type;\r
+       S16  nBPB;\r
+       U32  last_erc;\r
+       U32  nBlocks;\r
+       S8  *pDevOp;\r
+       S8  *pDevInit;\r
+       S8  *pDevSt;\r
+       S8   fDevReent;\r
+       S8   fSingleUser;\r
+       S16  wJob;\r
+       U32  OS1;\r
+       U32  OS2;\r
+       U32  OS3;\r
+       U32  OS4;\r
+       U32  OS5;\r
+       U32  OS6;\r
+       };\r
+\r
+static struct dcbtype fdcb[2];         /* two floppy device control blcocks */\r
+\r
+\r
+/* Exch, msgs space, and vars for FD Motor task */\r
+static U8 dor_crnt;            /* last value sent to DOR port */\r
+\r
+static U8 motor0_want;         /* desired motor0 state, TRUE = want ON */\r
+static U8 motor1_want;         /* desired motor1 state, TRUE = want ON */\r
+static U32 fd_tick;            /* Set to tick everytime we select a floppy */\r
+static U32 fd_newtick;         /* used to check tick time */\r
+\r
+/* Exch and msgs space for FD ISR */\r
+static U32 fd_exch;\r
+static U32 fd_msg[2];\r
+\r
+static U32 rgSectorMax[10] = {0, 720, 2400, 1440, 2880,\r
+                                               0, 0, 0, 0, 0};         /* set max sectors */\r
+\r
+static U8 *sectbuf;            /* sector buffer, 1 Page with AllocDMAPage */\r
+static U32 physectbuf; /* physical address of DMA buffer */\r
+\r
+/*======================================================*/\r
+/*=================== START OF CODE ====================*/\r
+/*======================================================*/\r
+\r
+static void enable_ints(void)\r
+{\r
+;\r
+#asm\r
+       STI\r
+#endasm\r
+}\r
+\r
+static void disable_ints(void)\r
+{\r
+;\r
+#asm\r
+       CLI\r
+#endasm\r
+}\r
+\r
+\r
+/********************************************************************\r
+ This small function becomes a thread (task) to control the motor.\r
+ The floppy motors are controlled serarately from the rest\r
+ of the floppy drive electronics. A single port controls both\r
+ floppies.  After a floppy is used you don't want to shut it right\r
+ off or you can forget about any dreams of decent throughput.\r
+ This wakes up every 3 seconds and checks to see if there was\r
+ any floppy activity in the past 3 seconds. If not, we shut off\r
+ the motor(s).\r
+**********************************************************************/\r
+\r
+static void fdmotor_task(void)\r
+{\r
+       enable_ints();\r
+\r
+MotorLoop:\r
+\r
+       Sleep(300);                                                     /* 3 seconds */\r
+\r
+       GetTimerTick(&fd_newtick);\r
+\r
+       if ((fd_newtick - fd_tick) > 300) {     /* not used in last 3 seconds */\r
+\r
+               if ((!motor0_want) && (dor_crnt & FD_MOTOR0))\r
+               { /* They want 0 off */\r
+                       disable_ints();\r
+                       dor_crnt &= ~FD_MOTOR0;\r
+                       OutByte( dor_crnt, DOR_PORT);\r
+                       enable_ints();\r
+               }\r
+\r
+               if ((!motor1_want) && (dor_crnt & FD_MOTOR1)) { /* They want 1 off */\r
+                       disable_ints();\r
+                       dor_crnt &= ~FD_MOTOR1;\r
+                       OutByte( dor_crnt, DOR_PORT);\r
+                       enable_ints();\r
+               }\r
+       }\r
+       goto MotorLoop;\r
+}\r
+\r
+/*******************************************\r
+ Set desired motor status flags and drive\r
+ select bit.  If drive select bit has changed\r
+ this sends the current DOR value to select\r
+ the correct drive. If the motor bit we wanted\r
+ isn't on, we will also send the command.\r
+ We DO NOT mess with the other motor bit.\r
+*******************************************/\r
+\r
+static void fd_select(U32 drive)\r
+{\r
+U8 fsend;\r
+U8 fdelay;\r
+\r
+       GetTimerTick(&fd_tick);         /* update last time selected */\r
+\r
+       fsend = FALSE;\r
+       fdelay = FALSE;\r
+\r
+       if (drive) {                                    /* drive 1 */\r
+               if (!(dor_crnt & 0x01)) {\r
+                       fsend = TRUE;\r
+                       dor_crnt |= 0x01;               /* select 1 */\r
+               }\r
+               if (!(dor_crnt & FD_MOTOR1)) {\r
+                       fsend = TRUE;\r
+                       fdelay = TRUE;\r
+                       dor_crnt |= FD_MOTOR1;  /* motor 1 on */\r
+               }\r
+       }\r
+       else {\r
+               if (dor_crnt & 0x01) {\r
+                       fsend = TRUE;\r
+                       dor_crnt &= 0xFE;               /* select 0 (turn 1 off) */\r
+               }\r
+               if (!(dor_crnt & FD_MOTOR0)) {\r
+                       fsend = TRUE;\r
+                       fdelay = TRUE;\r
+                       dor_crnt |= FD_MOTOR0;  /* motor 0 on */\r
+               }\r
+       }\r
+\r
+       if (fsend) {\r
+\r
+               disable_ints();\r
+\r
+               OutByte( dor_crnt, DOR_PORT);\r
+\r
+               enable_ints();\r
+\r
+               if (fdelay)\r
+                       Sleep(33);  /* delay 1/3th second if not already on! */\r
+       }\r
+\r
+}\r
+\r
+\r
+/*******************************************\r
+ Sets motor_want bit for specified motor.\r
+*******************************************/\r
+\r
+static void fd_motoroff(U32 motor)\r
+{\r
+/* Set what we want so motor task will know */\r
+\r
+if (motor)\r
+       motor1_want = FALSE;\r
+else\r
+       motor0_want = FALSE;\r
+}\r
+\r
+/*********************************************************\r
+    This is called ONCE to initialize the Driver.\r
+*********************************************************/\r
+\r
+U32 fdisk_setup(void)\r
+{\r
+U32 erc;\r
+\r
+fdcb[0].Name[0] = 'F';\r
+fdcb[0].Name[1] = 'D';\r
+fdcb[0].Name[2] = '0';\r
+fdcb[0].sbName  = 3;\r
+fdcb[0].type    = 1;\r
+fdcb[0].fDevReent = 0; /* not reentrant */\r
+fdcb[0].fSingleUser = 0;\r
+\r
+fdcb[1].Name[0] = 'F';\r
+fdcb[1].Name[1] = 'D';\r
+fdcb[1].Name[2] = '1';\r
+fdcb[1].sbName  = 3;\r
+fdcb[1].type    = 1;   /* this is set set to zero if not installed */\r
+fdcb[1].fDevReent = 0; /* not reentrant */\r
+fdcb[1].fSingleUser = 0;\r
+\r
+ dor_crnt |= (FD_INTS | FD_RESET);      /* set ints & reset bits high */\r
+ motor0_want = FALSE;\r
+ motor1_want = FALSE;\r
+ seek_status = 0;       /* clear seek status */\r
+ fdstatus.erc = 0;      /* clear global disk error */\r
+\r
+ erc = AllocExch(&fd_exch);            /* allocates exchanges for messaging */\r
+ if (erc)\r
+       return(erc);\r
+ erc = AllocDMAPage(1, &sectbuf, &physectbuf);\r
+ if (erc)\r
+       return(erc);\r
+\r
+/* Used to test DMA memory call\r
+ xprintf("DMA Address:  %08x, DMA Physical : %08x\r\n", sectbuf, physectbuf);\r
+ Sleep(200);\r
+*/\r
+\r
+ /* Set up the motor task */\r
+\r
+ SpawnTask( &fdmotor_task, 18, 0, &MotorStkTop, 1);\r
+\r
+ SetIRQVector(6, &fdisk_isr);\r
+ UnMaskIRQ(6);    /*  unmask IRQ 6 */\r
+\r
+ type0 = cmos_type(0);\r
+ if (type0)\r
+ {\r
+    fdcb[0].nBPB    = 512;\r
+    fdcb[0].nBlocks = rgSectorMax[type0];\r
+ }\r
+\r
+ type1 = cmos_type(1);\r
+ if (type1)\r
+ {\r
+    fdcb[1].nBPB    = 512;\r
+    fdcb[1].nBlocks = rgSectorMax[type1];\r
+ }\r
+\r
+/* reset the FDC */\r
+\r
+ if (fdstatus.erc= FDC_reset())\r
+       return (fdstatus.erc);\r
+\r
+/* now we attempt to select and recal both drives */\r
+\r
+ if (type0) {\r
+       fd_drive = 0;           /* select drive 0 */\r
+       fd_select(0);\r
+       if (erc = recal())\r
+       {\r
+               erc = recal();          /* try twice */\r
+       }\r
+       fd_motoroff(0);\r
+       if (erc) return (fdstatus.erc = erc);\r
+       }\r
+\r
+ if (type1) {\r
+       fd_drive = 1;           /* select drive 1 */\r
+       fd_select(1);\r
+       if (erc = recal())\r
+               erc = recal();          /* try twice */\r
+       if (erc) fdcb[1].type = 0;\r
+       fd_motoroff(1);\r
+       }\r
+\r
+ fdcb[0].pDevOp = &dev_op;\r
+ fdcb[0].pDevInit = &dev_init;\r
+ fdcb[0].pDevSt = &dev_stat;\r
+\r
+ fdcb[1].pDevOp = &dev_op;\r
+ fdcb[1].pDevInit = &dev_init;\r
+ fdcb[1].pDevSt = &dev_stat;\r
+\r
+ return(erc = InitDevDr(10, &fdcb, 2, 1));\r
+\r
+}\r
+\r
+/************************************************************\r
+ Reset the disk system\r
+*************************************************************/\r
+\r
+static U32 FDC_reset(void)\r
+{\r
+ U32 erc;\r
+\r
+ seek_status = 0;                                       /* Set to Recal on next seek */\r
+\r
+ disable_ints();\r
+\r
+ OutByte(0x08, DOR_PORT);       /* Drop reset signal on disk controller */\r
+ MicroDelay(75);                        /* Wait 1ms */\r
+ OutByte(0x0C, DOR_PORT);       /* Raise reset line on disk controller */\r
+\r
+ enable_ints();\r
+\r
+ /* wait for interrupt and return if error (timeout...?) */\r
+\r
+ if (erc = wait_int())\r
+       return(erc);\r
+\r
+  /* Send sense command to current drive to see if reset took.\r
+     If this is the first system reset it defaults to drive 0,\r
+     Head 0.\r
+  */\r
+\r
+\r
+ if (erc = send_fdc(8)) return(erc);\r
+\r
+  /* check results */\r
+\r
+ if (erc = results(2)) return(erc);\r
+\r
+ if (((FDC_STATUS[0] & 0xc0) == 0xc0 ) ||\r
+    ((FDC_STATUS[0] & 0xc0) == 0)) {\r
+\r
+     /* Send Specify command to controller */\r
+        send_fdc(3);\r
+        send_fdc(GetParm(0));\r
+        send_fdc(GetParm(1));\r
+        return(ok);\r
+ }\r
+ return(ErcBadFDC);\r
+}\r
+\r
+/*************************************************************\r
+ The ISR is very simple.\r
+ It just waits for an interrupt then sends a message to the\r
+ exchange where the FD Driver will be waiting.\r
+ The call that caused an interrupt to be generated reads the status\r
+ codes from the controller to determine if the last status was OK.\r
+****************************************************************/\r
+static void interrupt fdisk_isr(void)\r
+{\r
+       ISendMsg(fd_exch, 0xfffffffd, 0xfffffffd);\r
+       EndOfIRQ(6);\r
+}\r
+\r
+/*=========================================================\r
+Return the drive type for a specified drive.\r
+---------------------------------------------------------*/\r
+\r
+static U8 cmos_type (U8 drive_nr)\r
+{\r
+ U8 drive_type;\r
+ drive_type = 0;\r
+ if (drive_nr)\r
+       drive_type = ReadCMOS(0x10) & 0x0f;\r
+ else\r
+       drive_type = (ReadCMOS(0x10) >> 4) & 0x0f;\r
+ return(drive_type);\r
+}\r
+\r
+/*========================================================\r
+Send a byte to the FDC.\r
+--------------------------------------------------------*/\r
+\r
+static U32 send_fdc(U8 parm)\r
+{\r
+U32 i;\r
+U8  b, bjunk;\r
+\r
+i = 100;  /* try 100 times to send FDC command */\r
+do\r
+{\r
+       b = InByte(MSR_PORT);                           /* Get I/O status byte */\r
+       MicroDelay(100);                                        /* 1.5 milliseconds between tries */\r
+       if (b & RQM) {\r
+               if (b & DIO) {                                  /* He has something to send us... */\r
+                       bjunk = InByte(DATA_PORT);      /* Eat it! */\r
+                       MicroDelay(100);                        /* 1.5 milliseconds between I/O */\r
+               }\r
+               else {                                                  /* OK to send to him */\r
+                       OutByte(parm,DATA_PORT);\r
+                       MicroDelay(100);                        /* 1.5 milliseconds between I/O */\r
+                       return (ok);                            /* Sent it OK */\r
+               }\r
+       }\r
+ }\r
+ while (i--);\r
+ return (ErcSendFDC);          /* return ERROR */\r
+}\r
+\r
+/*==========================================================\r
+Get the indexed value from the disk parameter table.\r
+-------------------------------------------------------*/\r
+\r
+static U8 GetParm(U8 index)\r
+{\r
+ return( fdisk_table [fdstatus.type_now][index]);\r
+}\r
+\r
+\r
+/******************************************\r
+Wait for the hardware interrupt to occur.\r
+Time-out and return if no interrupt.\r
+********************************************/\r
+\r
+static U32 wait_int(void)\r
+{\r
+U32 erc;\r
+\r
+/* Set alarm for 3 seconds */\r
+erc = Alarm(fd_exch, 300);\r
+\r
+erc = WaitMsg(fd_exch, fd_msg);  /* Wait for message from ISR or Alarm */\r
+if (erc) {\r
+       KillAlarm(fd_exch);\r
+       return(erc); /* BAD NEWS FROM KERNEL!!! */\r
+       }\r
+\r
+if (fd_msg[0] != 0xfffffffd)\r
+{\r
+       return(fdstatus.erc = ErcFDCTimeOut);\r
+}\r
+else {\r
+       KillAlarm(fd_exch);\r
+       return(fdstatus.erc = ok);\r
+       }\r
+}\r
+\r
+\r
+/*******************************\r
+    Recalibrate the drive.\r
+********************************/\r
+\r
+static U32 recal(void)\r
+{\r
+U32 erc;\r
+\r
+ erc = send_fdc(7);                                                    /* recal command (2 bytes) */\r
+ if (!erc) erc = send_fdc(fd_drive);           /* second byte is drive */\r
+\r
+ /* wait for int on recal */\r
+ if (!erc) erc = wait_int();\r
+ if (!erc) erc = send_fdc(8);                          /* Sense command */\r
+ if (!erc) erc = results(2);                           /* expect 2 bytes */\r
+ if (!erc) {\r
+   if (FDC_STATUS[0] & 0x20)                                   /* Check seek-Ok bit */\r
+   {\r
+    if (FDC_STATUS[1]) return(ErcBadRecal);    /* Was it track 0? */\r
+    return(ok);\r
+   }\r
+   else return (ErcBadSeek);                           /* Seek bit NOT set */\r
+ }\r
+ return(erc);\r
+}\r
+/*******************************************************\r
+ Move the head to the selected track.\r
+*********************************************************/\r
+\r
+static U32 seek(void)\r
+{\r
+U32 erc;\r
+\r
+ if ((seek_status & (1 << fd_drive)) == 0)     /* need recal */\r
+ {\r
+\r
+  /* try 2 attemps at recalibrate, then error out */\r
+  if (recal())\r
+       if (erc = recal()) return (erc);                /* try again */\r
+\r
+  seek_status |= (1 << fd_drive);                      /* recal was done */\r
+\r
+  LAST_TRACK[fd_drive] = 0;                            /* clear track number */\r
+\r
+  /* if we want track zero, then just wait for head and exit */\r
+\r
+  if (fd_track == 0) {\r
+       wait_for_head();\r
+       return (ok);\r
+       }\r
+ }\r
+\r
+ if (fdisk_table[fdstatus.type_now][14] != 0)\r
+       fd_track *= 2;\r
+ if (LAST_TRACK[fd_drive] == fd_track)         /* already there */\r
+        return(ok);\r
+\r
+  /* update new position */\r
+ LAST_TRACK[fd_drive] = fd_track;\r
+\r
+ erc = send_fdc(0x0f);                                         /*Seek Cmd */\r
+ if (!erc) erc = send_fdc((fd_head << 2) | fd_drive);\r
+ if (!erc) erc = send_fdc(fd_track);\r
+\r
+  /* wait for int on seek command */\r
+ if (!erc) erc = wait_int();\r
+ if (!erc) erc = send_fdc(8);                          /* Sense command */\r
+ if (!erc) erc = results(2);                           /* expect 2 bytes */\r
+ if (!erc)\r
+       if (!(FDC_STATUS[0] & 0x20))                    /* Look for seek-Ok bit */\r
+       {\r
+     seek_status &= ~(1 << fd_drive);          /* needs recal! */\r
+        erc = ErcBadSeek;\r
+        return (erc);\r
+       }\r
+ if (!erc)\r
+        wait_for_head();\r
+ return (ok);\r
+}\r
+\r
+/*=======================================================\r
+Read a single byte from the data port (for status).\r
+Returns TRUE if it got one.\r
+--------------------------------------------------------*/\r
+\r
+static U32 read_data(U8 *pDataRet)\r
+{\r
+U16 tries;\r
+U8 status;\r
+\r
+       /* try 100 times while Busy */\r
+\r
+       for (tries=0; tries<1000; tries++) {\r
+               status = InByte(MSR_PORT);\r
+               MicroDelay(100);\r
+               if (status & RQM) {                                             /* RQM set */\r
+                       if (status & DIO) {                                     /* Direction IN if set */\r
+                               *pDataRet = InByte(DATA_PORT);  /* Get the data byte */\r
+                               MicroDelay(100);                                /* digest it...*/\r
+                               return(TRUE);\r
+                       }\r
+               }\r
+       }\r
+       return(FALSE);\r
+}\r
+\r
+\r
+/*=======================================================\r
+Read anything from the controller following an interrupt.\r
+This may include up to seven bytes of status.\r
+--------------------------------------------------------*/\r
+\r
+static U32 results(U32 expect)\r
+{\r
+unsigned indx;\r
+U8 status;\r
+\r
+       indx = 0;\r
+       while (indx < expect) {\r
+               if (read_data(&status)) {\r
+                       FDC_STATUS[indx++] = status;            /* save status */\r
+                       MicroDelay(100);\r
+               }\r
+               else return(ErcResults);\r
+       }\r
+       return(0);\r
+}\r
+\r
+\r
+\r
+/****************************************************\r
+  Purge the FDC of any status it is waiting to send.\r
+*****************************************************/\r
+\r
+static void purge_fdc (void)\r
+{\r
+ U8 b;\r
+\r
+       do {\r
+               b = InByte(MSR_PORT);\r
+               if (b & RQM) {\r
+                       if (b & DIO) {\r
+                               InByte(DATA_PORT);  /* eat the byte */\r
+                               MicroDelay(100);        /* Breath (1.5ms) */\r
+                        }\r
+                        else return;\r
+               }\r
+               else return;\r
+       }\r
+       while (1);\r
+}\r
+\r
+/*======================================================\r
+Wait for the head to settle after a seek for write.\r
+MicroDelay is in 15 us increments, while settle value\r
+is in milliseconds, so we turn millies into micros and\r
+divide by 15 to get value for MicroDelay call.\r
+-------------------------------------------------------*/\r
+\r
+static void wait_for_head(void)\r
+{\r
+U32 wait;\r
+\r
+if (fwrite) {\r
+ wait = GetParm(9); /* Head settle for write in milliseconds */\r
+ wait = (wait * 1000) / 15;\r
+ MicroDelay(wait);      /* delay in 15 us increments */\r
+ }\r
+}\r
+\r
+/*=========================================================\r
+ Checks for a media change for selected drive.\r
+ Returns:\r
+   0, or ErcNewMedia\r
+-------------------------------------------------------*/\r
+\r
+static U32 med_change(void)\r
+{\r
+\r
+/* if no disk change indicated return OK */\r
+\r
+ if (InByte(DIR_PORT) & DSKCHANGE_BIT) {\r
+       fdstatus.fNewMedia = 1;\r
+       return (ErcNewMedia);\r
+ }\r
+ else {\r
+       fdstatus.fNewMedia = 0;\r
+       return (ok);\r
+ }\r
+}\r
+\r
+\r
+/***********************************************************\r
+ Wait until an operation is complete, then accept the status\r
+ from the controller.\r
+************************************************************/\r
+\r
+static U32 get_fdc_status(void)\r
+{\r
+U32 erc;\r
+\r
+ if (erc = wait_int()) return(erc);\r
+ if (erc = results(7)) return(erc);\r
+ if (!(FDC_STATUS[0] & 0xc0)) return(ok);\r
+ if ((FDC_STATUS[0] & 0xc0) == 0x80) return(ErcBadCmd);\r
+ if ((FDC_STATUS[0] & 0xc0) == 0xC0) return(ErcReadyLine);\r
+\r
+ /* If we got here, get controller error status */\r
+\r
+ if (FDC_STATUS[1] & BIT7)\r
+       erc = ErcSectNotFound;\r
+ else if (FDC_STATUS[1] & BIT5)\r
+       erc = ErcCRC;\r
+ else if (FDC_STATUS[1] & BIT4)\r
+       erc = ErcOverRun;\r
+ else if (FDC_STATUS[1] & BIT2)\r
+       erc = ErcSectNotFound;\r
+ else if (FDC_STATUS[1] & BIT1)\r
+       erc = ErcReadOnly;\r
+ else if (FDC_STATUS[1] & BIT0)\r
+       erc = ErcAddrMark;\r
+ else fdstatus.erc = ErcBadFDC;\r
+ return(erc);\r
+}\r
+\r
+\r
+/*************************************************************\r
+ This is called for Read, Write or Verify commmands. The only\r
+ differences in these 3 commands is the DMA type/direction and\r
+ a single byte FDC command.\r
+*************************************************************/\r
+\r
+static U32 RdWrtVerf(U32 op)\r
+{\r
+U32 erc;\r
+S8  dmatype;                   /* 0 = Verify, 1 = IN, 2 = Out */\r
+S8  retrys;\r
+U32 count;\r
+\r
+erc = 0;\r
+retrys = 5;\r
+count = fd_nr_sectors * 512;\r
+\r
+while ((fd_nr_sectors) && (!erc)) {\r
+\r
+       switch(op) {\r
+               case(CmdRead):                                  /* Read */\r
+                       dmatype = 1;\r
+                       fd_fdc_command = FDC_READ;\r
+                       break;\r
+               case(2):                                        /* Write */\r
+                       fwrite = 1;\r
+                       dmatype = 2;\r
+                       CopyData(fd_pData, sectbuf, 512);\r
+                       fd_fdc_command = FDC_WRITE;\r
+                       break;\r
+               case(3):                                        /* Verify */\r
+                       dmatype = 0;\r
+                       fd_fdc_command = FDC_READ;\r
+       }\r
+\r
+       /*             PhyAddress  nBytes  ch   Type     Mode  */\r
+       erc = DmaSetUp(physectbuf, 512,    2,   dmatype, 1 );\r
+\r
+       if (!erc) {\r
+         while(retrys--)\r
+         {\r
+          erc = 0;\r
+          if (!erc) erc = seek();\r
+          if (!erc) erc = send_fdc(fd_fdc_command);\r
+          if (!erc) erc = send_fdc(((fd_head <<2) & BIT2) | fd_drive);\r
+          if (!erc) erc = send_fdc(fd_track);\r
+          if (!erc) erc = send_fdc(fd_head);\r
+          if (!erc) erc = send_fdc(fd_sector);\r
+          if (!erc) erc = send_fdc(GetParm(3));\r
+          if (!erc) erc = send_fdc(GetParm(4));\r
+          if (!erc) erc = send_fdc(GetParm(5));\r
+          if (!erc) erc = send_fdc(GetParm(6));\r
+          if (!erc) erc = get_fdc_status();\r
+          if (!erc) break;                                     /* exit loop with good operation */\r
+          MicroDelay(200);                                     /* wait 3 milliseconds... */\r
+         } /* While statement for 5 retrys */\r
+       }\r
+\r
+       if (!erc) {\r
+               if (op==CmdRead)\r
+                       CopyData(sectbuf, fd_pData, 512);\r
+           fdstatus.blocks_done++;\r
+           fd_pData+=512;\r
+        --fd_nr_sectors;                               /* One less sector */\r
+        ++fd_sector;                                   /* Next sector please */\r
+        if (fd_sector > fdisk_table[fdstatus.type_now][4]) {\r
+               fd_sector = 1;                          /* back to sect one */\r
+               ++fd_head;                                      /* next head please */\r
+               }\r
+        if (fd_head > 1) {\r
+               fd_head = 0;                            /* back to head 0 */\r
+               ++fd_track;                                     /* next track please */\r
+               }\r
+       }\r
+} /* while */\r
+return(erc);\r
+}\r
+\r
+\r
+/*************************************************************\r
+ This formats the track beginning at the block address given\r
+ in dLBA. dLBA must always be a multiple of the number of\r
+ sectors per track minus 1 for the disk type (usually 9 or 15).\r
+*************************************************************/\r
+\r
+static U32 format_track(void)\r
+{\r
+U32 erc;\r
+\r
+ fd_fdc_command = FDC_FORMAT;\r
+ fwrite = 1;           /* indicate write operation */\r
+\r
+ erc = DmaSetUp(fd_pData, 512, 2, 1, 1);\r
+ if (!erc) erc = send_fdc(3);                  /* specify command */\r
+ if (!erc) erc = send_fdc(GetParm(0));\r
+ if (!erc) erc = send_fdc(GetParm(1));\r
+ if (!erc) erc = seek();\r
+ if (!erc) erc = send_fdc(fd_fdc_command);\r
+ if (!erc) erc = send_fdc(((fd_head <<2) & BIT2) | fd_drive);\r
+ if (!erc) erc = send_fdc(GetParm(3));\r
+ if (!erc) erc = send_fdc(GetParm(4));\r
+ if (!erc) erc = send_fdc(GetParm(7));\r
+ if (!erc) erc = send_fdc(GetParm(8));\r
+ if (!erc) erc = get_fdc_status();\r
+ return(erc);\r
+}\r
+\r
+/******************************************************************************\r
+Now begins the PUBLIC routines that are interfaced to for all DEVICE DRIVERS\r
+*/\r
+\r
+/******************************************\r
+Called for all device operations.  This\r
+assigns physical device from logical number\r
+that outside callers use. For Floppy, 5=0\r
+and 6=1. This will check to make sure a\r
+drive type is assigned then calc physical\r
+head, cly, sector from dLBA.\r
+*******************************************/\r
+\r
+static U32 dev_op(U32 dDevice,\r
+                   U32 dOpNum,\r
+                   U32 dLBA,\r
+                   U32 dnBlocks,\r
+                   S8  *pData)\r
+{\r
+U32 erc, j;\r
+\r
+ fd_pData = pData;\r
+ fwrite = 0;\r
+ fdstatus.blocks_done = 0;     /* Reset values in Status record */\r
+ fdstatus.erc = 0;\r
+\r
+ /* Set internal drive number */\r
+\r
+ if (dDevice == 10)\r
+       fd_drive = 0;\r
+ else\r
+       fd_drive = 1;\r
+\r
+/* Check to see if we have a leftover interrupt message from last\r
+   command.  If so then we eat it (do nothing)\r
+*/\r
+\r
+ erc = CheckMsg(fd_exch, fd_msg);\r
+\r
+ if (fd_drive==0){\r
+       if (!type0) return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type0;\r
+       }\r
+ else {\r
+       if (!type1) return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type1;\r
+       }\r
+\r
+ if (dLBA > fdstatus.BlocksMax) \r
+       {\r
+/* for troubleshooting\r
+           xprintf("dLBA: %d, BlocksMax: %d, DriveType: %d\r\n",\r
+                dLBA, fdstatus.BlocksMax, fdstatus.type_now);\r
+*/\r
+               return(fdstatus.erc = ErcBadLBA);\r
+       }\r
+\r
+ fd_select(fd_drive);          /* turn motor on and select drive */\r
+\r
+ /* set the data rate register to value the drive */\r
+\r
+ OutByte(fdisk_table[fdstatus.type_now][12], DRR_PORT); /* Rate Cmd */\r
+\r
+ /* make sure any residual status is unloaded */\r
+\r
+ purge_fdc();\r
+\r
+ /* if a media change sensed, update status */\r
+\r
+/*\r
+\r
+ erc = med_change();\r
+ if (erc) {\r
+               erc = recal();\r
+               fd_motoroff(fd_drive);\r
+               if (erc) {\r
+                       return(fdstatus.erc=erc);\r
+               }\r
+               else\r
+                       return(fdstatus.erc=ErcNewMedia);\r
+ }\r
+\r
+*/\r
+\r
+ fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now];          /* set max sectors */\r
+\r
+ fd_nr_sectors = dnBlocks;\r
+\r
+ /* calculate the cylinder, head and sector from LBA */\r
+ /* 3 = nHeads, 4 = sectors/track */\r
+\r
+ fd_track = dLBA / (GetParm(3) * GetParm(4));\r
+ j = dLBA % (GetParm(3) * GetParm(4));\r
+\r
+ /* We now know what cylinder, calc head and sector */\r
+\r
+ fd_head = j / GetParm(4);\r
+ fd_sector = j % GetParm(4) + 1;  /* sector numbers start at 1 !!!! */\r
+\r
+/* for troubleshooting...\r
+ xprintf("\r\nDevice: %d, dLBA: %d, dOpNum: %d\r\n", dDevice, dLBA, dOpNum);\r
+*/\r
+\r
+ switch(dOpNum) {\r
+       case(CmdNull):\r
+               fdstatus.erc = ok;                              /* Null Command */\r
+               break;\r
+       case(CmdRead):                                          /* Read */\r
+       case(CmdWrite):                                         /* Write */\r
+       case(CmdVerify):                                        /* Verify */\r
+               fdstatus.erc = RdWrtVerf(dOpNum);\r
+               break;\r
+       case(CmdFmtBlk):                                        /*      Format Block */\r
+               fdstatus.erc = ErcBadOp;\r
+               break;\r
+       case(CmdFmtTrk):\r
+               break;                                                  /* Format Track */\r
+       case(CmdSeekTrk):                                       /* Seek Track */\r
+               break;\r
+       default:\r
+               fdstatus.erc = ErcBadOp;\r
+               break;\r
+       }\r
+\r
+ fd_motoroff(fd_drive);\r
+ return(fdstatus.erc);\r
+}\r
+\r
+\r
+/******************************************\r
+Called for indepth status report on ctrlr\r
+and drive specified. Returns 80 byte block\r
+of data including the drive parameter\r
+block (a 16 byte structure with all the\r
+timing and size params of the drive).\r
+This is called by the PUBLIC call DeviceStat!\r
+*******************************************/\r
+\r
+static U32 dev_stat(U32 dDevice,\r
+                         S8 * pStatRet,\r
+                         U32 dStatusMax,\r
+                         U32 *pdStatusRet)\r
+{\r
+S32 j;\r
+\r
+ /* Set internal drive number */\r
+ if (dDevice == 10)\r
+       fd_drive=0;\r
+ else fd_drive = 1;\r
+\r
+ if (fd_drive==0){\r
+       if (type0==0)\r
+               return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type0;\r
+    fdstatus.nCyl  = fdisk_table[type0][11]+1;  /* total physical cyls */\r
+    fdstatus.nHead = 2;                                                        /* total heads on device */\r
+    fdstatus.nSectors= fdisk_table[type0][4];   /* Sectors per track */\r
+    fdstatus.nBPS = 512;                                               /* Bytes per sect */\r
+ }\r
+ else {\r
+       if (type1==0) return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type1;\r
+    fdstatus.nCyl  = fdisk_table[type1][11]+1;  /* total physical cyls */\r
+    fdstatus.nHead = 2;                                                        /* total heads on device */\r
+    fdstatus.nSectors= fdisk_table[type1][4];   /* Sectors per track */\r
+    fdstatus.nBPS = 512;                                               /* Bytes per sect */\r
+ }\r
+\r
+ /* set max sectors in status record */\r
+\r
+ fdstatus.BlocksMax = rgSectorMax[fdstatus.type_now];\r
+\r
+ /* copy in the 16 bytes of floppy specific data */\r
+\r
+ CopyData(&fdisk_table[fdstatus.type_now], &fdstatus.params, 16);\r
+\r
+ /* Update disk status bytes for status return */\r
+\r
+ CopyData(&FDC_STATUS, &fdstatus.STATUS, 8);\r
+\r
+ if (dStatusMax < sStatus) j = dStatusMax;\r
+ else j = sStatus;\r
+\r
+ CopyData(&fdstatus, pStatRet, j);             /* copy the status data */\r
+\r
+ *pdStatusRet = j;             /* give em the size returned */\r
+\r
+return(0);\r
+}\r
+\r
+/******************************************\r
+Called to reset the floppy disk controller\r
+and to set the TYPE.  The type is one byte\r
+of the 64 byte status record passed to\r
+dev_init (offset 13). See the fdstatus struct.\r
+This should be called once for each floppy\r
+before it is used the first time after\r
+the driver is loaded, or after a fatal\r
+error is received (timeout etc.).\r
+This is called by the PUBLIC call DeviceInit!\r
+*******************************************/\r
+\r
+static S32 dev_init(U32  dDevice,\r
+                        S8  *pInitData,\r
+                        U32  sdInitData)\r
+\r
+{\r
+U32 i;\r
+\r
+ /* Read the init status block in */\r
+\r
+ if (sdInitData > sStatus) i = sStatus;                /* no more than 64 bytes! */\r
+ else i = sdInitData;\r
+\r
+ CopyData(pInitData, &FDStatTmp, i);           /* copy in their init data */\r
+\r
+ if (dDevice == 10)\r
+       fd_drive=0;                     /* Set internal drive number */\r
+ else\r
+       fd_drive = 1;\r
+\r
+ if (fd_drive==0){                                             /* set up for drive 0 */\r
+       type0 = FDStatTmp.type_now;\r
+       if (type0==0) return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type0;\r
+ }\r
+ else {\r
+       type1 = FDStatTmp.type_now;\r
+       if (type1==0) return(fdstatus.erc = ErcDriveType);\r
+       fdstatus.type_now = type1;\r
+ }\r
+ fd_select(fd_drive);\r
+ fdstatus.erc = FDC_reset();\r
+ fd_motoroff(fd_drive);\r
+ return(fdstatus.erc);\r
+}\r
+\r
+/*===========  THE END  =========================================*/\r