Page 1 of 2

ATA Identify drive

Posted: Sat Mar 05, 2005 7:15 am
by os_ambition
Hello,
Is ATA Identify drive command 0xEC is for real mode only?
I have a code for Identifying Drive that works in real mode but not in protected mode. I got the code from Hale Hilandis Ata driver.

Can you suggest something.

Thank u very much in advance.

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 7:28 am
by AR
Without knowing what exactly you're talking about, if it uses BIOS interrupts then no it won't work in protected mode. If it uses direct IO in Assembly then you will probably have to convert it to 32 bit assembly first.

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 8:21 am
by DennisCGc
Hi,
os_ambition wrote: Is ATA Identify drive command 0xEC is for real mode only?
No, every command is mode independant :) (that's a GoodThing of using the HDD I/O ports)

It means , if it's not working, you've got something wrong.
Maybe you can post the code. ;)

HTH

DennisCGc.

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 8:30 am
by os_ambition
I will send the code in several parts because its too long

Here is Part 1

Code: Select all

#define U32 unsigned long
#define S32 long
#define U16 unsigned int
#define S16 int
#define U8 unsigned char 
#define S8 char
#define CB_DATA  0  /*  data reg         in/out pio_base_addr1+0*/
#define CB_ERR   1  /*  error            in     pio_base_addr1+1*/
#define CB_FR    1  /*  feature reg         out pio_base_addr1+1*/
#define CB_SC    2  /*  sector count     in/out pio_base_addr1+2*/
#define CB_SN    3  /*  sector number    in/out pio_base_addr1+3*/
#define CB_CL    4  /*  cylinder low     in/out pio_base_addr1+4 */
#define CB_CH    5  /*  cylinder high    in/out pio_base_addr1+5  */
#define CB_DH    6  /*  device head      in/out pio_base_addr1+6   */
#define CB_STAT  7  /* primary status   in     pio_base_addr1+7   */
#define CB_CMD   7  /*  command             out pio_base_addr1+7*/
#define CB_ASTAT 6  /*  alternate status in     pio_base_addr2+6*/
#define CB_DC    6  /*   device control      out pio_base_addr2+6*/

#define CB_DH_LBA  0x40   /*  LBA bit */
#define CB_DH_DEV0 0x00   /*  select device 0 */
#define CB_DH_DEV1 0x10   /*  select device 1 */

/* device control reg (CB_DC) bits */

#define CB_DC_HOB    0x80  /*// High Order Byte (48-bit LBA) */
#define CB_DC_HD15   0x00  /*// bit 3 is reserved */
/*// #define CB_DC_HD15   0x08  // (old definition of bit 3) */
#define CB_DC_SRST   0x04  /*// soft reset */
#define CB_DC_NIEN   0x02  /*// disable interrupts*/

#define CMD_IDENTIFY_DEVICE              0xEC

extern far void OutByte(U8 Byte, U16 wPort);
extern far U8 InByte(U16 wPort);
extern far InWords(U32 dPort, U8 *pDataIn, U32 dBytes);

#define BUFFER_SIZE 4096
unsigned char buffer512[512];
void DELAY400NS(U16 HD_APort);
static U32 Identify_Drive(U16 HD_Port, U16 HD_APort, U8 Dev, U8 *pDataRet);
void Wait_Ready(U16 HD_Port);
void Wait_ReadyBusy(U16 HD_Port);
void Wait_DRQ(U16 HD_Port);
void Wait_Busy(U16 HD_Port);

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 8:40 am
by os_ambition
Here is Part 2

Code: Select all

U32 Init_HD_Driver(void)
{
 U32 erc;
 erc=Identify_Drive(0x1F0, 0x3F0, 0x00, buffer512);
 return erc;
}


void DELAY400NS(U16 HD_APort)

{
 InByte( HD_APort+ CB_ASTAT );
 InByte( HD_APort+ CB_ASTAT);
 InByte( HD_APort+ CB_ASTAT);
 InByte( HD_APort+ CB_ASTAT);
}

/*

*/
static U32 Identify_Drive(U16 HD_Port, U16 HD_APort, U8 Dev, U8 *pDataRet)
{
 U8 dc, dh, int_use_intr_flag, status;
 U32 erc;

int_use_intr_flag=1;
dc = int_use_intr_flag ? 0 : CB_DC_NIEN;
Wait_Busy(HD_Port); 
OutByte(  dc,HD_APort+CB_DC ); /*3F6 device control*/
Wait_ReadyBusy(HD_Port);
     /*1F1*/
OutByte( 0,HD_Port + CB_FR );
      /*1F2 ,sector count*/
OutByte( 0,HD_Port + CB_SC );

   /*1F3 sector number*/
OutByte( 0,HD_Port + CB_SN );

    /*cylinder low 1F4*/
OutByte( 0,HD_Port + CB_CL );

   /*cylinder hi 1F5*/
OutByte( 0,HD_Port + CB_CH );

dh = CB_DH_LBA | (Dev ? CB_DH_DEV1 : CB_DH_DEV0 );
dh = ( dh & 0xf0 ) | ( 0 & 0x0f );
         /*Drive and Head 1F6*/
OutByte( dh, HD_Port +CB_DH );
      Wait_Ready( HD_Port) ;
   OutByte( CMD_IDENTIFY_DEVICE, HD_Port + CB_CMD );
    DELAY400NS(HD_APort);

Wait_DRQ(HD_Port);
InWords(HD_Port, pDataRet, 512);
DELAY400NS(HD_APort);    /* delay so device can get the status updated*/

}
/*
//-----------------------------------------------------------------------------
// Wait_Ready: Pauses execution until Ready = 1 in the status register
// A timeout delay should be added to improve this function
//
// Parameters: None
//
// Returns: None
//
// Functions Used: ReadReg
//
//-----------------------------------------------------------------------------
*/
void Wait_Ready(U16 HD_Port) /*input=0x1F7, 0x177*/
{
   U8 statbyte;
   U8 iflag ;
   iflag=0;
   while (1)
     {
     statbyte = InByte(HD_Port + 7);    /* Read Status Register*/
     statbyte=statbyte & 0x40;
     if (statbyte == 0x40)
         break;     /* Ready bit is in pos 6*/
     }
}
/*
//-----------------------------------------------------------------------------
// Wait_ReadyBusy: Halts further execution until Ready = 1 and Busy = 0 in
// status register
//
// Parameters: None
//
// Returns: None
//
// Functions Used: ReadReg
//
//-----------------------------------------------------------------------------
*/
void Wait_ReadyBusy(U16 HD_Port)  /*input=0x1F7, 0x177 */
{
U8 statbyte;
U8 iflag;
iflag=0;
while (1)
     {
     statbyte = InByte(HD_Port + 7);   /* Read Status register*/
     /* If RDY = 1 and BUSY = 0 leave loop  */
     /*check bit 6 and bit 7 of 0x1F7*/
     if ((statbyte & 0x40) && ((statbyte & 0x80)==0)) break;
     }
}
void Wait_Busy(U16 HD_Port)  /*input=0x1F7, 0x177 */
{
U8 statbyte;
U8 iflag;
iflag=0;
while (1)
     {
     statbyte = InByte(HD_Port + 7);   /* Read Status register*/
     /* If RDY = 1 and BUSY = 0 leave loop  */
     /*check bit 6 and bit 7 of 0x1F7*/
     statbyte=statbyte & 0x80;
    /* if (!(statbyte & 0x80))*/
     if (statbyte==0)
        break;
     }
}
/*
//-----------------------------------------------------------------------------
// Wait_DRQ: Halts execution until DRQ is asserted
//
// Parameters: None
//
// Returns: None
//
// Functions Used: ReadReg
//
//-----------------------------------------------------------------------------
*/
void Wait_DRQ(U16 HD_Port) /*input=0x1F7, 0x177*/
{
U8 statbyte;
U8 iflag;
iflag=0;
while (1)
      {
       statbyte = InByte(HD_Port + 7);   /* DRQ is in status register*/
       /*check bit 3 */
       /*if (statbyte & 0x08) */
       statbyte=statbyte & 0x08;
       if (statbyte==0x08)
       break;
      }
}

The external 32-bit assembly code
;===============================================
; InByte(wPort)
; The Byte is read from the I/O Port specified and returned in AL
; wPort = [EBP+12]
;
PUBLIC __InByte:
      PUSH EBP
      MOV EBP, ESP
      MOV DX, WORD PTR [EBP+12]
      IN AL, DX
      AND EAX, 0FFh         ;Only get the byte
      MOV ESP, EBP
      POP EBP
      RETF 4                  ;
;
;===============================================
;===============================================
; OutByte(Byte, wPort)
; The Byte is sent out the I/O Port specified.
; Byte  = [EBP+16]
; wPort = [EBP+12]
;
PUBLIC __OutByte:
      PUSH EBP
      MOV EBP, ESP
      MOV DX, WORD PTR [EBP+12]
      MOV AL, BYTE PTR [EBP+16]
      OUT DX, AL
      POP EBP
      RETF 8              
; InWords(wPort, pDataIn, dBytes)
; The dBytes/2 are read in from wPort to pDataIn
; wPort    = [EBP+20]
; pDataIn  = [EBP+16]
; dBytes   = [EBP+12]
;
; ASSUMES ES == DS !!!! (In MMURTL it always does...)
;
PUBLIC __InWords:
      PUSH EBP
      MOV EBP, ESP
      MOV DX, WORD PTR [EBP+20]
      MOV EDI, DWORD PTR [EBP+16]
      MOV ECX, DWORD PTR [EBP+12]
      SHR ECX, 1      ;Make WORDS vice bytes
      CLD
      REP INSW
      MOV ESP, EBP
      POP EBP
      RETF 12               

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 10:09 am
by mystran
Maybe next time try the attachment feature?

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 11:52 am
by DennisCGc
Very odd. Maybe it has something todo with the fact you're reading from the "normal" status flag. (And if the HD is busy with something you requested, and you're reading the status flag, it'll simply stop.)

To prevent this you should use 0x3f6. (Alternate Status Flag).

Btw, this is merely a guess. Maybe some C guru is able to help him ?

HTH and Gl,

DennisCGc.

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 2:34 pm
by Dex4u
You only need to use it in real mode, on start up use it to find all your hdd drives and use 0xA1 to find your cd/dvd drives etc.
And store the port and drive numbers in vars, before moving to pmode.

Re:ATA Identify drive

Posted: Sat Mar 05, 2005 8:35 pm
by AR
I'm not certain but this looks like a problem:

Code: Select all

PUBLIC __OutByte:
      PUSH EBP
      MOV EBP, ESP
      MOV DX, >>WORD<< PTR [EBP+12]
      MOV AL, BYTE PTR [EBP+16]
      OUT DX, AL
      POP EBP
      RETF 8 
WORD should break down to 2 bytes, in protected mode it should be a DWORD. Remember that on the x86 the bit order is "High,Low" so you're reading the High off the stack (which will most likely always be 0). [You've got this in the other 2 assembly functions as well]

Something to remember about C/C++: They always use the CPU wordsize for stack objects, if you go char c; This reserves 32bits on the stack for the variable because aligned data is faster to access. So when you declare void func(char param); whenever you call func() you are always passing the equivalent of an INT on the stack. This also points out how futile it is trying to conserve memory using small data types like char and short, if there isn't a specific reason to not use int then just use int.

Two other things, you've got fars in the header, you should probably remove those. And:

Code: Select all

#define U16 unsigned int
#define S16 int
Which should be:

Code: Select all

#define U16 unsigned short
#define S16 short
An int is 32bits, char is 8, short is 16, long is 32 as well, long long is 64 (In protected mode)

Re:ATA Identify drive

Posted: Mon Mar 07, 2005 3:19 am
by Pype.Clicker
AR wrote: I'm not certain but this looks like a problem:

Code: Select all

PUBLIC __OutByte:
      PUSH EBP
      MOV EBP, ESP
      MOV DX, >>WORD<< PTR [EBP+12]
      MOV AL, BYTE PTR [EBP+16]
      OUT DX, AL
      POP EBP
      RETF 8 
WORD should break down to 2 bytes, in protected mode it should be a DWORD. Remember that on the x86 the bit order is "High,Low" so you're reading the High off the stack (which will most likely always be 0). [You've got this in the other 2 assembly functions as well]
actually, no. The Intel CPU stores "0x12345678" as =0x78; [i+1]=0x56; [i+2]=0x34; [i+3]=0x12 ... The fact the stack expands downwards doesn't change this.

Re:ATA Identify drive

Posted: Mon Mar 07, 2005 6:07 am
by AR
Well I wasn't exactly certain, I avoid pure assembly as much as possible :)

Re:ATA Identify drive

Posted: Mon Mar 07, 2005 6:40 am
by Solar
AR wrote: Something to remember about C/C++: They always use the CPU wordsize for stack objects...
Be careful with that not all architectures have a pointer being equivalent in size to an int. Not all architectures have all pointers the same size. double's are usually larger than int's.

Just acting out my allergy towards catch-all phrases when it comes to language standards. ;)

Re:ATA Identify drive

Posted: Mon Mar 14, 2005 11:05 pm
by os_ambition
Already solved this problem.

Thanks to all who responded.

Re:ATA Identify drive

Posted: Tue Mar 15, 2005 2:45 am
by Pype.Clicker
os_ambition wrote: Already solved this problem.

Thanks to all who responded.
glad for you ... maybe you might give us a hint on what was the actual problem (grow knowledge base, etc)

Re:ATA Identify drive

Posted: Tue Mar 15, 2005 5:02 am
by os_ambition
correction:

remove these two lines
dh = CB_DH_LBA | (Dev ? CB_DH_DEV1 : CB_DH_DEV0 );
dh = ( dh & 0xf0 ) | ( 0 & 0x0f );

and change to these lines
dh = Dev ? CB_DH_DEV1 : CB_DH_DEV0 ;
dh= dh | 0xE0; //0xEF also works

remove this line too
Wait_DRQ(HD_Port);

and change to

do
{
status = InByte( HD_Port + CB_STAT );
}while( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) != CB_STAT_DRQ );


and last add these defines

#define CB_STAT_BSY 0x80 /* busy*/
#define CB_STAT_DRQ 0x08 /* data request*/

that's all.