Some work on the seek latency feature

- rewrite of the logical sector handling code to make seek latency support possible
- implemented variable HD seek latency for seek and read commands
- TODO #1: CD-ROM seek command returns status immediately, but it clears the DSC bit
  and sets it at completion. Device should not accept ATAPI commands until DSC is set.
- TODO #2: clean up the seek latency code
This commit is contained in:
Volker Ruppert 2014-02-23 20:06:02 +00:00
parent c7f57b5547
commit 92c6120d7a
2 changed files with 51 additions and 15 deletions

View File

@ -341,6 +341,9 @@ void bx_hard_drive_c::init(void)
BX_INFO(("ata%d-%d: extra data outside of CHS address range", channel, device));
}
}
BX_HD_THIS channels[channel].drives[device].curr_lsector = 0;
BX_HD_THIS channels[channel].drives[device].next_lsector = 0;
BX_HD_THIS channels[channel].drives[device].last_lsector = BX_HD_THIS channels[channel].drives[device].hdimage->hd_size / 512;
} else if (SIM->get_param_enum("type", base)->get() == BX_ATA_DEVICE_CDROM) {
bx_list_c *cdrom_rt = (bx_list_c*)SIM->get_param(BXPN_MENU_RUNTIME_CDROM);
sprintf(pname, "cdrom%d", BX_HD_THIS cdrom_count + 1);
@ -591,6 +594,10 @@ void bx_hard_drive_c::register_state(void)
new bx_shadow_num_c(cdrom, "curr_lba", &BX_HD_THIS channels[i].drives[j].cdrom.curr_lba);
new bx_shadow_num_c(cdrom, "next_lba", &BX_HD_THIS channels[i].drives[j].cdrom.next_lba);
new bx_shadow_num_c(cdrom, "remaining_blocks", &BX_HD_THIS channels[i].drives[j].cdrom.remaining_blocks);
} else {
new bx_shadow_num_c(drive, "curr_lsector", &BX_HD_THIS channels[i].drives[j].curr_lsector);
new bx_shadow_num_c(drive, "last_lsector", &BX_HD_THIS channels[i].drives[j].last_lsector);
new bx_shadow_num_c(drive, "next_lsector", &BX_HD_THIS channels[i].drives[j].next_lsector);
}
new bx_shadow_data_c(drive, "buffer", BX_CONTROLLER(i, j).buffer, MAX_MULTIPLE_SECTORS * 512);
status = new bx_list_c(drive, "status");
@ -672,6 +679,7 @@ void bx_hard_drive_c::seek_timer()
DEV_ide_bmdma_start_transfer(channel);
break;
case 0x70: // SEEK
BX_SELECTED_DRIVE(channel).last_lsector = BX_SELECTED_DRIVE(channel).next_lsector;
controller->error_register = 0;
controller->status.busy = 0;
controller->status.drive_ready = 1;
@ -694,10 +702,6 @@ void bx_hard_drive_c::seek_timer()
case 0xbe: // read cd
ready_to_send_atapi(channel);
break;
case 0x2b: // seek
atapi_cmd_nop(controller);
raise_interrupt(channel);
break;
default:
BX_ERROR(("seek_timer(): ATAPI command 0x%02x not supported",
BX_DRIVE(channel, device).atapi.command));
@ -863,6 +867,7 @@ Bit32u bx_hard_drive_c::read(Bit32u address, unsigned io_len)
if (controller->num_sectors==0) {
controller->status.drq = 0;
BX_SELECTED_DRIVE(channel).last_lsector = BX_SELECTED_DRIVE(channel).curr_lsector;
} else { /* read next one into controller buffer */
controller->status.drq = 1;
controller->status.seek_complete = 1;
@ -1242,6 +1247,7 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
controller->status.drq = 0;
controller->status.err = 0;
controller->status.corrected_data = 0;
BX_SELECTED_DRIVE(channel).last_lsector = BX_SELECTED_DRIVE(channel).curr_lsector;
}
raise_interrupt(channel);
}
@ -1765,9 +1771,9 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
break;
}
BX_SELECTED_DRIVE(channel).cdrom.cd->seek(lba);
// start_seek(channel);
atapi_cmd_nop(controller);
raise_interrupt(channel);
// TODO: DSC bit must be cleared here and set after completion
}
break;
@ -2004,6 +2010,11 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
} else {
controller->buffer_size = 512;
}
if (!calculate_logical_address(channel, &logical_sector)) {
command_aborted(channel, value);
break;
}
BX_SELECTED_DRIVE(channel).next_lsector = logical_sector;
controller->current_command = value;
if (ide_read_sector(channel, controller->buffer,
@ -2052,6 +2063,11 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
} else {
controller->buffer_size = 512;
}
if (!calculate_logical_address(channel, &logical_sector)) {
command_aborted(channel, value);
break;
}
BX_SELECTED_DRIVE(channel).next_lsector = logical_sector;
controller->current_command = value;
// implicit seek done :^)
@ -2362,10 +2378,10 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
if (BX_SELECTED_IS_HD(channel)) {
BX_DEBUG(("write cmd 0x70 (SEEK) executing"));
if (!calculate_logical_address(channel, &logical_sector)) {
BX_ERROR(("initial seek to sector %lu out of bounds, aborting", (unsigned long)logical_sector));
command_aborted(channel, value);
break;
}
BX_SELECTED_DRIVE(channel).next_lsector = logical_sector;
controller->current_command = value;
controller->error_register = 0;
controller->status.busy = 1;
@ -2385,6 +2401,11 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
case 0xC8: // READ DMA
if (BX_SELECTED_IS_HD(channel) && BX_HD_THIS bmdma_present()) {
lba48_transform(controller, lba48);
if (!calculate_logical_address(channel, &logical_sector)) {
command_aborted(channel, value);
break;
}
BX_SELECTED_DRIVE(channel).next_lsector = logical_sector;
controller->current_command = value;
controller->error_register = 0;
controller->status.busy = 1;
@ -2404,6 +2425,11 @@ void bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
case 0xCA: // WRITE DMA
if (BX_SELECTED_IS_HD(channel) && BX_HD_THIS bmdma_present()) {
lba48_transform(controller, lba48);
if (!calculate_logical_address(channel, &logical_sector)) {
command_aborted(channel, value);
break;
}
BX_SELECTED_DRIVE(channel).next_lsector = logical_sector;
controller->status.drive_ready = 1;
controller->status.seek_complete = 1;
controller->status.drq = 1;
@ -2593,7 +2619,7 @@ bx_hard_drive_c::calculate_logical_address(Bit8u channel, Bit64s *sector)
Bit64s sector_count = BX_SELECTED_DRIVE(channel).hdimage->hd_size / 512;
if (logical_sector >= sector_count) {
BX_ERROR (("calc_log_addr: out of bounds ("FMT_LL"d/"FMT_LL"d)", logical_sector, sector_count));
BX_ERROR (("logical address out of bounds ("FMT_LL"d/"FMT_LL"d) - aborting command", logical_sector, sector_count));
return 0;
}
*sector = logical_sector;
@ -2622,6 +2648,7 @@ bx_hard_drive_c::increment_address(Bit8u channel, Bit64s *sector)
controller->cylinder_no = (Bit16u)((logical_sector >> 8) & 0xffff);
controller->sector_no = (Bit8u)((logical_sector) & 0xff);
}
*sector = logical_sector;
} else {
controller->sector_no++;
if (controller->sector_no > BX_SELECTED_DRIVE(channel).hdimage->spt) {
@ -2630,11 +2657,12 @@ bx_hard_drive_c::increment_address(Bit8u channel, Bit64s *sector)
if (controller->head_no >= BX_SELECTED_DRIVE(channel).hdimage->heads) {
controller->head_no = 0;
controller->cylinder_no++;
if (controller->cylinder_no >= BX_SELECTED_DRIVE(channel).hdimage->cylinders)
if (controller->cylinder_no >= BX_SELECTED_DRIVE(channel).hdimage->cylinders) {
controller->cylinder_no = BX_SELECTED_DRIVE(channel).hdimage->cylinders - 1;
}
}
}
}
}
void bx_hard_drive_c::identify_ATAPI_drive(Bit8u channel)
@ -3347,6 +3375,7 @@ void bx_hard_drive_c::bmdma_complete(Bit8u channel)
controller->status.write_fault = 0;
controller->status.seek_complete = 1;
controller->status.corrected_data = 0;
BX_SELECTED_DRIVE(channel).last_lsector = BX_SELECTED_DRIVE(channel).curr_lsector;
}
raise_interrupt(channel);
}
@ -3379,7 +3408,6 @@ bx_bool bx_hard_drive_c::ide_read_sector(Bit8u channel, Bit8u *buffer, Bit32u bu
Bit8u *bufptr = buffer;
do {
if (!calculate_logical_address(channel, &logical_sector)) {
BX_ERROR(("ide_read_sector() reached invalid sector %lu, aborting", (unsigned long)logical_sector));
command_aborted(channel, controller->current_command);
return 0;
}
@ -3398,6 +3426,7 @@ bx_bool bx_hard_drive_c::ide_read_sector(Bit8u channel, Bit8u *buffer, Bit32u bu
return 0;
}
increment_address(channel, &logical_sector);
BX_SELECTED_DRIVE(channel).curr_lsector = logical_sector;
bufptr += 512;
} while (--sector_count > 0);
@ -3415,7 +3444,6 @@ bx_bool bx_hard_drive_c::ide_write_sector(Bit8u channel, Bit8u *buffer, Bit32u b
Bit8u *bufptr = buffer;
do {
if (!calculate_logical_address(channel, &logical_sector)) {
BX_ERROR(("ide_write_sector() reached invalid sector %lu, aborting", (unsigned long)logical_sector));
command_aborted(channel, controller->current_command);
return 0;
}
@ -3434,6 +3462,7 @@ bx_bool bx_hard_drive_c::ide_write_sector(Bit8u channel, Bit8u *buffer, Bit32u b
return 0;
}
increment_address(channel, &logical_sector);
BX_SELECTED_DRIVE(channel).curr_lsector = logical_sector;
bufptr += 512;
} while (--sector_count > 0);
@ -3462,17 +3491,20 @@ void bx_hard_drive_c::start_seek(Bit8u channel)
{
Bit64s new_pos, prev_pos, max_pos;
Bit32u seek_time;
double fSeekTime;
double fSeekBase, fSeekTime;
if (BX_SELECTED_IS_CD(channel)) {
max_pos = BX_SELECTED_DRIVE(channel).cdrom.max_lba;
prev_pos = BX_SELECTED_DRIVE(channel).cdrom.curr_lba;
new_pos = BX_SELECTED_DRIVE(channel).cdrom.next_lba;
fSeekTime = 80000.0 * (double)abs((int)(new_pos - prev_pos + 1)) / (max_pos + 1);
fSeekBase = 80000.0;
} else {
// TODO: make HD seek latency variable
fSeekTime = 5000.0;
max_pos = (BX_SELECTED_DRIVE(channel).hdimage->hd_size / 512) - 1;
prev_pos = BX_SELECTED_DRIVE(channel).last_lsector;
new_pos = BX_SELECTED_DRIVE(channel).next_lsector;
fSeekBase = 5000.0;
}
fSeekTime = fSeekBase * (double)abs((int)(new_pos - prev_pos + 1)) / (max_pos + 1);
seek_time = (fSeekTime > 10.0) ? (Bit32u)fSeekTime : 10;
bx_pc_system.activate_timer(
BX_SELECTED_DRIVE(channel).seek_timer_index, seek_time, 0);

View File

@ -232,7 +232,6 @@ private:
// and an array of two drive structs
struct channel_t {
struct drive_t {
device_image_t* hdimage;
device_type_t device_type;
// 512 byte buffer for ID drive command
// These words are stored in native word endian format, as
@ -246,6 +245,11 @@ private:
sense_info_t sense;
atapi_t atapi;
device_image_t* hdimage;
Bit64s curr_lsector;
Bit64s last_lsector;
Bit64s next_lsector;
Bit8u model_no[41];
int statusbar_id;
Bit8u device_num; // for ATAPI identify & inquiry