On the IEC bus, CLK and DATA are wired-AND between all devices, so one drive can signal another drive directly (drive-to-drive). In VICE, every drive CPU is executed on its own, up to the main CPU clock, in drive_cpu_execute_all() (src/drive/drive.c). That function is called when the C64 accesses the serial port through CIA2 (src/iecbus/iecbus.c) and once per frame from drive_vsync_hook(). Each drive therefore runs alone for the whole interval, and a drive does not see another drive's line changes until the next sync point.
Result: if the C64 does not touch $DD00 (for example, it runs a JMP * loop), a drive sees no change on the bus from another drive for a whole frame, even though that drive toggles CLK every few microseconds. On real hardware the drives run in parallel and see each other at once. Drive-to-drive signalling therefore only works in VICE while the C64 keeps reading $DD00.
How to reproduce (x64sc, two 1581s on 8 and 9, true drive emulation; any drive type works with its own port addresses):
C64: SEI, keep ATN/CLK/DATA released ($DD00 AND #$C7), then JMP *.
Drive 8, code at $0500 (load and start it with the monitor: dev 8, load, r pc=0500): SEI; LDA #$7F/STA $400D; LDA #$7A/STA $4003; then 256 times: toggle bit 3 of $4001 (CLK out), about 20 cycles apart.
Drive 9, the same setup, then 256 times: LDA $4001 / STA $0800,X / INX / BNE (about 13 cycles per sample).
Start both from the monitor at the same time, let them run, read 9:$0800-$08FF.
Expected: bit 2 of the samples (CLK in) alternates every 1-2 samples. Actual: all 256 samples are identical. If the C64 loop is changed to LDA $DD00 / JMP, the samples alternate as expected (the CIA2 reads make VICE run the drives in step).
Proposed fix (patch attached, against 3.10 src/drive/drive.c): when more than one drive is enabled, drive_cpu_execute_all() runs all drives forward together in small steps of the main clock instead of one after the other, and drive_vsync_hook() goes through it as well. In the patch the step is set with an environment variable, VICE_DRIVE_SLICE=n (main CPU clocks), so that it is off by default and costs nothing when unused; a resource (for example "DriveSyncSlice") would fit VICE better. With n = 1 the test above shows the expected alternation with the C64 in JMP *, matching the CIA2-polling case. It costs emulation speed when on, since every drive is entered once per step.
please remove the use of that environment variable, this shouldn't be an option. And please rebase the patch on (and test against) current trunk :)
Thanks. Updated patch attached (drive-in-step-r46284.patch), against trunk r46284, no option and no environment variable.
What I found on trunk: with the default "no idle" method, drive_cycle_hook() catches every drive up to the main CPU clock after each main CPU instruction, so a drive does see another drive's line changes, unlike 3.10. The drives are still caught up one after the other though, a whole instruction at a time (and longer when the CPU is stalled by badlines), so one drive can run several cycles ahead of another. Drive-to-drive transfers that rely on bus timing (the IEC style: handshake per byte, bits timed) still fail on trunk.
The patch: drive_cpu_execute_in_step() runs all enabled drives forward together, one main CPU clock at a time, when more than one drive is enabled; with one drive it does exactly what it did before. drive_cpu_execute_all(), drive_catch_up_hook() and drive_cycle_hook() (for the drives in "no idle" mode) go through it.
Tests (x64sc, headless, r46284, four 1581s in true drive emulation, the C64 in a JMP * loop with the serial lines released; each drive sends a 256-byte block to the other three in turn, every byte checked):
transfer stock r46284 patched handshake per byte, 1 bit per CLK edge wrong bytes exact (431 drive cycles/byte) handshake per byte, 2 bits per timed step wrong bytes exact (154 drive cycles/byte)
two drives, one toggles CLK every 20 cycles, the other samples every 13 cycles: both stock and patched see every toggle (on 3.10 the second drive saw none).
Regression: a C64 <-> four 1581 program (C64 driven fast transfers, checksums over every transfer, screen compared) gives the same results and the same C64 cycle counts on stock and patched trunk.
Speed (warp, 50M C64 cycles, 1581s idling in DOS): 1 drive 5.0 s stock / 4.7 s patched (no change); 4 drives 6.3 s stock / 7.7 s patched (about 22 % slower, only when 2 or more drives are enabled).
I have not run the VICE testbench; please tell me if there is a test set you want it checked against.
we have very few drive (IEC bus) tests unfortunately, wish we had
i'll look at this later, thanks
merged in r46285 - thanks!
now we only need to find out what broke the parallel speeders (dolphin dos) - feel free to have a look as well :)
There seems to be a problem.
Emu hangs.
we should probably find out what broke the parallel speeders and then just remove the idle things :) (And i can see how using different methods for different drives doesnt work at all)
Last edit: gpz 2026-10-19
reproduced: drive 8 at the default idle method, drive 9 at "skip cycles", LOAD from 8 hangs. The cause is in r46285's drive_cpu_execute_in_step: it stepped all enabled drives from the oldest drive's clock. Drive 8 ("no idle") is run after every CPU instruction while drive 9 is only caught up on bus access, so drive 9 lagged by millions of cycles, and every step to an older clock set drive 8's last_clk back. Drive 8 then emulated the same time again and ran ahead of the computer, which broke the serial handshake.
The attached patch lets the oldest drive catch up alone, in one call, to the next oldest drive’s clock, and never runs a drive to a clock older than its own.
applied in r46291 - thanks!
That seems to work now.