dirkwhoffmann / vAmiga

vAmiga is a user-friendly Amiga 500, 1000, 2000 emulator for macOS
https://dirkwhoffmann.github.io/vAmiga
Other
306 stars 25 forks source link

Emulate floppy disks on the bit-level #769

Closed dirkwhoffmann closed 1 year ago

dirkwhoffmann commented 1 year ago

Right now, the disk controller treats the MFM bitstream as composed of bytes. As a result, the number of bits on a single track always has to be dividable by 8 and the sync words needs to be byte-aligned. This is fine for regular ADFs, but fails to work for some disks in extended ADF format.

It should be considered to make the floppy disk controller more flexible by letting it read single bits instead of bytes.

dirkwhoffmann commented 1 year ago

Before emulating the drive on the bit-level, some architectural changes should be made. Right now, the disk controller continuously reads byte after byte from the disk and feeds them into the FIFO buffer:

void
DiskController::serviceDiskEvent()
{        
    // Receive next byte from the selected drive
    executeFifo();

    // Schedule next event
    scheduleNextDiskEvent();
}

The following image from the patent shows what happens on the real machine:

Bildschirm­foto 2022-12-22 um 07 37 26

In the real disk controller, the bit stream coming from the drive is buffered in a data register 113. Whenever 16 bits have been received, the incoming word is written into the FIFO buffer. Sync word checking is done in the data register and not inside the FIFO (as implemented in vAmiga).

Next step: Emulate the data register and perform sync word checking right there.

dirkwhoffmann commented 1 year ago

This test case has been provided by @mras0:

I created a simple disk (read_regs_1) where track 0 is a normal amiga MFM track just shifted one bit. Note that it works fine in KS1.3 despite the data being wrong, because trackdisk.device doesn't use wordsync (it shifts the data using the blitter if necessary), try it under e.g. KS3.1 to see what I mean.

With the latest code changes, it works with KS3.1, too:

Bildschirm­foto 2022-12-22 um 13 00 34
dirkwhoffmann commented 1 year ago

Update: hoi_1/2_converted.ext works, too.

Next work items:

emoon commented 1 year ago

I would like to add that this would be a very nice feature to have (for developers) we only found out some of the timing issues when running on actual hardware with real floppies and doing a "floppy tester" to show lots of stats during the development of Eon.

For example when using a flash-drive (such as gotek) would report 0 latency (or similar at least) when doing seeking. Now I guess that makes sense as that is the case there, but that hides lots of issues when trying to get things working from actual floppies.

emoon commented 1 year ago

Another thing that we used during Eon was (in fs-uae) to set the drive to 90% speed to have some buffer due to crappy floppy disks/drives. I'm not sure if it's supported in vAmiga, but it's something that is nice to have as a dev also

dirkwhoffmann commented 1 year ago

I'm not sure if it's supported in vAmiga, but it's something that is nice to have as a dev also

It's not yet supported, but easy to do. Right now, the delay between the arrival times of two bytes is hard coded (it's 55.98 Agnus cycles which results in a disk rotation speed of 300rpm). This value could be altered according to some new config option.

void
DiskController::scheduleNextDiskEvent()
{
    /* Advance the delay counter to achieve a disk rotation speed of 300rpm.
     * Rotation speed can be measured with AmigaTestKit.adf which calculates
     * the delay between consecutive index pulses. 300rpm corresponds to a
     * index pulse delay of 200ms.
     */
    dskEventDelay += 55.98;
    DMACycle rounded = DMACycle(round(dskEventDelay));
    dskEventDelay -= rounded;

    if (turboMode()) {
        agnus.cancel<SLOT_DSK>();
    } else {
        agnus.scheduleRel<SLOT_DSK>(DMA_CYCLES(rounded), DSK_ROTATE);
    }
}
dirkwhoffmann commented 1 year ago

In the next version, there will a config option OPT_DRIVE_RPM. It seems to work already (but needs more testing). RPM can be changed on-the-fly in RetroShell and the effect is shown by AmigaTestKit:

Bildschirm­foto 2022-12-22 um 15 38 48

The implementation is pretty much straight forward:

void
DiskController::scheduleNextDiskEvent()
{
    static constexpr double bytesPerTrack = 12668.0;

    // How many revolutions per minute are we supposed to achieve?
    FloppyDrive *drive = getSelectedDrive();
    isize rpm = drive ? drive->config.rpm : 300;

    // Compute the time span between two incoming bytes
    double delay;

    if (rpm) {

        // Calculate the delay according to the desired rpm
        delay = (SEC(1) * 60.0) / (double)rpm / bytesPerTrack;

    } else {

        // Use the value that was hard-coded in vAmiga up to version v2.2
        delay = 8 * 55.98;
    }

    dskEventDelay += delay;
    double rounded = round(dskEventDelay);
    dskEventDelay -= rounded;

    if (turboMode()) {
        agnus.cancel<SLOT_DSK>();
    } else {
        agnus.scheduleRel<SLOT_DSK>(Cycle(rounded), DSK_ROTATE);
    }
}