--- ide.c 2007-02-02 17:35:42.000000000 +0100 +++ ide_new.c 2007-07-28 15:51:15.000000000 +0200 @@ -34,9 +34,12 @@ typedef enum libspectrum_ide_command { - LIBSPECTRUM_IDE_COMMAND_READ_SECTOR = 0x20, - LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR = 0x30, - LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE = 0xec, + LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_RETRY = 0x20, + LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_NORETRY = 0x21, + LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_RETRY = 0x30, + LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_NORETRY = 0x31, + LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATA = 0xec, + LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATAPI = 0xa1, LIBSPECTRUM_IDE_COMMAND_INITIALIZE_DEVICE_PARAMETERS = 0x91, } libspectrum_ide_command; @@ -128,6 +131,9 @@ int cylinders; int heads; int sectors; + + libspectrum_byte error; + libspectrum_byte status; } libspectrum_ide_drive; @@ -143,14 +149,12 @@ libspectrum_ide_unit selected; /* Register values */ - libspectrum_byte error; libspectrum_byte feature; libspectrum_byte sector_count; libspectrum_byte sector; libspectrum_byte cylinder_low; libspectrum_byte cylinder_high; libspectrum_byte head; - libspectrum_byte status; libspectrum_byte data2; /* Channel status */ @@ -389,10 +393,27 @@ chn->head = 0x00; /* Diagnostics passed */ - chn->error = 0x01; + if (chn->drive[LIBSPECTRUM_IDE_MASTER].disk) + chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0x01; + else + chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0xff; + + if (chn->drive[LIBSPECTRUM_IDE_SLAVE].disk) + chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0x01; + else + chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0xff; + /* Device ready, no PACKET support */ - chn->status = 0x72; + if (chn->drive[LIBSPECTRUM_IDE_MASTER].disk) + chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0x40; + else + chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0xff; + + if (chn->drive[LIBSPECTRUM_IDE_SLAVE].disk) + chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0x40; + else + chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0xff; /* Feature is write-only */ chn->feature = 0xff; @@ -405,8 +426,10 @@ chn->cylinder_low = 0xff; chn->cylinder_high = 0xff; chn->head = 0xff; - chn->error = 0xff; - chn->status = 0xff; + chn->drive[LIBSPECTRUM_IDE_MASTER].error = 0xff; + chn->drive[LIBSPECTRUM_IDE_SLAVE].error = 0xff; + chn->drive[LIBSPECTRUM_IDE_MASTER].status = 0xff; + chn->drive[LIBSPECTRUM_IDE_SLAVE].status = 0xff; chn->feature = 0xff; } @@ -524,6 +547,7 @@ read_data( libspectrum_ide_channel *chn ) { libspectrum_byte data; + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; /* Meaningful data is only returned in PIO input phase */ if( chn->phase != LIBSPECTRUM_IDE_PHASE_PIO_IN ) return 0xff; @@ -564,7 +588,7 @@ } else { /* all sectors done */ chn->phase = LIBSPECTRUM_IDE_PHASE_READY; - chn->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ; + drv->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ; } } @@ -576,23 +600,23 @@ libspectrum_ide_read( libspectrum_ide_channel *chn, libspectrum_ide_register reg ) { - /* Only read if the currently-selected drive exists */ - if( chn->drive[ chn->selected ].disk ) { + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; switch( reg ) { - case LIBSPECTRUM_IDE_REGISTER_DATA: return read_data( chn ); case LIBSPECTRUM_IDE_REGISTER_DATA2: return chn->data2; - case LIBSPECTRUM_IDE_REGISTER_ERROR_FEATURE: return chn->error; + case LIBSPECTRUM_IDE_REGISTER_ERROR_FEATURE: return drv->error; case LIBSPECTRUM_IDE_REGISTER_SECTOR_COUNT: return chn->sector_count; case LIBSPECTRUM_IDE_REGISTER_SECTOR: return chn->sector; case LIBSPECTRUM_IDE_REGISTER_CYLINDER_LOW: return chn->cylinder_low; case LIBSPECTRUM_IDE_REGISTER_CYLINDER_HIGH: return chn->cylinder_high; case LIBSPECTRUM_IDE_REGISTER_HEAD_DRIVE: return chn->head; - case LIBSPECTRUM_IDE_REGISTER_COMMAND_STATUS: return chn->status; + case LIBSPECTRUM_IDE_REGISTER_COMMAND_STATUS: + if (!drv->disk) return 0x00; + else return drv->status; + break; } - } return 0xff; } @@ -602,6 +626,8 @@ static void write_data( libspectrum_ide_channel *chn, libspectrum_byte data ) { + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; + /* Data register can only be written in PIO output phase */ if( chn->phase != LIBSPECTRUM_IDE_PHASE_PIO_OUT ) return; @@ -634,8 +660,8 @@ /* Write data to disk */ if ( write_hdf( chn ) ) { - chn->status |= LIBSPECTRUM_IDE_STATUS_ERR; - chn->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC; + drv->status |= LIBSPECTRUM_IDE_STATUS_ERR; + drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC; } if( chn->sector_count ) { @@ -644,7 +670,7 @@ } else { /* all sectors done */ chn->phase = LIBSPECTRUM_IDE_PHASE_READY; - chn->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ; + drv->status &= ~LIBSPECTRUM_IDE_STATUS_DRQ; } } @@ -685,8 +711,8 @@ /* Seek to the correct position */ if( sectornumber < 0 || sectornumber >= ( drv->cylinders * drv->heads * drv->sectors ) ) { - chn->status |= LIBSPECTRUM_IDE_STATUS_ERR; - chn->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_IDNF; + drv->status |= LIBSPECTRUM_IDE_STATUS_ERR; + drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_IDNF; return LIBSPECTRUM_ERROR_UNKNOWN; } @@ -694,6 +720,8 @@ /* advance registers to next sector, for multiple sector accesses */ chn->sector_count--; + if (!chn->sector_count) return LIBSPECTRUM_ERROR_NONE; + if( chn->head & LIBSPECTRUM_IDE_HEAD_LBA ) { /* increment using LBA scheme */ @@ -774,14 +802,14 @@ SET_WORD( chn->buffer, LIBSPECTRUM_IDE_IDENTITY_TOTAL_SECTORS_HI, ( sector_count & 0xffff0000 ) >> 16 ); } - + /* prevent read_data from trying to read from disk after identity block is completely read in */ chn->sector_count = 0; /* Initiate the PIO input phase */ chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_IN; - chn->status |= LIBSPECTRUM_IDE_STATUS_DRQ; + drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ; chn->datacounter = 0; } @@ -789,19 +817,21 @@ static void readsector( libspectrum_ide_channel *chn ) { + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; + if( seek( chn ) ) return; /* Read data from disk */ if( read_hdf( chn ) ) { - chn->status |= LIBSPECTRUM_IDE_STATUS_ERR; - chn->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC; + drv->status |= LIBSPECTRUM_IDE_STATUS_ERR; + drv->error = LIBSPECTRUM_IDE_ERROR_ABRT | LIBSPECTRUM_IDE_ERROR_UNC; } else { /* Initiate the PIO input phase */ chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_IN; - chn->status |= LIBSPECTRUM_IDE_STATUS_DRQ; + drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ; chn->datacounter = 0; } @@ -811,11 +841,13 @@ static void writesector( libspectrum_ide_channel *chn ) { + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; + if( seek( chn ) ) return; /* Initiate the PIO output phase */ chn->phase = LIBSPECTRUM_IDE_PHASE_PIO_OUT; - chn->status |= LIBSPECTRUM_IDE_STATUS_DRQ; + drv->status |= LIBSPECTRUM_IDE_STATUS_DRQ; chn->datacounter = 0; } @@ -824,42 +856,63 @@ init_device_params( libspectrum_ide_channel *chn ) { libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; + int size; + + if ( !chn->sector_count ) { + drv->status |= LIBSPECTRUM_IDE_STATUS_ERR; + drv->error = LIBSPECTRUM_IDE_ERROR_ABRT; + return; + } + + size = drv->heads * drv->sectors * drv->cylinders; + + if (size>16514064) size=16514064; + + drv->heads = ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD ) + 1; + drv->sectors = chn->sector_count; + drv->cylinders = size / (drv->heads * drv->sectors); + +//maybe this should be better to move to identify device + if ( drv->cylinders > 65535 ) drv->cylinders = 65535; + + chn->phase = LIBSPECTRUM_IDE_PHASE_READY; + + drv->error = LIBSPECTRUM_IDE_ERROR_OK; + drv->status &= ~(LIBSPECTRUM_IDE_STATUS_ERR | LIBSPECTRUM_IDE_STATUS_BSY | LIBSPECTRUM_IDE_STATUS_DRQ); + drv->status |= LIBSPECTRUM_IDE_STATUS_DRDY; - /* Return success iff the requested geometry matches the actual geometry - of the disk */ - if( chn->sector_count == drv->sectors && - ( chn->head & LIBSPECTRUM_IDE_HEAD_HEAD ) == drv->heads - 1 ) return; - - /* if not, return ABRT error */ - chn->status |= LIBSPECTRUM_IDE_STATUS_ERR; - chn->error = LIBSPECTRUM_IDE_ERROR_ABRT; } /* Execute a command */ static void execute_command( libspectrum_ide_channel *chn, libspectrum_byte data ) { - if( !chn->drive[ chn->selected].disk ) return; + libspectrum_ide_drive *drv = &chn->drive[ chn->selected ]; + + if( !drv->disk ) return; chn->phase = LIBSPECTRUM_IDE_PHASE_READY; /* Clear error conditions */ - chn->error = LIBSPECTRUM_IDE_ERROR_OK; - chn->status &= ~(LIBSPECTRUM_IDE_STATUS_ERR | LIBSPECTRUM_IDE_STATUS_BSY); - chn->status |= LIBSPECTRUM_IDE_STATUS_DRDY; + drv->error = LIBSPECTRUM_IDE_ERROR_OK; + drv->status &= ~(LIBSPECTRUM_IDE_STATUS_ERR | LIBSPECTRUM_IDE_STATUS_BSY); + drv->status |= LIBSPECTRUM_IDE_STATUS_DRDY; /* Perform command */ switch( data ) { - case LIBSPECTRUM_IDE_COMMAND_READ_SECTOR: readsector( chn ); break; - case LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR: writesector( chn ); break; - case LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE: identifydevice( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_RETRY: readsector( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_READ_SECTOR_NORETRY: readsector( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_RETRY: writesector( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_WRITE_SECTOR_NORETRY: writesector( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATA: identifydevice( chn ); break; + case LIBSPECTRUM_IDE_COMMAND_IDENTIFY_DRIVE_ATAPI: identifydevice( chn ); break; case LIBSPECTRUM_IDE_COMMAND_INITIALIZE_DEVICE_PARAMETERS: init_device_params( chn ); break; /* Unknown/unsupported commands */ default: - chn->status |= LIBSPECTRUM_IDE_STATUS_ERR; - chn->error = LIBSPECTRUM_IDE_ERROR_ABRT; + drv->status |= LIBSPECTRUM_IDE_STATUS_ERR; + drv->error = LIBSPECTRUM_IDE_ERROR_ABRT; } } @@ -876,8 +929,7 @@ case LIBSPECTRUM_IDE_REGISTER_SECTOR_COUNT: chn->sector_count = data; break; case LIBSPECTRUM_IDE_REGISTER_SECTOR: chn->sector = data; break; case LIBSPECTRUM_IDE_REGISTER_CYLINDER_LOW: chn->cylinder_low = data; break; - case LIBSPECTRUM_IDE_REGISTER_CYLINDER_HIGH: - chn->cylinder_high = data; break; + case LIBSPECTRUM_IDE_REGISTER_CYLINDER_HIGH: chn->cylinder_high = data; break; case LIBSPECTRUM_IDE_REGISTER_HEAD_DRIVE: chn->head = data; .