Floppy only reads Cylinder 0

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
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Floppy only reads Cylinder 0

Post by mohammedisam »

Hi there
I have a problem seeking the floppy disk. I am using a floppy image (floppy.img) in Oracle Virtual Box. The floppy driver is started correctly and I can read the FAT12 table and list the root directory alright. In fact any file/folder that is present on Cylinder 0 will read correctly. But whenever I access any higher Cylinder (starting from Cyl 1) the FDC doesn't seek into the correct address, and the results I get from the "Sense Interrupt" command are: st0: 0x24 (sometimes 0x20), st1 & st2 & cylinder & head are all zero, which means I didn't get to the correct cylinder (e.g. I am accessing a file at LBA 60, which is C1 H1 S7, but it ALWAYS fails). The function code is as follows:

Code: Select all

int floppy_read_sector(long lba)
{
  if(_cur_floppy_drive >= 4)
    return 0;
  
  unsigned int cylinder = 0, head = 0, sector = 1;
  LBA_to_CHS(lba, &cylinder, &head, &sector, _cur_floppy_drive);
  /* turn motor on */
  floppy_control_motor(1);
  /* seek to the CHS */
  uint32_t st1 = 0, cyl = 0;
  for(int i = 0; i < 10; i++)
  {
    floppy_cmd_send(0x0F);	//'Seek' command
    floppy_cmd_send((head << 2) | _cur_floppy_drive);
    floppy_cmd_send(cyl);
    floppy_wait_on_irq();
    /* tell FDC we handled the interrupt */
    floppy_cmd_send(0x08);	//'check interrupt' command
    cyl = floppy_read_data();	//st0
    st1 = floppy_read_data();	//st1
    cyl = floppy_read_data();	//st2
    cyl = floppy_read_data();
    printf("cyl: %u ", (unsigned)cyl);
    /* found cylinder? */
    if(cylinder == cyl)
      goto read_sector;
  }
  printf("Failed to read floppy\n");
  return 0;
read_sector:
  _floppy_read_sector((uint8_t)cylinder, (uint8_t)head, (uint8_t)sector);
  floppy_control_motor(0);
  return 1;
}
The hidden function _floppy_read_sector() does the actual DMA reading. The floppy_cmd_send() and floppy_read_data() functions simply wait for DR to be ready before sending (or reading) data from port 0x3F5. I didn't list those as the problem is here in this reading function: I never get beyond the check if(cylinder == cyl) if cylinder is > 0. If cylinder is zero, everything is fine.
Any ideas?
Thanx a lot folks :D
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Floppy only reads Cylinder 0

Post by Brendan »

Hi,
mohammedisam wrote:

Code: Select all

    floppy_cmd_send(0x08);	//'check interrupt' command
    cyl = floppy_read_data();	//st0
    st1 = floppy_read_data();	//st1
    cyl = floppy_read_data();	//st2
    cyl = floppy_read_data();
    printf("cyl: %u ", (unsigned)cyl);
The "sense interrupt status" command only returns 2 bytes of status (ST0 followed by "cylinder & 0xFF"). I have no idea why you're doing "floppy_read_data()" 4 times.


Some more notes...
  • You should move all the "seek()" stuff into a separate function.
  • You should check the values in ST0 to determine if seek worked or not. If seek didn't work (e.g. the command was aborted, the "equipment check" flag is set, etc) then the "cylinder" it returns can be wrong/misleading.
  • If the seek command fails, you should do a recalibrate command.
  • You should remember the last cylinder you used, and if someone wants to seek to the previously used cylinder then you should do nothing (avoid doing "seek to where we are already").
  • Turning the floppy motor on takes ages (something like a 300 ms delay as it gets up to speed?). Because of this you don't want to turn the floppy motor off immediately, but want to keep the motor on for 5 seconds or more afterwards. That way if more disk IO is needed "soon" the floppy motor is still on and you can skip the lengthy delay.
  • Whenever the floppy motor has been turned off, assume the user may have removed the disk from the disk drive. This means checking the "disk changed" flag in the Digital Input Register (if possible); and if that flag is broken using some other method (e.g. checksum of the disk's first cylinder) to determine if the disk is the same disk as before. If the disk was changed (or your "detect if disk changed using other methods" failed) you'd have wipe out the driver's current state (including setting your "lastCylinderUsed" to unknown) and go back to detecting if there's a disk in the drive, determining the disk's format (1680 KiB floppy, 1440 KiB floppy, 720 KiB floppy or whatever), etc.
  • Floppy is slow, so caching is important. I typically cache entire tracks, and never read less than an entire track into the disk cache.
  • Floppy disks are relatively tiny (compared to the amount of RAM in modern computers). This means that it's probably a good idea to cache the entire disk; and to do pre-fetching. Basically, as soon as the floppy is first used (and you've determined the disk's format) start pre-fetching the entire disk into your cache in the background (whenever there's nothing else for the floppy controller to do).
  • Floppy disks are also relatively unreliable. If "multiple sector reads" fail then fall back to one sector at a time, and if this happens often then stop trying to use "multiple sector reads". This increases the chance of successfully reading. For writing, I'd be tempted to write all sectors on a track, then verify that those sectors can be read correctly before moving on to the next track.

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.
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Floppy only reads Cylinder 0

Post by mohammedisam »

Hi Brendan
I sure found your notes worth their ink. You coincidentally answered some of the other questions I had about the FDC =D> Thanx 4 that.
Brendan wrote: [*]You should check the values in ST0 to determine if seek worked or not. If seek didn't work (e.g. the command was aborted, the "equipment check" flag is set, etc) then the "cylinder" it returns can be wrong/misleading.
As I mentioned, the st0 is always set to 0x20, which, according to the tutorial, is "normally" set after each recalibrate/seek command. So according to the st0 result, apparently the seek should have "succeeded", but actually it did not :shock: .
Brendan wrote: [*]If the seek command fails, you should do a recalibrate command.
Ok, I implemented the following function for recalibration after failed seek:

Code: Select all

int floppy_calibrate(uint32_t drive)
{
  uint32_t cyl;
  if(drive >= 4)
    return 0;
  
  for(int i = 0; i < 10; i++)
  {
    floppy_cmd_send(0x07);	//the calibrate command
    floppy_cmd_send(drive);
    floppy_wait_on_irq();
    /* tell FDC we handled the interrupt */
    floppy_cmd_send(0x08);	//'check interrupt' command
    floppy_read_data();	//st0
    cyl = floppy_read_data();

    //if we hit cylinder 0, we are done
    if(!cyl)
    {
      printf("Hit cyl 0!\n");
      return 1;
    }
  }
  return 0;
}
After a call to this function, I retry the seek/read function, but still the read fails with a constant cylinder number of 0, never above.
Brendan wrote: [*]Floppy disks are also relatively unreliable. If "multiple sector reads" fail then fall back to one sector at a time, and if this happens often then stop trying to use "multiple sector reads". This increases the chance of successfully reading. For writing, I'd be tempted to write all sectors on a track, then verify that those sectors can be read correctly before moving on to the next track.
So I should try sending a sector number of one to the "read command 0x06" ?

Thanx 4 ur input man
:D
Hanz
Member
Member
Posts: 29
Joined: Sun Mar 09, 2014 10:14 am

Re: Floppy only reads Cylinder 0

Post by Hanz »

Post Reply