From dbc96ac5eaee17a606cec28a9fced95e3bf4f364 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Tue, 21 Oct 2025 20:45:51 +0300 Subject: [PATCH] wozfdc.cpp: improve timing of data register reads When one of the even-numbered c08x locations is read, the FDC returns the value of its data register. However, its LSS (logic state sequencer) runs fast enough that between when the CPU puts the address on the bus and when it reads the result, it manages to complete one cycle. This is explained in Understanding the Apple II, page 9-22. This patch emulates this behavior. Its effect can be seen with the INIT command in DOS 3.2 and earlier. Without the patch, the command fails with an I/O error; with the patch, it succeeds. The reason is that it checks if the disk is write-protected after formatting every track, so it executes the following instructions while the FDC is in write mode: lda $c08d,x lda $c08e,x bmi error The way it's supposed to work is: 1. The second LDA instruction switches the FDC to "check write protect" mode. 2. The LSS runs for 1 cycle, which loads the write-protect status into the data register. 3. The data register is copied into A, which puts the write-protect status into the N flag. 4. The BMI instruction tests the status. Without step 2, the N flag is loaded with whatever was in the high bit of the data register, which seems to be 1 more often than not, so DOS thinks the disk is write-protected, and aborts. --- src/devices/machine/wozfdc.cpp | 9 +++++++-- src/devices/machine/wozfdc.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/devices/machine/wozfdc.cpp b/src/devices/machine/wozfdc.cpp index bee2f043e7ed1..dca739086deeb 100644 --- a/src/devices/machine/wozfdc.cpp +++ b/src/devices/machine/wozfdc.cpp @@ -207,6 +207,9 @@ uint8_t wozfdc_device::read(offs_t offset) control(offset); if(!(offset & 1)) { + // The FDC runs faster than the CPU, so it has time to run + // for one cycle before the CPU can observe the data register. + lss_sync(1); return data_reg; } return 0xff; @@ -352,14 +355,13 @@ void wozfdc_device::lss_start() floppy->set_write_splice(write_start_time); } -void wozfdc_device::lss_sync() +void wozfdc_device::lss_sync(uint64_t extra_cycles) { if(!active) return; attotime next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles-1)) : attotime::never; - uint64_t cycles_limit = time_to_cycles(machine().time()); uint64_t cycles_next_flux = next_flux != attotime::never ? time_to_cycles(next_flux) : uint64_t(-1); uint64_t cycles_next_flux_down = cycles_next_flux != uint64_t(-1) ? cycles_next_flux+1 : uint64_t(-1); @@ -368,6 +370,9 @@ void wozfdc_device::lss_sync() else address |= 0x10; + uint64_t cycles_limit = time_to_cycles(machine().time()) + extra_cycles; + assert(cycles <= cycles_limit); // make sure we aren't going back in time + while(cycles < cycles_limit) { uint64_t cycles_next_trans = cycles_limit; if(cycles_next_trans > cycles_next_flux && cycles < cycles_next_flux) diff --git a/src/devices/machine/wozfdc.h b/src/devices/machine/wozfdc.h index d61e9aab8a8e3..bb52dbbc0e996 100644 --- a/src/devices/machine/wozfdc.h +++ b/src/devices/machine/wozfdc.h @@ -49,7 +49,7 @@ class wozfdc_device: void a3_update_drive_sel(); void lss_start(); - void lss_sync(); + void lss_sync(uint64_t extra_cycles = 0); enum { MODE_IDLE, MODE_ACTIVE, MODE_DELAY