Page 1 of 1

Floppy disk IRQ handler

Posted: Wed Nov 24, 2010 5:26 am
by melgmry0101b
Hi everyone,
I am developing an operating system and now i am developing the floppy disk controller.
my problem is : My IRQ can't fire.
I am developing my OS in MSVC 2005
This is my code:

Code: Select all

void _cdecl i86_flpy_irq () {
	_asm add esp, 12
	_asm pushad
	_asm cli

	//! irq fired
	_FloppyDiskIRQ = 1;

	//! tell hal we are done
	interruptdone( FLOPPY_IRQ );

	_asm sti
	_asm popad
	_asm iretd
}
I want to know where is the problem.
----------------------
Thank you in advance

Re: Floppy disk IRQ handler

Posted: Wed Nov 24, 2010 7:55 am
by Combuster
You can not use regular C functions for interrupt handlers - use an assembly stub. You are now creating a double prologue/epilogue.

Re: Floppy disk IRQ handler

Posted: Wed Nov 24, 2010 8:10 am
by melgmry0101b
I am using ------->>>Microsoft Visual Studio 2005 <<<------- i don't know how to make an assembly stub in the VS

Re: Floppy disk IRQ handler

Posted: Wed Nov 24, 2010 8:32 am
by Love4Boobies
Visual Studio comes with MASM, which is an assembler. Use that to create your object file and link everything together after you're done.

Re: Floppy disk IRQ handler

Posted: Wed Nov 24, 2010 10:57 pm
by salil_bhagurkar
Once you are sure that your IRQs are working, you need to show us the code that ensures that the FDC IRQ would fire.

Re: Floppy disk IRQ handler

Posted: Thu Nov 25, 2010 8:41 am
by melgmry0101b
OK
This is the code:

Code: Select all


//**    flpydsk.cpp




#include <hal.h>
#include "flpydsk.h"


/*
**	Controller I/O Ports. Please see chapter for additional ports
*/

enum FLPYDSK_IO {

	FLPYDSK_DOR		=	0x3f2,
	FLPYDSK_MSR		=	0x3f4,
	FLPYDSK_FIFO	=	0x3f5,
	FLPYDSK_CTRL	=	0x3f7
};

/**
*	Bits 0-4 of command byte. Please see chapter for additional commands
*/

enum FLPYDSK_CMD {

	FDC_CMD_READ_TRACK	=	2,
	FDC_CMD_SPECIFY		=	3,
	FDC_CMD_CHECK_STAT	=	4,
	FDC_CMD_WRITE_SECT	=	5,
	FDC_CMD_READ_SECT	=	6,
	FDC_CMD_CALIBRATE	=	7,
	FDC_CMD_CHECK_INT	=	8,
	FDC_CMD_FORMAT_TRACK=	0xd,
	FDC_CMD_SEEK		=	0xf
};

/**
*	Additional command masks. Can be masked with above commands
*/

enum FLPYDSK_CMD_EXT {

	FDC_CMD_EXT_SKIP		=	0x20,	//00100000
	FDC_CMD_EXT_DENSITY		=	0x40,	//01000000
	FDC_CMD_EXT_MULTITRACK	=	0x80	//10000000
};

/*
**	Digital Output Register
*/

enum FLPYDSK_DOR_MASK {

	FLPYDSK_DOR_MASK_DRIVE0			=	0,	//00000000	= here for completeness sake
	FLPYDSK_DOR_MASK_DRIVE1			=	1,	//00000001
	FLPYDSK_DOR_MASK_DRIVE2			=	2,	//00000010
	FLPYDSK_DOR_MASK_DRIVE3			=	3,	//00000011
	FLPYDSK_DOR_MASK_RESET			=	4,	//00000100
	FLPYDSK_DOR_MASK_DMA			=	8,	//00001000
	FLPYDSK_DOR_MASK_DRIVE0_MOTOR	=	16,	//00010000
	FLPYDSK_DOR_MASK_DRIVE1_MOTOR	=	32,	//00100000
	FLPYDSK_DOR_MASK_DRIVE2_MOTOR	=	64,	//01000000
	FLPYDSK_DOR_MASK_DRIVE3_MOTOR	=	128	//10000000
};

/**
*	Main Status Register
*/

enum FLPYDSK_MSR_MASK {

	FLPYDSK_MSR_MASK_DRIVE1_POS_MODE	=	1,	//00000001
	FLPYDSK_MSR_MASK_DRIVE2_POS_MODE	=	2,	//00000010
	FLPYDSK_MSR_MASK_DRIVE3_POS_MODE	=	4,	//00000100
	FLPYDSK_MSR_MASK_DRIVE4_POS_MODE	=	8,	//00001000
	FLPYDSK_MSR_MASK_BUSY				=	16,	//00010000
	FLPYDSK_MSR_MASK_DMA				=	32,	//00100000
	FLPYDSK_MSR_MASK_DATAIO				=	64, //01000000
	FLPYDSK_MSR_MASK_DATAREG			=	128	//10000000
};

/**
*	Controller Status Port 0
*/

enum FLPYDSK_ST0_MASK {

	FLPYDSK_ST0_MASK_DRIVE0		=	0,		//00000000	=	for completness sake
	FLPYDSK_ST0_MASK_DRIVE1		=	1,		//00000001
	FLPYDSK_ST0_MASK_DRIVE2		=	2,		//00000010
	FLPYDSK_ST0_MASK_DRIVE3		=	3,		//00000011
	FLPYDSK_ST0_MASK_HEADACTIVE	=	4,		//00000100
	FLPYDSK_ST0_MASK_NOTREADY	=	8,		//00001000
	FLPYDSK_ST0_MASK_UNITCHECK	=	16,		//00010000
	FLPYDSK_ST0_MASK_SEEKEND	=	32,		//00100000
	FLPYDSK_ST0_MASK_INTCODE	=	64		//11000000
};

/*
** LPYDSK_ST0_MASK_INTCODE types
*/

enum FLPYDSK_ST0_INTCODE_TYP {

	FLPYDSK_ST0_TYP_NORMAL		=	0,
	FLPYDSK_ST0_TYP_ABNORMAL_ERR=	1,
	FLPYDSK_ST0_TYP_INVALID_ERR	=	2,
	FLPYDSK_ST0_TYP_NOTREADY	=	3
};

/**
*	GAP 3 sizes
*/

enum FLPYDSK_GAP3_LENGTH {

	FLPYDSK_GAP3_LENGTH_STD = 42,
	FLPYDSK_GAP3_LENGTH_5_14= 32,
	FLPYDSK_GAP3_LENGTH_3_5= 27
};

/*
**	Formula: 2^sector_number * 128, where ^ denotes "to the power of"
*/

enum FLPYDSK_SECTOR_DTL {

	FLPYDSK_SECTOR_DTL_128	=	0,
	FLPYDSK_SECTOR_DTL_256	=	1,
	FLPYDSK_SECTOR_DTL_512	=	2,
	FLPYDSK_SECTOR_DTL_1024	=	4
};

/**
*	Constants
*/

//! floppy irq
const int FLOPPY_IRQ = 6;

//! sectors per track
const int FLPY_SECTORS_PER_TRACK = 18;

//! dma tranfer buffer starts here and ends at 0x1000+64k
//! You can change this as needed. It must be below 16MB and in idenitity mapped memory!
int DMA_BUFFER = 0x1000;

//! FDC uses DMA channel 2
const int FDC_DMA_CHANNEL = 2;


//! used to wait in miliseconds
extern void sleep (int);


//! current working drive. Defaults to 0 which should be fine on most systems
static uint8_t	_CurrentDrive = 0;

//! set when IRQ fires
static volatile uint8_t _FloppyDiskIRQ = 0;


/**
*	DMA Routines.
*/

bool _cdecl dma_initialize_floppy(uint8_t* buffer, unsigned length){
   union{
      uint8_t byte[4];//Lo[0], Mid[1], Hi[2]
      unsigned long l;
   }a, c;

   a.l=(unsigned)buffer;
   c.l=(unsigned)length-1;

   //Check for buffer issues
   if ((a.l >> 24) || (c.l >> 16) || (((a.l & 0xffff)+c.l) >> 16)){
#ifdef _DEBUG
      _asm{
         mov      eax, 0x1337
         cli
         hlt
      }
#endif
      return false;
   }

   dma_reset (1);
   dma_mask_channel( FDC_DMA_CHANNEL );//Mask channel 2
   dma_reset_flipflop ( 1 );//Flipflop reset on DMA 1

   dma_set_address( FDC_DMA_CHANNEL, a.byte[0],a.byte[1]);//Buffer address
   dma_reset_flipflop( 1 );//Flipflop reset on DMA 1

   dma_set_count( FDC_DMA_CHANNEL, c.byte[0],c.byte[1]);//Set count
   dma_set_read ( FDC_DMA_CHANNEL );

   dma_unmask_all( 1 );//Unmask channel 2

   return true;
}

//! sets DMA base address
void flpydsk_set_dma (int addr) {

	DMA_BUFFER = addr;
}

/**
*	Basic Controller I/O Routines
*/

//! return fdc status
uint8_t flpydsk_read_status () {

	//! just return main status register
	return inportb (FLPYDSK_MSR);
}

//! write to the fdc dor
void flpydsk_write_dor (uint8_t val ) {

	//! write the digital output register
	outportb (FLPYDSK_DOR, val);
}

//! send command byte to fdc
void flpydsk_send_command (uint8_t cmd) {

	//! wait until data register is ready. We send commands to the data register
	for (int i = 0; i < 500; i++ )
		if ( flpydsk_read_status () & FLPYDSK_MSR_MASK_DATAREG )
			return outportb (FLPYDSK_FIFO, cmd);
}

//! get data from fdc
uint8_t flpydsk_read_data () {

	//! same as above function but returns data register for reading
	for (int i = 0; i < 500; i++ )
		if ( flpydsk_read_status () & FLPYDSK_MSR_MASK_DATAREG )
			return inportb (FLPYDSK_FIFO);

	return 0;
}

//! write to the configuation control register
void flpydsk_write_ccr (uint8_t val) {

	//! write the configuation control
	outportb (FLPYDSK_CTRL, val);
}

/**
*	Interrupt Handling Routines
*/

//! wait for irq to fire
inline void flpydsk_wait_irq () {

	//! wait for irq to fire
	while ( _FloppyDiskIRQ == 0)
		;
	_FloppyDiskIRQ = 0;
}


//!	floppy disk irq handler
void _cdecl i86_flpy_irq () {

	_asm add esp, 12
	_asm pushad
	_asm cli

	//! irq fired
	_FloppyDiskIRQ = 1;

	//! tell hal we are done
	interruptdone( FLOPPY_IRQ );

	_asm sti
	_asm popad
	_asm iretd
}

/**
*	Controller Command Routines
*/

//! check interrupt status command
void flpydsk_check_int (uint32_t* st0, uint32_t* cyl) {

	flpydsk_send_command (FDC_CMD_CHECK_INT);

	*st0 = flpydsk_read_data ();
	*cyl = flpydsk_read_data ();
}

//! turns the current floppy drives motor on/off
void flpydsk_control_motor (bool b) {

	//! sanity check: invalid drive
	if (_CurrentDrive > 3)
		return;

	uint8_t motor = 0;

	//! select the correct mask based on current drive
	switch (_CurrentDrive) {

		case 0:
			motor = FLPYDSK_DOR_MASK_DRIVE0_MOTOR;
			break;
		case 1:
			motor = FLPYDSK_DOR_MASK_DRIVE1_MOTOR;
			break;
		case 2:
			motor = FLPYDSK_DOR_MASK_DRIVE2_MOTOR;
			break;
		case 3:
			motor = FLPYDSK_DOR_MASK_DRIVE3_MOTOR;
			break;
	}

	//! turn on or off the motor of that drive
	if (b)
		flpydsk_write_dor (uint8_t(_CurrentDrive | motor | FLPYDSK_DOR_MASK_RESET | FLPYDSK_DOR_MASK_DMA));
	else
		flpydsk_write_dor (FLPYDSK_DOR_MASK_RESET);

	//! in all cases; wait a little bit for the motor to spin up/turn off
	sleep (20);
}

//! configure drive
void flpydsk_drive_data (uint8_t stepr, uint8_t loadt, uint8_t unloadt, bool dma ) {

	uint8_t data = 0;

	//! send command
	flpydsk_send_command (FDC_CMD_SPECIFY);
	data = ( (stepr & 0xf) << 4) | (unloadt & 0xf);
		flpydsk_send_command (data);
	data = (( loadt << 1 ) | ( (dma) ? 0 : 1 ) );
		flpydsk_send_command (data);
}

//! calibrates the drive
int flpydsk_calibrate (uint8_t drive) {

	uint32_t st0, cyl;

	if (drive >= 4)
		return -2;

	//! turn on the motor
	flpydsk_control_motor (true);

	for (int i = 0; i < 10; i++) {

		//! send command
		flpydsk_send_command ( FDC_CMD_CALIBRATE );
		flpydsk_send_command ( drive );
		flpydsk_wait_irq ();
		flpydsk_check_int ( &st0, &cyl);

		//! did we fine cylinder 0? if so, we are done
		if (!cyl) {

			flpydsk_control_motor (false);
			return 0;
		}
	}

	flpydsk_control_motor (false);
	return -1;
}

//! disable controller
void flpydsk_disable_controller () {

	flpydsk_write_dor (0);
}

//! enable controller
void flpydsk_enable_controller () {

	flpydsk_write_dor ( FLPYDSK_DOR_MASK_RESET | FLPYDSK_DOR_MASK_DMA);
}

//! reset controller
void flpydsk_reset () {

	uint32_t st0, cyl;

	//! reset the controller
	flpydsk_disable_controller ();
	flpydsk_enable_controller ();
	[color=red]flpydsk_wait_irq ();[/color]

	//! send CHECK_INT/SENSE INTERRUPT command to all drives
	for (int i=0; i<4; i++)
		flpydsk_check_int (&st0,&cyl);

	//! transfer speed 500kb/s
	flpydsk_write_ccr (0);

	//! pass mechanical drive info. steprate=3ms, unload time=240ms, load time=16ms
	flpydsk_drive_data (3,16,240,true);

	//! calibrate the disk
	flpydsk_calibrate ( _CurrentDrive );
}

//! read a sector
void flpydsk_read_sector_imp (uint8_t head, uint8_t track, uint8_t sector) {

	uint32_t st0, cyl;

	//! initialize DMA
	dma_initialize_floppy ((uint8_t*) DMA_BUFFER, 512 );

	//! set the DMA for read transfer
	dma_set_read ( FDC_DMA_CHANNEL );

	//! read in a sector
	flpydsk_send_command (
				FDC_CMD_READ_SECT | FDC_CMD_EXT_MULTITRACK | FDC_CMD_EXT_SKIP | FDC_CMD_EXT_DENSITY);
	flpydsk_send_command ( head << 2 | _CurrentDrive );
	flpydsk_send_command ( track);
	flpydsk_send_command ( head);
	flpydsk_send_command ( sector);
	flpydsk_send_command ( FLPYDSK_SECTOR_DTL_512 );
	flpydsk_send_command ( ( ( sector + 1 ) >= FLPY_SECTORS_PER_TRACK ) ? FLPY_SECTORS_PER_TRACK : sector + 1 );
	flpydsk_send_command ( FLPYDSK_GAP3_LENGTH_3_5 );
	flpydsk_send_command ( 0xff );

	//! wait for irq
	flpydsk_wait_irq ();

	//! read status info
	for (int j=0; j<7; j++)
		flpydsk_read_data ();

	//! let FDC know we handled interrupt
	flpydsk_check_int (&st0,&cyl);
}

//! seek to given track/cylinder
int flpydsk_seek ( uint8_t cyl, uint8_t head ) {

	uint32_t st0, cyl0;

	if (_CurrentDrive >= 4)
		return -1;

	for (int i = 0; i < 10; i++ ) {

		//! send the command
		flpydsk_send_command (FDC_CMD_SEEK);
		flpydsk_send_command ( (head) << 2 | _CurrentDrive);
		flpydsk_send_command (cyl);

		//! wait for the results phase IRQ
		flpydsk_wait_irq ();
		flpydsk_check_int (&st0,&cyl0);

		//! found the cylinder?
		if ( cyl0 == cyl)
			return 0;
	}

	return -1;
}


//! convert LBA to CHS
void flpydsk_lba_to_chs (int lba,int *head,int *track,int *sector) {

   *head = ( lba % ( FLPY_SECTORS_PER_TRACK * 2 ) ) / ( FLPY_SECTORS_PER_TRACK );
   *track = lba / ( FLPY_SECTORS_PER_TRACK * 2 );
   *sector = lba % FLPY_SECTORS_PER_TRACK + 1;
}

//! install floppy driver
void flpydsk_install (int irq) {

	//! install irq handler
	setvect (irq, i86_flpy_irq);

	//! reset the fdc
	flpydsk_reset ();

	//! set drive information
	flpydsk_drive_data (13, 1, 0xf, true);
}

//! set current working drive
void flpydsk_set_working_drive (uint8_t drive) {

	if (drive < 4)
		_CurrentDrive = drive;
}

//! get current working drive
uint8_t flpydsk_get_working_drive () {

	return _CurrentDrive;
}

//! read a sector
uint8_t* flpydsk_read_sector (int sectorLBA) {

	if (_CurrentDrive >= 4)
		return 0;

	//! convert LBA sector to CHS
	int head=0, track=0, sector=1;
	flpydsk_lba_to_chs (sectorLBA, &head, &track, &sector);

	//! turn motor on and seek to track
	flpydsk_control_motor (true);
	if (flpydsk_seek ((uint8_t)track, (uint8_t)head) != 0)
		return 0;

	//! read sector and turn motor off
	flpydsk_read_sector_imp ((uint8_t)head, (uint8_t)track, (uint8_t)sector);
	flpydsk_control_motor (false);

	//! warning: this is a bit hackish
	return (uint8_t*) DMA_BUFFER;
}

-------------------------------------------
Thank you in advance :D