Files
86Box/src/disk/hdd.c

554 lines
26 KiB
C
Raw Normal View History

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Common code to handle all sorts of hard disk images.
*
2020-03-25 00:46:02 +02:00
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2016-2019 Miran Grca.
* Copyright 2017-2019 Fred N. van Kempen.
*/
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/hdd.h>
#include <86box/cdrom.h>
#include <86box/video.h>
#include "cpu.h"
#define HDD_OVERHEAD_TIME 50.0
2022-09-18 17:13:50 -04:00
hard_disk_t hdd[HDD_NUM];
2022-07-19 23:52:18 +02:00
int
hdd_init(void)
{
/* Clear all global data. */
memset(hdd, 0x00, sizeof(hdd));
2023-05-29 01:30:51 -04:00
return 0;
}
int
hdd_string_to_bus(char *str, int cdrom)
{
2022-09-18 17:13:50 -04:00
if (!strcmp(str, "none"))
2023-05-29 01:30:51 -04:00
return HDD_BUS_DISABLED;
if (!strcmp(str, "mfm")) {
2022-09-18 17:13:50 -04:00
if (cdrom) {
no_cdrom:
ui_msgbox_header(MBX_ERROR, plat_get_string(STRING_INVALID_CONFIG), plat_get_string(STRING_NO_ST506_ESDI_CDROM));
2023-05-29 01:30:51 -04:00
return 0;
2022-09-18 17:13:50 -04:00
}
2023-05-29 01:30:51 -04:00
return HDD_BUS_MFM;
}
if (!strcmp(str, "esdi")) {
2022-09-18 17:13:50 -04:00
if (cdrom)
goto no_cdrom;
2023-05-29 01:30:51 -04:00
return HDD_BUS_ESDI;
}
2022-09-18 17:13:50 -04:00
if (!strcmp(str, "ide"))
2023-05-29 01:30:51 -04:00
return HDD_BUS_IDE;
2022-09-18 17:13:50 -04:00
if (!strcmp(str, "atapi"))
2023-05-29 01:30:51 -04:00
return HDD_BUS_ATAPI;
2022-09-18 17:13:50 -04:00
if (!strcmp(str, "xta"))
2023-05-29 01:30:51 -04:00
return HDD_BUS_XTA;
2022-09-18 17:13:50 -04:00
if (!strcmp(str, "scsi"))
2023-05-29 01:30:51 -04:00
return HDD_BUS_SCSI;
2023-05-29 01:30:51 -04:00
return 0;
}
char *
2023-06-26 12:47:04 -04:00
hdd_bus_to_string(int bus, UNUSED(int cdrom))
{
char *s = "none";
switch (bus) {
2022-09-18 17:13:50 -04:00
default:
2023-06-28 13:46:28 -04:00
case HDD_BUS_DISABLED:
2022-09-18 17:13:50 -04:00
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_MFM:
s = "mfm";
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_XTA:
s = "xta";
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_ESDI:
s = "esdi";
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_IDE:
s = "ide";
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_ATAPI:
s = "atapi";
break;
2022-09-18 17:13:50 -04:00
case HDD_BUS_SCSI:
s = "scsi";
break;
}
2023-05-29 01:30:51 -04:00
return s;
}
int
hdd_is_valid(int c)
{
if (hdd[c].bus == HDD_BUS_DISABLED)
2023-05-29 01:30:51 -04:00
return 0;
if (strlen(hdd[c].fn) == 0)
2023-05-29 01:30:51 -04:00
return 0;
2022-09-18 17:13:50 -04:00
if ((hdd[c].tracks == 0) || (hdd[c].hpc == 0) || (hdd[c].spt == 0))
2023-05-29 01:30:51 -04:00
return 0;
2023-05-29 01:30:51 -04:00
return 1;
}
double
hdd_seek_get_time(hard_disk_t *hdd, uint32_t dst_addr, uint8_t operation, uint8_t continuous, double max_seek_time)
{
if (!hdd->speed_preset)
return HDD_OVERHEAD_TIME;
2023-07-20 18:58:26 -04:00
const hdd_zone_t *zone = NULL;
if (hdd->num_zones <= 0) {
fatal("hdd_seek_get_time(): hdd->num_zones < 0)\n");
return 0.0;
}
2023-06-28 13:46:28 -04:00
for (uint32_t i = 0; i < hdd->num_zones; i++) {
2022-09-18 17:13:50 -04:00
zone = &hdd->zones[i];
if (zone->end_sector >= dst_addr)
break;
2022-07-19 23:52:18 +02:00
}
2022-09-18 17:13:50 -04:00
double continuous_times[2][2] = {
{hdd->head_switch_usec, hdd->cyl_switch_usec },
{ zone->sector_time_usec, zone->sector_time_usec}
};
2022-07-19 23:52:18 +02:00
double times[2] = { HDD_OVERHEAD_TIME, hdd->avg_rotation_lat_usec };
2022-09-18 17:13:50 -04:00
uint32_t new_track = zone->start_track + ((dst_addr - zone->start_sector) / zone->sectors_per_track);
uint32_t new_cylinder = new_track / hdd->phy_heads;
uint32_t cylinder_diff = abs((int) hdd->cur_cylinder - (int) new_cylinder);
2022-07-19 23:52:18 +02:00
bool sequential = dst_addr == hdd->cur_addr + 1;
2022-09-18 17:13:50 -04:00
continuous = continuous && sequential;
2022-07-19 23:52:18 +02:00
double seek_time = 0.0;
if (continuous)
2022-09-18 17:13:50 -04:00
seek_time = continuous_times[new_track == hdd->cur_track][!!cylinder_diff];
2022-07-19 23:52:18 +02:00
else {
2022-09-18 17:13:50 -04:00
if (!cylinder_diff)
seek_time = times[operation != HDD_OP_SEEK];
else {
seek_time = hdd->cyl_switch_usec + (hdd->full_stroke_usec * (double) cylinder_diff / (double) hdd->phy_cyl) + ((operation != HDD_OP_SEEK) * hdd->avg_rotation_lat_usec);
}
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
if (!max_seek_time || seek_time <= max_seek_time) {
2022-09-18 17:13:50 -04:00
hdd->cur_addr = dst_addr;
hdd->cur_track = new_track;
hdd->cur_cylinder = new_cylinder;
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
return seek_time;
}
static void
hdd_readahead_update(hard_disk_t *hdd)
{
2022-07-19 23:52:18 +02:00
uint64_t elapsed_cycles;
2023-05-29 01:30:51 -04:00
double elapsed_us;
double seek_time;
int32_t max_read_ahead;
2022-07-19 23:52:18 +02:00
uint32_t space_needed;
2022-07-19 23:52:18 +02:00
hdd_cache_t *cache = &hdd->cache;
if (cache->ra_ongoing) {
2022-09-18 17:13:50 -04:00
hdd_cache_seg_t *segment = &cache->segments[cache->ra_segment];
2022-09-18 17:13:50 -04:00
elapsed_cycles = tsc - cache->ra_start_time;
elapsed_us = (double) elapsed_cycles / cpuclock * 1000000.0;
/* Do not overwrite data not yet read by host */
max_read_ahead = (segment->host_addr + cache->segment_size) - segment->ra_addr;
2022-09-18 17:13:50 -04:00
seek_time = 0.0;
for (int32_t i = 0; i < max_read_ahead; i++) {
2022-09-18 17:13:50 -04:00
seek_time += hdd_seek_get_time(hdd, segment->ra_addr, HDD_OP_READ, 1, elapsed_us - seek_time);
if (seek_time > elapsed_us)
break;
2022-09-18 17:13:50 -04:00
segment->ra_addr++;
}
2022-07-19 23:52:18 +02:00
2022-09-18 17:13:50 -04:00
if (segment->ra_addr > segment->lba_addr + cache->segment_size) {
space_needed = segment->ra_addr - (segment->lba_addr + cache->segment_size);
segment->lba_addr += space_needed;
}
2022-07-19 23:52:18 +02:00
}
}
static double
hdd_writecache_flush(hard_disk_t *hdd)
{
2022-07-19 23:52:18 +02:00
double seek_time = 0.0;
2022-07-19 23:52:18 +02:00
while (hdd->cache.write_pending) {
2022-09-18 17:13:50 -04:00
seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, 0);
hdd->cache.write_addr++;
hdd->cache.write_pending--;
2022-07-19 23:52:18 +02:00
}
return seek_time;
}
static void
hdd_writecache_update(hard_disk_t *hdd)
{
2022-07-19 23:52:18 +02:00
uint64_t elapsed_cycles;
2023-05-29 01:30:51 -04:00
double elapsed_us;
double seek_time;
2022-07-19 23:52:18 +02:00
if (hdd->cache.write_pending) {
2022-09-18 17:13:50 -04:00
elapsed_cycles = tsc - hdd->cache.write_start_time;
elapsed_us = (double) elapsed_cycles / cpuclock * 1000000.0;
seek_time = 0.0;
while (hdd->cache.write_pending) {
seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, elapsed_us - seek_time);
if (seek_time > elapsed_us)
break;
hdd->cache.write_addr++;
hdd->cache.write_pending--;
}
2022-07-19 23:52:18 +02:00
}
}
double
hdd_timing_write(hard_disk_t *hdd, uint32_t addr, uint32_t len)
{
2022-09-18 17:13:50 -04:00
double seek_time = 0.0;
2022-07-19 23:52:18 +02:00
uint32_t flush_needed;
if (!hdd->speed_preset)
return HDD_OVERHEAD_TIME;
2022-07-19 23:52:18 +02:00
hdd_readahead_update(hdd);
hdd_writecache_update(hdd);
2022-07-19 23:52:18 +02:00
hdd->cache.ra_ongoing = 0;
2022-07-19 23:52:18 +02:00
if (hdd->cache.write_pending && (addr != (hdd->cache.write_addr + hdd->cache.write_pending))) {
2022-09-18 17:13:50 -04:00
/* New request is not sequential to existing cache, need to flush it */
seek_time += hdd_writecache_flush(hdd);
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
if (!hdd->cache.write_pending) {
2022-09-18 17:13:50 -04:00
/* Cache is empty */
hdd->cache.write_addr = addr;
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
hdd->cache.write_pending += len;
if (hdd->cache.write_pending > hdd->cache.write_size) {
2022-09-18 17:13:50 -04:00
/* If request is bigger than free cache, flush some data first */
flush_needed = hdd->cache.write_pending - hdd->cache.write_size;
for (uint32_t i = 0; i < flush_needed; i++) {
seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, 0);
hdd->cache.write_addr++;
}
2022-07-19 23:52:18 +02:00
}
2022-09-18 17:13:50 -04:00
hdd->cache.write_start_time = tsc + (uint32_t) (seek_time * cpuclock / 1000000.0);
2022-07-19 23:52:18 +02:00
return seek_time;
}
double
hdd_timing_read(hard_disk_t *hdd, uint32_t addr, uint32_t len)
{
2022-07-19 23:52:18 +02:00
double seek_time = 0.0;
if (!hdd->speed_preset)
return HDD_OVERHEAD_TIME;
2022-07-19 23:52:18 +02:00
hdd_readahead_update(hdd);
hdd_writecache_update(hdd);
2022-07-19 23:52:18 +02:00
seek_time += hdd_writecache_flush(hdd);
2022-09-18 17:13:50 -04:00
hdd_cache_t *cache = &hdd->cache;
2022-07-19 23:52:18 +02:00
hdd_cache_seg_t *active_seg = &cache->segments[0];
2022-07-19 23:52:18 +02:00
for (uint32_t i = 0; i < cache->num_segments; i++) {
2022-09-18 17:13:50 -04:00
hdd_cache_seg_t *segment = &cache->segments[i];
if (!segment->valid) {
active_seg = segment;
continue;
}
if (segment->lba_addr <= addr && (segment->lba_addr + cache->segment_size) >= addr) {
/* Cache HIT */
segment->host_addr = addr;
active_seg = segment;
if (addr + len > segment->ra_addr) {
uint32_t need_read = (addr + len) - segment->ra_addr;
for (uint32_t j = 0; j < need_read; j++) {
seek_time += hdd_seek_get_time(hdd, segment->ra_addr, HDD_OP_READ, 1, 0.0);
segment->ra_addr++;
}
}
if (addr + len > segment->lba_addr + cache->segment_size) {
/* Need to erase some previously cached data */
uint32_t space_needed = (addr + len) - (segment->lba_addr + cache->segment_size);
segment->lba_addr += space_needed;
}
goto update_lru;
} else {
if (segment->lru > active_seg->lru)
active_seg = segment;
}
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
/* Cache MISS */
2022-09-18 17:13:50 -04:00
active_seg->lba_addr = addr;
active_seg->valid = 1;
2022-07-19 23:52:18 +02:00
active_seg->host_addr = addr;
2022-09-18 17:13:50 -04:00
active_seg->ra_addr = addr;
2022-07-19 23:52:18 +02:00
for (uint32_t i = 0; i < len; i++) {
2022-09-18 17:13:50 -04:00
seek_time += hdd_seek_get_time(hdd, active_seg->ra_addr, HDD_OP_READ, i != 0, 0.0);
active_seg->ra_addr++;
2022-07-19 23:52:18 +02:00
}
update_lru:
2022-07-19 23:52:18 +02:00
for (uint32_t i = 0; i < cache->num_segments; i++)
2022-09-18 17:13:50 -04:00
cache->segments[i].lru++;
2022-07-19 23:52:18 +02:00
active_seg->lru = 0;
2022-09-18 17:13:50 -04:00
cache->ra_ongoing = 1;
cache->ra_segment = active_seg->id;
cache->ra_start_time = tsc + (uint32_t) (seek_time * cpuclock / 1000000.0);
2022-07-19 23:52:18 +02:00
return seek_time;
}
static void
hdd_cache_init(hard_disk_t *hdd)
{
2022-07-19 23:52:18 +02:00
hdd_cache_t *cache = &hdd->cache;
2022-09-18 17:13:50 -04:00
cache->ra_segment = 0;
cache->ra_ongoing = 0;
2022-07-19 23:52:18 +02:00
cache->ra_start_time = 0;
2023-05-29 01:30:51 -04:00
for (uint32_t i = 0; i < cache->num_segments; i++) {
2022-09-18 17:13:50 -04:00
cache->segments[i].valid = 0;
cache->segments[i].lru = 0;
cache->segments[i].id = i;
cache->segments[i].ra_addr = 0;
cache->segments[i].host_addr = 0;
2022-07-19 23:52:18 +02:00
}
}
static void
hdd_zones_init(hard_disk_t *hdd)
{
2023-05-29 01:30:51 -04:00
uint32_t lba = 0;
uint32_t track = 0;
uint32_t tracks;
2022-09-18 17:13:50 -04:00
double revolution_usec = 60.0 / (double) hdd->rpm * 1000000.0;
2022-07-19 23:52:18 +02:00
hdd_zone_t *zone;
2023-05-29 01:30:51 -04:00
for (uint32_t i = 0; i < hdd->num_zones; i++) {
2022-09-18 17:13:50 -04:00
zone = &hdd->zones[i];
zone->start_sector = lba;
zone->start_track = track;
zone->sector_time_usec = revolution_usec / (double) zone->sectors_per_track;
tracks = zone->cylinders * hdd->phy_heads;
lba += tracks * zone->sectors_per_track;
zone->end_sector = lba - 1;
track += tracks - 1;
2022-07-19 23:52:18 +02:00
}
}
static hdd_preset_t hdd_speed_presets[] = {
2022-11-19 08:49:04 -05:00
// clang-format off
{ .name = "RAM Disk (max. speed)", .internal_name = "ramdisk", .rcache_num_seg = 16, .rcache_seg_size = 128, .max_multiple = 32 },
{ .name = "[1989] 3500 RPM", .internal_name = "1989_3500rpm", .zones = 1, .avg_spt = 35, .heads = 2, .rpm = 3500, .full_stroke_ms = 40, .track_seek_ms = 8, .rcache_num_seg = 1, .rcache_seg_size = 16, .max_multiple = 8 },
{ .name = "[1992] 3600 RPM", .internal_name = "1992_3600rpm", .zones = 1, .avg_spt = 45, .heads = 2, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 6, .rcache_num_seg = 4, .rcache_seg_size = 16, .max_multiple = 8 },
{ .name = "[1994] 4500 RPM", .internal_name = "1994_4500rpm", .zones = 8, .avg_spt = 80, .heads = 4, .rpm = 4500, .full_stroke_ms = 26, .track_seek_ms = 5, .rcache_num_seg = 4, .rcache_seg_size = 32, .max_multiple = 16 },
{ .name = "[1996] 5400 RPM", .internal_name = "1996_5400rpm", .zones = 16, .avg_spt = 135, .heads = 4, .rpm = 5400, .full_stroke_ms = 24, .track_seek_ms = 3, .rcache_num_seg = 4, .rcache_seg_size = 64, .max_multiple = 16 },
{ .name = "[1997] 5400 RPM", .internal_name = "1997_5400rpm", .zones = 16, .avg_spt = 185, .heads = 6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 64, .max_multiple = 32 },
{ .name = "[1998] 5400 RPM", .internal_name = "1998_5400rpm", .zones = 16, .avg_spt = 300, .heads = 8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 32 },
{ .name = "[2000] 7200 RPM", .internal_name = "2000_7200rpm", .zones = 16, .avg_spt = 350, .heads = 6, .rpm = 7200, .full_stroke_ms = 15, .track_seek_ms = 2, .rcache_num_seg = 16, .rcache_seg_size = 128, .max_multiple = 32 },
2024-12-09 04:00:59 +07:00
{ .name = "[ESDI] Fujitsu M2263E", .internal_name = "M2263E", .model = "FUJITSU M2263E", .zones = 1, .avg_spt = 160, .heads = 8, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 4, .rcache_num_seg = 4, .rcache_seg_size = 16, .max_multiple = 1 },
{ .name = "[PIO IDE] IBM WDA-L42", .internal_name = "WDAL42", .model = "IBM-WDA-L42", .zones = 1, .avg_spt = 85, .heads = 2, .rpm = 3600, .full_stroke_ms = 33, .track_seek_ms = 2.5, .rcache_num_seg = 1, .rcache_seg_size = 32, .max_multiple = 1 },
{ .name = "[ATA-1] Conner CP3024", .internal_name = "CP3024", .model = "Conner Peripherals 20MB - CP3024", .zones = 1, .avg_spt = 33, .heads = 2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8, .rcache_num_seg = 1, .rcache_seg_size = 8, .max_multiple = 8 },
{ .name = "[ATA-1] Conner CP3044", .internal_name = "CP3044", .model = "Conner Peripherals 40MB - CP3044", .zones = 1, .avg_spt = 40, .heads = 2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8, .rcache_num_seg = 1, .rcache_seg_size = 8, .max_multiple = 8 },
{ .name = "[ATA-1] Conner CP3104", .internal_name = "CP3104", .model = "Conner Peripherals 104MB - CP3104", .zones = 1, .avg_spt = 33, .heads = 8, .rpm = 3500, .full_stroke_ms = 45, .track_seek_ms = 8, .rcache_num_seg = 4, .rcache_seg_size = 8, .max_multiple = 8 },
{ .name = "[ATA-1] IBM H3256-A3", .internal_name = "H3256A3", .model = "IBM-H3256-A3", .zones = 1, .avg_spt = 140, .heads = 2, .rpm = 3600, .full_stroke_ms = 32, .track_seek_ms = 4, .rcache_num_seg = 4, .rcache_seg_size = 96, .max_multiple = 8 },
{ .name = "[ATA-1] Maxtor 7131AT", .internal_name = "7131AT", .model = "Maxtor 7131AT", .zones = 2, .avg_spt = 154, .heads = 2, .rpm = 3551, .full_stroke_ms = 27, .track_seek_ms = 4.5, .rcache_num_seg = 1, .rcache_seg_size = 64, .max_multiple = 8 },
{ .name = "[ATA-1] Maxtor 7213AT", .internal_name = "7213AT", .model = "Maxtor 7213AT", .zones = 4, .avg_spt = 155, .heads = 4, .rpm = 3551, .full_stroke_ms = 28, .track_seek_ms = 6.5, .rcache_num_seg = 1, .rcache_seg_size = 64, .max_multiple = 8 },
{ .name = "[ATA-1] Maxtor 7245AT", .internal_name = "7245AT", .model = "Maxtor 7245AT", .zones = 4, .avg_spt = 149, .heads = 4, .rpm = 3551, .full_stroke_ms = 27, .track_seek_ms = 4.4, .rcache_num_seg = 8, .rcache_seg_size = 64, .max_multiple = 16 },
2024-12-09 04:00:59 +07:00
{ .name = "[ATA-2] IBM DBOA-2720", .internal_name = "DBOA2720", .model = "IBM-DBOA-2720", .zones = 2, .avg_spt = 135, .heads = 2, .rpm = 4000, .full_stroke_ms = 30, .track_seek_ms = 5, .rcache_num_seg = 4, .rcache_seg_size = 64, .max_multiple = 16 },
{ .name = "[ATA-2] Maxtor 7850AV", .internal_name = "7850AV", .model = "Maxtor 7850AV", .zones = 4, .avg_spt = 120, .heads = 4, .rpm = 3551, .full_stroke_ms = 31, .track_seek_ms = 3.7, .rcache_num_seg = 4, .rcache_seg_size = 64, .max_multiple = 8 },
{ .name = "[ATA-2] Maxtor 71336AP", .internal_name = "71336AP", .model = "Maxtor 71336AP", .zones = 4, .avg_spt = 105, .heads = 4, .rpm = 4480, .full_stroke_ms = 12, .track_seek_ms = 3.4, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 },
{ .name = "[ATA-2] Quantum Bigfoot 1.2AT", .internal_name = "BF12A011", .model = "QUANTUM BIGFOOT BF1.2A", .zones = 2, .avg_spt = 155, .heads = 2, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 3.5, .rcache_num_seg = 4, .rcache_seg_size = 128, .max_multiple = 16 },
{ .name = "[ATA-2] Quantum Bigfoot (CY4320A)", .internal_name = "CY4320A", .model = "QUANTUM BIGFOOT_CY4320A", .zones = 2, .avg_spt = 130, .heads = 2, .rpm = 4000, .full_stroke_ms = 29, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 32 },
{ .name = "[ATA-2] Quantum Fireball CR4.3AT", .internal_name = "CR43A013", .model = "QUANTUM FIREBALL CR4.3A", .zones = 2, .avg_spt = 110, .heads = 2, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 32 },
{ .name = "[ATA-2] Samsung PLS-31274A", .internal_name = "PLS31274A", .model = "SAMSUNG PLS-31274A", .zones = 4, .avg_spt = 110, .heads = 4, .rpm = 4500, .full_stroke_ms = 45, .track_seek_ms = 4.5, .rcache_num_seg = 4, .rcache_seg_size = 256, .max_multiple = 8 },
{ .name = "[ATA-2] Samsung Winner-1", .internal_name = "WNR31601A", .model = "SAMSUNG WNR-31601A", .zones = 8, .avg_spt = 110, .heads = 4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 },
{ .name = "[ATA-2] Seagate Medalist (ST3780A)", .internal_name = "ST3780A", .model = "ST3780A", .zones = 8, .avg_spt = 120, .heads = 4, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.5, .rcache_num_seg = 4, .rcache_seg_size = 256, .max_multiple = 16 },
{ .name = "[ATA-2] Seagate Medalist (ST31220A)", .internal_name = "ST31220A", .model = "ST31220A", .zones = 8, .avg_spt = 140, .heads = 6, .rpm = 4500, .full_stroke_ms = 27, .track_seek_ms = 3.5, .rcache_num_seg = 4, .rcache_seg_size = 256, .max_multiple = 16 },
{ .name = "[ATA-2] Seagate Medalist 210xe", .internal_name = "ST3250A", .model = "ST3250A", .zones = 4, .avg_spt = 148, .heads = 2, .rpm = 3811, .full_stroke_ms = 30, .track_seek_ms = 4.1, .rcache_num_seg = 8, .rcache_seg_size = 120, .max_multiple = 8 },
{ .name = "[ATA-2] Seagate Medalist 275xe", .internal_name = "ST3295A", .model = "ST3295A", .zones = 4, .avg_spt = 130, .heads = 2, .rpm = 3811, .full_stroke_ms = 30, .track_seek_ms = 3.4, .rcache_num_seg = 3, .rcache_seg_size = 120, .max_multiple = 8 },
{ .name = "[ATA-2] Seagate Medalist 1270SL", .internal_name = "ST51270A", .model = "ST51270A", .zones = 8, .avg_spt = 105, .heads = 3, .rpm = 5736, .full_stroke_ms = 25, .track_seek_ms = 2, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 },
2024-12-12 05:37:04 +07:00
{ .name = "[ATA-2] Western Digital Caviar 2850", .internal_name = "WDAC2850", .model = "WDC AC2850F", .zones = 4, .avg_spt = 115, .heads = 2, .rpm = 4500, .full_stroke_ms = 12, .track_seek_ms = 4, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 8 },
{ .name = "[ATA-2] Western Digital Caviar 31200", .internal_name = "WDAC31200", .model = "WDC AC31200F", .zones = 8, .avg_spt = 110, .heads = 4, .rpm = 4500, .full_stroke_ms = 12, .track_seek_ms = 4, .rcache_num_seg = 8, .rcache_seg_size = 64, .max_multiple = 16 },
{ .name = "[ATA-3] Samsung Winner 5X", .internal_name = "WU33205A", .model = "SAMSUNG WU33205A", .zones = 16, .avg_spt = 100, .heads = 4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 16 },
2024-12-09 04:00:59 +07:00
{ .name = "[ATA-4] Fujitsu MPD3043AT", .internal_name = "MPD3043AT", .model = "FUJITSU MPD3043AT", .zones = 5, .avg_spt = 95, .heads = 2, .rpm = 5400, .full_stroke_ms = 29, .track_seek_ms = 1.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 },
{ .name = "[ATA-4] Fujitsu MPD3064AT", .internal_name = "MPD3064AT", .model = "FUJITSU MPD3064AT", .zones = 7, .avg_spt = 95, .heads = 3, .rpm = 5400, .full_stroke_ms = 30, .track_seek_ms = 1.5, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 16 },
{ .name = "[ATA-4] Maxtor DiamondMax 2160", .internal_name = "86480D6", .model = "Maxtor 86480D6", .zones = 8, .avg_spt = 97, .heads = 4, .rpm = 5200, .full_stroke_ms = 18, .track_seek_ms = 1, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 32 },
{ .name = "[ATA-4] Maxtor DiamondMax 2880", .internal_name = "90432D3", .model = "Maxtor 90432D3", .zones = 16, .avg_spt = 90, .heads = 3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1, .rcache_num_seg = 8, .rcache_seg_size = 256, .max_multiple = 32 },
2024-12-09 04:00:59 +07:00
{ .name = "[ATA-4] Quantum Bigfoot TX4.3AT", .internal_name = "TX043A011", .model = "QUANTUM BIGFOOT TX4.3A", .zones = 2, .avg_spt = 120, .heads = 2, .rpm = 4000, .full_stroke_ms = 30, .track_seek_ms = 2.5, .rcache_num_seg = 8, .rcache_seg_size = 128, .max_multiple = 32 },
{ .name = "[ATA-4] Toshiba MK4006MAV", .internal_name = "MK4006MAV", .model = "TOSHIBA MK4006MAV", .zones = 8, .avg_spt = 130, .heads = 6, .rpm = 4200, .full_stroke_ms = 25, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 512, .max_multiple = 32 },
2024-12-12 05:37:04 +07:00
{ .name = "[ATA-4] Western Digital Caviar 33200", .internal_name = "WDAC33200", .model = "WDC AC33200LA", .zones = 16, .avg_spt = 110, .heads = 5, .rpm = 5200, .full_stroke_ms = 40, .track_seek_ms = 3, .rcache_num_seg = 8, .rcache_seg_size = 256, .max_multiple = 32 },
2024-12-09 04:00:59 +07:00
{ .name = "[ATA-5] Samsung SpinPoint V6800", .internal_name = "SV0682D", .model = "SAMSUNG SV0682D", .zones = 2, .avg_spt = 95, .heads = 2, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size = 512, .max_multiple = 32 },
{ .name = "[ATA-5] Western Digital Caviar 102AA", .internal_name = "WD102AA", .model = "WDC WD102AA-00ANA0", .zones = 8, .avg_spt = 95, .heads = 8, .rpm = 5400, .full_stroke_ms = 12, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size = 512, .max_multiple = 32 },
// clang-format on
};
int
hdd_preset_get_num(void)
2024-12-09 01:43:15 +07:00
{
return sizeof(hdd_speed_presets) / sizeof(hdd_preset_t);
}
const char *
hdd_preset_getname(int preset)
{
return hdd_speed_presets[preset].name;
}
const char *
hdd_preset_get_internal_name(int preset)
{
return hdd_speed_presets[preset].internal_name;
}
int
hdd_preset_get_from_internal_name(char *s)
{
int c = 0;
for (int i = 0; i < (sizeof(hdd_speed_presets) / sizeof(hdd_preset_t)); i++) {
2023-08-21 20:22:55 -04:00
if (!strcmp(hdd_speed_presets[c].internal_name, s))
return c;
c++;
}
return 0;
}
void
hdd_preset_apply(int hdd_id)
{
2022-07-19 23:52:18 +02:00
hard_disk_t *hd = &hdd[hdd_id];
2023-05-29 01:30:51 -04:00
double revolution_usec;
double zone_percent;
uint32_t disk_sectors;
uint32_t sectors_per_surface;
uint32_t cylinders;
uint32_t cylinders_per_zone;
uint32_t total_sectors = 0;
uint32_t spt;
uint32_t zone_sectors;
2022-07-19 23:52:18 +02:00
if (hd->speed_preset >= hdd_preset_get_num())
2022-09-18 17:13:50 -04:00
hd->speed_preset = 0;
2023-07-20 18:58:26 -04:00
const hdd_preset_t *preset = &hdd_speed_presets[hd->speed_preset];
2022-07-19 23:52:18 +02:00
hd->cache.num_segments = preset->rcache_num_seg;
hd->cache.segment_size = preset->rcache_seg_size;
hd->max_multiple_block = preset->max_multiple;
if (preset->model)
hd->model = preset->model;
2022-07-19 23:52:18 +02:00
if (!hd->speed_preset)
2022-09-18 17:13:50 -04:00
return;
2022-07-19 23:52:18 +02:00
hd->phy_heads = preset->heads;
2022-09-18 17:13:50 -04:00
hd->rpm = preset->rpm;
2022-09-18 17:13:50 -04:00
revolution_usec = 60.0 / (double) hd->rpm * 1000000.0;
2022-07-19 23:52:18 +02:00
hd->avg_rotation_lat_usec = revolution_usec / 2;
2022-09-18 17:13:50 -04:00
hd->full_stroke_usec = preset->full_stroke_ms * 1000;
hd->head_switch_usec = preset->track_seek_ms * 1000;
hd->cyl_switch_usec = preset->track_seek_ms * 1000;
2022-07-19 23:52:18 +02:00
hd->cache.write_size = 64;
2022-07-19 23:52:18 +02:00
hd->num_zones = preset->zones;
2022-09-18 17:13:50 -04:00
disk_sectors = hd->tracks * hd->hpc * hd->spt;
sectors_per_surface = (uint32_t) ceil((double) disk_sectors / (double) hd->phy_heads);
cylinders = (uint32_t) ceil((double) sectors_per_surface / (double) preset->avg_spt);
hd->phy_cyl = cylinders;
cylinders_per_zone = cylinders / preset->zones;
2023-05-29 01:30:51 -04:00
for (uint32_t i = 0; i < preset->zones; i++) {
2022-09-18 17:13:50 -04:00
zone_percent = i * 100 / (double) preset->zones;
2022-09-18 17:13:50 -04:00
if (i < preset->zones - 1) {
/* Function for realistic zone sector density */
double spt_percent = -0.00341684 * pow(zone_percent, 2) - 0.175811 * zone_percent + 118.48;
spt = (uint32_t) ceil((double) preset->avg_spt * spt_percent / 100);
} else
spt = (uint32_t) ceil((double) (disk_sectors - total_sectors) / (double) (cylinders_per_zone * preset->heads));
2022-09-18 17:13:50 -04:00
zone_sectors = spt * cylinders_per_zone * preset->heads;
total_sectors += zone_sectors;
2022-09-18 17:13:50 -04:00
hd->zones[i].cylinders = cylinders_per_zone;
hd->zones[i].sectors_per_track = spt;
2022-07-19 23:52:18 +02:00
}
2022-07-19 23:52:18 +02:00
hdd_zones_init(hd);
hdd_cache_init(hd);
}