Floppy Seek Error

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
Omega
Member
Member
Posts: 250
Joined: Sun May 25, 2008 2:04 am
Location: United States
Contact:

Floppy Seek Error

Post by Omega »

Myself and another member are having the same issues with this code:

Code: Select all

int first;
int second;
int current_track = -1;
short base_used;
volatile int irq6_state = 0;
int motor_on = 0;
int sr0=0;
int pcn=0;
int present = 1; //assume not present

// dma xfer for floppy
void set_dma(unsigned char *buff, short size, int read, int channel)
{
    char page = 0;
    char mode = 0;
    short offset = 0;
    if(read)
		mode = 0x48 + (char)channel;
	else
		mode = 0x44 + (char)channel;
	page = (char)((int)buff >> 16);
	offset = (short)((int)buff & 0xFFFF);
	size--;
	cli();
	outb(maskport[channel], (0x04 | channel)); // mask channel
	outb(clearport[channel], 0x00);                // stop trans
	outb(modeport[channel], mode);                 // type of trans
	outb(addrport[channel], LOW_BYTE(offset));     // send address
    outb(addrport[channel], HI_BYTE(offset));      // send address
    outb(pageport[channel], page);                 // send page
    outb(countport[channel], LOW_BYTE(size));      // send size
    outb(countport[channel], HI_BYTE(size));       // send size
    outb(maskport[channel], channel);              // unmask channel
    sti();
}

void get_floppy_type()
{
    unsigned char data;
    outb(0x70, 0x10);
    data = inb(0x71);
    first = data >> 4;
    second = data & 0xF;
}

void fdc_handler()
{
    irq6_state = 1;
	outb(0x20,0x20);
}

void start_motor()
{
    outb((base_used + 0x2), 0x1C);
    motor_on = 1;
}

void stop_motor()
{
    outb((base_used + 0x2), 0x00);
    motor_on = 0;
}

/* from intel manual */
void sendbyte(char byte)
{
    volatile int msr;
    int tmo;
    for(tmo = 0; tmo < 128; tmo++)
    {
        msr = inb((base_used + 0x04));
        if ((msr & 0xC0) == 0x80)
        {
            outb((base_used + 0x05), byte);
            return;
        }
        inb(0x80);
    }
}

/* from intel manual */
int getbyte()
{
    volatile int msr;
    int tmo;
    for (tmo = 0; tmo < 128; tmo++)
    {
        msr = inb((base_used + 0x04));
        if ((msr & 0xd0) == 0xd0)
	        return inb((base_used + 0x05));
        inb(0x80);
    }
    return -1;
}

void wait_irq6()
{
    while(irq6_state == 0);
    irq6_state = 0;   
}

int fdc_seek(char track)
{
    if(current_track == track)
        return 0;
		
	//sendbyte(0x07); //calibrate
    //sendbyte(0x00);
	
    sendbyte(0x0F); //seek cmd
    sendbyte(0x00);
    sendbyte(track);

    wait_irq6();
	
	sendbyte(0x08); /* Send sense interrupt status command. */
	sr0 = getbyte();     /* Read status register 0.              */
	pcn = getbyte();     /* Read present cylinder number.        */

	if (!FDC_SEEK_END(sr0))
	printf(0x0f,1,10,"Seek Error");
	return 1;
	
    current_track = track;
	printf(0x0f,1,10,"Seek OK");
    return 0;
}

void fdc_reset()
{
   outb((base_used + 0x02), 0);    // stop everything
   motor_on = 0;
   outb((base_used + 0x04), 0);     // data rate (500K/s)
   outb((base_used + 0x02), 0x0C);  // restart ints

   wait_irq6();
   
   unsigned char j;
   for (j = 0; j < 4; j++)
	{
		sendbyte(0x08); /* Send sense intr status cmd. */
		if (j == 0)
		{
			sr0 = getbyte(); /* Read status register 0.  */
			pcn = getbyte(); /* Read present cyl number. */
			continue;
		}
		(void) getbyte(); /* Discard status register 0.       */
		(void) getbyte(); /* Discard present cylinder number. */
	}

   sendbyte(0x03);                     // timing
   sendbyte(0xDF);                     // timing
   sendbyte(0x02);                     // timing  
   
   timer_wait_ms(1);
   inb(0x80);
   
   fdc_seek(1);
   
   sendbyte(0x07); //calibrate
   sendbyte(0x00);
}

void init_floppy()
{
    base_used = 0x3F0;
    get_floppy_type();
	fdc_reset();
}

void block2hts(int block,int *head,int *track,int *sector)
{
   *head = (block % (18 * 2)) / (18);
   *track = block / (18 * 2);
   *sector = block % 18 + 1;
}

int floppy_read_sector(unsigned char *buf, int block)
{
    int head, track, sector;
	block2hts(block,&head,&track,&sector);

    start_motor();
	
	outb((base_used + 0x7), 0);
	
	set_dma(0x00000000, 512, 0, 2);
	
	fdc_seek(track);

	timer_wait_ms(1);
	
	sendbyte(0xE6);
	sendbyte((char)head << 2);
	sendbyte((char)track);
	sendbyte((char)head);
	sendbyte((char)sector);
	sendbyte(0x02); //sector size
	sendbyte(0x12); //sectors per track
	sendbyte(0x1B);
	sendbyte(0xFF);
	
	wait_irq6();
	
	(void)getbyte();
	(void)getbyte();
	(void)getbyte();
	(void)getbyte();
	(void)getbyte();
	(void)getbyte();
	(void)getbyte();
	
	if(!FDC_NORM_TERM(sr0))
	{
		printf(0x0f,1,6,"Read Error (0x%x)",sr0);
		sendbyte(0x07); //re-calibrate
		sendbyte(0x00);
	} else {
		memcpy((unsigned int *)buf, (unsigned int *)0x00000000, 128);
	}

    stop_motor();
	
	return 0;
}

void fdc_install(void)
{
	irq_install_handler(6,fdc_handler);
}
The issue is that on real-hardware we receive a seek error then a read error. I have hardcoded my spt and heads count values which should be good to go for 1.44 MB disks which is what I am using and it works perfectly in MS VPC. In bochs it moves extremely slow to load and does nothing with no error. On real hardware it seeks then errors. The error is 0xFF when it should be 0x20. The read error returns 0x80, when it should be 0xC0. Can anyone see the problem with this code. I have compared it to many other codes, read countless guides, spent the wiki, spent datasheet, and still nothing could help, so it must be one of those minuet details I always overlook. If someone (with a working on real-hardware fdc driver preferably) could look at my logic and hopefully correct it or enlighten me that would be great. thanks in advance.
Free energy is indeed evil for it absorbs the light.
User avatar
Omega
Member
Member
Posts: 250
Joined: Sun May 25, 2008 2:04 am
Location: United States
Contact:

Re: Floppy Seek Error

Post by Omega »

Maybe I am asking too much. Let me show you what I have done to figure it out:

01. I fixed the check for 0x20 after a read. This is a bug and should be 0xC0 instead. Many codes do this and they are broken if they do.
02. I tested it three ways: Real-Hardware (FAIL), Virtual PC (PASS), Bochs (STILL THINKING)
03. Narrowed it down to the code that must be suspect:

Code: Select all

int fdc_seek(char track)
{
    if(current_track == track)
        return 0;
      
   //sendbyte(0x07); //calibrate
    //sendbyte(0x00);
   
    sendbyte(0x0F); //seek cmd
    sendbyte(0x00);
    sendbyte(track);

    wait_irq6();
   
   sendbyte(0x08); /* Send sense interrupt status command. */
   sr0 = getbyte();     /* Read status register 0.              */
   pcn = getbyte();     /* Read present cylinder number.        */

   if (!FDC_SEEK_END(sr0))
   printf(0x0f,1,10,"Seek Error");
   return 1;
   
    current_track = track;
   printf(0x0f,1,10,"Seek OK");
    return 0;
}
This one in particular arouses my attention as it is the first to fail during a read on real hardware. I have confirmed that by placing strategic halts throughout the code, plus I reach code lines beneath the fdc_seek function. I don't understand what is wrong with this code. It matches what is written in the wiki and other codes I have seen, so I am truly at a loss if this function is the one failing. What bothers me is that in VPC it will run without a hitch but on real-hardware it breaks.

I have faith in the rest of my code, faith that it too could be a component of the overall problem with this driver. However, I have walked nearly every line of this code and I am stumped!

Any ideas? Again the return for FDC_SEEK_END(sr0) function is 0xFF, and the return for FDC_NORM_TERM(sr0) function is 0x80. Thanks.
Free energy is indeed evil for it absorbs the light.
Post Reply