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