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.
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:
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?
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;
}
Last edited by Stevo14 on Sat Jun 21, 2008 2:54 am, edited 1 time in total.
~[Fluidium]~
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?
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?
Re: Floppy Transfers Without DMA
Well that would have been my intuitive guess also but both the manual and the tutorial say differently.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.
I never though about this, I'll make sure that it's not zero.bewing wrote:The default sector is probably 0, which is illegal.
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.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?
~[Fluidium]~
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.
Means: fd->index | (fd->current_head << 2)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.
Re: Floppy Transfers Without DMA
Well, it's better than no help at all. :wink:bewing wrote:Guess I'm not much help then. :D
I've checked. fd->current_sector equals 1 just before it is sent to the floppy controller.Stevo14 wrote:I never though about this, I'll make sure that it's not zero.bewing wrote:The default sector is probably 0, which is illegal.
This doesn't help either. :(bewing wrote:Means: fd->index | (fd->current_head << 2)
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
~[Fluidium]~
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:
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;
}
~[Fluidium]~