Code: Select all
//Important defined constants.
#define ATA_STAT_BSY BIT(7)// (busy)
#define ATA_STAT_DRDY BIT(6)//(device ready)
#define ATA_STAT_DF BIT(5)//(Device Fault)
#define ATA_STAT_DSC BIT(4)// (seek complete)
#define ATA_STAT_DRQ BIT(3)// (Data Transfer Requested)
#define ATA_STAT_CORR BIT(2)//(data corrected)
#define ATA_STAT_IDX BIT(1)// (index mark)
#define ATA_STAT_ERR BIT(0)// (error)
#define ATA_ERR_ABRT BIT(2)//(data corrected)
#define ATA_DATA 0
#define ATA_FEATURES_AND_ERRORS 1
#define ATA_SECTOR_COUNT 2
#define ATA_LBA_LOW 3
#define ATA_LBA_MID 4
#define ATA_LBA_HIGH 5
#define ATA_DRIVE_HEAD 6
#define ATA_DEVICE 6
#define ATA_COMMAND_STATUS 7
//Command: Write BSY & DRQ == 0
#define ATA_ALT_STATUS 0x206
#define ATA_DEVICE_CONT 0x206
#define ATA_COMMAND_ID 0xEC
char ReadSector(char drive, int sector, word *buffer, char count){
word hBase = 0x1F0;
int i,j;
if(drive > hard_drive_cnt){
#ifdef __DEBUG
DebugPrint("Attempt to read from non-existant drive: %d\n",drive);
#endif
return -1;
}
hBase = hard_drives[drive].port_range;
//Wait for status to clear
while(InPortByte(ATA_COMMAND_STATUS + hBase) & ATA_STAT_BSY);
OutPortByte(2,ATA_DEVICE_CONT + hBase); //Get rid of that pesky interrupt bit
//Wait for Drive ready(Set), and not Busy(Clear)
while((InPortByte(ATA_COMMAND_STATUS + hBase) & (ATA_STAT_BSY|ATA_STAT_DRDY))!= ATA_STAT_DRDY);
//Send a null byte to features.
OutPortByte(0 , ATA_FEATURES_AND_ERRORS + hBase);
//Send the count
OutPortByte(count , ATA_SECTOR_COUNT + hBase);
OutPortByte((char)sector , ATA_LBA_LOW + hBase);
OutPortByte((char)(sector>>8) , ATA_LBA_MID + hBase);
OutPortByte((char)(sector>>24) , ATA_LBA_HIGH + hBase);
//Don't worry about the SLAVE constant, it's just a remnant from a structure
OutPortByte((char)(0xE0 | ((hard_drives[drive].flags&SLAVE)<<1) | ((sector >> 24) & 0x0F)), ATA_DRIVE_HEAD+hBase);
while(InPortByte(ATA_COMMAND_STATUS + hBase) & ATA_STAT_DRDY != ATA_STAT_DRDY);
OutPortByte(0x20 , ATA_COMMAND_STATUS + hBase);
DeviceWait(hBase);
if(InPortByte(ATA_FEATURES_AND_ERRORS+hBase) & ATA_ERR_ABRT != 0)
return -1;
//Wait until there's data to read
while(InPortByte(ATA_COMMAND_STATUS + hBase) & ATA_STAT_DRQ != ATA_STAT_DRQ);
for(j=0; j<count; j++){
//Read in status before every read?
InPortByte(ATA_ALT_STATUS + hBase);
InPortByte(ATA_COMMAND_STATUS + hBase);
for(i=0; i< 256;i++){ //Endian swap?
buffer[i] = InPortWord(ATA_DATA+hBase);
}
}
//Clear Status register to remove pending interrupts?
//Doesn't make sense because interrupts are disabled
//And reading the ALT_STAT reg doesn't clear that bit.
InPortByte(ATA_ALT_STATUS + hBase);
InPortByte(ATA_COMMAND_STATUS + hBase);
return 0;
}
Code: Select all
DeviceWait(hBase);
Like I said, VMWare eats it up like candy, but my laptop hates it. Of course, this is all after the drives have been identified (Which also fails on real hardware, at least the command does, the drive detection code works fine.)