📄 floppy.c
字号:
/*
* Floppy access *
* v0.1: TDS *
* - basic routines *
* v0.2: TDS (30.03.2004) *
* - floppy read/write *
* - still buggy DMA code *
* - still buggy :-/ *
* v0.3: TDS (26.05.2004) *
* - #define TBADDR fixes *
* DMA problem *
* v0.4: TDS (09.08.2004) *
* - now supports up to 2 *
* drives still with *
* problems in detection *
* v0.5: Doug Gale *
* - Fixed CMOS access *
* - Relocated DMA buffer *
*/
#include <stdio.h>
#include <support.h>
#include <datatypes.h>
#include <drivers/dma.h>
#include <drivers/block/floppy.h>
#include <drivers/cmos.h>
#include <drivers/timer.h>
#include <multi.h>
#include <drivers/mem/mem.h>
#include <drivers/block/disk.h>
#include <irqa.h>
#include <interrupts.h>
#include <drivers/pic.h>
#define TBADDR 0x90000 /* physical address of track buffer located below 1M */
/* globals */
volatile BOOL done = FALSE; // volatile, cos it's changed by an interrupt
int mtick = 0;
dword tmout = 0;
struct floppy_t floppies[2];
unsigned char fdc = 0;
#define floppy (&floppies[fdc])
void floppy_sendbyte(int byte);
int floppy_getbyte(void);
BOOL floppy_wait(BOOL sensei);
BOOL floppy_read_block(int block, byte *blockbuff, unsigned long nosectors);
BOOL floppy_rw(int block, byte *blockbuff, BOOL read, dword nosectors, BOOL firsttry);
BOOL floppy_seek(int track);
void floppy_recalibrate(void);
/* helper functions */
// Called by IRQ handler
void floppy_process(void)
{
/* signal operation finished */
done = TRUE;
return;
}
void floppy_update(void)
{
if (tmout)
tmout--; /* bump timeout */
if (mtick > 0)
mtick--;
else
if (!mtick && floppy->motor)
{
outportb(floppy->FDC_DOR, 0x00); /* turn off floppy motor, maybe 0x0C? */
floppy->motor = FALSE;
}
}
/*floppy_sendbyte() routine from intel manual */
void floppy_sendbyte(int what)
{
volatile int msr;
int tmo;
for (tmo = 0;tmo < 128;tmo++)
{
msr = inportb(floppy->FDC_MSR);
if ((msr & 0xc0) == 0x80)
{
outportb(floppy->FDC_DATA, what);
return;
}
inportb(0x80); /* delay */
}
}
/* getbyte() routine from intel manual */
int floppy_getbyte(void)
{
volatile int msr;
int tmo;
for (tmo = 0;tmo < 128;tmo++)
{
msr = inportb(floppy->FDC_MSR);
if ((msr & 0xd0) == 0xd0)
{
return inportb(floppy->FDC_DATA);
}
inportb(0x80); /* delay */
}
return -1; /* read timeout */
}
/* this waits for FDC command to complete */
BOOL floppy_wait(BOOL sensei)
{
tmout = timer_ticks + 100;
/* wait for IRQ6 handler to signal command finished */
while (!done && (tmout > timer_ticks))
multi_yield();
/* read in command result bytes */
floppy->statsz = 0;
while ((floppy->statsz < 7) && (inportb(floppy->FDC_MSR) & (1<<4)))
{
floppy->status[floppy->statsz++] = floppy_getbyte();
}
if (sensei)
{
/* send a "sense interrupt status" command */
floppy_sendbyte(CMD_SENSEI);
floppy->sr0 = floppy_getbyte();
floppy->track = floppy_getbyte();
}
done = FALSE;
if (!tmout)
{
/* timed out! */
if (inportb(floppy->FDC_DIR) & 0x80) /* check for diskchange */
floppy->dchange = TRUE;
return FALSE;
}
else
return TRUE;
}
/*
* converts linear block address to head/track/sector
*
* blocks are numbered 0..heads*tracks*sectors-1
* blocks 0..sectors-1 are serviced by head #0
* blocks sectors..sectors*2-1 are serviced by head 1
*
* WARNING: garbage in == garbage out
*/
void block2hts(int block, int *head, int *track, int *sector)
{
if (floppy->geometry.sectors == 0)
{
dprintf("floppy: sectors can't be zero!\n");
return;
}
*head = ((block % (floppy->geometry.sectors * floppy->geometry.heads)) / (floppy->geometry.sectors));
*track = (block / (floppy->geometry.sectors * floppy->geometry.heads));
*sector = ((block % floppy->geometry.sectors) + 1);
}
/**** disk operations ****/
/* this gets the FDC to a known state */
void floppy_reset(void)
{
/* stop the motor and disable IRQ/DMA */
outportb(floppy->FDC_DOR, 0x00);
outportb(floppy->FDC_DOR, 0x0C);
outportb(floppy->FDC_DOR, 0x00);
delay(100); // give motor some time to stop
mtick = 0;
floppy->motor = FALSE;
/* program data rate (500K/s) */
outportb(floppy->FDC_DRS, 0);
/* re-enable interrupts */
outportb(floppy->FDC_DOR,0x0c);
/* resetting triggered an interrupt - handle it */
done = TRUE;
floppy_wait(TRUE);
/* specify drive timings (got these off the BIOS) */
floppy_sendbyte(CMD_SPECIFY);
floppy_sendbyte(0xdf); /* SRT = 3ms, HUT = 240ms */
floppy_sendbyte(0x02); /* HLT = 16ms, ND = 0 */
/* clear "disk change" status */
floppy_seek(1);
floppy_recalibrate();
floppy->dchange = FALSE;
}
void floppy_status(void)
{
char * status[] = {
"floppy drive 0 in seek mode/busy",
"floppy drive 1 in seek mode/busy",
"floppy drive 2 in seek mode/busy",
"floppy drive 3 in seek mode/busy",
"FDC read or write command in progress",
"FDC is in non-DMA mode",
"I/O direction; 1 = FDC to CPU; 0 = CPU to FDC",
"data reg ready for I/O to/from CPU (request for master)"};
int st = inportb(floppy->FDC_MSR);
int i;
printf("status: %X\n", st);
for (i=0; i<8; i++)
{
if (st & (1 << i))
{
printf("test %d == 0x%x (", i, 1<<i);
printf("%s)\n", status[i]);
}
}
}
/* this returns whether there was a disk change */
BOOL floppy_diskchange(void)
{
return floppy->dchange;
}
/* this turns the motor on */
void floppy_motoron(void)
{
if (!floppy->motor)
{
mtick = -1; /* stop motor kill countdown */
outportb(floppy->FDC_DOR, 0x1C);
delay(500); /* delay 500ms for motor to spin up */
floppy->motor = TRUE;
}
return;
}
/* this turns the motor off */
void floppy_motoroff(void)
{
if (floppy->motor) {
mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
}
return;
}
/* recalibrate the drive */
void floppy_recalibrate(void)
{
/* turn the motor on */
floppy_motoron();
/* send actual command bytes */
floppy_sendbyte(CMD_RECAL);
floppy_sendbyte(0);
/* wait until seek finished */
floppy_wait(TRUE);
/* turn the motor off */
floppy_motoroff();
}
/* seek to track */
BOOL floppy_seek(int track)
{
if (floppy->track == track) /* already there? */
return TRUE;
floppy_motoron();
/* send actual command bytes */
floppy_sendbyte(CMD_SEEK);
floppy_sendbyte(0);
floppy_sendbyte(track);
/* wait until seek finished */
if (!floppy_wait(TRUE))
return FALSE; /* timeout! */
/* now let head settle for 15ms */
delay(15);
floppy_motoroff();
/* check that seek worked */
if ((floppy->sr0 != 0x20) || (floppy->track != track))
return FALSE;
else
return TRUE;
}
/* checks drive geometry - call this after any disk change */
BOOL floppy_get_geo(void)
{
char tmp[512];
/* get drive in a known status before we do anything */
floppy_reset();
/* assume disk is 1.68M and try and read block #21 on first track */
floppy->geometry.cyls = DG168_TRACKS;
floppy->geometry.heads = DG168_HEADS;
floppy->geometry.sectors = DG168_SECTORS;
dprintf("floppy: Checking 1.68MB disk...\n");
if (floppy_read_block(20, (char *)tmp, 1))
{
return TRUE;
}
/* OK, not 1.68M - try again for 1.44M reading block #18 on first track */
floppy->geometry.cyls = DG144_TRACKS;
floppy->geometry.heads = DG144_HEADS;
floppy->geometry.sectors = DG144_SECTORS;
dprintf("floppy: Checking 1.44MB disk...\n");
if (floppy_read_block(17, (char *)tmp, 1))
{
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 floppy_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, §or);
block2hts(block+nosectors, &head, &track2, §or);
if(track!=track2)
{
for(loop=0; loop<nosectors; loop++)
result = floppy_rw(block+loop, blockbuff+(loop*512), TRUE, 1, TRUE);
return result;
}
return floppy_rw(block,blockbuff,TRUE,nosectors, TRUE);
}
/* write block (blockbuff is 512 byte buffer) */
BOOL floppy_write_block(int block,byte *blockbuff, unsigned long nosectors)
{
return floppy_rw(block,blockbuff,FALSE, nosectors, TRUE);
}
/*
* since reads and writes differ only by a few lines, this handles both. This
* function is called by read_block() and write_block()
*/
BOOL floppy_rw(int block, byte *blockbuff,BOOL read, unsigned long nosectors, BOOL firsttry)
{
int head,track,sector,tries, copycount = 0;
unsigned char *p_tbaddr = (char *)TBADDR;
unsigned char *p_blockbuff = blockbuff;
/* convert logical address into physical address */
block2hts(block, &head, &track, §or);
/* spin up the disk */
floppy_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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -