diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index c1bf04618..de0549ca4 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -784,6 +784,9 @@ fdc_sis(fdc_t *fdc) static void fdc_soft_reset(fdc_t *fdc) { + /* Reset boot status to POST on controller soft reset */ + fdd_boot_status_reset(); + if (fdc->power_down) { timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); fdc->interrupt = -5; @@ -2403,6 +2406,9 @@ fdc_reset(void *priv) fdc_t *fdc = (fdc_t *) priv; + /* Reset boot status to POST on controller reset */ + fdd_boot_status_reset(); + default_rwc = (fdc->flags & FDC_FLAG_START_RWC_1) ? 1 : 0; fdc->enable_3f1 = 1; diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index 30be0979a..35676258c 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -101,6 +101,9 @@ typedef struct fdd_pending_op_t { static fdd_pending_op_t fdd_pending[FDD_NUM]; +/* BIOS boot status tracking */ +static bios_boot_status_t bios_boot_status = BIOS_BOOT_POST; + char floppyfns[FDD_NUM][512]; char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; @@ -212,9 +215,9 @@ int fdd_do_log = ENABLE_FDD_LOG; static void fdd_log(const char *fmt, ...) { - va_list ap, ap_copy; + va_list ap; char timebuf[32]; - char fullbuf[1056]; // 32 + 1024 bytes for timestamp + message + char fullbuf[1056]; /* 32 + 1024 bytes for timestamp + message */ if (fdd_do_log) { uint32_t ticks = plat_get_ticks(); @@ -224,27 +227,58 @@ fdd_log(const char *fmt, ...) snprintf(timebuf, sizeof(timebuf), "[%07u.%03u] ", seconds, milliseconds); va_start(ap, fmt); - va_copy(ap_copy, ap); - strcpy(fullbuf, timebuf); - vsnprintf(fullbuf + strlen(timebuf), sizeof(fullbuf) - strlen(timebuf), fmt, ap_copy); - - va_end(ap_copy); + vsnprintf(fullbuf + strlen(timebuf), sizeof(fullbuf) - strlen(timebuf), fmt, ap); va_end(ap); - va_start(ap, fmt); - va_end(ap); - - char *msg = fullbuf; - va_start(ap, fmt); - pclog_ex("%s", (va_list) &msg); - va_end(ap); + pclog("%s", fullbuf); } } #else # define fdd_log(fmt, ...) #endif +/* + * BIOS boot status functions + * + * These functions track whether the system is in BIOS POST (Power-On Self Test) + * or has transitioned to normal operation. The POST state is set on: + * - System hard reset (fdd_reset) + * - FDC soft reset (fdd_boot_status_reset) + * + * POST is considered complete when the first floppy read operation occurs, + * indicating that BIOS has finished POST and is attempting to boot. + */ +bios_boot_status_t +fdd_get_boot_status(void) +{ + return bios_boot_status; +} + +void +fdd_set_boot_status(bios_boot_status_t status) +{ + if (bios_boot_status != status) { + fdd_log("BIOS boot status changed: %s -> %s\n", + bios_boot_status == BIOS_BOOT_POST ? "POST" : "NORMAL", + status == BIOS_BOOT_POST ? "POST" : "NORMAL"); + bios_boot_status = status; + } +} + +void +fdd_boot_status_reset(void) +{ + fdd_log("BIOS boot status reset to POST\n"); + bios_boot_status = BIOS_BOOT_POST; +} + +int +fdd_is_post_complete(void) +{ + return (bios_boot_status == BIOS_BOOT_NORMAL); +} + void fdd_set_audio_profile(int drive, int profile) { @@ -690,6 +724,7 @@ fdd_close(int drive) drives[drive].format = NULL; drives[drive].byteperiod = NULL; drives[drive].stop = NULL; + fdd_seek_in_progress[drive] = 0; d86f_destroy(drive); ui_sb_update_icon_state(SB_FLOPPY | drive, 1); } @@ -778,9 +813,15 @@ fdd_get_bitcell_period(int rate) void fdd_reset(void) { + /* Reset boot status to POST on system reset */ + fdd_boot_status_reset(); + for (uint8_t i = 0; i < FDD_NUM; i++) { drives[i].id = i; timer_add(&(fdd_poll_time[i]), fdd_poll, &drives[i], 0); + + /* Clear any pending seek state */ + fdd_seek_in_progress[i] = 0; } } @@ -789,6 +830,11 @@ fdd_readsector(int drive, int sector, int track, int side, int density, int sect { fdd_log("fdd_readsector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + /* First floppy read operation marks POST as complete */ + if (bios_boot_status == BIOS_BOOT_POST) { + fdd_set_boot_status(BIOS_BOOT_NORMAL); + } + if (fdd_seek_in_progress[drive]) { fdd_log("Seek in progress on drive %d, deferring READ (trk=%d->%d, side=%d, sec=%d)\n", drive, fdd[drive].track, track, side, sector); diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 9bd9aeea6..f5fc493d1 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -38,39 +38,6 @@ static fdd_audio_profile_config_t audio_profiles[FDD_AUDIO_PROFILE_MAX]; static int audio_profile_count = 0; -/* Audio sample structure */ -typedef struct { - char filename[512]; - int16_t *buffer; - int samples; - float volume; -} audio_sample_t; - -typedef struct { - int position; - int active; -} single_step_state_t; - -/* Multi-track seek audio state */ -typedef struct { - int position; - int active; - int duration_samples; - int from_track; - int to_track; - int track_diff; -} multi_seek_state_t; - -/* Drive type specific audio samples */ -typedef struct { - audio_sample_t spindlemotor_start; - audio_sample_t spindlemotor_loop; - audio_sample_t spindlemotor_stop; - /* Individual seek samples for each track count (indexed 0-78 for 1-79 tracks) */ - audio_sample_t seek_up[MAX_SEEK_SAMPLES]; - audio_sample_t seek_down[MAX_SEEK_SAMPLES]; -} drive_audio_samples_t; - /* Dynamic sample storage for each profile */ static drive_audio_samples_t profile_samples[FDD_AUDIO_PROFILE_MAX]; @@ -86,28 +53,115 @@ static multi_seek_state_t seek_state[FDD_NUM][MAX_CONCURRENT_SEEKS] = {}; extern uint64_t motoron[FDD_NUM]; extern char exe_path[2048]; -extern int fdd_get_audio_profile(int drive); - /* Forward declaration */ static int16_t *load_wav(const char *filename, int *sample_count); -# ifdef ENABLE_FDD_LOG -int fdc_do_log = ENABLE_FDD_LOG; +extern uint8_t *rom; +extern uint32_t biosmask; +extern uint32_t biosaddr; +typedef enum { + BIOS_VENDOR_UNKNOWN = 0, + BIOS_VENDOR_AMI, + BIOS_VENDOR_AWARD, + BIOS_VENDOR_PHOENIX, + BIOS_VENDOR_IBM, + BIOS_VENDOR_COMPAQ, + BIOS_VENDOR_OTHER +} bios_vendor_t; + +#ifdef ENABLE_FDD_LOG +int fdd_audio_do_log = ENABLE_FDD_LOG; static void fdd_log(const char *fmt, ...) { va_list ap; - if (fdc_do_log) { + if (fdd_audio_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } -# else +#else # define fdd_log(fmt, ...) -# endif +#endif + +/* Detect BIOS vendor by scanning ROM for signature strings */ +static bios_vendor_t +fdd_audio_detect_bios_vendor(void) +{ + if (!rom || biosmask == 0) + return BIOS_VENDOR_UNKNOWN; + + /* Search for BIOS vendor strings in ROM */ + for (uint32_t i = 0; i < (biosmask + 1); i++) { + /* AMI BIOS signatures */ + if ((i + 7) < (biosmask + 1)) { + if (memcmp(&rom[i], "AMIBIOS", 7) == 0) { + fdd_log("FDD Audio: Detected AMI BIOS\n"); + return BIOS_VENDOR_AMI; + } + if (memcmp(&rom[i], "American Megatrends", 19) == 0) { + fdd_log("FDD Audio: Detected AMI BIOS (American Megatrends)\n"); + return BIOS_VENDOR_AMI; + } + } + + /* Award BIOS signatures */ + if ((i + 5) < (biosmask + 1)) { + if (memcmp(&rom[i], "Award", 5) == 0) { + fdd_log("FDD Audio: Detected Award BIOS\n"); + return BIOS_VENDOR_AWARD; + } + } + + /* Phoenix BIOS signatures */ + if ((i + 7) < (biosmask + 1)) { + if (memcmp(&rom[i], "Phoenix", 7) == 0) { + fdd_log("FDD Audio: Detected Phoenix BIOS\n"); + return BIOS_VENDOR_PHOENIX; + } + } + + /* IBM BIOS signatures */ + if ((i + 3) < (biosmask + 1)) { + if (memcmp(&rom[i], "IBM", 3) == 0 && (i + 10) < (biosmask + 1)) { + if (memcmp(&rom[i], "IBM CORP", 8) == 0) { + fdd_log("FDD Audio: Detected IBM BIOS\n"); + return BIOS_VENDOR_IBM; + } + } + } + + /* Compaq BIOS signatures */ + if ((i + 6) < (biosmask + 1)) { + if (memcmp(&rom[i], "COMPAQ", 6) == 0) { + fdd_log("FDD Audio: Detected Compaq BIOS\n"); + return BIOS_VENDOR_COMPAQ; + } + } + } + + fdd_log("FDD Audio: BIOS vendor unknown\n"); + return BIOS_VENDOR_UNKNOWN; +} + +/* Determine if this BIOS uses POST-mode FDC seeks */ +static int +fdd_audio_get_bios_vendor(void) +{ + static bios_vendor_t detected_vendor = BIOS_VENDOR_UNKNOWN; + static int detection_done = 0; + + /* Only detect once */ + if (!detection_done) { + detected_vendor = fdd_audio_detect_bios_vendor(); + detection_done = 1; + } + + return detected_vendor; +} /* Logging function for audio profile parameters */ static void @@ -271,9 +325,63 @@ fdd_audio_load_profiles(void) snprintf(key, sizeof(key), "seek_down_%dtrack_volume", track_count); profile->seek_down[track_count - 1].volume = ini_section_get_double(section, key, 1.0); + /* POST mode seek down samples */ + snprintf(key, sizeof(key), "post_seek_down_%dtrack_file", track_count); + filename = ini_section_get_string(section, key, ""); + strncpy(profile->post_seek_down[track_count - 1].filename, filename, + sizeof(profile->post_seek_down[track_count - 1].filename) - 1); + profile->post_seek_down[track_count - 1].filename[sizeof(profile->post_seek_down[track_count - 1].filename) - 1] = '\0'; + + snprintf(key, sizeof(key), "post_seek_down_%dtrack_volume", track_count); + profile->post_seek_down[track_count - 1].volume = ini_section_get_double(section, key, 1.0); + + /* BIOS vendor-specific POST mode seek samples */ + static const char *bios_prefixes[] = { + NULL, /* BIOS_VENDOR_UNKNOWN */ + "amibios", /* BIOS_VENDOR_AMI */ + "award", /* BIOS_VENDOR_AWARD */ + "phoenix", /* BIOS_VENDOR_PHOENIX */ + "ibm", /* BIOS_VENDOR_IBM */ + "compaq", /* BIOS_VENDOR_COMPAQ */ + NULL /* BIOS_VENDOR_OTHER */ + }; + + for (int vendor = 1; vendor < BIOS_VENDOR_COUNT; vendor++) { + if (!bios_prefixes[vendor]) + continue; + + /* BIOS-specific POST mode seek up samples */ + snprintf(key, sizeof(key), "%s_post_seek_up_%dtrack_file", bios_prefixes[vendor], track_count); + filename = ini_section_get_string(section, key, ""); + strncpy(profile->bios_post_seek_up[vendor][track_count - 1].filename, filename, + sizeof(profile->bios_post_seek_up[vendor][track_count - 1].filename) - 1); + profile->bios_post_seek_up[vendor][track_count - 1].filename[sizeof(profile->bios_post_seek_up[vendor][track_count - 1].filename) - 1] = '\0'; + + snprintf(key, sizeof(key), "%s_post_seek_up_%dtrack_volume", bios_prefixes[vendor], track_count); + profile->bios_post_seek_up[vendor][track_count - 1].volume = ini_section_get_double(section, key, 1.0); + + /* BIOS-specific POST mode seek down samples */ + snprintf(key, sizeof(key), "%s_post_seek_down_%dtrack_file", bios_prefixes[vendor], track_count); + filename = ini_section_get_string(section, key, ""); + strncpy(profile->bios_post_seek_down[vendor][track_count - 1].filename, filename, + sizeof(profile->bios_post_seek_down[vendor][track_count - 1].filename) - 1); + profile->bios_post_seek_down[vendor][track_count - 1].filename[sizeof(profile->bios_post_seek_down[vendor][track_count - 1].filename) - 1] = '\0'; + + snprintf(key, sizeof(key), "%s_post_seek_down_%dtrack_volume", bios_prefixes[vendor], track_count); + profile->bios_post_seek_down[vendor][track_count - 1].volume = ini_section_get_double(section, key, 1.0); + + /* BIOS-specific POST mode seek time in milliseconds */ + snprintf(key, sizeof(key), "%s_post_seek_%dtrack_time_ms", bios_prefixes[vendor], track_count); + profile->bios_post_seek_time_ms[vendor][track_count - 1] = ini_section_get_double(section, key, 0.0); + } + /* Seek time in milliseconds - used for FDC timing, not sample playback */ snprintf(key, sizeof(key), "seek_%dtrack_time_ms", track_count); profile->seek_time_ms[track_count - 1] = ini_section_get_double(section, key, 6.0 * track_count); + + /* POST mode seek time in milliseconds */ + snprintf(key, sizeof(key), "post_seek_%dtrack_time_ms", track_count); + profile->post_seek_time_ms[track_count - 1] = ini_section_get_double(section, key, 0.0); } /* Load timing configurations */ @@ -362,7 +470,7 @@ load_profile_samples(int profile_id) &samples->seek_up[idx].samples); if (samples->seek_up[idx].buffer) { fdd_log(" Loaded seek_up[%d]: %s (%d samples, volume %.2f)\n", - track_count, config->seek_up[idx].filename, + idx, config->seek_up[idx].filename, samples->seek_up[idx].samples, config->seek_up[idx].volume); } } @@ -375,11 +483,75 @@ load_profile_samples(int profile_id) &samples->seek_down[idx].samples); if (samples->seek_down[idx].buffer) { fdd_log(" Loaded seek_down[%d]: %s (%d samples, volume %.2f)\n", - track_count, config->seek_down[idx].filename, + idx, config->seek_down[idx].filename, samples->seek_down[idx].samples, config->seek_down[idx].volume); } } - } + + /* Load POST mode seek samples if configured */ + if (config->post_seek_up[idx].filename[0]) { + if (samples->post_seek_up[idx].buffer == NULL) { + strcpy(samples->post_seek_up[idx].filename, config->post_seek_up[idx].filename); + samples->post_seek_up[idx].volume = config->post_seek_up[idx].volume; + samples->post_seek_up[idx].buffer = load_wav(config->post_seek_up[idx].filename, + &samples->post_seek_up[idx].samples); + if (samples->post_seek_up[idx].buffer) { + fdd_log(" Loaded POST seek_up[%d] (%d-track): %s (%d samples, volume %.2f)\n", + idx, track_count, config->post_seek_up[idx].filename, + samples->post_seek_up[idx].samples, config->post_seek_up[idx].volume); + } + } + } + + if (config->post_seek_down[idx].filename[0]) { + if (samples->post_seek_down[idx].buffer == NULL) { + strcpy(samples->post_seek_down[idx].filename, config->post_seek_down[idx].filename); + samples->post_seek_down[idx].volume = config->post_seek_down[idx].volume; + samples->post_seek_down[idx].buffer = load_wav(config->post_seek_down[idx].filename, + &samples->post_seek_down[idx].samples); + if (samples->post_seek_down[idx].buffer) { + fdd_log(" Loaded POST seek_down[%d] (%d-track): %s (%d samples, volume %.2f)\n", + idx, track_count, config->post_seek_down[idx].filename, + samples->post_seek_down[idx].samples, config->post_seek_down[idx].volume); + } + } + } + + /* Load BIOS vendor-specific POST mode seek samples if configured */ + static const char *bios_names[] = { + "UNKNOWN", "AMI", "AWARD", "PHOENIX", "IBM", "COMPAQ", "OTHER" + }; + + for (int vendor = 1; vendor < BIOS_VENDOR_COUNT; vendor++) { + if (config->bios_post_seek_up[vendor][idx].filename[0]) { + if (samples->bios_post_seek_up[vendor][idx].buffer == NULL) { + strcpy(samples->bios_post_seek_up[vendor][idx].filename, config->bios_post_seek_up[vendor][idx].filename); + samples->bios_post_seek_up[vendor][idx].volume = config->bios_post_seek_up[vendor][idx].volume; + samples->bios_post_seek_up[vendor][idx].buffer = load_wav(config->bios_post_seek_up[vendor][idx].filename, + &samples->bios_post_seek_up[vendor][idx].samples); + if (samples->bios_post_seek_up[vendor][idx].buffer) { + fdd_log(" Loaded %s POST seek_up[%d] (%d-track): %s (%d samples, volume %.2f)\n", + bios_names[vendor], idx, track_count, config->bios_post_seek_up[vendor][idx].filename, + samples->bios_post_seek_up[vendor][idx].samples, config->bios_post_seek_up[vendor][idx].volume); + } + } + } + + if (config->bios_post_seek_down[vendor][idx].filename[0]) { + if (samples->bios_post_seek_down[vendor][idx].buffer == NULL) { + strcpy(samples->bios_post_seek_down[vendor][idx].filename, config->bios_post_seek_down[vendor][idx].filename); + samples->bios_post_seek_down[vendor][idx].volume = config->bios_post_seek_down[vendor][idx].volume; + samples->bios_post_seek_down[vendor][idx].buffer = load_wav(config->bios_post_seek_down[vendor][idx].filename, + &samples->bios_post_seek_down[vendor][idx].samples); + if (samples->bios_post_seek_down[vendor][idx].buffer) { + fdd_log(" Loaded %s POST seek_down[%d] (%d-track): %s (%d samples, volume %.2f)\n", + bios_names[vendor], idx, track_count, config->bios_post_seek_down[vendor][idx].filename, + samples->bios_post_seek_down[vendor][idx].samples, config->bios_post_seek_down[vendor][idx].volume); + } + } + } + } + } } static drive_audio_samples_t * @@ -461,6 +633,21 @@ fdd_audio_get_seek_time(int drive, int track_count, int is_seek_down) /* Return configured seek time in microseconds */ if (track_count > 0 && track_count <= MAX_SEEK_SAMPLES) { + /* In POST mode, check for BIOS-specific timing first */ + if (fdd_get_boot_status() == BIOS_BOOT_POST) { + int bios_vendor = fdd_audio_get_bios_vendor(); + + /* Check BIOS vendor-specific timing first */ + if (bios_vendor > 0 && bios_vendor < BIOS_VENDOR_COUNT && + profile->bios_post_seek_time_ms[bios_vendor][track_count - 1] > 0.0) { + return profile->bios_post_seek_time_ms[bios_vendor][track_count - 1] * 1000.0; + } + + /* Fall back to generic POST timing */ + if (profile->post_seek_time_ms[track_count - 1] > 0.0) { + return profile->post_seek_time_ms[track_count - 1] * 1000.0; + } + } return profile->seek_time_ms[track_count - 1] * 1000.0; } @@ -489,6 +676,7 @@ fdd_audio_init(void) seek_state[i][j].from_track = -1; seek_state[i][j].to_track = -1; seek_state[i][j].track_diff = 0; + seek_state[i][j].sample_to_play = NULL; } } @@ -546,6 +734,30 @@ fdd_audio_close(void) samples->seek_down[track_count].buffer = NULL; samples->seek_down[track_count].samples = 0; } + if (samples->post_seek_up[track_count].buffer) { + free(samples->post_seek_up[track_count].buffer); + samples->post_seek_up[track_count].buffer = NULL; + samples->post_seek_up[track_count].samples = 0; + } + if (samples->post_seek_down[track_count].buffer) { + free(samples->post_seek_down[track_count].buffer); + samples->post_seek_down[track_count].buffer = NULL; + samples->post_seek_down[track_count].samples = 0; + } + + /* Free BIOS vendor-specific POST seek samples */ + for (int vendor = 0; vendor < BIOS_VENDOR_COUNT; vendor++) { + if (samples->bios_post_seek_up[vendor][track_count].buffer) { + free(samples->bios_post_seek_up[vendor][track_count].buffer); + samples->bios_post_seek_up[vendor][track_count].buffer = NULL; + samples->bios_post_seek_up[vendor][track_count].samples = 0; + } + if (samples->bios_post_seek_down[vendor][track_count].buffer) { + free(samples->bios_post_seek_down[vendor][track_count].buffer); + samples->bios_post_seek_down[vendor][track_count].buffer = NULL; + samples->bios_post_seek_down[vendor][track_count].samples = 0; + } + } } } @@ -629,16 +841,81 @@ fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track) track_diff = max_seek_tracks; } - /* Get the appropriate seek sample */ - int idx = track_diff - 1; - audio_sample_t *sample_to_use = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + int boot_status = fdd_get_boot_status(); + int bios_vendor = fdd_audio_get_bios_vendor(); + int idx = track_diff - 1; + int real_track_diff = to_track - from_track; + audio_sample_t *sample_to_use = NULL; + + if (boot_status == BIOS_BOOT_POST) { + if (bios_vendor == BIOS_VENDOR_AMI) { + /* AMI BIOS POST mode: use AMI-specific samples if available */ + + /* AMI BIOS quirk: for single-track seeks down (except 10->9), do not play audio */ + if (real_track_diff == -1 && (from_track != 10 || to_track != 9)) { + fdd_log("FDD Audio Drive %d: AMI BIOS quirk: for single-track seeks down (except 10->9), do not play audio\n", drive); + return; + } + + /* For 10->9 seek, use the 1-track sample (which should be the 10-0 sound) */ + sample_to_use = is_seek_down ? &samples->bios_post_seek_down[bios_vendor][idx] : &samples->bios_post_seek_up[bios_vendor][idx]; + + if (sample_to_use->buffer && sample_to_use->samples > 0) { + fdd_log("FDD Audio Drive %d: Using AMI BIOS POST mode seek sample (idx=%d, %s)\n", + drive, idx, is_seek_down ? "DOWN" : "UP"); + } else { + /* Fall back to generic POST sample */ + sample_to_use = is_seek_down ? &samples->post_seek_down[idx] : &samples->post_seek_up[idx]; + if (sample_to_use->buffer && sample_to_use->samples > 0) { + fdd_log("FDD Audio Drive %d: AMI BIOS sample not available, using generic POST sample (idx=%d, %s)\n", + drive, idx, is_seek_down ? "DOWN" : "UP"); + } else { + /* Fall back to normal sample */ + fdd_log("FDD Audio Drive %d: POST sample not available, using normal sample\n", drive); + sample_to_use = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + } + } + } else if (bios_vendor > 0 && bios_vendor < BIOS_VENDOR_COUNT) { + /* Other known BIOS vendors: try vendor-specific samples first */ + sample_to_use = is_seek_down ? &samples->bios_post_seek_down[bios_vendor][idx] : &samples->bios_post_seek_up[bios_vendor][idx]; + + if (sample_to_use->buffer && sample_to_use->samples > 0) { + fdd_log("FDD Audio Drive %d: Using BIOS vendor %d POST mode seek sample (idx=%d, %s)\n", + drive, bios_vendor, idx, is_seek_down ? "DOWN" : "UP"); + } else { + /* Fall back to generic POST sample */ + sample_to_use = is_seek_down ? &samples->post_seek_down[idx] : &samples->post_seek_up[idx]; + if (sample_to_use->buffer && sample_to_use->samples > 0) { + fdd_log("FDD Audio Drive %d: BIOS-specific sample not available, using generic POST sample (idx=%d, %s)\n", + drive, idx, is_seek_down ? "DOWN" : "UP"); + } else { + /* Fall back to normal sample */ + fdd_log("FDD Audio Drive %d: POST sample not available, using normal sample\n", drive); + sample_to_use = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + } + } + } else { + /* Unknown BIOS vendor POST mode */ + sample_to_use = is_seek_down ? &samples->post_seek_down[idx] : &samples->post_seek_up[idx]; + if (!sample_to_use->buffer || sample_to_use->samples == 0) { + fdd_log("FDD Audio Drive %d: POST sample not available, using normal sample\n", drive); + sample_to_use = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + } else { + fdd_log("FDD Audio Drive %d: Using POST mode seek sample (idx=%d, %s)\n", + drive, idx, is_seek_down ? "DOWN" : "UP"); + } + } + } else { + /* Use normal samples */ + sample_to_use = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + } /* Only proceed if we have the appropriate sample */ if (!sample_to_use || !sample_to_use->buffer || sample_to_use->samples == 0) return; - fdd_log("FDD Audio Drive %d: Multi-track seek %d -> %d (%d tracks, %s)\n", - drive, from_track, to_track, track_diff, is_seek_down ? "DOWN" : "UP"); + fdd_log("FDD Audio Drive %d: Multi-track seek %d -> %d (%d tracks, %s, POST=%d)\n", + drive, from_track, to_track, track_diff, is_seek_down ? "DOWN" : "UP", boot_status); /* Find an available seek slot */ int slot = -1; @@ -662,6 +939,7 @@ fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track) seek_state[drive][slot].from_track = from_track; seek_state[drive][slot].to_track = to_track; seek_state[drive][slot].track_diff = track_diff; + seek_state[drive][slot].sample_to_play = sample_to_use; fdd_log("FDD Audio Drive %d: Started seek in slot %d, duration %d samples\n", drive, slot, sample_to_use->samples); @@ -867,30 +1145,26 @@ fdd_audio_callback(int16_t *buffer, int length) if (!seek_state[drive][slot].active) continue; - int track_diff = seek_state[drive][slot].track_diff; - if (track_diff > 0 && track_diff <= MAX_SEEK_SAMPLES) { - int idx = track_diff - 1; - int is_seek_down = (seek_state[drive][slot].to_track < seek_state[drive][slot].from_track); - audio_sample_t *seek_sample = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + audio_sample_t *seek_sample = seek_state[drive][slot].sample_to_play; - if (seek_sample && seek_sample->buffer && seek_state[drive][slot].position < seek_sample->samples) { - /* Mix seek sound with existing audio */ - float seek_left = (float) seek_sample->buffer[seek_state[drive][slot].position * 2] / 131072.0f * seek_sample->volume; - float seek_right = (float) seek_sample->buffer[seek_state[drive][slot].position * 2 + 1] / 131072.0f * seek_sample->volume; + if (seek_sample && seek_sample->buffer && seek_state[drive][slot].position < seek_sample->samples) { + /* Mix seek sound with existing audio */ + float seek_left = (float) seek_sample->buffer[seek_state[drive][slot].position * 2] / 131072.0f * seek_sample->volume; + float seek_right = (float) seek_sample->buffer[seek_state[drive][slot].position * 2 + 1] / 131072.0f * seek_sample->volume; - left_sample += seek_left; - right_sample += seek_right; + left_sample += seek_left; + right_sample += seek_right; - seek_state[drive][slot].position++; - } else { - /* Seek sound finished */ - seek_state[drive][slot].active = 0; - seek_state[drive][slot].position = 0; - seek_state[drive][slot].duration_samples = 0; - seek_state[drive][slot].from_track = -1; - seek_state[drive][slot].to_track = -1; - seek_state[drive][slot].track_diff = 0; - } + seek_state[drive][slot].position++; + } else { + /* Seek sound finished */ + seek_state[drive][slot].active = 0; + seek_state[drive][slot].position = 0; + seek_state[drive][slot].duration_samples = 0; + seek_state[drive][slot].from_track = -1; + seek_state[drive][slot].to_track = -1; + seek_state[drive][slot].track_diff = 0; + seek_state[drive][slot].sample_to_play = NULL; } } @@ -983,30 +1257,26 @@ fdd_audio_callback(int16_t *buffer, int length) if (!seek_state[drive][slot].active) continue; - int track_diff = seek_state[drive][slot].track_diff; - if (track_diff > 0 && track_diff <= MAX_SEEK_SAMPLES) { - int idx = track_diff - 1; - int is_seek_down = (seek_state[drive][slot].to_track < seek_state[drive][slot].from_track); - audio_sample_t *seek_sample = is_seek_down ? &samples->seek_down[idx] : &samples->seek_up[idx]; + audio_sample_t *seek_sample = seek_state[drive][slot].sample_to_play; - if (seek_sample && seek_sample->buffer && seek_state[drive][slot].position < seek_sample->samples) { - /* Mix seek sound with existing audio */ - int16_t seek_left = (int16_t) ((float) seek_sample->buffer[seek_state[drive][slot].position * 2] / 4.0f * seek_sample->volume); - int16_t seek_right = (int16_t) ((float) seek_sample->buffer[seek_state[drive][slot].position * 2 + 1] / 4.0f * seek_sample->volume); + if (seek_sample && seek_sample->buffer && seek_state[drive][slot].position < seek_sample->samples) { + /* Mix seek sound with existing audio */ + int16_t seek_left = (int16_t) ((float) seek_sample->buffer[seek_state[drive][slot].position * 2] / 4.0f * seek_sample->volume); + int16_t seek_right = (int16_t) ((float) seek_sample->buffer[seek_state[drive][slot].position * 2 + 1] / 4.0f * seek_sample->volume); - left_sample += seek_left; - right_sample += seek_right; + left_sample += seek_left; + right_sample += seek_right; - seek_state[drive][slot].position++; - } else { - /* Seek sound finished */ - seek_state[drive][slot].active = 0; - seek_state[drive][slot].position = 0; - seek_state[drive][slot].duration_samples = 0; - seek_state[drive][slot].from_track = -1; - seek_state[drive][slot].to_track = -1; - seek_state[drive][slot].track_diff = 0; - } + seek_state[drive][slot].position++; + } else { + /* Seek sound finished */ + seek_state[drive][slot].active = 0; + seek_state[drive][slot].position = 0; + seek_state[drive][slot].duration_samples = 0; + seek_state[drive][slot].from_track = -1; + seek_state[drive][slot].to_track = -1; + seek_state[drive][slot].track_diff = 0; + seek_state[drive][slot].sample_to_play = NULL; } } diff --git a/src/include/86box/fdd.h b/src/include/86box/fdd.h index a173279a4..ef2a33489 100644 --- a/src/include/86box/fdd.h +++ b/src/include/86box/fdd.h @@ -26,6 +26,12 @@ #define SEEK_RECALIBRATE -999 #define DEFAULT_SEEK_TIME_MS 10.0 +/* BIOS boot status - used to detect POST vs normal operation */ +typedef enum { + BIOS_BOOT_POST = 0, /* System is in POST (Power-On Self Test) */ + BIOS_BOOT_NORMAL = 1 /* POST complete, normal operation */ +} bios_boot_status_t; + #ifdef __cplusplus extern "C" { #endif @@ -118,6 +124,12 @@ extern int fdd_hole(int drive); extern void fdd_stop(int drive); extern void fdd_do_writeback(int drive); +/* BIOS boot status functions */ +extern bios_boot_status_t fdd_get_boot_status(void); +extern void fdd_set_boot_status(bios_boot_status_t status); +extern void fdd_boot_status_reset(void); +extern int fdd_is_post_complete(void); + extern int motorspin; extern uint64_t motoron[FDD_NUM]; diff --git a/src/include/86box/fdd_audio.h b/src/include/86box/fdd_audio.h index 1d1b52d14..433aeaff8 100644 --- a/src/include/86box/fdd_audio.h +++ b/src/include/86box/fdd_audio.h @@ -16,6 +16,7 @@ #define EMU_FDD_AUDIO_H #include +#include <86box/fdd.h> #ifdef __cplusplus extern "C" { @@ -29,6 +30,9 @@ extern "C" { /* Maximum number of simultaneous seek sounds per drive */ #define MAX_CONCURRENT_SEEKS 8 +/* Number of BIOS vendors (for BIOS-specific samples) */ +#define BIOS_VENDOR_COUNT 7 + /* Audio sample configuration structure */ typedef struct { char filename[512]; @@ -45,7 +49,15 @@ typedef struct { audio_sample_config_t spindlemotor_stop; audio_sample_config_t seek_up[MAX_SEEK_SAMPLES]; audio_sample_config_t seek_down[MAX_SEEK_SAMPLES]; - double seek_time_ms[MAX_SEEK_SAMPLES]; /* Seek time in milliseconds for each track count */ + audio_sample_config_t post_seek_up[MAX_SEEK_SAMPLES]; + audio_sample_config_t post_seek_down[MAX_SEEK_SAMPLES]; + /* BIOS vendor-specific POST seek samples [vendor][track] */ + audio_sample_config_t bios_post_seek_up[BIOS_VENDOR_COUNT][MAX_SEEK_SAMPLES]; + audio_sample_config_t bios_post_seek_down[BIOS_VENDOR_COUNT][MAX_SEEK_SAMPLES]; + double seek_time_ms[MAX_SEEK_SAMPLES]; + double post_seek_time_ms[MAX_SEEK_SAMPLES]; + /* BIOS vendor-specific POST seek times [vendor][track] */ + double bios_post_seek_time_ms[BIOS_VENDOR_COUNT][MAX_SEEK_SAMPLES]; int total_tracks; /* 40 or 80 */ } fdd_audio_profile_config_t; @@ -76,6 +88,45 @@ typedef struct { uint32_t data_size; } wav_header_t; +/* Audio sample structure */ +typedef struct { + char filename[512]; + int16_t *buffer; + int samples; + float volume; +} audio_sample_t; + +typedef struct { + int position; + int active; +} single_step_state_t; + +/* Multi-track seek audio state */ +typedef struct { + int position; + int active; + int duration_samples; + int from_track; + int to_track; + int track_diff; + audio_sample_t *sample_to_play; +} multi_seek_state_t; + +/* Drive type specific audio samples */ +typedef struct { + audio_sample_t spindlemotor_start; + audio_sample_t spindlemotor_loop; + audio_sample_t spindlemotor_stop; + /* Individual seek samples for each track count (indexed 0-78 for 1-79 tracks) */ + audio_sample_t seek_up[MAX_SEEK_SAMPLES]; + audio_sample_t seek_down[MAX_SEEK_SAMPLES]; + audio_sample_t post_seek_up[MAX_SEEK_SAMPLES]; + audio_sample_t post_seek_down[MAX_SEEK_SAMPLES]; + /* BIOS vendor-specific POST seek samples [vendor][track] */ + audio_sample_t bios_post_seek_up[BIOS_VENDOR_COUNT][MAX_SEEK_SAMPLES]; + audio_sample_t bios_post_seek_down[BIOS_VENDOR_COUNT][MAX_SEEK_SAMPLES]; +} drive_audio_samples_t; + /* Fade duration: 75ms at 48kHz = 3600 samples */ #define FADE_DURATION_MS 75 #define FADE_SAMPLES (48000 * FADE_DURATION_MS / 1000) @@ -89,6 +140,8 @@ extern const char* fdd_audio_get_profile_internal_name(int id); extern int fdd_audio_get_profile_by_internal_name(const char *internal_name); extern double fdd_audio_get_seek_time(int drive, int track_count, int is_seek_down); extern void load_profile_samples(int profile_id); +extern int fdd_get_audio_profile(int drive); +extern bios_boot_status_t fdd_get_boot_status(void); #else