📄 ataiopio.c
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ATAIOPIO.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-2, ATA-3 and ATA-4 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 module contains inline assembler code so you'll
// also need Borland's Assembler.
//
// This C source contains the low level I/O port IN/OUT functions.
//********************************************************************
#include <dos.h>
#include "ataio.h"
//*************************************************************
//
// Host adapter base addresses.
//
//*************************************************************
unsigned int pio_base_addr1 = 0x1f0;
unsigned int pio_base_addr2 = 0x3f0;
unsigned int pio_memory_seg = 0;
int pio_memory_dt_opt = PIO_MEMORY_DT_OPT0;
unsigned int pio_bmcr_base_addr = 0;
unsigned int pio_reg_addrs[10];
unsigned char pio_last_write[10];
unsigned char pio_last_read[10];
int pio_xfer_width = 16;
//*************************************************************
//
// Set the host adapter i/o base addresses.
//
//*************************************************************
void pio_set_iobase_addr( unsigned int base1,
unsigned int base2,
unsigned int base3 )
{
pio_base_addr1 = base1;
pio_base_addr2 = base2;
pio_bmcr_base_addr = base3;
pio_memory_seg = 0;
pio_reg_addrs[ CB_DATA ] = pio_base_addr1 + 0; // 0
pio_reg_addrs[ CB_FR ] = pio_base_addr1 + 1; // 1
pio_reg_addrs[ CB_SC ] = pio_base_addr1 + 2; // 2
pio_reg_addrs[ CB_SN ] = pio_base_addr1 + 3; // 3
pio_reg_addrs[ CB_CL ] = pio_base_addr1 + 4; // 4
pio_reg_addrs[ CB_CH ] = pio_base_addr1 + 5; // 5
pio_reg_addrs[ CB_DH ] = pio_base_addr1 + 6; // 6
pio_reg_addrs[ CB_CMD ] = pio_base_addr1 + 7; // 7
pio_reg_addrs[ CB_DC ] = pio_base_addr2 + 6; // 8
pio_reg_addrs[ CB_DA ] = pio_base_addr2 + 7; // 9
}
//*************************************************************
//
// Set the host adapter memory base addresses.
//
//*************************************************************
void pio_set_memory_addr( unsigned int seg )
{
pio_base_addr1 = 0;
pio_base_addr2 = 8;
pio_bmcr_base_addr = 0;
pio_memory_seg = seg;
pio_memory_dt_opt = PIO_MEMORY_DT_OPT0;
pio_reg_addrs[ CB_DATA ] = pio_base_addr1 + 0; // 0
pio_reg_addrs[ CB_FR ] = pio_base_addr1 + 1; // 1
pio_reg_addrs[ CB_SC ] = pio_base_addr1 + 2; // 2
pio_reg_addrs[ CB_SN ] = pio_base_addr1 + 3; // 3
pio_reg_addrs[ CB_CL ] = pio_base_addr1 + 4; // 4
pio_reg_addrs[ CB_CH ] = pio_base_addr1 + 5; // 5
pio_reg_addrs[ CB_DH ] = pio_base_addr1 + 6; // 6
pio_reg_addrs[ CB_CMD ] = pio_base_addr1 + 7; // 7
pio_reg_addrs[ CB_DC ] = pio_base_addr2 + 6; // 8
pio_reg_addrs[ CB_DA ] = pio_base_addr2 + 7; // 9
}
//*************************************************************
//
// These functions do basic IN/OUT of byte and word values:
//
// pio_inbyte()
// pio_outbyte()
// pio_inword()
// pio_outword()
//
//*************************************************************
unsigned char pio_inbyte( unsigned int addr )
{
unsigned int regAddr;
unsigned char uc;
unsigned char far * ucp;
regAddr = pio_reg_addrs[ addr ];
if ( pio_memory_seg )
{
ucp = (unsigned char far *) MK_FP( pio_memory_seg, regAddr );
uc = * ucp;
}
else
{
// uc = (unsigned char) inportb( regAddr );
// READ THIS: If you get a compile error on the following
// statement you are trying to use BASM (the assembler
// built into Borland C). BASM can not assemble 386
// instructions. You must use Borland TASM as is shown
// in the EXAMPLE1.MAK or EXAMPLE2.MAK "make files".
asm .386
asm push ax
asm push dx
asm mov dx,regAddr
asm in al,dx
asm mov uc,al
asm pop dx
asm pop ax
}
pio_last_read[ addr ] = uc;
trc_llt( addr, uc, TRC_LLT_INB );
return uc;
}
//*************************************************************
void pio_outbyte( unsigned int addr, unsigned char data )
{
unsigned int regAddr;
unsigned char far * ucp;
regAddr = pio_reg_addrs[ addr ];
if ( pio_memory_seg )
{
ucp = (unsigned char far *) MK_FP( pio_memory_seg, regAddr );
* ucp = data;
}
else
{
// outportb( regAddr, data );
asm .386
asm push ax
asm push dx
asm mov dx,regAddr
asm mov al,data
asm out dx,al
asm pop dx
asm pop ax
}
pio_last_write[ addr ] = data;
trc_llt( addr, data, TRC_LLT_OUTB );
}
//*************************************************************
unsigned int pio_inword( unsigned int addr )
{
unsigned int regAddr;
unsigned int ui;
unsigned int far * uip;
regAddr = pio_reg_addrs[ addr ];
if ( pio_memory_seg )
{
uip = (unsigned int far *) MK_FP( pio_memory_seg, regAddr );
ui = * uip;
}
else
{
// ui = inport( regAddr );
asm .386
asm push ax
asm push dx
asm mov dx,regAddr
asm in ax,dx
asm mov ui,ax
asm pop dx
asm pop ax
}
trc_llt( addr, 0, TRC_LLT_INW );
return ui;
}
//*************************************************************
void pio_outword( unsigned int addr, unsigned int data )
{
unsigned int regAddr;
unsigned int far * uip;
regAddr = pio_reg_addrs[ addr ];
if ( pio_memory_seg )
{
uip = (unsigned int far *) MK_FP( pio_memory_seg, regAddr );
* uip = data;
}
else
{
// outport( regAddr, data );
asm .386
asm push ax
asm push dx
asm mov dx,regAddr
asm mov ax,data
asm out dx,ax
asm pop dx
asm pop ax
}
trc_llt( addr, 0, TRC_LLT_OUTW );
}
//*************************************************************
//
// These functions are normally used to transfer DRQ blocks:
//
// pio_drq_block_in()
// pio_drq_block_out()
//
//*************************************************************
// Note: pio_drq_block_in() is the primary way perform PIO
// Data In transfers. It will handle 8-bit, 16-bit and 32-bit
// I/O based data transfers and 8-bit and 16-bit PCMCIA Memory
// mode transfers.
void pio_drq_block_in( unsigned int addrDataReg,
unsigned int bufSeg, unsigned int bufOff,
long wordCnt )
{
long bCnt;
int memDtOpt;
unsigned int randVal;
unsigned int dataRegAddr;
unsigned int far * uip1;
unsigned int far * uip2;
unsigned char far * ucp1;
unsigned char far * ucp2;
unsigned long bufAddr;
// NOTE: wordCnt is the size of a DRQ data block/packet
// in words. The maximum value of wordCnt is normally:
// a) For ATA, 16384 words or 32768 bytes (64 sectors,
// only with READ/WRITE MULTIPLE commands),
// b) For ATAPI, 32768 words or 65536 bytes
// (actually 65535 bytes plus a pad byte).
// normalize bufSeg:bufOff
bufAddr = bufSeg;
bufAddr = bufAddr << 4;
bufAddr = bufAddr + bufOff;
if ( pio_memory_seg )
{
// PCMCIA Memory mode data transfer.
// set Data reg address per pio_memory_dt_opt
dataRegAddr = 0x0000;
memDtOpt = pio_memory_dt_opt;
if ( pio_memory_dt_opt == PIO_MEMORY_DT_OPTR )
{
randVal = * (unsigned int *) MK_FP( 0x40, 0x6c );
memDtOpt = randVal % 3;
}
if ( memDtOpt == PIO_MEMORY_DT_OPT8 )
dataRegAddr = 0x0008;
if ( memDtOpt == PIO_MEMORY_DT_OPTB )
{
dataRegAddr = 0x0400;
if ( pio_memory_dt_opt == PIO_MEMORY_DT_OPTR )
dataRegAddr = dataRegAddr | ( randVal & 0x03fe );
}
if ( pio_xfer_width == 8 )
{
// PCMCIA Memory mode 8-bit
bCnt = wordCnt * 2L;
ucp1 = (unsigned char far *) MK_FP( pio_memory_seg, dataRegAddr );
for ( ; bCnt > 0; bCnt -- )
{
bufSeg = (unsigned int) ( bufAddr >> 4 );
bufOff = (unsigned int) ( bufAddr & 0x0000000fL );
ucp2 = (unsigned char far *) MK_FP( bufSeg, bufOff );
* ucp2 = * ucp1;
bufAddr += 1;
if ( memDtOpt == PIO_MEMORY_DT_OPTB )
{
dataRegAddr += 1;
dataRegAddr = ( dataRegAddr & 0x03ff ) | 0x0400;
ucp1 = (unsigned char far *) MK_FP( pio_memory_seg, dataRegAddr );
}
}
trc_llt( addrDataReg, 0, TRC_LLT_INSB );
}
else
{
// PCMCIA Memory mode 16-bit
uip1 = (unsigned int far *) MK_FP( pio_memory_seg, dataRegAddr );
for ( ; wordCnt > 0; wordCnt -- )
{
bufSeg = (unsigned int) ( bufAddr >> 4 );
bufOff = (unsigned int) ( bufAddr & 0x0000000fL );
uip2 = (unsigned int far *) MK_FP( bufSeg, bufOff );
* uip2 = * uip1;
bufAddr += 2;
if ( memDtOpt == PIO_MEMORY_DT_OPTB )
{
dataRegAddr += 2;
dataRegAddr = ( dataRegAddr & 0x03fe ) | 0x0400;
uip1 = (unsigned int far *) MK_FP( pio_memory_seg, dataRegAddr );
}
}
trc_llt( addrDataReg, 0, TRC_LLT_INSW );
}
}
else
{
int pxw;
long wc;
// adjust pio_xfer_width - don't use DWORD if wordCnt is odd.
pxw = pio_xfer_width;
if ( ( pxw == 32 ) && ( wordCnt & 0x00000001L ) )
pxw = 16;
// Data transfer using INS instruction.
// Break the transfer into chunks of 32768 or fewer bytes.
while ( wordCnt > 0 )
{
bufSeg = (unsigned int) ( bufAddr >> 4 );
bufOff = (unsigned int) ( bufAddr & 0x0000000fL );
if ( wordCnt > 16384L )
wc = 16384;
else
wc = wordCnt;
if ( pxw == 8 )
{
// do REP INS
pio_rep_inbyte( addrDataReg, bufSeg, bufOff, wc * 2L );
}
else
if ( pxw == 32 )
{
// do REP INSD
pio_rep_indword( addrDataReg, bufSeg, bufOff, wc / 2L );
}
else
{
// do REP INSW
pio_rep_inword( addrDataReg, bufSeg, bufOff, wc );
}
bufAddr = bufAddr + ( wc * 2 );
wordCnt = wordCnt - wc;
}
}
return;
}
//*************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -