OSDev.org
https://forum.osdev.org/

Floppy Transfers Without DMA [solved]
https://forum.osdev.org/viewtopic.php?f=1&t=17274
Page 1 of 1

Author:  Stevo14 [ Thu Jun 19, 2008 9:47 am ]
Post subject:  Floppy Transfers Without DMA [solved]

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:
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?

Author:  bewing [ Thu Jun 19, 2008 3:36 pm ]
Post subject:  Re: Floppy Transfers Without DMA

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?

Author:  Stevo14 [ Fri Jun 20, 2008 12:25 am ]
Post subject:  Re: Floppy Transfers Without DMA

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.

Author:  bewing [ Fri Jun 20, 2008 5:07 am ]
Post subject:  Re: Floppy Transfers Without DMA

Guess I'm not much help then. :D -- But when you uncomment the CHS stuff, at least be sure to use parenthesis.

Quote:
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)

Author:  Stevo14 [ Fri Jun 20, 2008 9:06 am ]
Post subject:  Re: Floppy Transfers Without DMA

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:
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".

Author:  Stevo14 [ Sat Jun 21, 2008 12:43 am ]
Post subject:  Re: Floppy Transfers Without DMA

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:
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;
}

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/