//---------------------------------------------------------------------------- // IDE 19960417 CHG // Small and simple interface for an "standard" IDE Harddisk connected // to a 8051 cpu, in this case a DS5000, but all other 8051 compatible // cpu could be used, as long as they have some external RAM, 1KByte or so // // This code is provided as is, with NO responsibility by the author ! // ie. this is use on your own risk !!!!! // // Code is free to use, the only request i have is, that if you do some // enhancements, discoveries I would appreciate to get a copy/info on what // YOU have done ! // That way, we can all help each other with code etc etc etc. // // Made by // Carsten Groen, OZ9AAR // Kaervaenget 46, Gl. Sole // DK-8722 Hedensted // Denmark // email: cgroen@image.dk // //---------------------------------------------------------------------------- #include #include #include #include #include //---------------------------------------------------------------------------- // Constants... //---------------------------------------------------------------------------- #define TRUE 1 #define FALSE 0 #define CTRL 0 #define CMD 1 #define DRIVE0 0 //---------------------------------------------------------------------------- // Types //---------------------------------------------------------------------------- typedef struct { unsigned char Heads; unsigned int Tracks; unsigned int SectorsPerTrack; char Model[41]; } tdefDriveInfo; //---------------------------------------------------------------------------- // I/O definitions... // The IDE harddisk is just connected DIRECTLY to these I/O ports on the 8051 // See pin numbers on IDC connecter on IDE drive in one of the attached files. // REMEMBER to make a pullup on the P0 port (ex 4.7 K to vcc from each of the // 8 P0 pins //---------------------------------------------------------------------------- #define nCS1FX P00 #define nCS3FX P01 #define DA0 P02 #define DA1 P03 #define DA2 P04 #define nDASP P05 #define LSBDATA P1 #define MSBDATA P2 #define nDIOR P06 #define nDIOW P07 #define INTRQ P32 #define IORDY P33 #define RESET P34 #define ALLINPUT 0xFF //---------------------------------------------------------------------------- // Variables... //---------------------------------------------------------------------------- unsigned int Timer10mSec=0; // General timer, tick is 10 mSec unsigned char SectorBuffer[512]; //---------------------------------------------------------------------------- // Wait for a specific time in 10 mSec // Based on a 11.0592 Mhz crystal //---------------------------------------------------------------------------- void Delay(unsigned char t) { unsigned int i; if (t==0) return; while (t--) for(i=0;i<774; i++); } //---------------------------------------------------------------------------- // Dump a buffer as a hex listing //---------------------------------------------------------------------------- void HexDump( void *Dataa, int Len ) { unsigned char *Data=Dataa; unsigned char Line[80], *CurLine, *CurData; int linelen, j; unsigned short rc; unsigned char tmp; printf( "*************** HEX-DUMP ***************\n" ); while( Len ){ linelen = Len < 16 ? Len : 16; *Line='\0'; CurLine = Line; for( CurData=Data, j=0; j ' ' ? *Data : '_' ); Data++;CurLine+=1; } printf( Line ); printf("\n"); Len-=linelen; } rc = printf( "*************** HEXEND ***************\n" ); } //---------------------------------------------------------------------------- // Timer 0 interrupt service function // executes each 10 mSec @ 11.0592 MHz Crystal Clock //---------------------------------------------------------------------------- timer0() interrupt 1 using 1 { TH0 = 0xDC; // reload timer again TL0 = 0x00; // to 10 mSec timeout TR0 = 1; if (Timer10mSec) Timer10mSec--; } //---------------------------------------------------------------------------- // Show state of nDASP, INTRQ and IORDY signals //---------------------------------------------------------------------------- void ShowInputs(void) { printf("Signals from Drive: nDASP=%s, INTRQ=%s, IORDY=%s\n", nDASP ? "FALSE":"TRUE", INTRQ ? "TRUE":"FALSE", IORDY ? "TRUE":"FALSE"); } //---------------------------------------------------------------------------- // Select address and CS signals //---------------------------------------------------------------------------- void SetAddress(unsigned char cs, unsigned char adr) { DA0=((adr & 0x01)==0x01); DA1=((adr & 0x02)==0x02); DA2=((adr & 0x04)==0x04); if (cs==CTRL) { nCS1FX=1; nCS3FX=0; } else { nCS1FX=0; nCS3FX=1; } } //---------------------------------------------------------------------------- // Read data WORD from Drive //---------------------------------------------------------------------------- unsigned int ReadWORD(unsigned char cs, unsigned char adr) { unsigned int tmp; MSBDATA=ALLINPUT; LSBDATA=ALLINPUT; SetAddress(cs,adr); nDIOR=0; tmp=(MSBDATA<<8)+LSBDATA; nDIOR=1; nCS1FX=1; nCS3FX=1; return tmp; } //---------------------------------------------------------------------------- // Read data BYTE from Drive //---------------------------------------------------------------------------- unsigned char ReadBYTE(unsigned char cs, unsigned char adr) { unsigned char tmp; MSBDATA=ALLINPUT; LSBDATA=ALLINPUT; SetAddress(cs,adr); nDIOR=0; tmp=LSBDATA; nDIOR=1; nCS1FX=1; nCS3FX=1; return tmp; } //---------------------------------------------------------------------------- // Write data WORD to Drive //---------------------------------------------------------------------------- void WriteWORD(unsigned char cs, unsigned char adr, unsigned int dat) { SetAddress(cs,adr); // OBS MSB/LSB is swapped (see writeSector function) MSBDATA=dat; LSBDATA=(dat>>8); nDIOW=0; nDIOW=1; nCS1FX=1; nCS3FX=1; } //---------------------------------------------------------------------------- // Write data BYTE to Drive //---------------------------------------------------------------------------- void WriteBYTE(unsigned char cs, unsigned char adr, unsigned char dat) { SetAddress(cs,adr); MSBDATA=0; LSBDATA=dat; nDIOW=0; nDIOW=1; nCS1FX=1; nCS3FX=1; } //---------------------------------------------------------------------------- // Send Identify Command to Drive, and fetch resulting data //---------------------------------------------------------------------------- unsigned char IdentifyDrive(bit DriveNo, unsigned char *Buffer, tdefDriveInfo *DriveInfo) { unsigned int i; unsigned char Tmp; WriteBYTE(CMD, 6, 0xA0 + (DriveNo ? 0x10:0x00)); // Select drive WriteBYTE(CMD, 1, 0); WriteBYTE(CMD, 2, 1); WriteBYTE(CMD, 3, 1); WriteBYTE(CMD, 4, 0); WriteBYTE(CMD, 5, 0); WriteBYTE(CMD, 7, 0xEC); Timer10mSec=10000; while ((ReadBYTE(CMD,7) & 0x08)!=0x08 && Timer10mSec); // Wait for DRQ or timeout if (Timer10mSec==0) return 0xFF; // Fetch the data... MSBDATA=ALLINPUT; LSBDATA=ALLINPUT; // Select address and activate CS SetAddress(CMD, 0); // Two bytes at a time for (i=0; i<512; i+=2) { nDIOR=0; *(Buffer+i)=LSBDATA; *(Buffer+i+1)=MSBDATA; nDIOR=1; } // Disable CS nCS1FX=1; nCS3FX=1; // Extract drive info DriveInfo->Heads = (*(Buffer+7)<<8) + *(Buffer+6); DriveInfo->Tracks = (*(Buffer+3)<<8) + *(Buffer+2); DriveInfo->SectorsPerTrack = (*(Buffer+13)<<8) + *(Buffer+12); // Swap bytes, because of 16 bit transfer... for (i=0; i<40; i+=2) { Tmp=*(Buffer+54+i); *(Buffer+54+i)=*(Buffer+55+i); *(Buffer+55+i)=Tmp; } memcpy(DriveInfo->Model, Buffer+54, 80); // Terminate string... DriveInfo->Model[40]='\0'; // Return the error register... return ReadBYTE(CMD, 1); } //---------------------------------------------------------------------------- // Read one sector, identified by drive, head, track and sector // Returns contents of the Error Register (0x00 is no error detected) //---------------------------------------------------------------------------- unsigned char ReadSector(unsigned char Drive, unsigned char Head, unsigned int Track, unsigned char Sector, unsigned char *Buffer) { unsigned int i; // Prepare parameters... WriteBYTE(CMD,6, 0xA0+(Drive ? 0x10:00)+Head); // CHS mode/Drive/Head WriteBYTE(CMD,5, Track>>8); // MSB of track WriteBYTE(CMD,4, Track); // LSB of track WriteBYTE(CMD,3, Sector); // sector WriteBYTE(CMD,2, 0x01); // 1 sector // Issue read sector command... WriteBYTE(CMD,7, 0x20); // Read sector(s) command Timer10mSec=10000; while ((ReadBYTE(CMD,7) & 0x08)!=0x08 && Timer10mSec); // Wait for DRQ or timeout if (Timer10mSec==0) return 0xFF; // Fetch the sector... MSBDATA=ALLINPUT; LSBDATA=ALLINPUT; // Select address and activate CS SetAddress(CMD, 0); // Two bytes at a time for (i=0; i<512; i+=2) { nDIOR=0; *(Buffer+i)=LSBDATA; *(Buffer+i+1)=MSBDATA; nDIOR=1; } // Disable CS nCS1FX=1; nCS3FX=1; // Return the error register... return ReadBYTE(CMD, 1); } //---------------------------------------------------------------------------- // Write one sector, identified by drive, head, track and sector // Returns contents of the Error Register (0x00 is no error detected) //---------------------------------------------------------------------------- unsigned char WriteSector(unsigned char Drive, unsigned char Head, unsigned int Track, unsigned char Sector, unsigned char *Buffer) { unsigned int i; // Prepare parameters... WriteBYTE(CMD,6, 0xA0+(Drive ? 0x10:00)+Head); // CHS mode/Drive/Head WriteBYTE(CMD,5, Track>>8); // MSB of track WriteBYTE(CMD,4, Track); // LSB of track WriteBYTE(CMD,3, Sector); // sector WriteBYTE(CMD,2, 0x01); // 1 sector // Issue write sector command... WriteBYTE(CMD,7, 0x30); // Write sector(s) command Timer10mSec=10000; while ((ReadBYTE(CMD,7) & 0x08)!=0x08 && Timer10mSec); // Wait for DRQ or timeout if (Timer10mSec==0) return 0xFF; // Select address and activate CS SetAddress(CMD, 0); // write sector data... for (i=0; i<512; i+=2) { LSBDATA=*(Buffer+i); MSBDATA=*(Buffer+i+1); nDIOW=0; nDIOW=1; } // Disable CS nCS1FX=1; nCS3FX=1; Timer10mSec=10000; while ((ReadBYTE(CMD,7) & 0xC0)!=0x40 && Timer10mSec); // Wait for DRDY and NOT BUSY if (Timer10mSec==0) return 0xFF; // or timeout // Return the error register... return ReadBYTE(CMD, 1); } //---------------------------------------------------------------------------- // Set drive mode (STANDBY, IDLE) //---------------------------------------------------------------------------- #define STANDBY 0 #define IDLE 1 #define SLEEP 2 unsigned char SetMode(bit DriveNo, unsigned char Mode, bit PwrDown) { WriteBYTE(CMD, 6, 0xA0 + (DriveNo ? 0x10:0x00)); // Select drive WriteBYTE(CMD, 2, (PwrDown ? 0x01:0x00)); // Enable automatic power down switch (Mode) { case STANDBY: WriteBYTE(CMD,7, 0xE2); break; case IDLE: WriteBYTE(CMD,7, 0xE3); break; // NOTE: To recover from sleep, either issue a soft or hardware reset ! // (But not on all drives, f.ex seagate ST3655A it's not nessecary to reset // but only to go in Idle mode, But on a Conner CFA170A it's nessecary with // a reset) case SLEEP: WriteBYTE(CMD,7, 0xE6); break; } Timer10mSec=10000; while ((ReadBYTE(CMD,7) & 0xC0)!=0x40 && Timer10mSec); // Wait for DRDY & NOT BUSY if (Timer10mSec==0) return 0xFF; // or timeout // Return the error register... return ReadBYTE(CMD, 1); } //---------------------------------------------------------------------------- // Show all IDE registers //---------------------------------------------------------------------------- void ShowRegisters(bit DriveNo) { WriteBYTE(CMD,6, 0xA0 + (DriveNo ? 0x10:0x00)); // Select drive printf("Reg 0=%02BX 1=%02BX 2=%02BX 3=%02BX 4=%02BX 5=%02BX 6=%02BX 7=%02BX \n", ReadBYTE(CMD, 0), ReadBYTE(CMD, 1), ReadBYTE(CMD, 2), ReadBYTE(CMD, 3), ReadBYTE(CMD, 4), ReadBYTE(CMD, 5), ReadBYTE(CMD, 6), ReadBYTE(CMD, 7)); } //---------------------------------------------------------------------------- // Main part //---------------------------------------------------------------------------- void main() { unsigned int i; char cmdbuf[15]; tdefDriveInfo DriveInfo; int Head=0, Track=0, Sector=1; //------------------------------ // Initialize outputs... //------------------------------ nCS1FX=1; nCS3FX=1; DA0 =0; DA1 =0; DA2 =0; nDIOR =1; nDIOW =1; RESET =0; // Reset drive //------------------------------ // configure inputs... //------------------------------ nDASP =1; LSBDATA =ALLINPUT; MSBDATA =ALLINPUT; INTRQ =1; IORDY =1; //------------------------------ // Configure onchip resources... //------------------------------ TMOD |= 0x01; // TMOD: timer 0, mode 1, 16 timer TH0 = 0xDC; // Set timeout period for Timer 0 TL0 = 0x00; // to 10 mSec timeout ET0 = 1; // Enable Timer 0 interrupts TR0 = 1; // Start timer 0 SCON = 0x50; // SCON: mode 1, 8-bit UART, enable rcvr */ TMOD |= 0x20; // TMOD: timer 1, mode 2, 8-bit reload */ TH1 = 0xFD; // TH1: reload value for 9.6 Kbaud */ PCON = 0x00; // PCON: Double baudrate, 19.2 Kbaud */ TR1 = 1; // TR1: timer 1 run */ //ES = 1; // ES: Enable serial port int */ TI = 1; // TI: set TI to send first char of UART */ EA = 1; // Global int enable //---------------------------------------------------------------------------- // Let things settle, and after that, remove RESET from drive //---------------------------------------------------------------------------- Delay(10); RESET=1; // Remove reset to drive //---------------------------------------------------------------------------- // ? //---------------------------------------------------------------------------- printf("--------------------------------------------\n"); printf("---------IDE EXERCISER (c) CHG 1996---------\n"); printf("--------------------------------------------\n"); ShowInputs(); printf("Waiting for Initial DRDY & NOT BUSY from drive\n"); WriteBYTE(CMD,6, 0xA0); // Set drive/Head register, ie. select drive 0 while ((ReadBYTE(CMD,7) & 0xC0)!=0x40); // Wait for DRDY and NOT BUSY printf("Drive is ready!\n"); printf("Spinning drive up (Going to IDLE mode)\n"); printf("SetMode=%02bX\n", SetMode(DRIVE0, IDLE, TRUE)); printf("Drive is ready!\n"); printf("ReadSector=%02bX\n", ReadSector(DRIVE0, 0, 0, 1, SectorBuffer)); HexDump(SectorBuffer, 512); for (i=0; i<512; i++) SectorBuffer[i]=i; printf("WriteSector=%02bX\n", WriteSector(DRIVE0, 0, 0, 1, SectorBuffer)); printf("ReadSector=%02bX\n", ReadSector(DRIVE0, 0, 0, 1, SectorBuffer)); HexDump(SectorBuffer, 512); while (1) { printf ("\nCommand: "); gets(cmdbuf, sizeof(cmdbuf)); for (i=0; cmdbuf[i]!=0; i++) cmdbuf[i] = toupper(cmdbuf[i]); for (i = 0; cmdbuf[i] == ' '; i++); /* skip blanks */ switch (cmdbuf[i]) { case 'S': SetMode(DRIVE0, STANDBY, TRUE); break; case 'I': SetMode(DRIVE0, IDLE, TRUE); break; case 'D': IdentifyDrive(DRIVE0, SectorBuffer, &DriveInfo); HexDump(SectorBuffer, 256); printf("Model = %s\n", DriveInfo.Model); printf("Heads = %bu\n",DriveInfo.Heads); printf("Tracks = %u\n", DriveInfo.Tracks); printf("Sectors/Track = %u\n", DriveInfo.SectorsPerTrack); break; case 'R': ShowRegisters(DRIVE0); break; case '?': printf("--------------------------------------------\n"); printf("---------IDE EXERCISER (c) CHG 1996---------\n"); printf("--------------------------------------------\n"); printf("Commands:\n"); printf(" S go to Standby mode\n"); printf(" I go to Idle mode\n"); printf(" D Identify drive\n"); printf(" R Show all registers\n"); printf(" G ,, Read sector\n"); printf("--------------------------------------------\n"); break; case 'G': sscanf (&cmdbuf[i+1], "%d,%d,%d", &Head, &Track, &Sector); printf("ReadSector=%02bX\n", ReadSector(DRIVE0, Head, Track, Sector, SectorBuffer)); HexDump(SectorBuffer, 512); printf("Head=%d, Track=%d, Sector=%d\n", Head, Track, Sector); break; } } }