FDD problem

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
Ferrarius
Member
Member
Posts: 69
Joined: Sun Oct 28, 2007 4:10 pm

FDD problem

Post by Ferrarius »

Hi everyone,

I've very recently decided to pick up OSdev again as another project is on hold. I decided to write the FDD driver for my os, in bochs it works perfectly yet on physical hardware the sector isn't read (I'm testing with sector 0). Before trying to read everything I first call init_FDD().

On two laptops the sector doesn't get read, and ST0 is 0x40 where ST1 is 0x01 after the read command.

Does anyone know what's wrong here?

Thank you for the effort.

Code: Select all

#include "fdd.h"
#include "port.h"
#include "sleep.h"
#include "vidstub.h"

#define SECTORS 18
#define HEADS 2
#define CYLINDERS 63

#define FDD_DOR 0x3F2
#define FDD_MSR 0x3F4
#define FDD_FIFO 0x3F5
#define FDD_CTRL 0x3F7

char FDD_int;

char init_FDD()
{
	FDD_int = 0;
	
	//Reset the controller
	outb(FDD_DOR,0);
	//Restart the controller
	outb(FDD_DOR,0x0c);		//DMA + enable
	
	while(FDD_int == 0){}; //Include a time-out someday
	string_out("Reset--IRQ\n\0");		//DEBUG
	
	unsigned int lc;
	for(lc=0;lc<4;lc++)
	{
		FDD_send_cmd(FDD_CMD_SENSE_INT);
		FDD_recv_cmd();
		FDD_recv_cmd();
	}
	string_out("Reset SIs handled\n\0");
	
	//Configure
	FDD_send_cmd(0x13);
	FDD_send_cmd(0);
	FDD_send_cmd(0x28);
	FDD_send_cmd(0);
	
	//Send the specify command
	FDD_send_cmd(FDD_CMD_SPECIFY);
	FDD_send_cmd(0xbf);	/* SRT = 5ms, HUT = 240ms */ 
	FDD_send_cmd(0x02); /* HLT = 16ms, ND = 0 */	//Got these from GazOS
	string_out("END_SPECIFY\n\0");		//DEBUG
	
	return 0;
}

char get_sector(unsigned short LBA,unsigned int address)
{
	if((address & 0xFF000000) > 0)
	{
		string_out("Memory address too high for DMA\n\0");
		return 1;	//Memory address too high
	};
	CHS_FDD_t my_CHS;
	my_CHS = LBA_to_CHS(LBA);		//Get CHS value
	
		//Initiate the motor (aftewards, wait for 300 ms)
	outb(FDD_DOR,FDD_DOR_MASK_DRIVE0_MOTOR | FDD_DOR_MASK_RESET | FDD_DOR_MASK_DMA);
	b_sleep(600);		//Sleep for 300 milliseconds
	string_out("START_MOTOR\n\0");		//DEBUG
	
	unsigned char loop_count;
	//Calibrate
	for(loop_count = 0;loop_count < 10;loop_count++)
	{
		if(FDD_calibrate() == 0)
		{
			loop_count = 0;
			break;
		}
	}
	if(loop_count != 0)
	{
		string_out("Error with calibrate command\n\0");
		return 1;
	}
	
	//Seek
	for(loop_count = 0;loop_count < 10;loop_count++)
	{
		if(FDD_seek(my_CHS) == 0)
		{
			loop_count = 0;
			break;
		}
	}
	if(loop_count != 0)
	{
		string_out("Error with seek command\n\0");
		return 1;
	}
	
	//Actual read
	for(loop_count = 0;loop_count < 10;loop_count++)
	{
		init_dma(address);
		if(FDD_read(my_CHS) == 0)
		{
			loop_count = 0;
			break;
		}
	}
	if(loop_count != 0)
	{
		string_out("Error with read command\n\0");
		return 1;
	}
	
	return 0;
}
			
CHS_FDD_t LBA_to_CHS(unsigned short LBA)
{
	CHS_FDD_t new_CHS;
	new_CHS.cylinder = (LBA / (SECTORS * HEADS));		//Wikipedia
	new_CHS.head = ((LBA / SECTORS)%HEADS);
	new_CHS.sectors = ((LBA % SECTORS) + 1);
	return new_CHS;
}

char FDD_calibrate()
{
	FDD_int = 0;
	FDD_send_cmd(FDD_CMD_CALIBRATE);
	FDD_send_cmd(0);
	while(FDD_int == 0){}; //Include a time-out someday
	string_out("Calibrate--IRQ\n\0");		//DEBUG
	FDD_send_cmd(FDD_CMD_SENSE_INT);		//Must be done to check status of calibrate command
	char PCN,ST0;
	ST0 = FDD_recv_cmd();
	PCN = FDD_recv_cmd();
	hint_out(ST0,3);
	b_sleep(1000);
	if(PCN == 0 && ST0 == 0x20)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

char FDD_seek(CHS_FDD_t my_CHS)
{
	FDD_int = 0;
	FDD_send_cmd(FDD_CMD_SEEK);
	unsigned char cmd_byte = 0;
	cmd_byte = my_CHS.head << 2;
	FDD_send_cmd(cmd_byte);	//Send the x00 with x = head;
	cmd_byte = my_CHS.cylinder;
	FDD_send_cmd(cmd_byte);	//Send the New Cylinder Number
	
	while(FDD_int == 0){}; //Include a time-out someday
	string_out("Seek--IRQ\n\0");		//DEBUG
	FDD_send_cmd(FDD_CMD_SENSE_INT);		//Must be done to check status of seek command
	char ST0 = FDD_recv_cmd();
	char PCN = FDD_recv_cmd();
	
	if(PCN != my_CHS.cylinder || ST0 != 0x20)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

char FDD_read(CHS_FDD_t my_CHS)
{
				//Read command
	string_out("bla\n\0");
	hint_out(my_CHS.cylinder,3);
	hint_out(my_CHS.head,3);	
	hint_out(my_CHS.sectors,3);
	FDD_int = 0;
	FDD_send_cmd(0xE0 | FDD_CMD_READ_SECT);		//0xC0 == double density + MT
	unsigned char cmd_byte = my_CHS.head << 2;
	FDD_send_cmd(cmd_byte);	//Send the x00 with x = head;
	string_out(" cmd_byte head\n\0");
	hint_out(cmd_byte,3);
	cmd_byte = my_CHS.cylinder;
	FDD_send_cmd(cmd_byte);	//Send the New Cylinder Number
	cmd_byte = my_CHS.head;
	FDD_send_cmd(cmd_byte);		//Send head again (stupid controller)
	cmd_byte = my_CHS.sectors;
	FDD_send_cmd(cmd_byte);	//Send the Sector
	FDD_send_cmd(2);		//Sector size, 2 = 512
	FDD_send_cmd(SECTORS);	//EOT
	FDD_send_cmd(0x1B);		//GPL	
	FDD_send_cmd(0xFF);		//DTL
	
	b_sleep(1000);
	while(FDD_int == 0){}; //Include a time-out someday
	string_out("Read--IRQ\n\0");		//DEBUG
	b_sleep(500);
	unsigned char ST0,ST1,ST2,C,H,R,N;
	ST0 = FDD_recv_cmd();
	ST1 = FDD_recv_cmd();
	ST2 = FDD_recv_cmd();
	C = FDD_recv_cmd();
	H = FDD_recv_cmd();
	R = FDD_recv_cmd();
	N = FDD_recv_cmd();
	
	hint_out(C,3);
	hint_out(H,3);
	hint_out(R,3);
	string_out(" ST0/1/2\n\0");
	hint_out(ST0,3);
	hint_out(ST1,3);
	hint_out(ST2,3);
	if(my_CHS.sectors == SECTORS)
	{
		if(my_CHS.head == 0)
		{
			if(my_CHS.head != H && R == 1)			//success
			{
				return 0;
			}
		}
		else if(my_CHS.head != H && R == 1 && C == (my_CHS.cylinder+1))			//success
		{		
			return 0;
		}
	}
	
	if(my_CHS.sectors < SECTORS)
	{
		if(R == (my_CHS.sectors+1))		//Success
		{
			return 0;
		}
	}
	return 1;
}

char FDD_send_cmd(unsigned char command)
{
	unsigned char MSR_Status;
	while(1)
	{
		MSR_Status = inb(FDD_MSR);
		if(MSR_Status & FDD_MSR_MASK_DATAREG)
		{
			break;
		}
	}
	
	outb(FDD_FIFO,command);
	b_sleep(10);
	return 0;
}

unsigned char FDD_recv_cmd()
{
	unsigned char MSR_Status;
	while(1)
	{
		MSR_Status = inb(FDD_MSR);
	
		if(MSR_Status & FDD_MSR_MASK_DATAREG)
		{
			break;
		}
	}
	unsigned char retval;
	retval = inb(FDD_FIFO);
	b_sleep(10);
	return retval;
}

void FDD_IRQ()
{
	FDD_int = 1;
	outb(0x20, 0x20);		//Reset the master
	return;
}

char init_fdd()
{
	return 0;
}

char init_dma(unsigned int address)
{
	outb(0x0A,0x06);	//Mask DMA channel
	
	//reset the flip-flop
	outb(0xD8,0xFF);
	outb(0x0C,0xFF);
	
	unsigned char addr_part;
	addr_part = address;		//Get lower part
	outb(0x04,addr_part);
	addr_part = address >> 8;	//Get higher part
	outb(0x04,addr_part);
	addr_part = address >> 16;	//Get page
	outb(0x81,addr_part);
		
	//reset the flip-flop
	outb(0xD8,0xFF);
	outb(0x0C,0xFF);
	
	//Set the sector size (0x1FF)
	outb(0x05,0xFF);
	outb(0x05,0x01);
	
	//Mode of operation -- 00000110, demand transfer,to mem, channel 2
	outb(0x0b,0x06);
	
	//unmask the channel
	outb(0x0A,0x02);

	return 0;
}
Modular Interface Kernel With a lot of bugs ;)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: FDD problem

Post by Brendan »

Hi,

When a track is formatted the floppy driver writes an "address mark" at the start of each sector, which contains some numbers to identify the sector and includes "logical cylinder, logical head and logical sector" numbers that (if I remember right) don't necessarily have to have anything to do with the physical cylinder, header or sector (but typically do for convenience, unless you're doing a copy protection thing from the 1980s).

Your error ("ST0 is 0x40 where ST1 is 0x01") means "abnormal termination, address mark not found". Basically the floppy drive searched the track that happens to be under the floppy heads, and couldn't find a sector who's address mark contained the "logical cylinder, logical head and logical sector" numbers that you asked for.

My guess is that it couldn't read the address mark from the disk because the "data rate" is wrong (e.g. left at the default 240 Kbps when you'd need to set it to 500 Kbps for 1440 KiB floppies); or you didn't seek to the correct cylinder/track (e.g. searching physical cylinder #0 for a sector who's address mark says "logical cylinder number #3" because you didn't seek to physical cylinder #3 first).

General notes: I wouldn't rely on the MT/"Multi-track" feature to change heads for you, and the "deleted sectors" stuff isn't used for 80x86 floppy. You should have a higher level "read()" function to split larger reads into multiple smaller reads that don't pass DMA or track boundaries (which also handles retries, etc). You should also keep track of the current details (data rate, HRT, SRT, etc) and the current track for each floppy drive (as there may be 2 floppy drives attached to the same controller) so you can determine when "seek()" is needed and so that you can switch between floppy drives easily (because if there's 2 floppy drives that want different data rates, etc; then you have to set the data rate, etc each time you change from one drive to the other). It is worthwhile maintaining a cache for each floppy (e.g. I'd cache whole tracks not sectors) and it should be possible to detect if/when a disk is present and when a disk is removed (so you can flush those caches when the disk has been changed). It is possible to auto-detect the floppy disk's type (e.g. 1680 KiB, 1440 KiB, 1200 KiB, 720 KiB, etc) using a clever sequence of test (e.g. try the first sector at different data rates until you know the correct data rate, then try different sectors in the first track until you know the number of sectors, etc). It is not possible to determine the floppy drive's type (e.g. 3.5 inch or 5.25 inch) but you can auto-detect the floppy disk's type and use that to guess the flopppy drive's type (e.g. if floppy disk is 1440 KiB assume 3.5 inch drive, if floppy disk is 1200 KiB assume 5.25 inch drive, etc) and this information can be used to reduce the number of tests needed for media detection (e.g. if you guess it's not a 5.25 inch drive, then you don't need to test if a newly inserted disk is 1200 KiB).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Ferrarius
Member
Member
Posts: 69
Joined: Sun Oct 28, 2007 4:10 pm

Re: FDD problem

Post by Ferrarius »

I thank you greatly, setting the data rate to 0 has indeed fixed the problem ^^
Modular Interface Kernel With a lot of bugs ;)
Post Reply