Update documentation with lots more stuff about disk densities.
@@ -70,8 +70,9 @@ following friendly articles:
|
||||
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
|
||||
science ∾ the sector map ∾ clock detection and the histogram
|
||||
|
||||
- [Checking your drive](doc/driveresponse.md) ∾ you can't do that with that ∾
|
||||
measuring your drive's ability to work with exotic formats
|
||||
- [Disk densities](doc/driveresponse.md) ∾ what's the difference between an HD
|
||||
and DD disk? ∾ you can't do that with that ∾ measuring your drive's ability to
|
||||
work with exotic formats ∾ I think my drive is broken
|
||||
|
||||
Which?
|
||||
------
|
||||
|
||||
@@ -1,25 +1,79 @@
|
||||
Analysing drive response
|
||||
========================
|
||||
Disk and drive density
|
||||
======================
|
||||
|
||||
**tl;dr:** with 5.25" drives, it is _vitally important_ that you set
|
||||
`--drive.high_density=0|1` correctly when working with disks.
|
||||
|
||||
Not all PC drives are made equal. Some are less equal than others.
|
||||
|
||||
The way floppy disk storage works is that the floppy drive controller will
|
||||
generate a series of pulses, which the drive stores on the disk. Then, when the
|
||||
disk is read, the drive will reproduce the same series of pulses and return it
|
||||
to the floppy drive controller. The data is stored in the intervals between
|
||||
to the floppy drive controller. The data is stored by the intervals between
|
||||
pulses.
|
||||
|
||||
The problem is that some PC drives assume that they're going to be used with
|
||||
IBM scheme disks, which use particular pulse intervals --- in the case of DD
|
||||
disks, intervals are always 4us, 6us or 8us. So, in a misguided attempt to
|
||||
improve reliability, they sometimes... tidy... the incoming pulse stream. This
|
||||
can have nasty effects if you're not a disk which _doesn't_ use those intervals.
|
||||
However, the underlying physics of the magnetic media put limitations on how far
|
||||
apart the pulses can be. If they're too close, they will physically move further
|
||||
apart... and if they're too far apart, the drive will detect spurious pulses
|
||||
that don't exist in real life (due to the way the drive's automatic gain
|
||||
adjustment on the head amplifiers work).
|
||||
|
||||
In addition, they won't work properly if the intervals are too great, or too
|
||||
small. Partly this is a limitation of the underlying physics of the magnetic
|
||||
media, and partly it's due to the drive's automatic gain adjustment: if the
|
||||
drive doesn't see a pulse, it'll start ramping up the gain of its amplifier,
|
||||
until it starts interpreting random noise as a valid pulse.
|
||||
So, it's very important what kind of disks you use, and what kind of drives you
|
||||
put those disks in: these can vary.
|
||||
|
||||
Disk densities
|
||||
--------------
|
||||
|
||||
Disks usually come in two varieties: SD/DD/QD disks, and HD disks. (There's
|
||||
also ED and TD but they're fairly specialist and FluxEngine doesn't support
|
||||
them.)
|
||||
|
||||
SD (single density), DD (double density) and QD (quad density) actually refer to
|
||||
the way the data is stored on the disk. SD usually implies FM, and DD implies
|
||||
MFM, which allows 60% more data; QD implies MFM _plus_ narrower tracks, doubling
|
||||
the track count and hence the amount of data. This distinction was important
|
||||
back when you had to buy pre-formatted disks, so you needed disks which matched
|
||||
your drive. FluxEngine, of course, formats them itself.
|
||||
|
||||
HD disks use a different magnetic medium, based on cobalt rather than iron
|
||||
oxide. This allows the pulses on the disk to be much closer together. This
|
||||
doubles the amount of data yet again. The downside of HD disks is that the
|
||||
pulses _have_ to be closer together so you're likely to have problems if you try
|
||||
to write SD/DD/QD formats onto HD disks.
|
||||
|
||||
It's also worth noting that 3.5" SD/DD/QD disks are much more like 3.5" HD disks
|
||||
than 5.25" SD/DD/QD disks are like 5.25" HD disks. If you own an Amiga which
|
||||
uses 3.5" DD disks, you'll know that HD disks will mostly work just fine in an
|
||||
Amiga; but if you own a C64 with 5.25" SD disks, you'll know that HD disks are
|
||||
likely to be error-prone.
|
||||
|
||||
Drive types
|
||||
-----------
|
||||
|
||||
Of course, the disk itself is only half the story...
|
||||
|
||||
Old floppy disk drives vary in what formats they support. The usual variables are:
|
||||
disk rotation speed (either 300rpm or 360rpm); number of tracks (either 40 or
|
||||
80); and whether they can support HD disks. (Plus, of course, whether it's 3.5"
|
||||
or 5.25", but the technology is largely identical so I'm ignoring that.)
|
||||
|
||||
'Modern' drives are pretty consistent in that they run at 300rpm (for a 3.5"
|
||||
drive) or 360rpm (for a 5.25" drive), have 80 tracks, and support HD.
|
||||
|
||||
There are two additional wrinkles:
|
||||
|
||||
- the drive must be configured for SS/DD/QD media or HD media. 3.5" drives can
|
||||
do this automatically, as there's a hole in the disk case which a drive sensor
|
||||
can detect, but 5.25" drives don't, and must be told what medium is being used
|
||||
by the computer. (FluxEngine uses the `--drive.high_density=0|1` option for
|
||||
this.)
|
||||
|
||||
- some drives are designed to only support IBM formats, which use pulse widths
|
||||
of 4µs, 6µs or 8µs, and when given pulses that aren't this wide they behave...
|
||||
poorly.
|
||||
|
||||
Actually measuring things
|
||||
-------------------------
|
||||
|
||||
FluxEngine has a tool to analyse a drive and report on this behaviour. It works
|
||||
by writing a sequence of timed pulses to the disk, then reading them back and
|
||||
@@ -27,52 +81,166 @@ seeing what the drive actually reports. To use it, do:
|
||||
|
||||
```
|
||||
fluxengine analyse driveresponse --cylinder 0 \
|
||||
--min-interval-us=0 --max-interval-us=30 --interval-step-us=.1 \
|
||||
--min-interval-us=0 --max-interval-us=15 --interval-step-us=.1 \
|
||||
--write-img=driveresponse.png
|
||||
```
|
||||
|
||||
This will scan all intervals from 0us to 30us, at 0.1us steps, draw a graph,
|
||||
and write out the result. The graphs look like this.
|
||||
This will scan all intervals from 0µs to 15µs, at 0.1µs steps, draw a graph,
|
||||
and write out the result.
|
||||
|
||||
Using mismatched media and drive configuration
|
||||
----------------------------------------------
|
||||
|
||||
There are four possible options when using 5.25" media in my Panasonic JU-475
|
||||
5.25" drive. The following table shows what the analysis tool shows for all
|
||||
these options.
|
||||
|
||||
(Click to expand)
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="sony-mpf920-dd.png"><img src="sony-mpf920-dd.png" style="width:40%" alt="Sony MPF-920, DD"></a>
|
||||
<a href="sony-mpf920-hd.png"><img src="sony-mpf920-hd.png" style="width:40%" alt="Sony MPF-920, HD"></a>
|
||||
<table class="datatable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>DD drive</th>
|
||||
<th>HD drive</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>DD media</th>
|
||||
<td>
|
||||
<a href="ju475-dd-lo.png">
|
||||
<img src="ju475-dd-lo.png" style="width:100%"
|
||||
alt="Panasonic JU-475, DD media, DD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="ju475-dd-hi.png">
|
||||
<img src="ju475-dd-hi.png" style="width:100%"
|
||||
alt="Panasonic JU-475, DD media, HD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>HD media</th>
|
||||
<td>
|
||||
<a href="ju475-hd-lo.png">
|
||||
<img src="ju475-hd-lo.png" style="width:100%"
|
||||
alt="Panasonic JU-475, HD media, DD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="ju475-hd-hi.png">
|
||||
<img src="ju475-hd-hi.png" style="width:100%"
|
||||
alt="Panasonic JU-475, HD media, HD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
This is the analysis from the [Sony
|
||||
MPF-920](https://docs.sony.com/release/MPF920Z.pdf) 3.5" drive I mostly use for
|
||||
testing. The left-hand image shows the result from a DD disk, while the right
|
||||
hand image shows the result from a HD disk.
|
||||
The X axis shows the width of the pulse being sent to the drive. This gets
|
||||
recorded to the disk (repeatedly). The Y axis shows a heatmap of the pulses read
|
||||
back again. What we want to see is a solid diagonal line, showing that the
|
||||
pulses are being read back accurately. However, once the pulse width gets wide
|
||||
enough, spurious pulses appear, which show up on the graph as the rainbow smear
|
||||
in the bottom left corner. You can see that this is much more pronounced on the
|
||||
HD media than it is on the DD media --- the DD media is capable of accurately
|
||||
reproducing pulses up to about 12µs, while the HD media only goes up to 7µs.
|
||||
|
||||
The horizontal axis is the width of pulse being written; the vertical axis and
|
||||
heatmap shows the distribution of pulses being read back. You can see the
|
||||
diagonal line, which represents correct pulses. The triangular smear in the
|
||||
bottom right shows spurious pulses which are being read back because the
|
||||
interval is too great; these start at about 12us for DD disks and 7us for HD
|
||||
disks. This is an artifact of the different magnetic media for the two types of
|
||||
disk.
|
||||
This demonstrates that some formats, such as the [Apple II](doc/disk-apple2.md)
|
||||
which uses a maximum pulse width of 8µs, cannot be written to HD media at all.
|
||||
|
||||
(This, by the way, is why you shouldn't use DD formats on HD disks. The
|
||||
intervals on a DD disk can go up to 8us, which is on the edge of the ability of
|
||||
an HD disk and drive to correctly report back the pulses.)
|
||||
We would also normally see spurious pulses on the left of the graph, indicating
|
||||
that pulses are too close together to be reproduced accurately, but we're not
|
||||
--- the Panasonic is a good drive.
|
||||
|
||||
You also note the hard cut-off on the left: this represents the filter on the
|
||||
drive, which will simply refuse to report pulse intervals shorter than about
|
||||
1.5us. FluxEngine itself can't write intervals shorter than 2us.
|
||||
For comparison, following is the same table for my Sony MPF-90 3.5" drive.
|
||||
|
||||
For comparison purposes, here's another set of graphs.
|
||||
(Click to expand)
|
||||
|
||||
<div style="text-align: center">
|
||||
<a href="fdd-90206-dd.png"><img src="fdd-90206-dd.png" style="width:40%" alt="FDD-90206, DD"></a>
|
||||
<a href="fdd-90206-hd.png"><img src="fdd-90206-hd.png" style="width:40%" alt="FDD-90206, HD"></a>
|
||||
<table class="datatable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>DD drive</th>
|
||||
<th>HD drive</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>DD media</th>
|
||||
<td>
|
||||
<a href="mpf90-dd-lo.png">
|
||||
<img src="mpf90-dd-lo.png" style="width:100%"
|
||||
alt="Sony MPF-90, DD media, DD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>HD media</th>
|
||||
<td>
|
||||
<a href="mpf90-hd-lo.png">
|
||||
<img src="mpf90-hd-lo.png" style="width:100%"
|
||||
alt="Sony MPF-90, HD media, DD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="mpf90-hd-hi.png">
|
||||
<img src="mpf90-hd-hi.png" style="width:100%"
|
||||
alt="Sony MPF-90, HD media, HD drive setting">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
As 3.5" drives autodetect the media type, I had to tape over the hole in a HD
|
||||
disk to make the drive treat it as a DD disk. Unfortunately, doing it the other
|
||||
way around would require drilling a hole in a DD disk, which I'm unwilling to
|
||||
do! So there are only three graphs here.
|
||||
|
||||
We get the same smear of spurious pulses in the bottom left, but we _also_ get
|
||||
some incorrect pulses on the left. In addition, the line itself only starts at
|
||||
about 2.5µs --- it turns out that this drive simply returns nothing at all for
|
||||
pulses shorter than that.
|
||||
|
||||
(FluxEngine itself can't write intervals shorter than 2us.)
|
||||
|
||||
And, finally, here is one additional pair of graphs.
|
||||
|
||||
<div style="text-align: center">
|
||||
<table class="datatable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>DD drive</th>
|
||||
<th>HD drive</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>DD media</th>
|
||||
<td>
|
||||
<a href="fdd-90206-dd.png">
|
||||
<img src="fdd-90206-dd.png" style="width:100%" alt="FDD-90206, DD">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>HD media</th>
|
||||
<td></td>
|
||||
</td>
|
||||
<td>
|
||||
<a href="fdd-90206-hd.png">
|
||||
<img src="fdd-90206-hd.png" style="width:100%" alt="FDD-90206, HD">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
This is from another drive I have; it's an unbranded combo
|
||||
card-reader-and-floppy drive unit; the 90206 is the only identification mark it
|
||||
has. The DD graph shows that intervals below about 4us are reported as double
|
||||
card-reader-and-3.5"-floppy drive unit; the 90206 is the only identification mark it
|
||||
has. The DD graph shows that intervals below about 4µs are reported as double
|
||||
what they should be: so, this drive won't work on [Macintosh 800kB
|
||||
formats](disk-macintosh.md) at all, because they use intervals starting at
|
||||
2.6us, below this limit. But it should work on PC formats --- just.
|
||||
2.6µs, below this limit. But it should work on PC formats --- just.
|
||||
|
||||
|
||||
BIN
doc/ju475-dd-hi.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
doc/ju475-dd-lo.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
doc/mpf90-dd-lo.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/mpf90-hd-hi.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc/mpf90-hd-lo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@@ -69,6 +69,11 @@ static IntFlag imgHeight(
|
||||
"Height of output graph",
|
||||
600);
|
||||
|
||||
static IntFlag buckets(
|
||||
{ "--buckets" },
|
||||
"Number of heatmap buckets",
|
||||
250);
|
||||
|
||||
/* This is the Turbo colourmap.
|
||||
* https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html
|
||||
*/
|
||||
@@ -225,7 +230,7 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
csv.open(writeCsv);
|
||||
|
||||
int numRows = (maxInterval - minInterval) / intervalStep;
|
||||
const int numColumns = 512;
|
||||
const int numColumns = buckets;
|
||||
std::vector<std::vector<double>> frequencies(numRows, std::vector<double>(numColumns, 0.0));
|
||||
|
||||
double interval;
|
||||
@@ -237,16 +242,17 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
std::cout << fmt::format("Interval {:.2f}: ", ticks * US_PER_TICK);
|
||||
std::cout << std::flush;
|
||||
|
||||
/* Write the test pattern. */
|
||||
|
||||
if (interval >= 2.0)
|
||||
{
|
||||
/* Write the test pattern. */
|
||||
|
||||
Fluxmap outFluxmap;
|
||||
while (outFluxmap.duration() < period)
|
||||
{
|
||||
outFluxmap.appendInterval(ticks);
|
||||
outFluxmap.appendPulse();
|
||||
}
|
||||
|
||||
usbWrite(destHead, outFluxmap.rawBytes(), 0);
|
||||
|
||||
/* Read the test pattern in again. */
|
||||
@@ -353,8 +359,8 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
}
|
||||
|
||||
draw_y_axis(painter, colourbarBounds.x1-5, colourbarBounds.y2, colourbarBounds.y1, 0.0, 1.0, 0.1, "{:.1f}");
|
||||
draw_y_axis(painter, graphBounds.x1-5, graphBounds.y2, graphBounds.y1, 0.0, 512.0/TICKS_PER_US, 5.0, "{:.0f}");
|
||||
draw_y_graticules(painter, graphBounds.x1, graphBounds.y2, graphBounds.x2, graphBounds.y1, 0.0, 512.0/TICKS_PER_US, 5.0);
|
||||
draw_y_axis(painter, graphBounds.x1-5, graphBounds.y2, graphBounds.y1, 0.0, buckets/TICKS_PER_US, 5.0, "{:.0f}");
|
||||
draw_y_graticules(painter, graphBounds.x1, graphBounds.y2, graphBounds.x2, graphBounds.y1, 0.0, buckets/TICKS_PER_US, 5.0);
|
||||
draw_x_axis(painter, graphBounds.x1, graphBounds.x2, graphBounds.y2+5, minInterval, maxInterval, 5.0, "{:.0f}");
|
||||
draw_x_graticules(painter, graphBounds.x1, graphBounds.y1, graphBounds.x2, graphBounds.y2, minInterval, maxInterval, 5.0);
|
||||
|
||||
|
||||