Floppy Transfers Without DMA [solved]

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
Stevo14
Member
Member
Posts: 179
Joined: Fri Mar 07, 2008 3:40 am
Location: Arad, Romania

Floppy Transfers Without DMA [solved]

Post by Stevo14 »

I'm trying to avoid building DMA into my kernel. It can be loaded as a module later when my kernel implements multitasking. So for now all hard drive transfers are in PIO mode. I would also like to make this the case for my floppy driver. I've followed the floppy tutorial on this forum up to the point where it initializes DMA and starts the transfer. At this point I picked up the manual and started reading about non-DMA transfers. Supposedly (if I read correctly) you just have to send the CHS information after a read command and then read a byte (or two, I'm not sure) after each interrupt.

Here is the read function in it's entirety:

Code: Select all

int fdc_read(fdc_drive_t *fd, unsigned char *data, unsigned int sector, unsigned int offset, unsigned int length)
{
	if(!fdc_motor_on(fd))
		return 0;

	//check if a disk is in the drive
	unsigned char disk_not_changed = inportb(fd->ports.digital_input) & 0x80;
	if(disk_not_changed)
	{
		write_string("DNC");
		//this is gaurenteed to change the disk if it exists
		if(!fdc_seek(fd, 79))
		{
			write_string(" failed seek! ");
			return 0;
		}
		if(!fdc_seek(fd, 0))
		{
			write_string(" failed seek! ");
			return 0;
		}
	}
	disk_not_changed = inportb(fd->ports.digital_input) & 0x80;
	if(disk_not_changed)
	{	
		write_string(" No disk in drive. ");
		//there is no disk in the drive
		return 0;
	}

	if(!fdc_seek(fd, sector))
	{
		write_string(" failed seek! ");
		return 0;
	}

	//send the read command
	fdc_send_command(fd, FDC_READ_DATA);
	//fdc_send_data(fd, fd->index | fd->current_head << 2);	//head and drive 
	//fdc_send_data(fd, fd->current_cylinder);
	//fdc_send_data(fd, fd->current_head);
	//fdc_send_data(fd, fd->current_sector);
	//fdc_send_data(fd, 2);						// bytes per sector (2 = 512)
	//fdc_send_data(fd, fd->current_sector + 1);	// "end of track" the final sector of this track
	//fdc_send_data(fd, 0x1B);					//GAP3 length
	//fdc_send_data(fd, 0xFF);					//data length (0xFF because bytes per sector != 0)

	// first read status information
   unsigned char st0, st1, st2, rcy, rhe, rse, bps;
   fdc_get_data(fd, &st0);
	write_hex(st0);
halt();
	fdc_get_data(fd, &st1);
	fdc_get_data(fd, &st2);
   /*
    * These are cylinder/head/sector values, updated with some
    * rather bizarre logic, that I would like to understand.
    *
    */
   fdc_get_data(fd, &rcy);
   fdc_get_data(fd, &rhe);
   fdc_get_data(fd, &rse);
   // bytes per sector, should be what we programmed in
   fdc_get_data(fd, &bps);

	//get the data
	int i = 0; unsigned char *tmp = data;
	for(i = 0; i < 512; i++)
	{
		fdc_wait_interrupt(fd);
		fdc_get_data(fd, tmp);
		write_hex(*tmp);
		tmp++;
	}
	
	if(!fdc_motor_off(fd))
		return 0;

	return 1;
}
The problem is that the controller returns 0x80 (Invalid Command) immediately after the read command is sent (it happens no matter if the CHS sending code is commented or not). Have I done something horribly wrong?
Last edited by Stevo14 on Sat Jun 21, 2008 2:54 am, edited 1 time in total.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Floppy Transfers Without DMA

Post by bewing »

I have not done PIO floppy stuff yet, but my initial guess is that you cannot send the read command until AFTER you have set up the CHS address. The default sector is probably 0, which is illegal.

Also I'm worried about the way you calculate one CHS value: fd->index | fd->current_head << 2
-- are you sure the precedence of that calculation is right?
User avatar
Stevo14
Member
Member
Posts: 179
Joined: Fri Mar 07, 2008 3:40 am
Location: Arad, Romania

Re: Floppy Transfers Without DMA

Post by Stevo14 »

bewing wrote:I have not done PIO floppy stuff yet, but my initial guess is that you cannot send the read command until AFTER you have set up the CHS address.
Well that would have been my intuitive guess also but both the manual and the tutorial say differently.
bewing wrote:The default sector is probably 0, which is illegal.
I never though about this, I'll make sure that it's not zero.
bewing wrote:Also I'm worried about the way you calculate one CHS value: fd->index | fd->current_head << 2
-- are you sure the precedence of that calculation is right?
No, I'm not sure if that calculation is correct, that is part of why I asked here. :) fd->index is just the drive number (0, 1, 2, or 3) and fd->current_head << 2 is supposed to correctly set the head bit. I'll be sure to check this also.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Floppy Transfers Without DMA

Post by bewing »

Guess I'm not much help then. :D -- But when you uncomment the CHS stuff, at least be sure to use parenthesis.
d->index is just the drive number (0, 1, 2, or 3) and fd->current_head << 2 is supposed to correctly set the head bit.
Means: fd->index | (fd->current_head << 2)
User avatar
Stevo14
Member
Member
Posts: 179
Joined: Fri Mar 07, 2008 3:40 am
Location: Arad, Romania

Re: Floppy Transfers Without DMA

Post by Stevo14 »

bewing wrote:Guess I'm not much help then. :D
Well, it's better than no help at all. :wink:
Stevo14 wrote:
bewing wrote:The default sector is probably 0, which is illegal.
I never though about this, I'll make sure that it's not zero.
I've checked. fd->current_sector equals 1 just before it is sent to the floppy controller.
bewing wrote:Means: fd->index | (fd->current_head << 2)
This doesn't help either. :(

I also checked to see if the controller has any error bits set before I send the read command and the controller reports no errors until I send the read command.

I suspect that this line is probably at fault:

Code: Select all

fdc_send_data(fd, fd->current_sector + 1);	// "end of track" the final sector of this track
I have really no idea what is supposed to be sent, except that the manual calls it "end of track".
User avatar
Stevo14
Member
Member
Posts: 179
Joined: Fri Mar 07, 2008 3:40 am
Location: Arad, Romania

Re: Floppy Transfers Without DMA

Post by Stevo14 »

Ok, this is stupid... I forgot to set the "NO-DMA" bit in the specify command. #-o But I also needed to set the "MFM mode selector" bit in the read command in order for Qemu to accept it. Anyways, PIO mode works now. Yay! :D

EDIT: The final working read function:

Code: Select all

int fdc_read(fdc_drive_t *fd, unsigned char *data, unsigned int sector, unsigned int offset, unsigned int length)
{
	unsigned char st0, st1, st2, rcy, rhe, rse, bps, cyl;

	if(!fdc_motor_on(fd))
		return 0;

	//check if a disk is in the drive
	unsigned char disk_not_changed = inportb(fd->ports.digital_input) & 0x80;
	if(disk_not_changed)
	{
		//write_string("DNC");
		//this is gaurenteed to change the disk if it exists
		if(!fdc_seek(fd, 79))
		{
			write_string(" failed seek! ");
			return 0;
		}
		if(!fdc_seek(fd, 0))
		{
			write_string(" failed seek! ");
			return 0;
		}
	}
	disk_not_changed = inportb(fd->ports.digital_input) & 0x80;
	if(disk_not_changed)
	{	
		write_string(" No disk in drive. ");
		//there is no disk in the drive
		return 0;
	}

	if(!fdc_seek(fd, sector))
	{
		write_string(" failed seek! ");
		return 0;
	}

	//send the read command
	fdc_send_command(fd, FDC_READ_DATA);
	fdc_send_data(fd, fd->index | (fd->current_head << 2));	//head and drive 
	fdc_send_data(fd, fd->current_cylinder);
	fdc_send_data(fd, fd->current_head);
	fdc_send_data(fd, fd->current_sector);
	fdc_send_data(fd, 2);						// bytes per sector (2 = 512)
	fdc_send_data(fd, 1);	// "end of track" the final sector of this track (I have no idea what it does)
	fdc_send_data(fd, 0x1B);					//GAP3 length
	fdc_send_data(fd, 0xFF);					//data length (0xFF because bytes per sector != 0)

	//get the data
	fdc_wait_interrupt(fd);//wait for it...
	//GO!
	int i = 0; unsigned char *tmp = data;
	for(i = 0; i < 512; i++)
	{	
		fdc_get_data(fd, tmp);
		write_hex(*tmp);
		tmp++;
	}

	// read status information
   fdc_get_data(fd, &st0);
	write_hex(st0);
	fdc_get_data(fd, &st1);
	fdc_get_data(fd, &st2);
   /*
    * These are cylinder/head/sector values, updated with some
    * rather bizarre logic, that I would like to understand.
    */
   fdc_get_data(fd, &rcy);
   fdc_get_data(fd, &rhe);
   fdc_get_data(fd, &rse);
   // bytes per sector, should be what we programmed in
   fdc_get_data(fd, &bps);

	if(!fdc_motor_off(fd))
		return 0;

	return 1;
}
Post Reply