[solved] FDC FIFO error

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
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

[solved] FDC FIFO error

Post by onlyonemac »

Hi,

I'm trying to read form a floppy disk. The code works on QEMU but fails with st1 = 0x10, which is apparently a FIFO error.

Code: Select all

static void floppy_read_sectors(uint8_t track, uint8_t head, uint8_t start_sector, uint8_t sector_count, uint32_t buffer)
{
	fdc_motor_on();
	fdc_do_command(FDC_CMD_SEEK, (uint8_t[]){ 0, track }, NULL);
	fdc_do_command(FDC_CMD_SENSE, NULL, fdc_results);
	dma_setup(buffer, ((uint16_t) sector_count) * 512, FALSE);
	fdc_do_command(FDC_CMD_READ, (uint8_t[]){ head << 2, track, head, start_sector + 1, 2, sector_count, 0x1B, 0xFF }, fdc_results);
	fdc_motor_off();
}
(Yes I know I'm not waiting for the motor to spin up but for now the seek gives an adequate delay.)

Code: Select all

static void fdc_motor_on()
{
	outb(FDC_REGISTER_DOR, 0x1C);
}

static void fdc_motor_off()
{
	outb(FDC_REGISTER_DOR, 0x0C);
}

Code: Select all

static void fdc_do_command(fdc_command command, uint8_t* parameters, uint8_t* results)
{
	uint32_t	current_parameter;
	uint32_t	parameter_count;
	uint32_t	current_result;
	uint32_t	result_count;
	uint32_t	irq_expected;

	while ((inb(FDC_REGISTER_MSR) & 0xC0) != 0x80)
	{
		// wait
	}

	switch (command)
	{
		case FDC_CMD_READ:
		case FDC_CMD_WRITE:
			parameter_count = 8;
			result_count = 7;
			irq_expected = TRUE;
			break;
		case FDC_CMD_SEEK:
			parameter_count = 2;
			result_count = 0;
			irq_expected = TRUE;
			break;
		case FDC_CMD_RECALIBRATE:
			parameter_count = 1;
			result_count = 0;
			irq_expected = TRUE;
			break;
		case FDC_CMD_SENSE:
			parameter_count = 0;
			result_count = 2;
			irq_expected = FALSE;
			break;
		case FDC_CMD_CONFIGURE:
			parameter_count = 3;
			result_count = 0;
			irq_expected = FALSE;
			break;
		case FDC_CMD_SPECIFY:
			parameter_count = 2;
			result_count = 0;
			irq_expected = FALSE;
			break;
		case FDC_CMD_LOCK:
		case FDC_CMD_UNLOCK:
			parameter_count = 0;
			result_count = 1;
			irq_expected = FALSE;
			break;
		case FDC_CMD_VERSION:
			parameter_count = 0;
			result_count = 1;
			irq_expected = FALSE;
			break;
	}

	if (irq_expected == TRUE)
	{
		instance->irq = FALSE;
	}
	outb(FDC_REGISTER_FIFO, command);
	current_parameter = 0;
	while (current_parameter < parameter_count)
	{
		while ((inb(FDC_REGISTER_MSR) & 0xC0) != 0x80)
		{
			// wait
		}
		outb(FDC_REGISTER_FIFO, parameters[current_parameter]);
		current_parameter++;
	}
	if (irq_expected == TRUE)
	{
		fdc_wait_for_irq();
	}
	current_result = 0;
	while (current_result < result_count)
	{
		while ((inb(FDC_REGISTER_MSR) & 0xC0) != 0xC0)
		{
			// wait
		}
		results[current_result] = inb(FDC_REGISTER_FIFO);
		current_result++;
	}
}

Code: Select all

static void dma_setup(uint32_t address, uint16_t length, uint8_t direction)	// direction = FALSE to read from disk; direction = TRUE to write to disk
{
	outb(0x0B, direction == FALSE ? 0x06 : 0x0A);
	outb(0x0C, 0x00);
	outb(0x04, address & 0x000000FF);
	outb(0x04, (address & 0x0000FF00) >> 8);
	outb(0x0C, 0x00);
	outb(0x05, (length - 1) & 0x00FF);
	outb(0x05, ((length - 1) & 0xFF00) >> 8);
	outb(0x81, (address & 0x00FF0000) >> 16);
}
Here's the code that I'm using to initialise the FDC - I'm guessing that the FIFO is not configured correctly?

Code: Select all

fdc_motor_off();
fdc_do_command(FDC_CMD_CONFIGURE, (uint8_t[]){ 0, 0x17, 0 }, NULL);
fdc_do_command(FDC_CMD_LOCK, NULL, fdc_results);
fdc_reset();
fdc_motor_on();
fdc_do_command(FDC_CMD_RECALIBRATE, (uint8_t[]){ 0 }, NULL);
fdc_do_command(FDC_CMD_SENSE, NULL, fdc_results);
fdc_motor_off();

Code: Select all

static void fdc_reset()
{
	instance->irq = FALSE;
	outb(FDC_REGISTER_DSR, 0x80);
	fdc_wait_for_irq();
	fdc_do_command(FDC_CMD_SPECIFY, (uint8_t[]){ (8 << 4) | 5, 15 << 1 }, NULL);
}
Thanks,

onlyonemac
Last edited by onlyonemac on Tue Aug 09, 2016 7:55 am, edited 1 time in total.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
User avatar
sleephacker
Member
Member
Posts: 97
Joined: Thu Aug 06, 2015 6:41 am
Location: Netherlands

Re: FDC FIFO error

Post by sleephacker »

st1 = 0x10 could also be a DMA error, to quote the datasheet on bit 4 in st1:
Becomes set if the 82077AA does not receive CPU or DMA
service within the required time interval, resulting in data
overrun or underrun
I also noticed that you didn't mask DMA channel 2 while setting up the transfer, which I believe is required before doing anything, so that might be the problem.
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: FDC FIFO error

Post by onlyonemac »

sleephacker wrote:I also noticed that you didn't mask DMA channel 2 while setting up the transfer, which I believe is required before doing anything, so that might be the problem.
I thought that was only necessary if you were setting up the FDC first, and you didn't want the DMA trying to do the transfer before you'd finished setting it up, but I will try that anyway.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: FDC FIFO error

Post by onlyonemac »

Yes, it's working now. :-)
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Post Reply