📄 ataioint.c
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ATAIOINT.C
//
// by Hale Landis (hlandis@ata-atapi.com)
//
// There is no copyright and there are no restrictions on the use
// of this ATA Low Level I/O Driver code. It is distributed to
// help other programmers understand how the ATA device interface
// works and it is distributed without any warranty. Use this
// code at your own risk.
//
// This code is based on the ATA-x, and ATA/ATAPI-x standards and
// on interviews with various ATA controller and drive designers.
//
// This code has been run on many ATA (IDE) drives and
// MFM/RLL controllers. This code may be a little
// more picky about the status it sees at various times. A real
// BIOS probably would not check the status as carefully.
//
// Compile with one of the Borland C or C++ compilers.
//
// This C source contains the low level interrupt set up and
// interrupt handler functions.
//********************************************************************
#include <dos.h>
#include "ataio.h"
//*************************************************************
//
// Global interrupt mode data
//
//*************************************************************
int int_use_intr_flag = 0; // use INT mode if != 0
volatile int int_intr_cntr; // interrupt counter, incremented
// each time there is an interrupt
volatile int int_intr_flag; // interrupt flag, incremented
// for each device interrupt
unsigned int int_bmcr_addr; // BMCR/BMIDE status reg i/o address
volatile unsigned char int_bm_status; // BMCR/BMIDE status
unsigned int int_ata_addr; // ATA status reg I/O address
volatile unsigned char int_ata_status; // ATA status
//*************************************************************
//
// Local data
//
//*************************************************************
// interrupt handler function info...
static void interrupt ( far * int_org_int_vect ) (); // save area for the
// system's INT vector
static void far interrupt int_handler( void ); // our INT handler
// our interrupt handler's data...
static int int_got_it_now = 0; // != 0, our interrupt handler
//is installed now
static int int_irq_number = 0; // IRQ number in use, 1 to 15
static int int_int_vector = 0; // INT vector in use,
// INT 8h-15h and INT 70H-77H.
static int int_shared = 0; // shared flag
// system interrupt controller data...
#define PIC0_CTRL 0x20 // PIC0 i/o address
#define PIC0_MASK 0x21 // PIC0 i/o address
#define PIC1_CTRL 0xA0 // PIC1 i/o address
#define PIC1_MASK 0xA1 // PIC1 i/o address
#define PIC0_ENABLE_PIC1 0xFB // mask to enable PIC1 in PIC0
static pic_enable_irq[8] = // mask to enable
{ 0xFE, 0xFD, 0xFB, 0xF7, // IRQ 0-7 in PIC 0 or
0xEF, 0xDF, 0xBF, 0x7F }; // IRQ 8-15 in PIC 1
#define PIC_EOI 0x20 // end of interrupt
//*************************************************************
//
// Enable interrupt mode -- get the IRQ number we are using.
//
// The function MUST be called before interrupt mode can
// be used!
//
// If this function is called then the int_disable_irq()
// function MUST be called before exiting to DOS.
//
//*************************************************************
int int_enable_irq( int shared, int irqNum,
unsigned int bmAddr, unsigned int ataAddr )
// shared: 0 is not shared, != 0 is shared
// irqNum: 1 to 15
// bmAddr: i/o address for BMCR/BMIDE Status register
// ataAddr: i/o address for the ATA Status register
{
// error if interrupts enabled now
// error if invalid irq number
// error if bmAddr is < 100H
// error if shared and bmAddr is 0
// error if ataAddr is < 100H
if ( int_use_intr_flag )
return 1;
if ( ( irqNum < 1 ) || ( irqNum > 15 ) )
return 2;
if ( irqNum == 2 )
return 2;
if ( bmAddr && ( bmAddr < 0x0100 ) )
return 3;
if ( shared && ( ! bmAddr ) )
return 4;
if ( ataAddr < 0x0100 )
return 5;
// save the input parameters
int_shared = shared;
int_irq_number = irqNum;
int_bmcr_addr = bmAddr;
int_ata_addr = ataAddr;
// convert IRQ number to INT number
// and
// enable the interrupt in the PIC
if ( irqNum < 8 )
{
int_int_vector = irqNum + 8;
// In PIC0 change the IRQ 0-7 enable bit to 0
outportb( PIC0_MASK, ( inportb( PIC0_MASK )
& pic_enable_irq[ irqNum ] ) );
}
else
{
int_int_vector = 0x70 + ( irqNum - 8 );
// In PIC0 change the PIC1 enable bit to 0 (enable IRQ 2)
// In PIC1 change the IRQ enable bit to 0
outportb( PIC0_MASK, ( inportb( PIC0_MASK ) & PIC0_ENABLE_PIC1 ) );
outportb( PIC1_MASK, ( inportb( PIC1_MASK )
& pic_enable_irq[ irqNum - 8 ] ) );
}
// interrupts use is now enabled
int_use_intr_flag = 1;
int_got_it_now = 0;
// Done.
return 0;
}
//*************************************************************
//
// Disable interrupt mode.
//
// If the int_enable_irq() function has been called,
// this function MUST be called before exiting to DOS.
//
//*************************************************************
void int_disable_irq( void )
{
// if our interrupt handler is installed now,
// restore the system's (the original) interrupt handler.
if ( int_got_it_now )
{
// Disable interrupts.
// Restore the system's interrupt vector.
// Enable interrupts.
disable();
setvect( int_int_vector, int_org_int_vect );
enable();
}
// Reset all the interrupt data.
int_use_intr_flag = 0;
int_got_it_now = 0;
}
//*************************************************************
//
// Install our interrupt handler.
//
// Interrupt mode MUST be setup by calling int_enable_irq()
// before calling this function.
//
//*************************************************************
void int_save_int_vect( void )
{
// Do nothing if interrupt use not enabled now.
// Do nothing if our interrupt handler is installed now.
if ( ! int_use_intr_flag )
return;
if ( int_got_it_now )
return;
// Disable interrupts.
// Save the interrupt vector.
disable();
int_org_int_vect = getvect( int_int_vector );
// install our interrupt handler
setvect( int_int_vector, int_handler );
// Our interrupt handler is installed now.
int_got_it_now = 1;
// Reset the interrupt flag.
int_intr_cntr = 0;
int_intr_flag = 0;
// Enable interrupts.
enable();
}
//*************************************************************
//
// Restore the interrupt vector.
//
// Interrupt mode MUST be setup by calling int_enable_irq()
// before calling this function.
//
//*************************************************************
void int_restore_int_vect( void )
{
// Do nothing if interrupt useis disabled now.
// Do nothing if our interrupt handler is not installed.
if ( ! int_use_intr_flag )
return;
if ( ! int_got_it_now )
return;
// Disable interrupts.
// Restore the interrupt vector.
// Enable interrupts.
disable();
setvect( int_int_vector, int_org_int_vect );
enable();
// Our interrupt handler is not installed now.
int_got_it_now = 0;
}
//*************************************************************
//
// ATADRVR's Interrupt Handler.
//
//*************************************************************
static void far interrupt int_handler( void )
{
// increment the interrupt counter
int_intr_cntr ++ ;
// if BMCR/BMIDE present read the BMCR/BMIDE status
// else just read the device status.
if ( int_bmcr_addr )
{
// PCI ATA controller...
// ... read BMCR/BMIDE status
int_bm_status = inportb( int_bmcr_addr );
//... check if Interrupt bit = 1
if ( int_bm_status & BM_SR_MASK_INT )
{
// ... Interrupt=1...
// ... increment interrupt flag,
// ... read ATA status,
// ... reset Interrupt bit.
int_intr_flag ++ ;
int_ata_status = inportb( int_ata_addr );
outportb( int_bmcr_addr, BM_SR_MASK_INT );
}
}
else
{
// legacy ATA controller...
// ... increment interrupt flag,
// ... read ATA status.
int_intr_flag ++ ;
int_ata_status = inportb( int_ata_addr );
}
// if interrupt is shared, jump to the original handler...
if ( int_shared )
{
// pop all regs
asm pop bp
asm pop edi
asm pop esi
asm pop ds
asm pop es
asm pop edx
asm pop ecx
asm pop ebx
asm pop eax
// put cs:ip of next interrupt handler on stack
asm push ax // will be bp+8
asm push ax // will be bp+6
asm push ax
asm push bp
asm push ds
asm mov bp,DGROUP
asm mov ds,bp
asm mov bp, sp
asm mov ax, word ptr DGROUP:int_org_int_vect
asm mov [bp+6], ax
asm mov ax, word ptr DGROUP:int_org_int_vect+2
asm mov [bp+8], ax
asm pop ds
asm pop bp
asm pop ax
// jump to the original interrupt handler
asm retf
// never get here
}
// interrupt is not shared...
// send End-of-Interrupt (EOI) to the interrupt controller(s).
outportb( PIC0_CTRL, PIC_EOI );
if ( int_irq_number >= 8 )
outportb( PIC1_CTRL, PIC_EOI );
// IRET here (return from interrupt)
}
// end ataioint.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -