Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/internals/chipset.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ armed between disk-word boundaries; WORDSYNC is the explicit mode that
realigns framing to a matched sync word before transfer. Supported image
formats: ADF (read/write), gzip ADZ, single file ZIP, DMS (decompressed by
`dms.rs`), UAE extended ADF, and read-only SCP flux images.
Connected mechanisms with no media keep the active-low disk-change line
asserted; a step pulse only clears that latch once media is actually
present, so guest software sees a no-disk condition rather than unreadable
track data.

Standard ADF and AmigaDOS tracks are synthesized as one PAL-sized
revolution: 11 sectors occupy 5984 MFM words, and the generated revolution
Expand Down
55 changes: 55 additions & 0 deletions src/floppy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ pub struct FloppyController {
impl Default for FloppyController {
fn default() -> Self {
let mut drives: [FloppyDrive; 4] = std::array::from_fn(|_| FloppyDrive::default());
drives[0].assert_no_media_change();
for drive in drives.iter_mut().skip(1) {
drive.external_id = 0;
}
Expand Down Expand Up @@ -1454,6 +1455,9 @@ impl FloppyDrive {
self.external_id_bit = 0;
self.external_id_hold_deactivate = false;
self.write_protected_sense = true;
if self.image.is_none() && self.external_id != 0 {
self.assert_no_media_change();
}
self.status_settle_cck = DISK_STATUS_SETTLE_CCK;
}

Expand Down Expand Up @@ -1572,6 +1576,11 @@ impl FloppyDrive {
}
}

fn assert_no_media_change(&mut self) {
self.disk_change = true;
self.disk_change_sense = true;
}

/// Advance disk rotation by `cck` cycles, returning whether an index
/// pulse occurred and whether any word crossed matched `sync_word` (the
/// free-running DSKSYNC comparator). `sync_word` is `None` when the
Expand Down Expand Up @@ -3843,6 +3852,52 @@ mod tests {
Ok(())
}

#[test]
fn empty_internal_drive_keeps_disk_change_asserted_until_media_steps() -> Result<()> {
let media = temp_adf()?;
let mut ctrl = FloppyController::default();
let selected = !CIAB_DSKSEL0;
let inward_high = selected & !CIAB_DSKDIREC;

ctrl.write_prb(inward_high);
let status = ctrl.cia_a_status_bits();
assert_eq!(status & CIAA_DSKCHANGE, 0);
assert_ne!(status & CIAA_DSKRDY, 0);

ctrl.write_prb(inward_high & !CIAB_DSKSTEP);
ctrl.tick(DISK_STATUS_SETTLE_CCK, 0, &mut []);
assert_eq!(ctrl.cia_a_status_bits() & CIAA_DSKCHANGE, 0);

ctrl.insert_disk_image(0, media.clone(), true)?;
assert_eq!(ctrl.cia_a_status_bits() & CIAA_DSKCHANGE, 0);

ctrl.write_prb(inward_high);
wait_step_floor(&mut ctrl);
ctrl.write_prb(inward_high & !CIAB_DSKSTEP);
ctrl.tick(DISK_STATUS_SETTLE_CCK, 0, &mut []);
assert_ne!(ctrl.cia_a_status_bits() & CIAA_DSKCHANGE, 0);

let _ = fs::remove_file(media);
Ok(())
}

#[test]
fn connected_empty_external_drive_keeps_disk_change_asserted_through_step_poll() {
let mut ctrl = FloppyController::default();
ctrl.set_connected_drives([true, true, false, false]);

let selected = drive_select_prb(1, false);
let inward_high = selected & !CIAB_DSKDIREC;
ctrl.write_prb(inward_high);
let status = ctrl.cia_a_status_bits();
assert_eq!(status & CIAA_DSKCHANGE, 0);
assert_ne!(status & CIAA_DSKRDY, 0);

ctrl.write_prb(inward_high & !CIAB_DSKSTEP);
ctrl.tick(DISK_STATUS_SETTLE_CCK, 0, &mut []);
assert_eq!(ctrl.cia_a_status_bits() & CIAA_DSKCHANGE, 0);
}

#[test]
fn write_protect_line_settles_after_inserted_disk_change() -> Result<()> {
let protected = temp_adf()?;
Expand Down
Loading