⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fdc.c

📁 一个极小型的操作系统
💻 C
字号:
/*GazOS Operating SystemCopyright (C) 1999  Gareth Owen <gaz@athene.co.uk>This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.*/#include "dma.h"
#include "fdc.h"
#include "video.h"#include "delay.h"#include "gdt.h"#include "idt.h"#include "8259.h"#include "io.h"
/* globals */
static volatile BOOL done = FALSE;
static BOOL dchange = FALSE;
static BOOL motor = FALSE;
static int mtick = 0;
static volatile int tmout = 0;
static BYTE status[7] = { 0 };
static BYTE statsz = 0;
static BYTE sr0 = 0;
static BYTE fdc_track = 0xff;
static DrvGeom geometry = { DG144_HEADS,DG144_TRACKS,DG144_SPT };

unsigned long tbaddr = 0x80000L;    /* physical address of track buffer located below 1M */

/* prototypes */
extern void floppy_ISR();extern void _int1c();void sendbyte(int byte);
int getbyte();
void irq6(void);
void int1c(void);
BOOL waitfdc(BOOL sensei);
BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors);

/* helper functions */

/* init driver */
void init_floppy(void)
{
   int i;
   
   /* allocate track buffer (must be located below 1M) */
   /* see above for address assignment, floppy DMA buffer is at 0x80000) */   
   /* install IRQ6 handler */
   set_vector(floppy_ISR, M_VEC+6, (D_INT+D_PRESENT+D_DPL1));
   enable_irq(6);
   set_vector(_int1c, 0x1c, (D_INT+D_PRESENT+D_DPL1));

   reset();

   /* get floppy controller version */
   sendbyte(CMD_VERSION);
   i = getbyte();
   kprint("Initialising Floppy driver...  ");
   if (i == 0x80)
     kprint("NEC765 controller found\n");
   else
     kprint("Enhanced controller found\n");
}

/* deinit driver 
*///void deinit(void)
//{
//   set_irq_handler(6,NULL,&oldirq6);
//   kprint("uninstalling IRQ6 handler\n");
//   set_irq_handler(0x1c,NULL,&oldint1c);
//   kprint("uninstalling timer handler\n");
//   
//   /* stop motor forcefully */
//   outportb(FDC_DOR,0x0c);
//}
/* sendbyte() routine from intel manual */
void sendbyte(int byte)
{
   volatile int msr;
   int tmo;
   
   for (tmo = 0;tmo < 128;tmo++) {
      msr = inportb(FDC_MSR);
      if ((msr & 0xc0) == 0x80) {
	 outportb(FDC_DATA,byte);
	 return;
      }
      inportb(0x80);   /* delay */
   }
}

/* getbyte() routine from intel manual */
int getbyte()
{
   volatile int msr;
   int tmo;
   
   for (tmo = 0;tmo < 128;tmo++) {
      msr = inportb(FDC_MSR);
      if ((msr & 0xd0) == 0xd0) {
	 return inportb(FDC_DATA);
      }
      inportb(0x80);   /* delay */
   }

   return -1;   /* read timeout */
}

/* this waits for FDC command to complete */
BOOL waitfdc(BOOL sensei)
{
   tmout = 50000;   /* set timeout to 1 second */
     
   /* wait for IRQ6 handler to signal command finished */
   while (!done && tmout)
     ;
   /* read in command result bytes */
   statsz = 0;
   while ((statsz < 7) && (inportb(FDC_MSR) & (1<<4))) {
      status[statsz++] = getbyte();
   }

   if (sensei) {
      /* send a "sense interrupt status" command */
      sendbyte(CMD_SENSEI);
      sr0 = getbyte();
      fdc_track = getbyte();
   }
   
   done = FALSE;
   
   if (!tmout) {
      /* timed out! */
      if (inportb(FDC_DIR) & 0x80)  /* check for diskchange */
	dchange = TRUE;
      return FALSE;   } else     return TRUE;
}

/* This is the IRQ6 handler */
void irq6(void)
{
   /* signal operation finished */
   done = TRUE;

   /* EOI the PIC */
   outportb(0x20,0x20);
}

/* This is the timer (int 1ch) handler */
void int1c(void)
{
   if (tmout) --tmout;     /* bump timeout */
   
   if (mtick > 0)
     --mtick;
   else if (!mtick && motor) {
      outportb(FDC_DOR,0x0c);  /* turn off floppy motor */
      motor = FALSE;
   }
}

/*
 * converts linear block address to head/track/sector
 * 
 * blocks are numbered 0..heads*tracks*spt-1
 * blocks 0..spt-1 are serviced by head #0
 * blocks spt..spt*2-1 are serviced by head 1
 * 
 * WARNING: garbage in == garbage out
 */
void block2hts(int block,int *head,int *track,int *sector)
{
   *head = (block % (geometry.spt * geometry.heads)) / (geometry.spt);
   *track = block / (geometry.spt * geometry.heads);
   *sector = block % geometry.spt + 1;
}

/**** disk operations ****/

/* this gets the FDC to a known state */
void reset(void)
{
   /* stop the motor and disable IRQ/DMA */
   outportb(FDC_DOR,0);
   
   mtick = 0;
   motor = FALSE;

   /* program data rate (500K/s) */
   outportb(FDC_DRS,0);

   /* re-enable interrupts */
   outportb(FDC_DOR,0x0c);

   /* resetting triggered an interrupt - handle it */
   done = TRUE;
   waitfdc(TRUE);

   /* specify drive timings (got these off the BIOS) */
   sendbyte(CMD_SPECIFY);
   sendbyte(0xdf);  /* SRT = 3ms, HUT = 240ms */
   sendbyte(0x02);  /* HLT = 16ms, ND = 0 */
   
   /* clear "disk change" status */
   seek(1);
   recalibrate();

   dchange = FALSE;
}
/* this returns whether there was a disk change */
BOOL diskchange(void)
{
   return dchange;
}

/* this turns the motor on */
void motoron(void)
{
   if (!motor) {
      mtick = -1;     /* stop motor kill countdown */
      outportb(FDC_DOR,0x1c);
      delay(500); /* delay 500ms for motor to spin up */
      motor = TRUE;
   }
}

/* this turns the motor off */
void motoroff(void)
{
   if (motor) {
      mtick = 13500;   /* start motor kill countdown: 36 ticks ~ 2s */
   }
}

/* recalibrate the drive */
void recalibrate(void)
{
   /* turn the motor on */
   motoron();
   
   /* send actual command bytes */
   sendbyte(CMD_RECAL);
   sendbyte(0);

   /* wait until seek finished */
   waitfdc(TRUE);
   
   /* turn the motor off */
   motoroff();
}

/* seek to track */
BOOL seek(int track)
{
   if (fdc_track == track)  /* already there? */
     return TRUE;
   
//   motoron();
   
   /* send actual command bytes */
   sendbyte(CMD_SEEK);
   sendbyte(0);
   sendbyte(track);

   /* wait until seek finished */
   if (!waitfdc(TRUE))
     return FALSE;     /* timeout! */

   /* now let head settle for 15ms */
   delay(15);
   
//   motoroff();
   
   /* check that seek worked */
   if ((sr0 != 0x20) || (fdc_track != track))
     return FALSE;
   else
     return TRUE;
}

/* checks drive geometry - call this after any disk change */
BOOL log_disk(DrvGeom *g)
{
   /* get drive in a known status before we do anything */
   reset();

   /* assume disk is 1.68M and try and read block #21 on first track */
   geometry.heads = DG168_HEADS;
   geometry.tracks = DG168_TRACKS;
   geometry.spt = DG168_SPT;

   if (read_block(20,NULL,1)) {
      /* disk is a 1.68M disk */
      if (g) {
	 g->heads = geometry.heads;
	 g->tracks = geometry.tracks;
	 g->spt = geometry.spt;
      }
      return TRUE;             
   }
   
   /* OK, not 1.68M - try again for 1.44M reading block #18 on first track */
   geometry.heads = DG144_HEADS;
   geometry.tracks = DG144_TRACKS;
   geometry.spt = DG144_SPT;

   if (read_block(17,NULL,1)) {
      /* disk is a 1.44M disk */
      if (g) {
	 g->heads = geometry.heads;
	 g->tracks = geometry.tracks;
	 g->spt = geometry.spt;
      }
      return TRUE;
   }
   
   /* it's not 1.44M or 1.68M - we don't support it */
   return FALSE;
}

/* read block (blockbuff is 512 byte buffer) */
BOOL read_block(int block,BYTE *blockbuff, unsigned long nosectors)
{
	int track=0, sector=0, head=0, track2=0, result=0, loop=0;// The FDC can read multiple sides at once but not multiple tracks		block2hts(block, &head, &track, &sector);	block2hts(block+nosectors, &head, &track2, &sector);		if(track!=track2)	{		for(loop=0; loop<nosectors; loop++)			result = fdc_rw(block+loop, blockbuff+(loop*512), TRUE, 1);		return result;	}   return fdc_rw(block,blockbuff,TRUE,nosectors);
}

/* write block (blockbuff is 512 byte buffer) */
BOOL write_block(int block,BYTE *blockbuff, unsigned long nosectors)
{
   return fdc_rw(block,blockbuff,FALSE, nosectors);
}

/*
 * since reads and writes differ only by a few lines, this handles both.  This
 * function is called by read_block() and write_block()
 */
BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors)
{
   int head,track,sector,tries, copycount = 0;
   unsigned char *p_tbaddr = (char *)0x80000;   unsigned char *p_blockbuff = blockbuff;   
   /* convert logical address into physical address */
   block2hts(block,&head,&track,&sector);
   
   /* spin up the disk */
   motoron();

   if (!read && blockbuff) {
      /* copy data from data buffer into track buffer */
      for(copycount=0; copycount<(nosectors*512); copycount++) {      	*p_tbaddr = *p_blockbuff;      	p_blockbuff++;      	p_tbaddr++;      }   }
   
   for (tries = 0;tries < 3;tries++) {
      /* check for diskchange */
      if (inportb(FDC_DIR) & 0x80) {
	 dchange = TRUE;
	 seek(1);  /* clear "disk change" status */
	 recalibrate();
	 motoroff();
	 kprint("FDC: Disk change detected. Trying again.\n");	 	 return fdc_rw(block, blockbuff, read, nosectors);      }
      /* move head to right track */
      if (!seek(track)) {
	 motoroff();
	 kprint("FDC: Error seeking to track\n");	 return FALSE;
      }
      
      /* program data rate (500K/s) */
      outportb(FDC_CCR,0);
      
      /* send command */
      if (read) {
	 dma_xfer(2,tbaddr,nosectors*512,FALSE);
	 sendbyte(CMD_READ);
      } else {
	 dma_xfer(2,tbaddr,nosectors*512,TRUE);
	 sendbyte(CMD_WRITE);
      }
      
      sendbyte(head << 2);
      sendbyte(track);
      sendbyte(head);
      sendbyte(sector);
      sendbyte(2);               /* 512 bytes/sector */
      sendbyte(geometry.spt);
      if (geometry.spt == DG144_SPT)
	sendbyte(DG144_GAP3RW);  /* gap 3 size for 1.44M read/write */
      else
	sendbyte(DG168_GAP3RW);  /* gap 3 size for 1.68M read/write */
      sendbyte(0xff);            /* DTL = unused */
            /* wait for command completion */
      /* read/write don't need "sense interrupt status" */
      if (!waitfdc(TRUE)) {
	kprint("Timed out, trying operation again after reset()\n");	reset();	return fdc_rw(block, blockbuff, read, nosectors);      }      
      if ((status[0] & 0xc0) == 0) break;   /* worked! outta here! */
      recalibrate();  /* oops, try again... */
   }
   
   /* stop the motor */
   motoroff();

   if (read && blockbuff) {
      /* copy data from track buffer into data buffer */
      p_blockbuff = blockbuff;      p_tbaddr = (char *) 0x80000;      for(copycount=0; copycount<(nosectors*512); copycount++) {      	*p_blockbuff = *p_tbaddr;      	p_blockbuff++;      	p_tbaddr++;      }   }

   return (tries != 3);
}

/* this formats a track, given a certain geometry */
BOOL format_track(BYTE track,DrvGeom *g)
{
   int i,h,r,r_id,split;
   BYTE tmpbuff[256];
   unsigned char *p_tbaddr = (char *)0x8000;   unsigned int copycount = 0;
   /* check geometry */
   if (g->spt != DG144_SPT && g->spt != DG168_SPT)
     return FALSE;
   
   /* spin up the disk */
   motoron();

   /* program data rate (500K/s) */
   outportb(FDC_CCR,0);

   seek(track);  /* seek to track */

   /* precalc some constants for interleave calculation */
   split = g->spt / 2;
   if (g->spt & 1) split++;
   
   for (h = 0;h < g->heads;h++) {
      /* for each head... */
      
      /* check for diskchange */
      if (inportb(FDC_DIR) & 0x80) {
	 dchange = TRUE;
	 seek(1);  /* clear "disk change" status */
	 recalibrate();
	 motoroff();
	 return FALSE;
      }

      i = 0;   /* reset buffer index */
      for (r = 0;r < g->spt;r++) {
	 /* for each sector... */

	 /* calculate 1:2 interleave (seems optimal in my system) */
	 r_id = r / 2 + 1;
	 if (r & 1) r_id += split;
	 
	 /* add some head skew (2 sectors should be enough) */
	 if (h & 1) {
	    r_id -= 2;
	    if (r_id < 1) r_id += g->spt;
	 }
      
	 /* add some track skew (1/2 a revolution) */
	 if (track & 1) {
	    r_id -= g->spt / 2;
	    if (r_id < 1) r_id += g->spt;
	 }
	 
	 /**** interleave now calculated - sector ID is stored in r_id ****/

	 /* fill in sector ID's */
	 tmpbuff[i++] = track;
	 tmpbuff[i++] = h;
	 tmpbuff[i++] = r_id;
	 tmpbuff[i++] = 2;
      }

      /* copy sector ID's to track buffer */
      for(copycount = 0; copycount<i; copycount++) {      	*p_tbaddr = tmpbuff[copycount];      	p_tbaddr++;      }//      movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
      
      /* start dma xfer */
      dma_xfer(2,tbaddr,i,TRUE);
      
      /* prepare "format track" command */
      sendbyte(CMD_FORMAT);
      sendbyte(h << 2);
      sendbyte(2);
      sendbyte(g->spt);
      if (g->spt == DG144_SPT)      
	sendbyte(DG144_GAP3FMT);    /* gap3 size for 1.44M format */
      else
	sendbyte(DG168_GAP3FMT);    /* gap3 size for 1.68M format */
      sendbyte(0);     /* filler byte */
	 
      /* wait for command to finish */
      if (!waitfdc(FALSE))
	return FALSE;
      
      if (status[0] & 0xc0) {
	 motoroff();
	 return FALSE;
      }
   }
   
   motoroff();
   
   return TRUE;
}
asm (   ".globl _int1c        \n"   "_int1c:              \n"   "   pusha               \n" /* Save all registers               */   "   pushw %ds           \n" /* Set up the data segment          */   "   pushw %es           \n"   "   pushw %ss           \n" /* Note that ss is always valid     */   "   pushw %ss           \n"   "   popw %ds            \n"   "   popw %es            \n"   "                       \n"   "   call int1c          \n"   "                       \n"   "   popw %es            \n"   "   popw %ds            \n" /* Restore registers                */   "   popa                \n"   "   iret                \n" /* Exit interrupt                   */);asm (   ".globl floppy_ISR        \n"   "floppy_ISR:              \n"   "   pusha               \n" /* Save all registers               */   "   pushw %ds           \n" /* Set up the data segment          */   "   pushw %es           \n"   "   pushw %ss           \n" /* Note that ss is always valid     */   "   pushw %ss           \n"   "   popw %ds            \n"   "   popw %es            \n"   "                       \n"   "   call irq6           \n"   "                       \n"   "   popw %es            \n"   "   popw %ds            \n" /* Restore registers                */   "   popa                \n"   "   iret                \n" /* Exit interrupt                   */);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -