Skip to content

imagedev/floppy: batch sequential flux writes - #15425

Merged
rb6502 merged 2 commits into
mamedev:masterfrom
RealDeuce:batch-floppy
Aug 16, 2026
Merged

imagedev/floppy: batch sequential flux writes#15425
rb6502 merged 2 commits into
mamedev:masterfrom
RealDeuce:batch-floppy

Conversation

@RealDeuce

@RealDeuce RealDeuce commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Replace the batched write_flux(start, end, count, transitions) interface
with an event-driven streaming interface: write_start, write_flux_change,
write_end and write_flush. This makes the floppy device own the write-gate
lifecycle and the transition buffer rather than requiring every controller
to maintain its own.

Some floppy controllers commit generated write flux in very small spans.
This is especially visible while formatting, where a controller can emit a
full track byte-by-byte. With the old interface each commit edited the
persistent track vector immediately, making format operations orders of
magnitude slower than the emulated disk timing. The new interface solves
this naturally: transitions accumulate in the floppy device and are written
to the track representation only at flush points.

write_start activates the write gate and snapshots the current track
identity. write_flux_change appends a single transition timestamp to an
internal buffer. write_end deactivates the write gate and flushes buffered
transitions to the track. write_flush commits transitions earlier than a
given time without ending the write, retaining later transitions for a
subsequent flush or end.

All controllers now call write_flux_change directly as transitions are
generated rather than maintaining local buffers. Controllers that use
speculative execution with checkpoint/rollback (wd_fdc, 64h156, c2040fdc,
paulafdc) previously needed local 32-entry write buffers to avoid
duplicating transitions after rollback. The floppy device now handles this
transparently: write_flux_change detects out-of-sequence timestamps and
discards stale entries, so replayed transitions replace their predecessors
cleanly.

The hp9895 controller supports simultaneous read and write for write-with-
verify loopback. It previously read back transitions from the fdc_pll
write buffer. Since that buffer no longer exists, hp9895 now maintains its
own loopback buffer that captures transition times as they are generated
and serves them back through get_next_transition when the write gate is
active.

Add a floppy parameter to fdc_pll_t::start_writing so that write_start is
called on the floppy device at the correct point in the PLL lifecycle.
All controllers that use fdc_pll_t (wd_fdc, swim3, mc6843, upd765, i8271,
hdc92x4, hp9885, isbc202, c8050fdc, victor9k_fdc, hp9895) now pass their
floppy pointer through start_writing. The same change applies to the
wd_fdc digital PLL and the paulafdc PLL.

The old fdc_pll_t::write_next_bit had a lazy-init path that implicitly
started writing on the first bit if start_writing had not been called.
Three controllers (upd765, i8271, hdc92x4) relied on this for sector
writes -- only their format-track paths called start_writing explicitly.
That lazy init is removed; the sector-write paths now call start_writing
at the point where they transition from reading gap data to writing sector
data.

@galibert

galibert commented Jun 2, 2026

Copy link
Copy Markdown
Member

I think buffering in the floppy_device instead of the individual fdcs is a good idea. But if we do that, we need to commit to it. That means changing the interface in floppy_device from the existing one to something along the lines of write_start(time), write_end(time), write_flux_change(time) and write_flush(time). Would you be ready to try for that?

@RealDeuce

Copy link
Copy Markdown
Contributor Author

Honestly, I really sweated this one being sure I couldn't actually break anything... I'm not sure how good the CI is in this project, but honestly, the idea of being able to break everything with a floppy gives the the willies.

I can maybe dig in a bit this weekend, I only really looked at the one drive implementation so maybe taking a wider look will help settle my stomach.

@RealDeuce

Copy link
Copy Markdown
Contributor Author

Ah, took a quick look, yeah, I see what you mean now... it looks like there's a few different solutions to the problem. I'll poke at it during the week and see what I come up with.

@RealDeuce

RealDeuce commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Nerd Sniping

@RealDeuce

Copy link
Copy Markdown
Contributor Author

The HP and VTech ones were the biggest brain twisters in there. Only about 97% sure I've retained the VTech time travelling behaviour. 😄

@RealDeuce

Copy link
Copy Markdown
Contributor Author

Updated the top comment since it looks like you do squash merges here.

@startaq

startaq commented Aug 14, 2026

Copy link
Copy Markdown
Member

I assume this is still something we want - please do a rebase to resolve the conflicts @RealDeuce.

Some floppy controllers commit generated write flux in very small spans.
This is especially visible while formatting, where a controller can emit a
full track byte-by-byte and each byte-level commit previously edited the
persistent track vector immediately. That turns one logical sequential track
write into thousands of repeated track scans and insertions, making format
operations orders of magnitude slower than the emulated disk timing.

Keep the existing write path semantics by delaying only contiguous writes to
the same cylinder, side and subcylinder. The accumulated span is flushed
before any operation that can observe or depend on committed media state,
including cache fills, write-splice updates and image commit. Non-contiguous
writes or writes to another track also flush first, so existing callers that
issue isolated writes continue to take the same path with no behavioral
change beyond avoiding redundant intermediate vector edits.

The pending span stores the original start/end positions and concatenated
flux-change positions, then reuses the existing wspan_remove_damaged and
wspan_write helpers when flushed. This preserves the existing damaged-region
handling and track modification logic while making sequential write streams
behave like the single larger write they represent.
@RealDeuce
RealDeuce force-pushed the batch-floppy branch 2 times, most recently from fc84bbc to f9d78ad Compare August 16, 2026 10:39
Replace the batched write_flux(start, end, count, transitions) interface with
an event-driven streaming interface: write_start, write_flux_change, write_end
and write_flush.  This makes the floppy device own the write-gate lifecycle
and the transition buffer rather than requiring every controller to maintain
its own.

write_start activates the write gate and snapshots the current track identity.
write_flux_change appends a single transition timestamp to an internal buffer.
write_end deactivates the write gate and flushes buffered transitions to the
track.  write_flush commits transitions earlier than a given time without
ending the write, retaining later transitions for a subsequent flush or end.

All controllers now call write_flux_change directly as transitions are
generated rather than maintaining local buffers.  Controllers that use
speculative execution with checkpoint/rollback (wd_fdc, 64h156, c2040fdc,
paulafdc) previously needed local 32-entry write buffers to avoid duplicating
transitions after rollback.  The floppy device now handles this transparently:
write_flux_change detects out-of-sequence timestamps and discards stale
entries, so replayed transitions replace their predecessors cleanly.

The hp9895 controller supports simultaneous read and write for write-with-
verify loopback.  It previously read back transitions from the fdc_pll write
buffer.  Since that buffer no longer exists, hp9895 now maintains its own
loopback buffer that captures transition times as they are generated and
serves them back through get_next_transition when the write gate is active.

Add a floppy parameter to fdc_pll_t::start_writing so that write_start is
called on the floppy device at the correct point in the PLL lifecycle.  All
controllers that use fdc_pll_t (wd_fdc, swim3, mc6843, upd765, i8271,
hdc92x4, hp9885, isbc202, c8050fdc, victor9k_fdc, hp9895) now pass their
floppy pointer through start_writing.  The same change applies to the wd_fdc
digital PLL and the paulafdc PLL.

The old fdc_pll_t::write_next_bit had a lazy-init path that implicitly
started writing on the first bit if start_writing had not been called.  Three
controllers (upd765, i8271, hdc92x4) relied on this for sector writes -- only
their format-track paths called start_writing explicitly.  That lazy init is
removed; the sector-write paths now call start_writing at the point where they
transition from reading gap data to writing sector data.

The batching performance benefit from the earlier write_flux optimization is
preserved: transitions accumulate in the floppy device and are written to the
track representation only at flush points, avoiding repeated track vector
edits during sequential writes such as formatting.
@RealDeuce

Copy link
Copy Markdown
Contributor Author

I've redone it, did agat_fdc and it should be merge-ready again. To be honest though, I've pretty much assumed it's not something you want.

@rb6502

rb6502 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

I agree with @galibert that this is structurally a win, and once CI passes I'll merge it if he doesn't first. We have roughly 6 weeks until the release freeze for people to test things and a relatively small number of controllers involved. So it's certainly scary, but it's not unsurmountable. The existence of software lists makes it relatively easy to test a lot of systems, for instance.

@rb6502
rb6502 merged commit c690a9e into mamedev:master Aug 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants