Famicom Disk System Transfers: Byte Service, CRC, and IRQ State
Model FDS transfers as controller-driven byte events, separating timer IRQs, data service, motor state, CRC, BIOS sequencing, and uncertain acknowledgements.
The Famicom Disk System does not expose a modern block-read API to game software. Its RAM adapter contains a disk controller that serializes data from a disk side, raises a byte-transfer condition, and relies on the 6502 and BIOS routines to service data and status. The BIOS also manages disk insertion, motor control, file blocks, CRC, and timer-based delays. An emulator that copies a complete file into RAM at load time may boot a game, but it bypasses the transfer protocol that games and BIOS code can observe.
The FDS controller’s registers are documented largely through reverse engineering. The NESdev hardware reference is detailed but explicitly marks areas with incomplete or conflicting evidence; its user-research pages refine some behaviors. FCEU-derived implementations provide comparative code evidence, but they are not the silicon specification. Where references disagree, preserve the disagreement in tests and avoid presenting one interpretation as settled hardware behavior.
The adapter is a serial transfer device
The RAM adapter contains program RAM, character RAM, the FDS BIOS, and the 2C33 controller. The BIOS controls disk access and provides services to disk software. A disk image is not simply a cartridge ROM mapped into the 6502 address space: data is transferred through controller registers while the BIOS and software respond to status and interrupt events.
The central data registers are $4031 for data read from the disk and $4024 for data written to the disk. The controller’s status at $4030 includes a byte-transfer indication. The byte event represents a completed unit of serial transfer; it is not equivalent to the BIOS file loader finishing a whole block. Software or BIOS code must consume the byte or supply the next byte according to direction and mode.
The controller has independent controls for enabling disk I/O, motor and scan behavior, read/write direction, CRC, and byte-transfer IRQ generation. Keep those controls separate from disk-image selection and from whether a frontend has inserted a side. A frontend may provide side selection, but the emulated drive still has internal ready, protect, and transfer state.
Separate the timer IRQ from the disk byte IRQ
Two interrupt mechanisms are often conflated. The FDS timer uses reload registers $4020 and $4021 and a control register at $4022. Its counter advances in CPU-cycle-related time and may repeat or stop according to control bits. The disk byte-transfer interrupt is associated with the control state at $4025 and the byte-transfer flag. A game can use the timer for its own work while the BIOS or disk routine relies on transfer events.
Treat them as separate sources that can both feed the CPU IRQ line. Each needs its own enable, pending state, acknowledgement path, and timing source. Disabling disk I/O through $4023 affects disk-related behavior but should not be implemented as if it were merely clearing the timer reload value. Likewise, stopping the timer does not mean the disk has stopped shifting bytes.
The NESdev register reference warns that enabling a repeating timer with a zero reload can produce an interrupt on every CPU cycle. It also warns that timer IRQs should be disabled before disk access because the BIOS disk transfer code uses IRQs. This is a useful example of why a single boolean “FDS IRQ active” is insufficient: diagnostic traces must identify which source asserted the line.
Interpret status and acknowledgements conservatively
Register $4030 exposes status including the byte-transfer flag, timer interrupt state, CRC result, and other fields. Register $4031 returns the read byte; writing $4024 supplies outgoing data. Documentation describes servicing data as part of clearing or acknowledging disk-side conditions, but the precise relationship between reading $4030 and acknowledging the byte IRQ has conflicting reports.
One reference describes $4030 reads as acknowledging timer and disk IRQs and says the byte-transfer flag is reset when $4024, $4031, or $4030 is serviced. A later user-research page reports that the byte flag and its IRQ are cleared by servicing $4024 or $4031, and not by reading $4030. Those statements cannot both describe the same complete behavior. An emulator should not hide the discrepancy behind an undocumented simplification.
Isolate the status/acknowledgement rule in one controller method and write focused tests against known hardware traces or trusted test ROMs. Record the exact CPU read or write, status before and after, IRQ line level, direction, and transfer state. Make a model-compatibility switch only if evidence shows a hardware revision difference; do not introduce one solely to silence a game-specific symptom.
Drive state, CRC, and byte timing
The FDS control register governs drive operation and transfer modes. The motor state, scan/reset behavior, read-versus-write direction, CRC enable, and interrupt enable must not be collapsed into one “disk active” flag. The BIOS sequences these controls while preparing a read or write. The disk-ready and write-protect information exposed through status can affect whether the requested operation progresses.
CRC is part of the physical disk transfer format. The FDS BIOS enables CRC for file blocks and checks the controller’s result after data transfer. The commonly used FDS disk-image file omits the physical gaps and CRC bytes, so an emulator usually has to synthesize the transfer-side timing and CRC behavior from the logical representation. Do not tell users that the file contains the full raw magnetic track when it does not.
Byte cadence is documented in current reverse-engineered references, but exact units and conditions require care. One research page estimates a byte period from the master clock and CPU cycles and notes that the event depends on control bits, CRC state, motor state, and read/write mode. Other sections mark behavior as needing more research. Keep a single explicit emulated clock unit and avoid using a host timer. For high-confidence compatibility, corroborate the chosen cadence against BIOS behavior, open-source implementations, and test software.
BIOS sequencing and disk-side changes
The BIOS drives disk selection and file transfer through routines that communicate with the controller. It checks the disk identifier and block types, transfers bytes, handles CRC results, and reports errors. If an emulator bypasses this code by preloading a complete file, it can mask errors in $4030 status, IRQ ordering, disk-ready transitions, or read/write protection.
Disk-side insertion and ejection should occur through the emulator’s device boundary, not by changing memory while a BIOS transfer is mid-byte. Define whether a side change is rejected, deferred until the motor stops, or reported as a not-ready condition. The behavior should be deterministic and visible in logs. Preserve the disk-side identity and transfer offset in save states so that restoring mid-read resumes at the same logical position.
Persistent disk writes also need a clear policy. A software FDS image may represent a writable side, but the frontend path may be read-only or the user may not want modifications committed. Keep the emulated write-protect status consistent with the persistence policy, stage changes safely, and do not silently discard data after telling software that a write succeeded.
A compact event-driven model
Represent the controller with separate state for enabled register groups, drive motor, selected side, read/write direction, byte transfer phase, shift-register contents, CRC state, timer count, timer IRQ pending state, disk IRQ pending state, and ready/protect flags. On each emulated transfer event, update the byte buffer and status according to the chosen documented model, assert the appropriate IRQ if enabled, and wait for the CPU or BIOS to service the register.
A schematic read path is:
if disk_enabled and drive_ready and transfer_event:
data_register = next_disk_byte()
byte_pending = true
if byte_irq_enabled:
disk_irq_pending = true
if cpu_reads_data_register:
return data_register
acknowledge_serviced_byte()
This is a state-machine sketch, not a complete register implementation. The production path must match the controller’s documented control bits, byte cadence, CRC handling, and unresolved acknowledgement behavior selected by the core.
Diagnostics and acceptance tests
Log each write to $4020-$4025 and each read from $4030-$4033 with emulated cycle, disk side, transfer direction, motor and ready state, byte offset, CRC state, and separate IRQ flags. Include BIOS PC and CPU IRQ entry/return when investigating hangs. This distinguishes a missing side from a stuck motor, an unacknowledged byte, or timer-IRQ interference.
Test: disabled disk I/O, inserted and missing side, write-protected side, motor start/stop, read byte, write byte, CRC pass and error, side change during an active operation, repeated timer IRQ, simultaneous timer and disk IRQ, and save/restore mid-transfer. Compare logical output and timing traces to a trusted reference. If a test checks only that a game reaches its title screen, it has not validated byte-level behavior.
Acceptance criteria
A reliable FDS implementation services disk data through register-visible byte events, keeps timer and disk IRQ sources distinct, models CRC and drive status, and preserves mid-transfer state. It states which reference interpretation it uses for disputed acknowledgements and makes that rule testable. It never substitutes wall-clock timing for controller time or exposes disk writes as successful when persistence cannot be honored.
The disk subsystem is both media and protocol. Preserving its byte service, interrupts, and BIOS-visible state is what makes the emulated adapter behave like a device instead of a preloaded archive.
Related:
- Sega Master System Line Interrupts: VDP Counter Reloads and Raster Timing
- NES APU Timing: DMC DMA, Frame Sequencing, and CPU Bus Side Effects
Sources: