From 836f855683bc822e113ab1b715d5579dc74c5a6a Mon Sep 17 00:00:00 2001 From: Toni Riikonen Date: Sat, 27 Sep 2025 11:51:38 +0300 Subject: [PATCH 01/11] Fixes #6220 floppy disk issues with OS/2 and NT 3.1 systems (#6232) * Initial spindle emulation working for windows atleast * Spingle motor spin-up, spin-down implemented with smooth transitions to motor-on loop. * Moved fdd audio emulation to a separate file * Multiple drives sound emulation * Single sector movement sound emulations implemented * Rename project to Immersive86Box and update details Updated README to reflect the new project name and added details about the Immersive86Box features and future plans. * Revise contribution guidelines in CONTRIBUTING.md * Update vulnerability reporting instructions * System fan-sound next feature after basic fdd sound emulation is ready * v0.5 multitrack audio seek sfx * Removed unnecessary stuff * no .vs folder for git * Added currently used fdd sound effects and readme.txt for source of the files and intallation instructions * Add audio emulation installation instructions Added instructions for audio emulation installation. * Code and audio samples merged * Simplify audio emulation installation instructions * FDC seeking fixed, not instant anymore drive is set to busy during the operation and when it's finished at call fdc to set the appropriate fdc flags. Also added time logic to fdd to calculate seek duration and a callback function for it. * FDD sound samples volume control * Menu options to enable / disable fdd sound for all drives. DISABLE_FDD_AUDIO definition added, to disable the feature via cmake/build. * Revert readme etc. changes * Revert "Revise contribution guidelines in CONTRIBUTING.md" This reverts commit 98a0478225bbf8bf0fb0e7edfd5c00636ecc0eed. * Revert "Update vulnerability reporting instructions" This reverts commit 7d32cb659b018b26bdaa4a1e06ee9c3d09278aac. * Fixed merge issue * Removed excess files * Fixed PCJr seeking not to break the FDC implementation. Now seeking will take the "correct" amount of time for each system and the seek time is based on the track count. E.g. 40 track FDD system causes 40 track seek time to be 80/40 * 6ms * 40 tracks + 50ms = 480ms + 50ms -> 530ms. 80 track system full seek is 80/80 * 6ms * 80 + 50ms = 530ms, 40 track seek would take 240 + 50 = 290ms. * Fixed PS/1, PS/2 and PS/55 FDD issues. * FDD_AUDIO: Updating samples looked in executablePath/samples and if now found there, looks in the executable directory * Updated installation instructions * Removed samples path strcat use * fdd_audio 5.25 samples and support added * FDD audio timing/volume tunings * Timing fixes for authentity and special longer timings for PCJr * Fixed second drive motor keeps running when first drive is only accessed. * Fixed PCJr random failure issue, timings * CodeQL fix for load_wav-function. Check the filename to be proper filename * Revert "Fixed second drive motor keeps running when first drive is only accessed." This reverts commit 307b173ae7d40c9efafed8d432e01cce9808b111. * Teac 5.25" drive samples added. Added per drive audio selection to FDD settings. * Fixes merge problem * Fixes #6220: OS/2 Warp 3.0 install disk issue and NT 3.1 floppy disk issues. Changed the fdc->stat bit to 0x10 which states fdc busy. Previous implementation with seek time 0, set the ready flag 0x80 immediadely - which was correct for that time, but now as the drive is busy during seek, the value should 0x10. Implemented a backup for fdd commands during fdd seek to be processes after the seek is completed. --------- Co-authored-by: Toni Riikonen --- src/floppy/fdc.c | 1522 ++++++++++++++++++++-------------------- src/floppy/fdd.c | 151 +++- src/floppy/fdd_audio.c | 1 + 3 files changed, 914 insertions(+), 760 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index bcf4a1299..212bf9ce0 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -184,7 +184,7 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x80; + fdc->stat = 0x10; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->head = 0; @@ -470,9 +470,9 @@ uint8_t fdc_get_shadow(fdc_t *fdc) { uint8_t ret = (fdc->rate & 0x03) | - ((fdc->pretrk & 0x07) << 2) | - (fdc->power_down ? 0x40 : 0x00) | - ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); + ((fdc->pretrk & 0x07) << 2) | + (fdc->power_down ? 0x40 : 0x00) | + ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); return ret; } @@ -532,36 +532,36 @@ fdc_update_rate(fdc_t *fdc, int drive) fdc->enh_mode && !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 500; else if ((fdc->rwc[drive] == 3) && fdc->enh_mode && - !(fdc->flags & FDC_FLAG_SMC661)) + !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 250; else switch (fdc->rate) { - default: - break; - case 0: /*High density*/ - fdc->bit_rate = 500; - break; - case 1: /*Double density (360 rpm)*/ - switch (fdc->drvrate[drive]) { - default: - break; - case 0: - fdc->bit_rate = 300; - break; - case 1: - fdc->bit_rate = 500; - break; - case 2: - fdc->bit_rate = 2000; - break; - } - break; - case 2: /*Double density*/ - fdc->bit_rate = 250; - break; - case 3: /*Extended density*/ - fdc->bit_rate = 1000; - break; - } + default: + break; + case 0: /*High density*/ + fdc->bit_rate = 500; + break; + case 1: /*Double density (360 rpm)*/ + switch (fdc->drvrate[drive]) { + default: + break; + case 0: + fdc->bit_rate = 300; + break; + case 1: + fdc->bit_rate = 500; + break; + case 2: + fdc->bit_rate = 2000; + break; + } + break; + case 2: /*Double density*/ + fdc->bit_rate = 250; + break; + case 3: /*Extended density*/ + fdc->bit_rate = 1000; + break; + } fdc->bitcell_period = (1000000 / fdc->bit_rate) * 2; /*Bitcell period in ns*/ } @@ -695,7 +695,7 @@ void fdc_seek(fdc_t *fdc, int drive, int params) { fdd_seek(real_drive(fdc, drive), params); - fdc->stat |= (1 << fdc->drive); + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << fdc->drive); } static void @@ -719,8 +719,10 @@ fdc_io_command_phase1(fdc_t *fdc, int out) fdc->dtl = fdc->params[7]; fdc->rw_track = fdc->params[1]; + int implied_seek = 0; if (fdc->config & 0x40) { if (fdc->rw_track != fdc->pcn[fdc->params[0] & 3]) { + implied_seek = 1; fdc_seek(fdc, fdc->drive, ((int) fdc->rw_track) - ((int) fdc->pcn[fdc->params[0] & 3])); fdc->pcn[fdc->params[0] & 3] = fdc->rw_track; } @@ -730,6 +732,12 @@ fdc_io_command_phase1(fdc_t *fdc, int out) ui_sb_update_icon_write(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); else ui_sb_update_icon(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); + + if (implied_seek) { + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << real_drive(fdc, fdc->drive)); /* CB=1, per-drive busy */ + return; + } + fdc->stat = out ? 0x10 : 0x50; if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) { fdc->stat |= 0x20; @@ -791,7 +799,7 @@ fdc_soft_reset(fdc_t *fdc) } fdc_ctrl_reset(fdc); - } + } } static void @@ -806,565 +814,566 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) switch (addr & 7) { - case 0: - return; - case 1: - return; - case 2: /*DOR*/ - if (fdc->flags & FDC_FLAG_PCJR) { - if ((fdc->dor & 0x40) && !(val & 0x40)) { - timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); - fdc->watchdog_count = 1000; - picintc(1 << fdc->irq); - } - if ((val & 0x80) && !(fdc->dor & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -1; - ui_sb_update_icon(SB_FLOPPY | 0, 0); - ui_sb_update_icon_write(SB_FLOPPY | 0, 0); - fdc_ctrl_reset(fdc); - } - if (!fdd_get_flags(0)) - val &= 0xfe; - fdd_set_motor_enable(0, val & 0x01); - fdc->st0 &= ~0x07; - fdc->st0 |= (fdd_get_head(0) ? 4 : 0); - } else { - /* - Writing this bit to logic "1" will enable the DRQ, - nDACK, TC and FINTR outputs. This bit being a - logic "0" will disable the nDACK and TC inputs, and - hold the DRQ and FINTR outputs in a high - impedance state. - */ - if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { - fdc->tc = 1; - fdc->fintr = 0; - picintc(1 << fdc->irq); - } - if (!(val & 4)) { - fdd_stop(real_drive(fdc, val & 3)); - fdc->stat = 0x00; - fdc->pnum = fdc->ptot = 0; - } - if ((val & 4) && !(fdc->dor & 4)) - fdc_soft_reset(fdc); - /* We can now simplify this since each motor now spins separately. */ - for (int i = 0; i < FDD_NUM; i++) { - drive_num = real_drive(fdc, i); - if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) - val &= ~(0x10 << drive_num); - else - fdd_set_motor_enable(i, (val & (0x10 << drive_num))); - } - drive_num = real_drive(fdc, val & 0x03); - current_drive = drive_num; - fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); - } - fdc->dor = val; - return; - case 3: /* TDR */ - if (fdc->enh_mode) { - if (fdc->flags & FDC_FLAG_SMC661) { - fdc_set_swap(fdc, !!(val & 0x20)); - fdc_update_densel_force(fdc, (val & 0x18) >> 3); + if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) + switch (addr & 7) { + case 0: + return; + case 1: + return; + case 2: /*DOR*/ + if (fdc->flags & FDC_FLAG_PCJR) { + if ((fdc->dor & 0x40) && !(val & 0x40)) { + timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); + fdc->watchdog_count = 1000; + picintc(1 << fdc->irq); + } + if ((val & 0x80) && !(fdc->dor & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -1; + ui_sb_update_icon(SB_FLOPPY | 0, 0); + ui_sb_update_icon_write(SB_FLOPPY | 0, 0); + fdc_ctrl_reset(fdc); + } + if (!fdd_get_flags(0)) + val &= 0xfe; + fdd_set_motor_enable(0, val & 0x01); + fdc->st0 &= ~0x07; + fdc->st0 |= (fdd_get_head(0) ? 4 : 0); } else { - drive = real_drive(fdc, fdc->dor & 3); - fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + /* + Writing this bit to logic "1" will enable the DRQ, + nDACK, TC and FINTR outputs. This bit being a + logic "0" will disable the nDACK and TC inputs, and + hold the DRQ and FINTR outputs in a high + impedance state. + */ + if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { + fdc->tc = 1; + fdc->fintr = 0; + picintc(1 << fdc->irq); + } + if (!(val & 4)) { + fdd_stop(real_drive(fdc, val & 3)); + fdc->stat = 0x00; + fdc->pnum = fdc->ptot = 0; + } + if ((val & 4) && !(fdc->dor & 4)) + fdc_soft_reset(fdc); + /* We can now simplify this since each motor now spins separately. */ + for (int i = 0; i < FDD_NUM; i++) { + drive_num = real_drive(fdc, i); + if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) + val &= ~(0x10 << drive_num); + else + fdd_set_motor_enable(i, (val & (0x10 << drive_num))); + } + drive_num = real_drive(fdc, val & 0x03); + current_drive = drive_num; + fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); } - } - /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) - The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. - If it fails, then floppy drives will be treated as DD drives. */ - if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (val & 0x04) { - fdc->tfifo = 8; - fdc->fifointest = 1; - } else { - fdc->tfifo = 1; - fdc->fifointest = 0; + fdc->dor = val; + return; + case 3: /* TDR */ + if (fdc->enh_mode) { + if (fdc->flags & FDC_FLAG_SMC661) { + fdc_set_swap(fdc, !!(val & 0x20)); + fdc_update_densel_force(fdc, (val & 0x18) >> 3); + } else { + drive = real_drive(fdc, fdc->dor & 3); + fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + } } - fifo_reset(fdc->fifo_p); - fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); - fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); - } - return; - case 4: /* DSR */ - if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { - if (!(val & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -6; + /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) + The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. + If it fails, then floppy drives will be treated as DD drives. */ + if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (val & 0x04) { + fdc->tfifo = 8; + fdc->fifointest = 1; + } else { + fdc->tfifo = 1; + fdc->fifointest = 0; + } + fifo_reset(fdc->fifo_p); + fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); + fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); } - if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) - fdc_soft_reset(fdc); - } - fdc->dsr = val; - return; - case 5: /*Command register*/ - if (fdc->fifointest) { - /* Write FIFO buffer in the test mode (PS/55) */ - fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); - fifo_write(val, fdc->fifo_p); - if (fifo_get_full(fdc->fifo_p)) - fdc->stat &= ~0x80; - break; - } - if ((fdc->stat & 0xf0) == 0xb0) { - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->dat = val; - fdc->stat &= ~0x80; - } else { + return; + case 4: /* DSR */ + if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { + if (!(val & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -6; + } + if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) + fdc_soft_reset(fdc); + } + fdc->dsr = val; + return; + case 5: /*Command register*/ + if (fdc->fifointest) { + /* Write FIFO buffer in the test mode (PS/55) */ + fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); fifo_write(val, fdc->fifo_p); if (fifo_get_full(fdc->fifo_p)) fdc->stat &= ~0x80; + break; } - break; - } - if (fdc->pnum == fdc->ptot) { - if ((fdc->stat & 0xf0) != 0x80) { - /* If bit 4 of the MSR is set, or the MSR is 0x00, - the fdc_t is NOT in the command phase, therefore - do NOT accept commands. */ - return; + if ((fdc->stat & 0xf0) == 0xb0) { + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->dat = val; + fdc->stat &= ~0x80; + } else { + fifo_write(val, fdc->fifo_p); + if (fifo_get_full(fdc->fifo_p)) + fdc->stat &= ~0x80; + } + break; } + if (fdc->pnum == fdc->ptot) { + if ((fdc->stat & 0xf0) != 0x80) { + /* If bit 4 of the MSR is set, or the MSR is 0x00, + the fdc_t is NOT in the command phase, therefore + do NOT accept commands. */ + return; + } - fdc->stat &= 0xf; + fdc->stat &= 0xf; - fdc->tc = 0; - fdc->data_ready = 0; + fdc->tc = 0; + fdc->data_ready = 0; - fdc->command = val; - fdc->stat |= 0x10; - fdc_log("Starting FDC command %02X\n", fdc->command); - fdc->error = 0; + fdc->command = val; + fdc->stat |= 0x10; + fdc_log("Starting FDC command %02X\n", fdc->command); + fdc->error = 0; - if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || - ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || - ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || - ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || - ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) - fdc->processed_cmd = fdc->command & 0x1f; - else - fdc->processed_cmd = fdc->command; + if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || + ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || + ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || + ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || + ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) + fdc->processed_cmd = fdc->command & 0x1f; + else + fdc->processed_cmd = fdc->command; - switch (fdc->processed_cmd) { - case 0x01: /*Mode*/ - if (fdc->flags & FDC_FLAG_NSC) { - fdc->pnum = 0; - fdc->ptot = 4; + switch (fdc->processed_cmd) { + case 0x01: /*Mode*/ + if (fdc->flags & FDC_FLAG_NSC) { + fdc->pnum = 0; + fdc->ptot = 4; + fdc->stat |= 0x90; + fdc->format_state = 0; + } else + fdc_bad_command(fdc); + break; + case 0x02: /*Read track*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->pnum = 0; + fdc->ptot = 8; fdc->stat |= 0x90; - fdc->format_state = 0; - } else - fdc_bad_command(fdc); - break; - case 0x02: /*Read track*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x03: /*Specify*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x04: /*Sense drive status*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x05: /*Write data*/ - case 0x09: /*Write deleted data*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x06: /*Read data*/ - case 0x0c: /*Read deleted data*/ - case 0x11: /*Scan equal*/ - case 0x19: /*Scan low or equal*/ - case 0x16: /*Verify*/ - case 0x1d: /*Scan high or equal*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; - if ((fdc->command & 0x1F) == 0x16) - fdc->deleted = 2; - fdc->deleted |= (fdc->command & 0x20); - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x17: /*Powerdown mode*/ - if (!(fdc->flags & FDC_FLAG_ALI)) { - fdc_bad_command(fdc); + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - } - fallthrough; - case 0x07: /*Recalibrate*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x08: /*Sense interrupt status*/ - fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); - fdc->lastdrive = fdc->drive; - fdc_sis(fdc); - break; - case 0x0a: /*Read sector ID*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x0d: /*Format track*/ - fdc->pnum = 0; - fdc->ptot = 5; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - fdc->format_state = 0; - break; - case 0x0e: /*Dump registers*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); + case 0x03: /*Specify*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = 0x0e; - fdc_callback(fdc); - break; - case 0x0f: /*Seek*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x18: /*NSC*/ - if (!(fdc->flags & FDC_FLAG_NSC)) { - fdc_bad_command(fdc); - break; - } - fallthrough; - case 0x10: /*Get version*/ - case 0x14: /*Unlock*/ - case 0x94: /*Lock*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); - break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = fdc->command; - fdc_callback(fdc); - break; - case 0x12: /*Set perpendicular mode*/ - if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + case 0x04: /*Sense drive status*/ fdc->pnum = 0; fdc->ptot = 1; fdc->stat |= 0x90; - } else - fdc_bad_command(fdc); - break; - case 0x13: /*Configure*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); break; - } - fdc->pnum = 0; - fdc->ptot = 3; - fdc->stat |= 0x90; - break; - default: - fdc_bad_command(fdc); - break; - } - } else { - fdc->stat = 0x10 | (fdc->stat & 0xf); - fdc->params[fdc->pnum++] = val; - if (fdc->pnum == 1) { - if (command_has_drivesel[fdc->command & 0x1F]) { - if (fdc->flags & FDC_FLAG_PCJR) - fdc->drive = 0; - else - fdc->drive = fdc->dor & 3; - fdc->rw_drive = fdc->params[0] & 3; - if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) - fdc->stat |= (1 << real_drive(fdc, fdc->drive)); - } - } - if (fdc->pnum == fdc->ptot) { - fdc_log("Got all params %02X\n", fdc->command); - fifo_reset(fdc->fifo_p); - fdc->interrupt = fdc->processed_cmd; - fdc->reset_stat = 0; - /* Disable timer if enabled. */ - timer_disable(&fdc->timer); - /* Start timer if needed at this point. */ - switch (fdc->interrupt & 0x1f) { - case 0x02: /* Read a track */ - case 0x03: /* Specify */ - case 0x0a: /* Read sector ID */ - case 0x05: /* Write data */ - case 0x06: /* Read data */ - case 0x09: /* Write deleted data */ - case 0x0c: /* Read deleted data */ - case 0x11: /* Scan equal */ - case 0x12: /* Perpendicular mode */ - case 0x16: /* Verify */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - /* Do nothing. */ + case 0x05: /*Write data*/ + case 0x09: /*Write deleted data*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - case 0x07: /* Recalibrate */ - case 0x0f: /* Seek */ - if (fdc->flags & FDC_FLAG_PCJR) - timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); - else - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + case 0x06: /*Read data*/ + case 0x0c: /*Read deleted data*/ + case 0x11: /*Scan equal*/ + case 0x19: /*Scan low or equal*/ + case 0x16: /*Verify*/ + case 0x1d: /*Scan high or equal*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; + if ((fdc->command & 0x1F) == 0x16) + fdc->deleted = 2; + fdc->deleted |= (fdc->command & 0x20); + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - default: - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); - break; - } - /* Process the firt phase of the command. */ - switch (fdc->processed_cmd) { - case 0x02: /* Read a track */ - fdc_io_command_phase1(fdc, 0); - fdc->read_track_sector.id.c = fdc->params[1]; - fdc->read_track_sector.id.h = fdc->params[2]; - fdc->read_track_sector.id.r = 1; - fdc->read_track_sector.id.n = fdc->params[4]; - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; + case 0x17: /*Powerdown mode*/ + if (!(fdc->flags & FDC_FLAG_ALI)) { + fdc_bad_command(fdc); + break; } - fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x03: /* Specify */ - fdc->stat = 0x80; - fdc->specify[0] = fdc->params[0]; - fdc->specify[1] = fdc->params[1]; - fdc->dma = (fdc->specify[1] & 1) ^ 1; - if (!fdc->dma) - dma_set_drq(fdc->dma_ch, 0); - break; - case 0x04: /*Sense drive status*/ - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - break; - case 0x05: /* Write data */ - case 0x09: /* Write deleted data */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x11: /* Scan equal */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x16: /* Verify */ - if (fdc->params[0] & 0x80) - fdc->sc = fdc->params[7]; fallthrough; - case 0x06: /* Read data */ - case 0x0c: /* Read deleted data */ - fdc_io_command_phase1(fdc, 0); - fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { - /* DMA is in verify mode, treat this like a VERIFY command. */ - fdc_log("Verify-mode read!\n"); - fdc->tc = 1; - fdc->deleted |= 2; - } - fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + case 0x07: /*Recalibrate*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; break; - - case 0x07: /* Recalibrate */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << real_drive(fdc, fdc->drive)); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->st0 = fdc->params[0] & 3; - fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; - fdc->st0 |= 0x80; - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { - fdc_log("Failed recalibrate\n"); - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) - fdc->st0 = 0x70 | (fdc->params[0] & 3); - else - fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->pcn[fdc->params[0] & 3] = 0; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x08: /*Sense interrupt status*/ + fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); + fdc->lastdrive = fdc->drive; + fdc_sis(fdc); + break; + case 0x0a: /*Read sector ID*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + break; + case 0x0d: /*Format track*/ + fdc->pnum = 0; + fdc->ptot = 5; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + fdc->format_state = 0; + break; + case 0x0e: /*Dump registers*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) - fdc_seek(fdc, fdc->drive, -fdc->max_track); - fdc_log("Recalibrating...\n"); - fdc->seek_dir = fdc->step = 1; + fdc->lastdrive = fdc->drive; + fdc->interrupt = 0x0e; + fdc_callback(fdc); break; - case 0x0a: /* Read sector ID */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { - fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) - fdc->stat = 0x70; - else - fdc->stat = 0x50; + case 0x0f: /*Seek*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; + break; + case 0x18: /*NSC*/ + if (!(fdc->flags & FDC_FLAG_NSC)) { + fdc_bad_command(fdc); + break; + } + fallthrough; + case 0x10: /*Get version*/ + case 0x14: /*Unlock*/ + case 0x94: /*Lock*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); + break; + } + fdc->lastdrive = fdc->drive; + fdc->interrupt = fdc->command; + fdc_callback(fdc); + break; + case 0x12: /*Set perpendicular mode*/ + if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; } else - fdc_noidam(fdc); + fdc_bad_command(fdc); break; - case 0x0d: /* Format */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - fdc->gap = fdc->params[3]; - fdc->format_sectors = fdc->params[2]; - fdc->format_n = fdc->params[1]; - fdc->format_state = 1; - fdc->stat = 0x10; - break; - case 0x0f: /* Seek */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << fdc->drive); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->head = 0; /* TODO: See if this is correct. */ - fdc->st0 = fdc->params[0] & 0x03; - fdc->st0 |= (fdc->params[0] & 4); - fdc->st0 |= 0x80; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { - /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->command & 0x80) { - if (fdc->command & 0x40) - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - else - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } else - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x13: /*Configure*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if (fdc->command & 0x80) { - if (fdc->params[1]) { - if (fdc->command & 0x40) { - /* Relative seek inwards. */ - fdc->seek_dir = 0; - fdc_seek(fdc, fdc->drive, fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - } else { - /* Relative seek outwards. */ - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, -fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } - fdc->step = 1; - } else { - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - } else { - fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); - if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { - fdc_log("Failed seek\n"); - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) - fdc->seek_dir = 0; - else - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - fdc->step = 1; - } + fdc->pnum = 0; + fdc->ptot = 3; + fdc->stat |= 0x90; break; - case 0x12: /* Perpendicular mode */ - fdc->stat = 0x80; - if (fdc->params[0] & 0x80) - fdc->perp = fdc->params[0] & 0x3f; - else { - fdc->perp &= 0xfc; - fdc->perp |= (fdc->params[0] & 0x03); - } - return; - default: + fdc_bad_command(fdc); break; } - } else - fdc->stat = 0x90 | (fdc->stat & 0xf); - } - return; - case 7: - if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) - return; - fdc->rate = val & 0x03; - if (fdc->flags & FDC_FLAG_PS2) - fdc->noprec = !!(val & 0x04); - return; + } else { + fdc->stat = 0x10 | (fdc->stat & 0xf); + fdc->params[fdc->pnum++] = val; + if (fdc->pnum == 1) { + if (command_has_drivesel[fdc->command & 0x1F]) { + if (fdc->flags & FDC_FLAG_PCJR) + fdc->drive = 0; + else + fdc->drive = fdc->dor & 3; + fdc->rw_drive = fdc->params[0] & 3; + if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) + fdc->stat |= (1 << real_drive(fdc, fdc->drive)); + } + } + if (fdc->pnum == fdc->ptot) { + fdc_log("Got all params %02X\n", fdc->command); + fifo_reset(fdc->fifo_p); + fdc->interrupt = fdc->processed_cmd; + fdc->reset_stat = 0; + /* Disable timer if enabled. */ + timer_disable(&fdc->timer); + /* Start timer if needed at this point. */ + switch (fdc->interrupt & 0x1f) { + case 0x02: /* Read a track */ + case 0x03: /* Specify */ + case 0x0a: /* Read sector ID */ + case 0x05: /* Write data */ + case 0x06: /* Read data */ + case 0x09: /* Write deleted data */ + case 0x0c: /* Read deleted data */ + case 0x11: /* Scan equal */ + case 0x12: /* Perpendicular mode */ + case 0x16: /* Verify */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + /* Do nothing. */ + break; + case 0x07: /* Recalibrate */ + case 0x0f: /* Seek */ + if (fdc->flags & FDC_FLAG_PCJR) + timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); + else + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + default: + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + } + /* Process the firt phase of the command. */ + switch (fdc->processed_cmd) { + case 0x02: /* Read a track */ + fdc_io_command_phase1(fdc, 0); + fdc->read_track_sector.id.c = fdc->params[1]; + fdc->read_track_sector.id.h = fdc->params[2]; + fdc->read_track_sector.id.r = 1; + fdc->read_track_sector.id.n = fdc->params[4]; + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x03: /* Specify */ + fdc->stat = 0x80; + fdc->specify[0] = fdc->params[0]; + fdc->specify[1] = fdc->params[1]; + fdc->dma = (fdc->specify[1] & 1) ^ 1; + if (!fdc->dma) + dma_set_drq(fdc->dma_ch, 0); + break; + case 0x04: /*Sense drive status*/ + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + break; + case 0x05: /* Write data */ + case 0x09: /* Write deleted data */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x11: /* Scan equal */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x16: /* Verify */ + if (fdc->params[0] & 0x80) + fdc->sc = fdc->params[7]; + fallthrough; + case 0x06: /* Read data */ + case 0x0c: /* Read deleted data */ + fdc_io_command_phase1(fdc, 0); + fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { + /* DMA is in verify mode, treat this like a VERIFY command. */ + fdc_log("Verify-mode read!\n"); + fdc->tc = 1; + fdc->deleted |= 2; + } + fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; - default: - break; - } + case 0x07: /* Recalibrate */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << real_drive(fdc, fdc->drive)); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->st0 = fdc->params[0] & 3; + fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; + fdc->st0 |= 0x80; + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { + fdc_log("Failed recalibrate\n"); + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) + fdc->st0 = 0x70 | (fdc->params[0] & 3); + else + fdc->st0 = 0x20 | (fdc->params[0] & 3); + fdc->pcn[fdc->params[0] & 3] = 0; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) + fdc_seek(fdc, fdc->drive, -fdc->max_track); + fdc_log("Recalibrating...\n"); + fdc->seek_dir = fdc->step = 1; + break; + case 0x0a: /* Read sector ID */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { + fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) + fdc->stat = 0x70; + else + fdc->stat = 0x50; + } else + fdc_noidam(fdc); + break; + case 0x0d: /* Format */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + fdc->gap = fdc->params[3]; + fdc->format_sectors = fdc->params[2]; + fdc->format_n = fdc->params[1]; + fdc->format_state = 1; + fdc->stat = 0x10; + break; + case 0x0f: /* Seek */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << fdc->drive); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->head = 0; /* TODO: See if this is correct. */ + fdc->st0 = fdc->params[0] & 0x03; + fdc->st0 |= (fdc->params[0] & 4); + fdc->st0 |= 0x80; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { + /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->command & 0x80) { + if (fdc->command & 0x40) + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + else + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } else + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->command & 0x80) { + if (fdc->params[1]) { + if (fdc->command & 0x40) { + /* Relative seek inwards. */ + fdc->seek_dir = 0; + fdc_seek(fdc, fdc->drive, fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + } else { + /* Relative seek outwards. */ + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, -fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } + fdc->step = 1; + } else { + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + } else { + fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); + if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { + fdc_log("Failed seek\n"); + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) + fdc->seek_dir = 0; + else + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + fdc->step = 1; + } + break; + case 0x12: /* Perpendicular mode */ + fdc->stat = 0x80; + if (fdc->params[0] & 0x80) + fdc->perp = fdc->params[0] & 0x3f; + else { + fdc->perp &= 0xfc; + fdc->perp |= (fdc->params[0] & 0x03); + } + return; + + default: + break; + } + } else + fdc->stat = 0x90 | (fdc->stat & 0xf); + } + return; + case 7: + if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) + return; + fdc->rate = val & 0x03; + if (fdc->flags & FDC_FLAG_PS2) + fdc->noprec = !!(val & 0x04); + return; + + default: + break; + } } uint8_t @@ -1376,213 +1385,214 @@ fdc_read(uint16_t addr, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2)) switch (addr & 7) { - case 0: /* STA */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - /* TODO: - Bit 2: INDEX (best return always 0 as it goes by very fast) - */ - if (fdc->seek_dir) /* nDIRECTION */ - ret |= 0x01; - if (writeprot[drive]) /* WRITEPROT */ - ret |= 0x02; - if (!fdd_get_head(drive)) /* nHDSEL */ - ret |= 0x08; - if (fdd_track0(drive)) /* TRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (dma_get_drq(fdc->dma_ch)) /* DRQ */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x04; - /* TODO: - Bit 2: nINDEX (best return always 1 as it goes by very fast) - */ - if (!fdc->seek_dir) /* DIRECTION */ - ret |= 0x01; - if (!writeprot[drive]) /* nWRITEPROT */ - ret |= 0x02; - if (fdd_get_head(drive)) /* HDSEL */ - ret |= 0x08; - if (!fdd_track0(drive)) /* nTRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else - ret = 0xff; - break; - case 1: /* STB */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x80; - switch (drive) { /* -Drive Select 1,0 */ - case 0: - ret |= 0x43; - break; - case 1: - ret |= 0x23; - break; - case 2: - ret |= 0x62; - break; - case 3: - ret |= 0x61; - break; - - default: - break; - } - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0xc0; - ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ - } else { - if (is486 || !fdc->enable_3f1) + if (!fdc->power_down || ((addr & 7) == 2)) + switch (addr & 7) { + case 0: /* STA */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + /* TODO: + Bit 2: INDEX (best return always 0 as it goes by very fast) + */ + if (fdc->seek_dir) /* nDIRECTION */ + ret |= 0x01; + if (writeprot[drive]) /* WRITEPROT */ + ret |= 0x02; + if (!fdd_get_head(drive)) /* nHDSEL */ + ret |= 0x08; + if (fdd_track0(drive)) /* TRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (dma_get_drq(fdc->dma_ch)) /* DRQ */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x04; + /* TODO: + Bit 2: nINDEX (best return always 1 as it goes by very fast) + */ + if (!fdc->seek_dir) /* DIRECTION */ + ret |= 0x01; + if (!writeprot[drive]) /* nWRITEPROT */ + ret |= 0x02; + if (fdd_get_head(drive)) /* HDSEL */ + ret |= 0x08; + if (!fdd_track0(drive)) /* nTRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else ret = 0xff; - else { - if (fdc->flags & FDC_FLAG_UMC) { - drive = real_drive(fdc, fdc->dor & 1); - ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; - } else { - /* TODO: What is this and what is it used for? - It's almost identical to the PS/2 MCA mode. */ - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x70; - ret &= ~(drive ? 0x40 : 0x20); - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + break; + case 1: /* STB */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x80; + switch (drive) { /* -Drive Select 1,0 */ + case 0: + ret |= 0x43; + break; + case 1: + ret |= 0x23; + break; + case 2: + ret |= 0x62; + break; + case 3: + ret |= 0x61; + break; + + default: + break; + } + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0xc0; + ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } else { + if (is486 || !fdc->enable_3f1) + ret = 0xff; + else { + if (fdc->flags & FDC_FLAG_UMC) { + drive = real_drive(fdc, fdc->dor & 1); + ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; + } else { + /* TODO: What is this and what is it used for? + It's almost identical to the PS/2 MCA mode. */ + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x70; + ret &= ~(drive ? 0x40 : 0x20); + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } } } - } - break; - case 2: - ret = fdc->dor; - break; - case 3: - drive = real_drive(fdc, fdc->dor & 3); - /* TODO: FDC_FLAG_PS2_TDR? */ - if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { - /* PS/1 Model 2121 seems return drive type in port - * 0x3f3, despite the 82077AA fdc_t not implementing - * this. This is presumably implemented outside the - * fdc_t on one of the motherboard's support chips. - * - * Confirmed: 00=1.44M 3.5 - * 10=2.88M 3.5 - * 20=1.2M 5.25 - * 30=1.2M 5.25 - * - * as reported by Configur.exe. - */ - if (fdd_is_525(drive)) - ret = 0x20; - else if (fdd_is_ed(drive)) - ret = 0x10; - else - ret = 0x00; - /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ - } else if (!fdc->enh_mode) - ret = 0x20; - else if (fdc->flags & FDC_FLAG_SMC661) - ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); - else - ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); - break; - case 4: /*Status*/ - ret = fdc->stat; - break; - case 5: /*Data*/ - if (fdc->fifointest) { - /* Read FIFO buffer in the test mode (PS/55) */ - ret = fifo_read(fdc->fifo_p); break; - } - if ((fdc->stat & 0xf0) == 0xf0) { - fdc->stat &= ~0x80; - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->data_ready = 0; - ret = fdc->dat; - } else - ret = fifo_read(fdc->fifo_p); + case 2: + ret = fdc->dor; break; - } - if (fdc->paramstogo) { - fdc->stat &= ~0x80; - fdc_log("%i parameters to go\n", fdc->paramstogo); - fdc->paramstogo--; - ret = fdc->res[10 - fdc->paramstogo]; - if (!fdc->paramstogo) - fdc->stat = 0x80; - else - fdc->stat |= 0xC0; - } else if (fdc->dma) { - ret = fdc->dat; - break; - } else { - fdc->stat &= ~0x80; - if (lastbyte) - fdc->stat = 0x80; - lastbyte = 0; - ret = fdc->dat; - fdc->data_ready = 0; - } - fdc->stat &= 0xf0; - break; - case 7: /*Disk change*/ - drive = real_drive(fdc, fdc->dor & 3); - - if (fdc->flags & FDC_FLAG_PS2) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; - ret |= (fdc->dor & 0x08); - ret |= (fdc->noprec << 2); - ret |= (fdc->rate & 0x03); - } else - ret = 0x00; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - ret |= ((fdc->rate & 0x03) << 1); - ret |= fdc_get_densel(fdc, drive); - ret |= 0x78; - } else - ret = 0xf9; - } else { - if (fdc->dor & (0x10 << drive)) { - if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) - ret = 0x00; + case 3: + drive = real_drive(fdc, fdc->dor & 3); + /* TODO: FDC_FLAG_PS2_TDR? */ + if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { + /* PS/1 Model 2121 seems return drive type in port + * 0x3f3, despite the 82077AA fdc_t not implementing + * this. This is presumably implemented outside the + * fdc_t on one of the motherboard's support chips. + * + * Confirmed: 00=1.44M 3.5 + * 10=2.88M 3.5 + * 20=1.2M 5.25 + * 30=1.2M 5.25 + * + * as reported by Configur.exe. + */ + if (fdd_is_525(drive)) + ret = 0x20; + else if (fdd_is_ed(drive)) + ret = 0x10; else + ret = 0x00; + /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ + } else if (!fdc->enh_mode) + ret = 0x20; + else if (fdc->flags & FDC_FLAG_SMC661) + ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); + else + ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); + break; + case 4: /*Status*/ + ret = fdc->stat; + break; + case 5: /*Data*/ + if (fdc->fifointest) { + /* Read FIFO buffer in the test mode (PS/55) */ + ret = fifo_read(fdc->fifo_p); + break; + } + if ((fdc->stat & 0xf0) == 0xf0) { + fdc->stat &= ~0x80; + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->data_ready = 0; + ret = fdc->dat; + } else + ret = fifo_read(fdc->fifo_p); + break; + } + if (fdc->paramstogo) { + fdc->stat &= ~0x80; + fdc_log("%i parameters to go\n", fdc->paramstogo); + fdc->paramstogo--; + ret = fdc->res[10 - fdc->paramstogo]; + if (!fdc->paramstogo) + fdc->stat = 0x80; + else + fdc->stat |= 0xC0; + } else if (fdc->dma) { + ret = fdc->dat; + break; + } else { + fdc->stat &= ~0x80; + if (lastbyte) + fdc->stat = 0x80; + lastbyte = 0; + ret = fdc->dat; + fdc->data_ready = 0; + } + fdc->stat &= 0xf0; + break; + case 7: /*Disk change*/ + drive = real_drive(fdc, fdc->dor & 3); + + if (fdc->flags & FDC_FLAG_PS2) { + if (fdc->dor & (0x10 << drive)) { + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; + ret |= (fdc->dor & 0x08); + ret |= (fdc->noprec << 2); + ret |= (fdc->rate & 0x03); + } else + ret = 0x00; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (fdc->dor & (0x10 << drive)) { ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - } else - ret = 0x00; - if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ - ret ^= 0x80; + ret |= ((fdc->rate & 0x03) << 1); + ret |= fdc_get_densel(fdc, drive); + ret |= 0x78; + } else + ret = 0xf9; + } else { + if (fdc->dor & (0x10 << drive)) { + if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) + ret = 0x00; + else + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; + } else + ret = 0x00; + if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ + ret ^= 0x80; - /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ - if (fdc->flags & FDC_FLAG_TOSHIBA) { - ret |= (3 << 5); - ret |= 0x01; - } else - ret |= 0x7F; - } + /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ + if (fdc->flags & FDC_FLAG_TOSHIBA) { + ret |= (3 << 5); + ret |= 0x01; + } else + ret |= 0x7F; + } - fdc->step = 0; - break; - default: - ret = 0xff; - } + fdc->step = 0; + break; + default: + ret = 0xff; + } fdc_log("[%04X:%08X] Read FDC %04X %02X [%i:%02X]\n", CS, cpu_state.pc, addr, ret, drive, fdc->dor & (0x10 << drive)); return ret; } @@ -1886,7 +1896,7 @@ fdc_callback(void *priv) } else fdc->interrupt = -3; timer_set_delay_u64(&fdc->timer, 2048 * TIMER_USEC); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); return; case 0x0d: /*Format track*/ if (fdc->format_state == 1) { @@ -1929,7 +1939,7 @@ fdc_callback(void *priv) return; case 0x0f: /*Seek*/ fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); // Interrupts and callbacks in the fdd callback function return; case 0x10: /*Version*/ @@ -2863,4 +2873,4 @@ const device_t fdc_ps2_mca_device = { .speed_changed = NULL, .force_redraw = NULL, .config = NULL -}; +}; \ No newline at end of file diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index a0bef7ac6..212317f49 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -79,11 +79,34 @@ typedef struct fdd_t { fdd_t fdd[FDD_NUM]; +enum { + FDD_OP_NONE = 0, + FDD_OP_READ, + FDD_OP_WRITE, + FDD_OP_COMPARE, + FDD_OP_READADDR, + FDD_OP_FORMAT +}; + +typedef struct fdd_pending_op_t { + int pending; + int op; + int sector; + int track; + int side; + int density; + int sector_size; + uint8_t fill; +} fdd_pending_op_t; + +static fdd_pending_op_t fdd_pending[FDD_NUM]; + char floppyfns[FDD_NUM][512]; char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; pc_timer_t fdd_poll_time[FDD_NUM]; pc_timer_t fdd_seek_timer[FDD_NUM]; +int fdd_seek_in_progress[FDD_NUM] = { 0, 0, 0, 0 }; static int fdd_notfound = 0; static int driveloaders[FDD_NUM]; @@ -293,10 +316,49 @@ fdd_seek_complete_callback(void *priv) { DRIVE *drive = (DRIVE *) priv; + fdd_seek_in_progress[drive->id] = 0; + fdd_log("fdd_seek_complete_callback(drive=%d) - TIMER FIRED! seek_in_progress=1\n", drive->id); fdd_log("Notifying FDC of seek completion\n"); fdd_do_seek(drive->id, fdd[drive->id].track); - fdc_seek_complete_interrupt(fdd_fdc, drive->id); + + int had_pending = fdd_pending[drive->id].pending; + if (had_pending) { + fdd_pending_op_t *po = &fdd_pending[drive->id]; + fdd_log("Starting deferred op %d after seek on drive %d (trk=%d, side=%d, sec=%d)\n", + po->op, drive->id, po->track, po->side, po->sector); + + switch (po->op) { + case FDD_OP_READ: + if (drives[drive->id].readsector) + drives[drive->id].readsector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_WRITE: + if (drives[drive->id].writesector) + drives[drive->id].writesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_COMPARE: + if (drives[drive->id].comparesector) + drives[drive->id].comparesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_READADDR: + if (drives[drive->id].readaddress) + drives[drive->id].readaddress(drive->id, po->side, po->density); + break; + case FDD_OP_FORMAT: + if (drives[drive->id].format) + drives[drive->id].format(drive->id, po->side, po->density, po->fill); + break; + default: + break; + } + + po->pending = 0; + po->op = FDD_OP_NONE; + } + + if (!had_pending) + fdc_seek_complete_interrupt(fdd_fdc, drive->id); } void @@ -306,6 +368,11 @@ fdd_seek(int drive, int track_diff) if (!track_diff) return; + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek already in progress for drive %d, ignoring new seek request\n", drive); + return; + } + int old_track = fdd[drive].track; fdd[drive].track += track_diff; @@ -327,12 +394,14 @@ fdd_seek(int drive, int track_diff) /* Multi-track seek */ fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track); } - + if (old_track + track_diff < 0) { fdd_do_seek(drive, fdd[drive].track); return; } + fdd_seek_in_progress[drive] = 1; + if (!fdd_seek_timer[drive].callback) { timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0); } @@ -629,7 +698,7 @@ fdd_set_motor_enable(int drive, int motor_enable) { fdd_log("fdd_set_motor_enable(%d, %d)\n", drive, motor_enable); fdd_audio_set_motor_enable(drive, motor_enable); - + if (motor_enable && !motoron[drive]) { timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(drive)); } else if (!motor_enable && motoron[drive]) { @@ -700,6 +769,22 @@ void fdd_readsector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_readsector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + 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); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READ, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].readsector) drives[drive].readsector(drive, sector, track, side, density, sector_size); else @@ -710,6 +795,22 @@ void fdd_writesector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_writesector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring WRITE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_WRITE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].writesector) drives[drive].writesector(drive, sector, track, side, density, sector_size); else @@ -719,6 +820,21 @@ fdd_writesector(int drive, int sector, int track, int side, int density, int sec void fdd_comparesector(int drive, int sector, int track, int side, int density, int sector_size) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring COMPARE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_COMPARE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].comparesector) drives[drive].comparesector(drive, sector, track, side, density, sector_size); else @@ -728,6 +844,19 @@ fdd_comparesector(int drive, int sector, int track, int side, int density, int s void fdd_readaddress(int drive, int side, int density) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring READADDRESS (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READADDR, + .track = fdd[drive].track, + .side = side, + .density = density + }; + return; + } + if (drives[drive].readaddress) drives[drive].readaddress(drive, side, density); } @@ -735,6 +864,20 @@ fdd_readaddress(int drive, int side, int density) void fdd_format(int drive, int side, int density, uint8_t fill) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring FORMAT (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_FORMAT, + .track = fdd[drive].track, + .side = side, + .density = density, + .fill = fill + }; + return; + } + if (drives[drive].format) drives[drive].format(drive, side, density, fill); else @@ -777,7 +920,7 @@ fdd_init(void) if (fdd_sounds_enabled) { fdd_audio_init(); - } + } } void diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 409ddfe8c..04ae9ba68 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -176,6 +176,7 @@ static int16_t * load_wav(const char *filename, int *sample_count) { FILE *f = NULL; + char full_path[2048]; if ((filename == NULL) || (strlen(filename) == 0)) return NULL; From fe0277c03135168a0f97582ea6537e4df0c75987 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 13:28:23 +0200 Subject: [PATCH 02/11] FDC: Fix a warning and the incorrect status flags. --- src/floppy/fdc.c | 6 +++--- src/floppy/fdd_audio.c | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 212bf9ce0..44adc569b 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -184,7 +184,7 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x10; + fdc->stat = 0x80; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->head = 0; @@ -1211,7 +1211,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) case 0x07: /* Recalibrate */ fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = 0x10 | (1 << real_drive(fdc, fdc->drive)); + fdc->stat = (1 << real_drive(fdc, fdc->drive)); if (!(fdc->flags & FDC_FLAG_PCJR)) fdc->stat |= 0x80; fdc->st0 = fdc->params[0] & 3; @@ -1266,7 +1266,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) break; case 0x0f: /* Seek */ fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = 0x10 | (1 << fdc->drive); + fdc->stat = (1 << fdc->drive); if (!(fdc->flags & FDC_FLAG_PCJR)) fdc->stat |= 0x80; fdc->head = 0; /* TODO: See if this is correct. */ diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 04ae9ba68..409ddfe8c 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -176,7 +176,6 @@ static int16_t * load_wav(const char *filename, int *sample_count) { FILE *f = NULL; - char full_path[2048]; if ((filename == NULL) || (strlen(filename) == 0)) return NULL; From bc085ac02f5481cd0d4df344340fadf2384d9636 Mon Sep 17 00:00:00 2001 From: andresdelcampo <33843515+andresdelcampo@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:31:46 +0200 Subject: [PATCH 03/11] Change window resizing logic when using 4:3 aspect ratio (#6233) Change window resizing logic when using 4:3 aspect ratio to resize content only. Fixed issues in Remember size and position that are derived from the change. There is a slight flicker while resizing with force 4:3 aspect ratio. --- src/qt/qt_mainwindow.cpp | 154 +++++++++++++++++---------------------- src/qt/qt_mainwindow.hpp | 4 +- 2 files changed, 70 insertions(+), 88 deletions(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 954547078..fbc2d8a47 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -84,6 +84,7 @@ extern bool cpu_thread_running; #include #include #include +#include #include #include #include @@ -171,8 +172,6 @@ extern "C" void qt_blit(int x, int y, int w, int h, int monitor_index); extern MainWindow *main_window; -bool MainWindow::s_adjustingForce43 = false; - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) @@ -933,11 +932,15 @@ MainWindow::closeEvent(QCloseEvent *event) } } if (window_remember) { - window_w = ui->stackedWidget->width(); - window_h = ui->stackedWidget->height(); + // If maximized, persist the normal (restorable) geometry + const bool wasMax = isMaximized(); + QRect normal = wasMax ? this->normalGeometry() : this->geometry(); + // Save WINDOW size (not the content widget’s 4:3 box) + window_w = normal.width(); + window_h = normal.height(); if (!QApplication::platformName().contains("wayland")) { - window_x = this->geometry().x(); - window_y = this->geometry().y(); + window_x = normal.x(); + window_y = normal.y(); } for (int i = 1; i < MONITORS_NUM; i++) { if (renderers[i]) { @@ -1021,65 +1024,56 @@ void MainWindow::updateShortcuts() seq = QKeySequence::fromString(acc_keys[accID].seq); ui->actionMute_Unmute->setShortcut(seq); } - -void -MainWindow::adjustForForce43(const QSize &newWinSize) + +void +MainWindow::applyContentLayoutForCurrentState() { - // Only act in resizable mode with Force 4:3 enabled and not fullscreen - if (!(vid_resize == 1 && force_43 > 0) || video_fullscreen || s_adjustingForce43) - return; + auto applyFill = [this](const QRect& r){ + ui->stackedWidget->setGeometry(r); + ui->stackedWidget->onResize(r.width(), r.height()); + if (monitors[0].mon_scrnsz_x != r.width() || monitors[0].mon_scrnsz_y != r.height()) { + monitors[0].mon_scrnsz_x = r.width(); + monitors[0].mon_scrnsz_y = r.height(); + plat_resize_request(r.width(), r.height(), 0); + } + }; - s_adjustingForce43 = true; + auto apply43 = [this](const QRect& area){ + int areaW = area.width(); + int areaH = area.height(); + if (areaW <= 0 || areaH <= 0) return; - // Height consumed by menu/status/toolbars - int chromeH = menuBar()->height() - + (hide_status_bar ? 0 : statusBar()->height()) - + (hide_tool_bar ? 0 : ui->toolBar->height()); + int targetW = areaW; + int targetH = (areaW * 3) / 4; + if (targetH > areaH) { + targetH = areaH; + targetW = (areaH * 4) / 3; + } - // Compute client area size in device‑independent pixels - double dpr = (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.0); - int winW = newWinSize.width(); - int winH = newWinSize.height(); - int clientW = static_cast(winW / dpr); - int clientH = static_cast((winH - chromeH) / dpr); + const int x = area.x() + (areaW - targetW) / 2; + const int y = area.y() + (areaH - targetH) / 2; - if (clientW <= 0 || clientH <= 0) { - s_adjustingForce43 = false; - return; - } + ui->stackedWidget->setGeometry(x, y, targetW, targetH); + ui->stackedWidget->onResize(targetW, targetH); - // Decide which dimension the user changed most – adjust the other - int curW = static_cast(width() / dpr); - int curH = static_cast((height() - chromeH) / dpr); - bool widthChanged = std::abs(clientW - curW) >= std::abs(clientH - curH); + if (monitors[0].mon_scrnsz_x != targetW || monitors[0].mon_scrnsz_y != targetH) { + monitors[0].mon_scrnsz_x = targetW; + monitors[0].mon_scrnsz_y = targetH; + plat_resize_request(targetW, targetH, 0); + } + }; - int targetW, targetH; - if (widthChanged) { - // user dragged width – compute matching height for 4:3 - targetW = clientW; - targetH = (clientW * 3) / 4; - } else { - // user dragged height – compute matching width for 4:3 - targetH = clientH; - targetW = (clientH * 4) / 3; - } + QWidget *cw = this->centralWidget(); + if (!cw) return; - // Convert back to window size including chrome and apply - int newW = static_cast(targetW * dpr); - int newH = static_cast(targetH * dpr) + chromeH; - if (newW != winW || newH != winH) - resize(newW, newH); + const QRect area = cw->contentsRect(); - // Update emulator framebuffer size and notify platform - monitors[0].mon_scrnsz_x = targetW; - monitors[0].mon_scrnsz_y = targetH; - plat_resize_request(targetW, targetH, 0); + // Fullscreen always fills (legacy behavior) + if (video_fullscreen) { applyFill(area); return; } - // Allow renderer widget to grow and recompute scaling - ui->stackedWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - ui->stackedWidget->onResize(width(), height()); - - s_adjustingForce43 = false; + // Windowed: enforce 4:3 only when requested, otherwise fill + if (force_43 > 0) apply43(area); + else applyFill(area); } void @@ -1088,26 +1082,10 @@ MainWindow::resizeEvent(QResizeEvent *event) //qDebug() << pos().x() + event->size().width(); //qDebug() << pos().y() + event->size().height(); - // Enforce 4:3 aspect ratio in resizable mode when the option is set - adjustForForce43(event->size()); - - if (vid_resize == 1 || video_fullscreen) - return; + // Always let QMainWindow do its layout first + QMainWindow::resizeEvent(event); - int newX = pos().x(); - int newY = pos().y(); - - if (((frameGeometry().x() + event->size().width() + 1) > util::screenOfWidget(this)->availableGeometry().right())) { - //move(util::screenOfWidget(this)->availableGeometry().right() - size().width() - 1, pos().y()); - newX = util::screenOfWidget(this)->availableGeometry().right() - frameGeometry().width() - 1; - if (newX < 1) newX = 1; - } - - if (((frameGeometry().y() + event->size().height() + 1) > util::screenOfWidget(this)->availableGeometry().bottom())) { - newY = util::screenOfWidget(this)->availableGeometry().bottom() - frameGeometry().height() - 1; - if (newY < 1) newY = 1; - } - move(newX, newY); + applyContentLayoutForCurrentState(); } void @@ -1201,12 +1179,25 @@ MainWindow::showEvent(QShowEvent *event) monitors[0].mon_scrnsz_y = fixed_size_y; } if (window_remember && vid_resize == 1) { - ui->stackedWidget->setFixedSize(window_w, window_h); + const QSize target(window_w, window_h); + const QSize prevMin = ui->stackedWidget->minimumSize(); + const QSize prevMax = ui->stackedWidget->maximumSize(); + + ui->stackedWidget->setMinimumSize(target); + ui->stackedWidget->setMaximumSize(target); #ifndef Q_OS_MACOS QApplication::processEvents(); #endif this->adjustSize(); + + ui->stackedWidget->setMinimumSize(prevMin); + ui->stackedWidget->setMaximumSize(prevMax); + ui->stackedWidget->resize(target); } + + QTimer::singleShot(0, this, [this]{ + applyContentLayoutForCurrentState(); + }); } void @@ -1509,6 +1500,7 @@ MainWindow::on_actionFullscreen_triggered() { if (video_fullscreen > 0) { showNormal(); + QTimer::singleShot(0, this, [this]{ applyContentLayoutForCurrentState(); }); ui->menubar->show(); if (!hide_status_bar) ui->statusbar->show(); @@ -2146,16 +2138,7 @@ MainWindow::on_actionForce_4_3_display_ratio_triggered() { video_toggle_option(ui->actionForce_4_3_display_ratio, &force_43); - // When turning on Force 4:3 in resizable mode, immediately snap to 4:3 - if (vid_resize == 1 && !video_fullscreen) { - ui->stackedWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - if (force_43 > 0) { - adjustForForce43(size()); - } else { - // Turning off: refresh renderer scaling - ui->stackedWidget->onResize(width(), height()); - } - } + QTimer::singleShot(0, this, [this]{ applyContentLayoutForCurrentState(); }); } void @@ -2524,4 +2507,3 @@ void MainWindow::on_actionCGA_composite_settings_triggered() isNonPause = false; config_save(); } - diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index f562c2ea9..77e8fe7c4 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -179,8 +179,8 @@ private: std::shared_ptr mm; static bool s_adjustingForce43; // guard against recursion - void adjustForForce43(const QSize &newWinSize); - + void applyContentLayoutForCurrentState(); + void updateShortcuts(); void processKeyboardInput(bool down, uint32_t keycode); #ifdef Q_OS_MACOS From 3fd58dde8f5d619c24001c7c1bf4f3cd516326d7 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 27 Sep 2025 17:32:00 +0600 Subject: [PATCH 04/11] Implement YUV aperture on Mach64 VT2 (#6234) Some GDBSTUB fixes --- src/gdbstub.c | 4 + src/video/vid_ati_mach64.c | 336 ++++++++++++++++++++++++++----------- 2 files changed, 243 insertions(+), 97 deletions(-) diff --git a/src/gdbstub.c b/src/gdbstub.c index cbbf0ede7..6e1e9af26 100644 --- a/src/gdbstub.c +++ b/src/gdbstub.c @@ -879,6 +879,7 @@ e22: /* Read by qwords, then by dwords, then by words, then by bytes. */ i = 0; + cpl_override = 1; if (is386) { for (; i < (k & ~7); i += 8) { *((uint64_t *) buf) = readmemql(j); @@ -900,6 +901,7 @@ e22: buf[0] = readmembl(j++); gdbstub_client_respond_hex(client, buf, 1); } + cpl_override = 0; break; case 'M': /* write memory */ @@ -934,6 +936,7 @@ e22: /* Write by qwords, then by dwords, then by words, then by bytes. */ p = client->packet; i = 0; + cpl_override = 1; if (is386) { for (; i < (k & ~7); i += 8) { writememql(j, *((uint64_t *) p)); @@ -955,6 +958,7 @@ e22: writemembl(j++, p[0]); p++; } + cpl_override = 0; /* Respond positively. */ goto ok; diff --git a/src/video/vid_ati_mach64.c b/src/video/vid_ati_mach64.c index 6a9b9ea34..221af0d3f 100644 --- a/src/video/vid_ati_mach64.c +++ b/src/video/vid_ati_mach64.c @@ -39,6 +39,7 @@ #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #include <86box/vid_ati_eeprom.h> +#include <86box/bswap.h> #ifdef CLAMP # undef CLAMP @@ -80,6 +81,7 @@ enum { typedef struct mach64_t { mem_mapping_t linear_mapping; mem_mapping_t mmio_mapping; + mem_mapping_t linear_mapping_big_endian; mem_mapping_t mmio_linear_mapping; mem_mapping_t mmio_linear_mapping_2; @@ -98,6 +100,8 @@ typedef struct mach64_t { uint8_t pci_slot; uint8_t irq_state; + uint8_t on_board; + uint8_t pci_regs[256]; uint8_t int_line; @@ -272,7 +276,7 @@ typedef struct mach64_t { uint32_t cur_clr0; uint32_t cur_clr1; - uint32_t overlay_dat[1024]; + uint32_t overlay_dat[2048]; uint32_t overlay_graphics_key_clr; uint32_t overlay_graphics_key_msk; uint32_t overlay_video_key_clr; @@ -286,12 +290,17 @@ typedef struct mach64_t { uint32_t scaler_height_width; int scaler_format; int scaler_update; + int scaler_yuv_aper; uint32_t buf_offset[2]; uint32_t buf_pitch[2]; int overlay_v_acc; + uint32_t overlay_uv_addr; + uint32_t overlay_cur_y; + uint32_t overlay_base; + uint8_t thread_run; void *i2c; void *ddc; @@ -381,6 +390,9 @@ void mach64_ext_writeb(uint32_t addr, uint8_t val, void *priv); void mach64_ext_writew(uint32_t addr, uint16_t val, void *priv); void mach64_ext_writel(uint32_t addr, uint32_t val, void *priv); +uint8_t mach64_readb_be(uint32_t addr, void *priv); +void mach64_writeb_be(uint32_t addr, uint8_t val, void *priv); + #ifdef ENABLE_MACH64_LOG int mach64_do_log = ENABLE_MACH64_LOG; @@ -591,6 +603,7 @@ mach64_updatemapping(mach64_t *mach64) mach64_log("Update mapping - PCI disabled\n"); mem_mapping_disable(&svga->mapping); mem_mapping_disable(&mach64->linear_mapping); + mem_mapping_disable(&mach64->linear_mapping_big_endian); mem_mapping_disable(&mach64->mmio_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping_2); @@ -645,14 +658,16 @@ mach64_updatemapping(mach64_t *mach64) } } else { /*2*8 MB aperture*/ - mem_mapping_set_addr(&mach64->linear_mapping, mach64->linear_base, (8 << 20) - 0x4000); - mem_mapping_set_addr(&mach64->mmio_linear_mapping, mach64->linear_base + ((8 << 20) - 0x4000), 0x4000); - mem_mapping_set_addr(&mach64->mmio_linear_mapping_2, mach64->linear_base + ((16 << 20) - 0x4000), 0x4000); + mem_mapping_set_addr(&mach64->linear_mapping, mach64->linear_base, (8 << 20) - 4096); + mem_mapping_set_addr(&mach64->mmio_linear_mapping, mach64->linear_base + ((8 << 20) - 4096), 4096); + mem_mapping_set_addr(&mach64->linear_mapping_big_endian, mach64->linear_base + (8 << 20), (8 << 20) - 0x1000); + mem_mapping_set_addr(&mach64->mmio_linear_mapping_2, mach64->linear_base + ((16 << 20) - 0x1000), 0x1000); } } else { mem_mapping_disable(&mach64->linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping_2); + mem_mapping_disable(&mach64->linear_mapping_big_endian); } } @@ -2310,8 +2325,11 @@ mach64_vblank_start(svga_t *svga) svga->overlay.ena = (mach64->overlay_scale_cntl & OVERLAY_EN) && (overlay_cmp_mix != 1); - mach64->overlay_v_acc = 0; - mach64->scaler_update = 1; + mach64->overlay_v_acc = 0; + mach64->scaler_update = 1; + mach64->overlay_uv_addr = svga->overlay.addr; + mach64->overlay_cur_y = 0; + mach64->overlay_base = svga->overlay.addr; } uint8_t @@ -2389,6 +2407,10 @@ mach64_ext_readb(uint32_t addr, void *priv) case 0x4a: ret = mach64->scaler_format; break; + + case 0x4b: + ret = mach64->scaler_yuv_aper; + break; default: ret = 0xff; @@ -2558,7 +2580,7 @@ mach64_ext_readb(uint32_t addr, void *priv) case 0xc7: READ8(addr, mach64->dac_cntl); - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { ret &= 0xf9; if (i2c_gpio_get_scl(mach64->i2c)) ret |= 0x04; @@ -2957,7 +2979,8 @@ mach64_ext_readw(uint32_t addr, void *priv) if (!(addr & 0x400)) { mach64_log("mach64_ext_readw: addr=%04x\n", addr); - ret = 0xffff; + ret = mach64_ext_readb(addr, priv); + ret |= mach64_ext_readb(addr + 1, priv) << 8; } else switch (addr & 0x3ff) { case 0xb4: @@ -2986,7 +3009,8 @@ mach64_ext_readl(uint32_t addr, void *priv) if (!(addr & 0x400)) { mach64_log("mach64_ext_readl: addr=%04x\n", addr); - ret = 0xffffffff; + ret = mach64_ext_readw(addr, priv); + ret |= mach64_ext_readw(addr + 2, priv) << 16; } else switch (addr & 0x3ff) { case 0x18: @@ -3087,6 +3111,10 @@ mach64_ext_writeb(uint32_t addr, uint8_t val, void *priv) case 0x4a: mach64->scaler_format = val & 0xf; break; + + case 0x4b: + mach64->scaler_yuv_aper = val; + break; case 0x80: case 0x81: @@ -4106,76 +4134,113 @@ mach64_int_hwcursor_draw(svga_t *svga, int displine) } \ } while (0) -#define DECODE_VYUY422() \ - do { \ - for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x += 2) { \ - uint8_t y1, y2; \ - int8_t u, v; \ - int dR, dG, dB; \ - int r, g, b; \ - \ - y1 = src[0]; \ - u = src[1] - 0x80; \ - y2 = src[2]; \ - v = src[3] - 0x80; \ - src += 4; \ - \ - dR = (359 * v) >> 8; \ - dG = (88 * u + 183 * v) >> 8; \ - dB = (453 * u) >> 8; \ - \ - r = y1 + dR; \ - CLAMP(r); \ - g = y1 - dG; \ - CLAMP(g); \ - b = y1 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x] = (r << 16) | (g << 8) | b; \ - \ - r = y2 + dR; \ - CLAMP(r); \ - g = y2 - dG; \ - CLAMP(g); \ - b = y2 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x + 1] = (r << 16) | (g << 8) | b; \ - } \ +#define DECODE_VYUY422() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + y1 = src[0]; \ + u = src[1] - 0x80; \ + y2 = src[2]; \ + v = src[3] - 0x80; \ + src += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ } while (0) -#define DECODE_YVYU422() \ - do { \ - for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x += 2) { \ - uint8_t y1, y2; \ - int8_t u, v; \ - int dR, dG, dB; \ - int r, g, b; \ - \ - u = src[0] - 0x80; \ - y1 = src[1]; \ - v = src[2] - 0x80; \ - y2 = src[3]; \ - src += 4; \ - \ - dR = (359 * v) >> 8; \ - dG = (88 * u + 183 * v) >> 8; \ - dB = (453 * u) >> 8; \ - \ - r = y1 + dR; \ - CLAMP(r); \ - g = y1 - dG; \ - CLAMP(g); \ - b = y1 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x] = (r << 16) | (g << 8) | b; \ - \ - r = y2 + dR; \ - CLAMP(r); \ - g = y2 - dG; \ - CLAMP(g); \ - b = y2 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x + 1] = (r << 16) | (g << 8) | b; \ - } \ +#define DECODE_YVYU422() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + u = src[0] - 0x80; \ + y1 = src[1]; \ + v = src[2] - 0x80; \ + y2 = src[3]; \ + src += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ + } while (0) + +#define DECODE_YUV12_PACKED() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + u = uvsrc[3] - 0x80; \ + y1 = src[0]; \ + v = uvsrc[2] - 0x80; \ + y2 = src[1]; \ + src += 4; \ + uvsrc += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ } while (0) void @@ -4185,11 +4250,13 @@ mach64_overlay_draw(svga_t *svga, int displine) int x; int h_acc = 0; int h_max = (mach64->scaler_height_width >> 16) & 0x3ff; + int src_w = h_max; int h_inc = mach64->overlay_scale_inc >> 16; int v_max = mach64->scaler_height_width & 0x3ff; int v_inc = mach64->overlay_scale_inc & 0xffff; uint32_t *p; uint8_t *src = &svga->vram[svga->overlay.addr]; + uint8_t *uvsrc = src; int old_y = mach64->overlay_v_acc; int y_diff; int video_key_fn = mach64->overlay_key_cntl & 5; @@ -4198,6 +4265,11 @@ mach64_overlay_draw(svga_t *svga, int displine) p = &buffer32->line[displine][svga->x_add + mach64->svga.overlay_latch.x]; + if (mach64->overlay_cur_y >= 2) { + /* Avoid corrupt UV data on YUV12 packed modes */ + uvsrc = &svga->vram[mach64->overlay_base + svga->overlay.pitch * 2 * (!(mach64->overlay_cur_y & 1) ? (mach64->overlay_cur_y + 1) : mach64->overlay_cur_y)]; + } + if (mach64->scaler_update) { switch (mach64->scaler_format) { case 0x3: @@ -4209,6 +4281,9 @@ mach64_overlay_draw(svga_t *svga, int displine) case 0x6: DECODE_ARGB8888(); break; + case 0xa: + DECODE_YUV12_PACKED(); + break; case 0xb: DECODE_VYUY422(); break; @@ -4217,7 +4292,7 @@ mach64_overlay_draw(svga_t *svga, int displine) break; default: - mach64_log("Unknown Mach64 scaler format %x\n", mach64->scaler_format); + pclog("Unknown Mach64 scaler format %x\n", mach64->scaler_format); /*Fill buffer with something recognisably wrong*/ for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x++) mach64->overlay_dat[x] = 0xff00ff; @@ -4354,6 +4429,7 @@ mach64_overlay_draw(svga_t *svga, int displine) svga->overlay.addr += svga->overlay.pitch * 2 * y_diff; mach64->scaler_update = y_diff; + mach64->overlay_cur_y += y_diff; } static void @@ -4389,7 +4465,7 @@ mach64_io_remove(mach64_t *mach64) io_removehandler(0x01ce, 0x0002, mach64_in, NULL, NULL, mach64_out, NULL, NULL, mach64); if (mach64->block_decoded_io && mach64->block_decoded_io < 0x10000) - io_removehandler(mach64->block_decoded_io, 0x0400, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); + io_removehandler(mach64->block_decoded_io, 0x0100, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); } static void @@ -4429,7 +4505,7 @@ mach64_io_set(mach64_t *mach64) io_sethandler(0x01ce, 0x0002, mach64_in, NULL, NULL, mach64_out, NULL, NULL, mach64); if (mach64->use_block_decoded_io && mach64->block_decoded_io && mach64->block_decoded_io < 0x10000) - io_sethandler(mach64->block_decoded_io, 0x0400, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); + io_sethandler(mach64->block_decoded_io, 0x0100, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); } static uint8_t @@ -4478,7 +4554,6 @@ static void mach64_write_linear(uint32_t addr, uint8_t val, void *priv) { svga_t *svga = (svga_t *) priv; - cycles -= svga->monitor->mon_video_timing_write_b; addr &= svga->decode_mask; @@ -4507,10 +4582,38 @@ mach64_writew_linear(uint32_t addr, uint16_t val, void *priv) static void mach64_writel_linear(uint32_t addr, uint32_t val, void *priv) { - svga_t *svga = (svga_t *) priv; + svga_t *svga = (svga_t *) priv; + mach64_t *mach64 = (mach64_t *) svga->priv; cycles -= svga->monitor->mon_video_timing_write_l; + if (((mach64->scaler_yuv_aper >> 4) & 0xc) && !!(addr & 0x800000) == !(mach64->scaler_yuv_aper & 0x20)) { + uint32_t offset_from_base = addr & 0x7FFFFF; + if (addr & 0x800000) bswap32s(&val); + if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x4) { // Y plane + offset_from_base <<= 1; + svga->vram[offset_from_base & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 1) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 4) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 5) & svga->vram_mask] = ((val >> 24) & 0xFF); + } + else if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x8 || ((mach64->scaler_yuv_aper >> 4) & 0xc) == 0xc) { + offset_from_base <<= 2; + if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x8) { // U plane + svga->vram[(offset_from_base + 3) & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 7) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 11) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 15) & svga->vram_mask] = ((val >> 24) & 0xFF); + } else { // V plane + svga->vram[(offset_from_base + 2) & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 6) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 10) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 14) & svga->vram_mask] = ((val >> 24) & 0xFF); + } + } + return; + } + addr &= svga->decode_mask; if (addr >= svga->vram_max) return; @@ -4519,6 +4622,42 @@ mach64_writel_linear(uint32_t addr, uint32_t val, void *priv) *(uint32_t *) &svga->vram[addr] = val; } +uint8_t +mach64_readb_be(uint32_t addr, void *priv) +{ + return mach64_read_linear(addr, priv); +} + +uint16_t +mach64_readw_be(uint32_t addr, void *priv) +{ + return bswap16(mach64_readw_linear(addr, priv)); +} + +uint32_t +mach64_readl_be(uint32_t addr, void *priv) +{ + return bswap32(mach64_readl_linear(addr, priv)); +} + +void +mach64_writeb_be(uint32_t addr, uint8_t val, void *priv) +{ + return mach64_write_linear(addr, val, priv); +} + +void +mach64_writew_be(uint32_t addr, uint16_t val, void *priv) +{ + return mach64_writew_linear(addr, bswap16(val), priv); +} + +void +mach64_writel_be(uint32_t addr, uint32_t val, void *priv) +{ + return mach64_writel_linear(addr, bswap32(val), priv); +} + uint8_t mach64_pci_read(UNUSED(int func), int addr, void *priv) { @@ -4564,30 +4703,31 @@ mach64_pci_read(UNUSED(int func), int addr, void *priv) return mach64->linear_base >> 24; case 0x14: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return 0x01; /*Block decoded IO address*/ return 0x00; case 0x15: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 8; return 0x00; case 0x16: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 16; return 0x00; case 0x17: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 24; return 0x00; + case 0x30: - return mach64->pci_regs[0x30] & 0x01; /*BIOS ROM address*/ + return (mach64->on_board) ? 0 : (mach64->pci_regs[0x30] & 0x01); /*BIOS ROM address*/ case 0x31: return 0x00; case 0x32: - return mach64->pci_regs[0x32]; + return (mach64->on_board) ? 0 : mach64->pci_regs[0x32]; case 0x33: - return mach64->pci_regs[0x33]; + return (mach64->on_board) ? 0 : mach64->pci_regs[0x33]; case 0x3c: return mach64->int_line; @@ -4619,7 +4759,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) break; case 0x12: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) val = 0; mach64->linear_base = (mach64->linear_base & 0xff000000) | ((val & 0x80) << 16); mach64_updatemapping(mach64); @@ -4630,16 +4770,16 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) break; case 0x15: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); - mach64->block_decoded_io = (mach64->block_decoded_io & 0xffff0000) | ((val & 0xfc) << 8); + mach64->block_decoded_io = (mach64->block_decoded_io & 0xffff0000) | ((val & 0xff) << 8); if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_set(mach64); } break; case 0x16: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->block_decoded_io = (mach64->block_decoded_io & 0xff00fc00) | (val << 16); @@ -4648,7 +4788,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) } break; case 0x17: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->block_decoded_io = (mach64->block_decoded_io & 0x00fffc00) | (val << 24); @@ -4660,6 +4800,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) case 0x30: case 0x32: case 0x33: + if (mach64->on_board) return; mach64->pci_regs[addr] = val; if (mach64->pci_regs[0x30] & 0x01) { uint32_t biosaddr = (mach64->pci_regs[0x32] << 16) | (mach64->pci_regs[0x33] << 24); @@ -4679,7 +4820,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->io_base = val & 0x03; - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) mach64->use_block_decoded_io = val & 0x04; if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_set(mach64); @@ -4700,7 +4841,7 @@ mach64_common_init(const device_t *info) svga = &mach64->svga; mach64->type = info->local & 0xff; - mach64->vram_size = device_get_config_int("memory"); + mach64->vram_size = (info->local & (1 << 20)) ? 4 : device_get_config_int("memory"); mach64->vram_mask = (mach64->vram_size << 20) - 1; if (mach64->type > MACH64_GX) @@ -4717,9 +4858,10 @@ mach64_common_init(const device_t *info) mach64_overlay_draw); mem_mapping_add(&mach64->linear_mapping, 0, 0, mach64_read_linear, mach64_readw_linear, mach64_readl_linear, mach64_write_linear, mach64_writew_linear, mach64_writel_linear, NULL, MEM_MAPPING_EXTERNAL, svga); + mem_mapping_add(&mach64->linear_mapping_big_endian, 0, 0, mach64_readb_be, mach64_readw_be, mach64_readl_be, mach64_writeb_be, mach64_writew_be, mach64_writel_be, NULL, MEM_MAPPING_EXTERNAL, svga); mem_mapping_add(&mach64->mmio_linear_mapping, 0, 0, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); mem_mapping_add(&mach64->mmio_linear_mapping_2, 0, 0, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); - mem_mapping_add(&mach64->mmio_mapping, 0xbc000, 0x04000, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); + mem_mapping_add(&mach64->mmio_mapping, 0xbf000, 0x1000, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); mem_mapping_disable(&mach64->mmio_mapping); mach64_io_set(mach64); From 9ef1b194867a92ba681c4e482b08ad09c79702d7 Mon Sep 17 00:00:00 2001 From: Verloren50000 <110334428+Verloren50000@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:32:46 +0800 Subject: [PATCH 05/11] Add the BCM FR510 (Packard Bell/NEC OEM) ROM + update (#6231) * machine.h: FR510 added. * m_at_sockets7.c: BCM FR510 added, including the BIOS Selector. * machine_table.c: BCM FR510 is now added. * machine_table.c: fr510-generic * m_at_sockets7.c: AGP Bridge for BCM FR510 * machine_table.c: FR510 PCIOnly * m_at_socket7.c: Fixes at FR510 * m_at_sockets7.c: 510S228.BIN -> 510S128.BIN * machine_table.c: an update for FR510 --- src/include/86box/machine.h | 4 +++ src/machine/m_at_sockets7.c | 69 +++++++++++++++++++++++++++++++++++++ src/machine/machine_table.c | 48 ++++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index b88d8f492..4fd0e16e8 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1069,6 +1069,10 @@ extern int machine_at_m560_init(const machine_t *); /* m_at_sockets7.c */ /* ALi ALADDiN V */ extern int machine_at_p5a_init(const machine_t *); +#ifdef EMU_DEVICE_H +extern const device_t fr510_device; +#endif +extern int machine_at_fr510_init(const machine_t *); extern int machine_at_m579_init(const machine_t *); extern int machine_at_gwlucas_init(const machine_t *); extern int machine_at_5aa_init(const machine_t *); diff --git a/src/machine/m_at_sockets7.c b/src/machine/m_at_sockets7.c index a39b2cef4..a70bccc03 100644 --- a/src/machine/m_at_sockets7.c +++ b/src/machine/m_at_sockets7.c @@ -74,6 +74,75 @@ machine_at_p5a_init(const machine_t *model) return ret; } +static const device_config_t fr510_config[] = { + // clang-format off + { + .name = "bios", + .description = "BIOS Version", + .type = CONFIG_BIOS, + .default_string = "fr510", + .default_int = 0, + .file_filter = "", + .spinner = { 0 }, + .bios = { + { .name = "Award Modular BIOS v4.51PG - Revision 1.19 (01/15/1999)", .internal_name = "fr510_1999", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/FR510119.bin", "" } }, + { .name = "Award Modular BIOS v4.51PG - Revision SCC-128 (11/27/2002)", .internal_name = "fr510", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/510S128.BIN", "" } }, + { .files_no = 0 } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } + // clang-format on +}; + +const device_t fr510_device = { + .name = "BCM FR510 (Packard Bell/NEC OEM)", + .internal_name = "fr510_device", + .flags = 0, + .local = 0, + .init = NULL, + .close = NULL, + .reset = NULL, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = fr510_config +}; + +int +machine_at_fr510_init(const machine_t *model) +{ + int ret = 0; + const char* fn; + + /* No ROMs available */ + if (!device_available(model->device)) + return ret; + + device_context(model->device); + fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); + ret = bios_load_linear(fn, 0x000c0000, 262144, 0); + device_context_restore(); + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); + pci_register_slot(0x14, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1); + device_add(&ali1541_device); + device_add(&ali1543c_device); /* +0 */ + device_add(&sst_flash_39sf020_device); + spd_register(SPD_TYPE_SDRAM, 0x7, 512); + + if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)) + device_add(machine_get_snd_device(machine)); + + return ret; +} + int machine_at_m579_init(const machine_t *model) { diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 8c9055c9b..1ae51e5c7 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -12388,7 +12388,7 @@ const machine_t machines[] = { }, /* The BIOS sends KBC command B3 which indicates an AMI (or VIA VT82C42N) KBC. */ { - .name = "[i430FX] NEC PowerMate V", + .name = "[i430FX] NEC PowerMate Vxxx", .internal_name = "powermatev", .type = MACHINE_TYPE_SOCKET5, .chipset = MACHINE_CHIPSET_INTEL_430FX, @@ -15329,7 +15329,7 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, - /* This has the Phoenix MultiKey KBC firmware on the NSC Suepr I/O chip. */ + /* This has the Phoenix MultiKey KBC firmware on the NSC Super I/O chip. */ { .name = "[i430TX] Packard Bell PB790", .internal_name = "an430tx", @@ -16007,6 +16007,50 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* OEM-only BCM FR510, has the ALi M1543C southbridge with on-chip KBC. */ + { + .name = "[ALi ALADDiN V] BCM FR510 (Packard Bell/NEC OEM)", + .internal_name = "fr510", + .type = MACHINE_TYPE_SOCKETS7, + .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, + .init = machine_at_fr510_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 2200, + .max_voltage = 3200, + .min_multi = 1.5, + .max_multi = 5.5 + }, + .bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB, /* Has internal video: ATI 3D Rage IIc AGP (Rage 2) */ + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_SOUND | MACHINE_USB, + .ram = { + .min = 8192, + .max = 262144, + .step = 8192 + }, + .nvrmask = 255, + .jumpered_ecp_dma = 0, + .default_jumpered_ecp_dma = -1, + .kbc_device = NULL, + .kbc_params = 0x00000000, + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = &fr510_device, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = &cs4235_onboard_device, + .net_device = NULL + }, /* M1534c kbc */ { .name = "[ALi ALADDiN V] Gateway Lucas", From f692c00c6d6ec4ae5c34c102be0b8c11875a325c Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 15:21:13 +0200 Subject: [PATCH 06/11] Remove the BCM FR510 because it turned out to be improperly implemented fixes #6236. --- src/include/86box/machine.h | 4 --- src/machine/m_at_sockets7.c | 69 ------------------------------------- src/machine/machine_table.c | 44 ----------------------- 3 files changed, 117 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 4fd0e16e8..b88d8f492 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1069,10 +1069,6 @@ extern int machine_at_m560_init(const machine_t *); /* m_at_sockets7.c */ /* ALi ALADDiN V */ extern int machine_at_p5a_init(const machine_t *); -#ifdef EMU_DEVICE_H -extern const device_t fr510_device; -#endif -extern int machine_at_fr510_init(const machine_t *); extern int machine_at_m579_init(const machine_t *); extern int machine_at_gwlucas_init(const machine_t *); extern int machine_at_5aa_init(const machine_t *); diff --git a/src/machine/m_at_sockets7.c b/src/machine/m_at_sockets7.c index a70bccc03..a39b2cef4 100644 --- a/src/machine/m_at_sockets7.c +++ b/src/machine/m_at_sockets7.c @@ -74,75 +74,6 @@ machine_at_p5a_init(const machine_t *model) return ret; } -static const device_config_t fr510_config[] = { - // clang-format off - { - .name = "bios", - .description = "BIOS Version", - .type = CONFIG_BIOS, - .default_string = "fr510", - .default_int = 0, - .file_filter = "", - .spinner = { 0 }, - .bios = { - { .name = "Award Modular BIOS v4.51PG - Revision 1.19 (01/15/1999)", .internal_name = "fr510_1999", .bios_type = BIOS_NORMAL, - .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/FR510119.bin", "" } }, - { .name = "Award Modular BIOS v4.51PG - Revision SCC-128 (11/27/2002)", .internal_name = "fr510", .bios_type = BIOS_NORMAL, - .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/510S128.BIN", "" } }, - { .files_no = 0 } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } - // clang-format on -}; - -const device_t fr510_device = { - .name = "BCM FR510 (Packard Bell/NEC OEM)", - .internal_name = "fr510_device", - .flags = 0, - .local = 0, - .init = NULL, - .close = NULL, - .reset = NULL, - .available = NULL, - .speed_changed = NULL, - .force_redraw = NULL, - .config = fr510_config -}; - -int -machine_at_fr510_init(const machine_t *model) -{ - int ret = 0; - const char* fn; - - /* No ROMs available */ - if (!device_available(model->device)) - return ret; - - device_context(model->device); - fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); - ret = bios_load_linear(fn, 0x000c0000, 262144, 0); - device_context_restore(); - - machine_at_common_init_ex(model, 2); - - pci_init(PCI_CONFIG_TYPE_1); - pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); - pci_register_slot(0x14, PCI_CARD_NORMAL, 1, 2, 3, 4); - pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1); - device_add(&ali1541_device); - device_add(&ali1543c_device); /* +0 */ - device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x7, 512); - - if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)) - device_add(machine_get_snd_device(machine)); - - return ret; -} - int machine_at_m579_init(const machine_t *model) { diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 1ae51e5c7..cd68127e1 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -16007,50 +16007,6 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, - /* OEM-only BCM FR510, has the ALi M1543C southbridge with on-chip KBC. */ - { - .name = "[ALi ALADDiN V] BCM FR510 (Packard Bell/NEC OEM)", - .internal_name = "fr510", - .type = MACHINE_TYPE_SOCKETS7, - .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, - .init = machine_at_fr510_init, - .p1_handler = machine_generic_p1_handler, - .gpio_handler = NULL, - .available_flag = MACHINE_AVAILABLE, - .gpio_acpi_handler = NULL, - .cpu = { - .package = CPU_PKG_SOCKET5_7, - .block = CPU_BLOCK_NONE, - .min_bus = 50000000, - .max_bus = 66666667, - .min_voltage = 2200, - .max_voltage = 3200, - .min_multi = 1.5, - .max_multi = 5.5 - }, - .bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB, /* Has internal video: ATI 3D Rage IIc AGP (Rage 2) */ - .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_SOUND | MACHINE_USB, - .ram = { - .min = 8192, - .max = 262144, - .step = 8192 - }, - .nvrmask = 255, - .jumpered_ecp_dma = 0, - .default_jumpered_ecp_dma = -1, - .kbc_device = NULL, - .kbc_params = 0x00000000, - .kbc_p1 = 0x00000cf0, - .gpio = 0xffffffff, - .gpio_acpi = 0xffffffff, - .device = &fr510_device, - .kbd_device = NULL, - .fdc_device = NULL, - .sio_device = NULL, - .vid_device = NULL, - .snd_device = &cs4235_onboard_device, - .net_device = NULL - }, /* M1534c kbc */ { .name = "[ALi ALADDiN V] Gateway Lucas", From 1fc4dda73e623686ef0a7bda9b3a1aaf9676602e Mon Sep 17 00:00:00 2001 From: Maxwell Scott Date: Sat, 27 Sep 2025 20:43:45 +0700 Subject: [PATCH 07/11] Add MSI name for Gateway Lucas (#6235) * Fix bus speeds for FR510 + add MSI name for Lucas * Removed table for FR510 --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index cd68127e1..2462ccd7a 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -16009,7 +16009,7 @@ const machine_t machines[] = { }, /* M1534c kbc */ { - .name = "[ALi ALADDiN V] Gateway Lucas", + .name = "[ALi ALADDiN V] Gateway Lucas (MSI MS-5185)", .internal_name = "gwlucas", .type = MACHINE_TYPE_SOCKETS7, .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, From c22bd0e63ce79beb81dd8e26c2c945f90538e18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= <13226155+dhrdlicka@users.noreply.github.com> Date: Sat, 27 Sep 2025 15:55:54 +0200 Subject: [PATCH 08/11] Update pull request template [skip ci] Added a checklist item for local testing validation. --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e09b5636f..9e5521cf4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,6 +5,7 @@ _Briefly describe what you are submitting._ Checklist ========= * [ ] Closes #xxx +* [ ] I have tested my changes locally and validated that the functionality works as intended * [ ] I have discussed this with core contributors already * [ ] This pull request requires changes to the ROM set * [ ] I have opened a roms pull request - https://github.com/86Box/roms/pull/changeme/ From 58b4af4689f364c9d4c1a9d9106e6398166e5029 Mon Sep 17 00:00:00 2001 From: Maxwell Scott Date: Sat, 27 Sep 2025 21:26:31 +0700 Subject: [PATCH 09/11] Slightly name correction to Vectra VL 5 + added codename (#6237) * Fix bus speeds for FR510 + add MSI name for Lucas * Removed table for FR510 * Name changes to Vectra VL 5 Series 4 Also added codename. --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 2462ccd7a..8c8dc6b91 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -12973,7 +12973,7 @@ const machine_t machines[] = { /* Has a SM(S)C FDC37C932 Super I/O chip with on-chip KBC with AMI MegaKey (revision '5') KBC firmware. */ { - .name = "[i430FX] HP Vectra VL 5 Series 4", + .name = "[i430FX] HP Vectra VL 5/xxx Series 4 (Chimay)", .internal_name = "vectra54", .type = MACHINE_TYPE_SOCKET7_3V, .chipset = MACHINE_CHIPSET_INTEL_430FX, From 74aa15644d177d0b16df075885fb5ef808d55105 Mon Sep 17 00:00:00 2001 From: andresdelcampo <33843515+andresdelcampo@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:53:45 +0200 Subject: [PATCH 10/11] Fix remember size and position regression in which vertical size could grow when reopening the VM (#6239) * Change window resizing logic when using 4:3 aspect ratio Change window resizing logic when using 4:3 aspect ratio to resize content only. Fixed issues in Remember size and position that are derived from the change. There is a slight flicker while resizing with force 4:3 aspect ratio. * Fix regression that adds vertical size when showing toolbar and statusbar and remembering size and po The recent rework on the force aspect 4:3 which needed to revisit the remembering of size and position introduced a regression that would save the size without considering the non-content window elements that could be showed or hidden. Now it calculates it accordingly. --- src/qt/qt_mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index fbc2d8a47..e88bedd90 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -936,8 +936,11 @@ MainWindow::closeEvent(QCloseEvent *event) const bool wasMax = isMaximized(); QRect normal = wasMax ? this->normalGeometry() : this->geometry(); // Save WINDOW size (not the content widget’s 4:3 box) + const int chromeHeight = geometry().height() - ui->stackedWidget->height(); window_w = normal.width(); - window_h = normal.height(); + window_h = normal.height() - chromeHeight; + if (window_h < 0) + window_h = 0; if (!QApplication::platformName().contains("wayland")) { window_x = normal.x(); window_y = normal.y(); From bc41f8bbb6472809a878d2233e472b8baf8f289a Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 27 Sep 2025 23:57:12 +0600 Subject: [PATCH 11/11] Fix sign position of DDA accumulator registers (#6241) --- src/video/vid_s3_virge.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index d9e1d91ef..ac96aedef 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -1952,9 +1952,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) break; case 0x8190: virge->streams.sec_ctrl = val; - virge->streams.dda_horiz_accumulator = val & 0xfff; - if (val & 0x1000) - virge->streams.dda_horiz_accumulator |= ~0xfff; + virge->streams.dda_horiz_accumulator = val & 0x7ff; + if (val & 0x800) + virge->streams.dda_horiz_accumulator |= ~0x7ff; virge->streams.sdif = (val >> 24) & 7; break; @@ -2030,9 +2030,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) virge->streams.k2_vert_scale |= ~0x7ff; break; case 0x81e8: - virge->streams.dda_vert_accumulator = val & 0xfff; - if (val & 0x1000) - virge->streams.dda_vert_accumulator |= ~0xfff; + virge->streams.dda_vert_accumulator = val & 0x7ff; + if (val & 0x800) + virge->streams.dda_vert_accumulator |= ~0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount;