mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge branch 'master' into micropolis-275
This commit is contained in:
42
.clang-format
Normal file
42
.clang-format
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignArrayOfStructures: Left
|
||||
AlignEscapedNewlines: Left
|
||||
AllowAllArgumentsOnNextLine: 'true'
|
||||
AllowAllConstructorInitializersOnNextLine: 'false'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||
AllowShortBlocksOnASingleLine: 'true'
|
||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'true'
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: 'false'
|
||||
BreakConstructorInitializers: 'AfterColon'
|
||||
BreakBeforeBraces: Allman
|
||||
BreakInheritanceList: AfterColon
|
||||
BreakStringLiterals: 'true'
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
ColumnLimit: '80'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
|
||||
IncludeBlocks: Preserve
|
||||
IndentWrappedFunctionNames: 'false'
|
||||
KeepEmptyLinesAtTheStartOfBlocks: 'true'
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'true'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'true'
|
||||
SpaceAfterTemplateKeyword: 'true'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeCtorInitializerColon: 'false'
|
||||
SpaceBeforeInheritanceColon: 'true'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
...
|
||||
9
.github/workflows/ccpp.yml
vendored
9
.github/workflows/ccpp.yml
vendored
@@ -53,3 +53,12 @@ jobs:
|
||||
run: |
|
||||
make
|
||||
|
||||
- name: zip
|
||||
run: |
|
||||
zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.event.repository.name }}.${{ github.sha }}
|
||||
path: fluxengine.zip
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
:4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F031F9264602F04EF994F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07EFF18B9794820
|
||||
:40054000FFF7BFFE04E000F07DFF0028F7D10BE000F072FF10B902F031F9F9E77248FFF7B0FE032001F098F8032000F077FF0128D4D16E48FFF7EEFE6D490320FFF734FE85
|
||||
:4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C
|
||||
:4005C000D4034D0303238DF820308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD44
|
||||
:4005C000D4034D0303238DF820308DF8213010238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD43
|
||||
:400600000B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF782FD2F
|
||||
:400640004FF000080DF1200A02F0B8F84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76EFD3A465146022000F04DFFB9F10109EBD108F10108B8F1400F58
|
||||
:40068000E2D12E4B38E04FF0010A4FF000080DF1200B02F093F84FF0000959460120FFF7A3FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B4641
|
||||
@@ -4615,12 +4615,12 @@
|
||||
:0200000490105A
|
||||
:04000000BC90ACAF55
|
||||
:0200000490303A
|
||||
:0200000090CF9F
|
||||
:0200000090D09E
|
||||
:0200000490402A
|
||||
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
|
||||
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
|
||||
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
|
||||
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
||||
:0200000490501A
|
||||
:0C00000000012E16106900002E30A138FF
|
||||
:0C00000000012E16106900002E30A139FE
|
||||
:00000001FF
|
||||
2
Makefile
2
Makefile
@@ -83,3 +83,5 @@ clean:
|
||||
@mkdir -p $(OBJDIR)
|
||||
@sh $< > $@
|
||||
|
||||
compdb:
|
||||
@ninja -f .obj/build.ninja -t compdb > compile_commands.json
|
||||
|
||||
@@ -103,6 +103,8 @@ people who've had it work).
|
||||
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
|
||||
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
|
||||
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | and probably the 400kB too |
|
||||
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
|
||||
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | |
|
||||
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
|
||||
{: .datatable }
|
||||
|
||||
@@ -125,9 +127,9 @@ at least, check the CRC so what data's there is probably good.
|
||||
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
|
||||
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
|
||||
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
|
||||
| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
|
||||
| [Northstar](doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
|
||||
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
|
||||
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 5.25" GCR encoded |
|
||||
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
|
||||
{: .datatable }
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ metadata. Systems which use IBM scheme disks include but are not limited to:
|
||||
- the TRS-80
|
||||
- late era Commodore machines (the 1571 and so on)
|
||||
- most CP/M machines
|
||||
- NEC PC-88 series
|
||||
- NEC PC-98 series
|
||||
- Sharp X68000
|
||||
- Fujitsu FM Towns
|
||||
- etc
|
||||
|
||||
FluxEngine supports reading these. However, some variants are more peculiar
|
||||
@@ -93,11 +97,12 @@ correct format to use.
|
||||
Mixed-format disks
|
||||
------------------
|
||||
|
||||
Some disks, usually those belonging to early CP/M machines, have more than one
|
||||
format on the disk at once. Typically, the first few tracks will be low-density
|
||||
FM encoded and will be read by the machine's ROM; those tracks contain new
|
||||
floppy drive handling code capable of coping with MFM data, and so the rest of
|
||||
the disk will use that, allowing them to store more data.
|
||||
Some disks, such as those belonging to early CP/M machines, or N88-Basic disks
|
||||
(for PC-88 and PC-98), have more than one format on the disk at once. Typically,
|
||||
the first few tracks will be low-density FM encoded and will be read by the
|
||||
machine's ROM; those tracks contain new floppy drive handling code capable of
|
||||
coping with MFM data, and so the rest of the disk will use that, allowing them
|
||||
to store more data.
|
||||
|
||||
FluxEngine can read these fine, but it tends to get a bit confused when it sees
|
||||
tracks with differing numbers of sectors --- if track 0 has 32 sectors but
|
||||
@@ -105,6 +110,31 @@ track 1 has 16, it will assume that sectors 16..31 are missing on track 1 and
|
||||
size the image file accordingly. This can be worked around by specifying the
|
||||
size of each track; see the `eco1` read profile for an example.
|
||||
|
||||
Writing can be made to work too, but there is currently no example. Please [get
|
||||
in touch](https://github.com/davidgiven/fluxengine/issues/new) if you have
|
||||
specific requirements (nothing's come up yet).
|
||||
N88-Basic format floppies can be written by either specifying the `n88basic`
|
||||
format, or by using D88 or NFD format images which include explicit sector
|
||||
layout information.
|
||||
|
||||
Writing other formats can be made to work too, by creating a custom format
|
||||
specifier, using the `n88basic` format as an example.
|
||||
Please [get in touch](https://github.com/davidgiven/fluxengine/issues/new) if
|
||||
you have specific requirements.
|
||||
|
||||
360rpm 3.5" disks
|
||||
-----------------
|
||||
|
||||
Japanese PCs (NEC PC-98, Sharp X68000, Fujitsu FM Towns) spin their floppy
|
||||
drives at 360rpm rather than the more typical 300rpm. This was done in order
|
||||
to be fully backwards compatible with 5.25" disks, while using the exact
|
||||
same floppy controller. Later models of the PC-9821, as well as most USB floppy
|
||||
drives, feature "tri-mode" support which in addition to normal 300rpm modes,
|
||||
can change their speed to read and write 360rpm DD and HD disks.
|
||||
|
||||
Neither the FluxEngine or Greaseweazle hardware can currently command a
|
||||
tri-mode drive to spin at 360rpm, however an older 360rpm-only drive will work
|
||||
to read these formats.
|
||||
|
||||
Alternately, the FluxEngine software can resale the flux pulses to enable
|
||||
reading and writing these formats with a plain 300rpm drive. To do this,
|
||||
specify the following two additional options:
|
||||
|
||||
--flux_source.rescale=1.2 --flux_sink.rescale=1.2
|
||||
|
||||
@@ -16,8 +16,8 @@ the speed zone allocation on head 1 differ from head 0...
|
||||
| 1 | 4-15 | 0-7 | 18 | 224.5 |
|
||||
| 2 | 16-26 | 8-18 | 17 | 212.2 |
|
||||
| 3 | 27-37 | 19-29 | 16 | 199.9 |
|
||||
| 4 | 38-48 | 30-40 | 15 | 187.6 |
|
||||
| 5 | 49-59 | 41-51 | 14 | 175.3 |
|
||||
| 4 | 38-47\* | 30-39\* | 15 | 187.6 |
|
||||
| 5 | 48-59 | 40-51 | 14 | 175.3 |
|
||||
| 6 | 60-70 | 52-62 | 13 | 163.0 |
|
||||
| 7 | 71-79 | 63-74 | 12 | 149.6 |
|
||||
| 8 | | 75-79 | 11 | 144.0 |
|
||||
@@ -26,8 +26,13 @@ the speed zone allocation on head 1 differ from head 0...
|
||||
FluxEngine, the disk always spins at 360 rpm, which corresponds to a rotational
|
||||
period of 166 ms.)
|
||||
|
||||
FluxEngine can read and write the single-sided variant of these. (Double-sided
|
||||
will be trivial to do, it's just not done yet.)
|
||||
\*The Victor 9000 Hardware Reference Manual has a bug in the documentation
|
||||
and lists Zone 4 as ending with track 48 on head 0 and track 40 on head 1.
|
||||
The above table matches observed data on various disks and the assembly
|
||||
code in the boot loader, which ends Zone 4 with track 47 on head 0
|
||||
and track 39 on Head 1.
|
||||
|
||||
FluxEngine can read and write both the single-sided and double-sided variants.
|
||||
|
||||
Reading discs
|
||||
-------------
|
||||
@@ -35,10 +40,14 @@ Reading discs
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read victor9k-ss
|
||||
fluxengine read <format>
|
||||
|
||||
```
|
||||
|
||||
You should end up with an `victor9k.img` which is 627200 bytes long.
|
||||
...where `<format>` can be `victor9k_ss` or `victor9k_ds`.
|
||||
|
||||
For `victor9k_ss` you should end up with an `victor9k.img` which is 627200 bytes long.
|
||||
For `victor9k_ds` you should end up with an `victor9k.img` which is 1224192 bytes long.
|
||||
|
||||
**Big warning!** The image is triangular, where each track occupies a different
|
||||
amount of space. Victor disk images are complicated due to the way the tracks
|
||||
@@ -50,7 +59,7 @@ Writing discs
|
||||
Just do:
|
||||
|
||||
```
|
||||
fluxengine read victor9k-ss -i victor9k.img
|
||||
fluxengine read victor9k_ss -i victor9k.img
|
||||
```
|
||||
|
||||
**Big warning!** This uses the same triangular disk image that reading uses.
|
||||
|
||||
@@ -16,6 +16,13 @@ FluxEngine makes things complicated when you're not using the FluxEngine client
|
||||
software with a FluxEngine board, but I'm afraid it's too late to change that
|
||||
now. Sorry.
|
||||
|
||||
**If you are using GreaseWeazle-compatible hardware** such as the
|
||||
[adafruit-floppy](https://github.com/adafruit/Adafruit_Floppy) project, then
|
||||
FluxEngine will still work; however, as the USB VID/PID won't be that of a real
|
||||
GreaseWeazle, the the FluxEngine client can't autodetect it. Instead, you'll
|
||||
need to specify the serial port manually with something like
|
||||
`--usb.greaseweazle.port=/dev/ttyACM0` or `--usb.greaseweazle.port=COM5`.
|
||||
|
||||
**If you were using a previous version on Windows** you might have installed
|
||||
the WinUSB driver. That's no longer needed, and will in fact not work. You'll
|
||||
need to use Zadig to restore the old driver; to do this, make sure the left
|
||||
@@ -30,6 +37,8 @@ Supported features with the GreaseWeazle include:
|
||||
- simple reading and writing of disks, seeking etc
|
||||
- erasing disks
|
||||
- determining disk rotation speed
|
||||
- both Shugart and normal IBM buses (via
|
||||
`--usb.greaseweazle.bus_type=SHUGART` or `IBMPC`; the default is `IBMPC`)
|
||||
|
||||
What doesn't work
|
||||
-----------------
|
||||
|
||||
41
doc/using.md
41
doc/using.md
@@ -267,7 +267,8 @@ FluxEngine also supports a number of file system image formats. When using the
|
||||
- `<filename.fdi>`
|
||||
|
||||
Read from a [FDI image file](https://www.pc98.org/project/doc/hdi.html),
|
||||
commonly used by PC-98 emulators. **Read Only.**
|
||||
commonly used by PC-98 emulators. Supports automatically configuring
|
||||
the encoder. **Read Only.**
|
||||
|
||||
- `<filename.d88>`
|
||||
|
||||
@@ -280,16 +281,34 @@ FluxEngine also supports a number of file system image formats. When using the
|
||||
The D88 reader should be used with the `ibm` profile and will override
|
||||
most encoding parameters on a track-by-track basis.
|
||||
|
||||
- `<filename.nfd>`
|
||||
|
||||
Read from a [NFD r0 image file](https://www.pc98.org/project/doc/nfdr0.html),
|
||||
commonly used by various Japanese PC emulators, including the NEC PC-98. **Read Only.**
|
||||
|
||||
Only r0 version files are currently supported.
|
||||
|
||||
The NFD reader should be used with the `ibm` profile and will override
|
||||
most encoding parameters on a track-by-track basis.
|
||||
|
||||
- `<filename.ldbs>`
|
||||
|
||||
Write to a [LDBS generic image
|
||||
file](https://www.seasip.info/Unix/LibDsk/ldbs.html). **Write only.**
|
||||
Write to a [LDBS generic image
|
||||
file](https://www.seasip.info/Unix/LibDsk/ldbs.html). **Write only.**
|
||||
|
||||
- `<filename.d64>`
|
||||
|
||||
Write to a [D64 image
|
||||
file](http://unusedino.de/ec64/technical/formats/d64.html), commonly used
|
||||
by Commodore 64 emulators. **Write only.**
|
||||
Write to a [D64 image
|
||||
file](http://unusedino.de/ec64/technical/formats/d64.html), commonly used by
|
||||
Commodore 64 emulators. **Write only.**
|
||||
|
||||
- `<filename.raw>`
|
||||
|
||||
Write undecoded data to a raw binary file. **Write only.** This gives you the
|
||||
underlying MFM, FM or GCR stream, without actually doing the decode into
|
||||
user-visible bytes. However, the decode is still done in order to check for
|
||||
correctness. Individual records are separated by three `\\0` bytes and tracks
|
||||
are seperated by four `\\0` bytes; tracks are emitted in CHS order.
|
||||
|
||||
### High density disks
|
||||
|
||||
@@ -340,6 +359,16 @@ behaviour.
|
||||
useful for using very old drives with FluxEngine itself. If you use this
|
||||
option, then any index marks in the sampled flux are, of course, garbage.
|
||||
|
||||
- `--flux_source.rescale=X, --flux_sink.rescale=X`
|
||||
|
||||
When reading or writing a floppy on a drive that doesn't match the
|
||||
original drive RPM, the flux periods can be scaled to compensate.
|
||||
|
||||
For example, to read or write a PC-98 1.2MB (360rpm) floppy using a 300rpm
|
||||
floppy drive:
|
||||
|
||||
`--flux_source.rescale=1.2 --flux_sink.rescale=1.2`
|
||||
|
||||
## Visualisation
|
||||
|
||||
When using `fluxengined read` (either from a real disk or from a flux file) you
|
||||
|
||||
@@ -132,6 +132,7 @@ void AbstractDecoder::pushRecord(const Fluxmap::Position& start, const Fluxmap::
|
||||
|
||||
auto record = std::make_shared<Record>();
|
||||
_trackdata->records.push_back(record);
|
||||
_sector->records.push_back(record);
|
||||
|
||||
record->startTime = start.ns();
|
||||
record->endTime = end.ns();
|
||||
|
||||
@@ -108,3 +108,22 @@ std::vector<Fluxmap> Fluxmap::split() {
|
||||
maps.push_back(map);
|
||||
return maps;
|
||||
}
|
||||
|
||||
void Fluxmap::rescale(double scale) {
|
||||
if (scale != 1.0) {
|
||||
auto bytesOrig = _bytes;
|
||||
_bytes = Bytes();
|
||||
_duration = 0;
|
||||
_ticks = 0;
|
||||
int lastEvent = 0;
|
||||
for (unsigned i=0; i<bytesOrig.size(); i++)
|
||||
{
|
||||
lastEvent += bytesOrig[i] & 0x3f;
|
||||
if (bytesOrig[i] & 0xc0) {
|
||||
appendInterval(lastEvent * scale + 0.5);
|
||||
findLastByte() |= bytesOrig[i] & 0xc0;
|
||||
lastEvent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
|
||||
void precompensate(int threshold_ticks, int amount_ticks);
|
||||
std::vector<Fluxmap> split();
|
||||
void rescale(double scale);
|
||||
|
||||
private:
|
||||
uint8_t& findLastByte();
|
||||
|
||||
@@ -29,6 +29,7 @@ message Fl2FluxSinkProto {
|
||||
}
|
||||
|
||||
message FluxSinkProto {
|
||||
optional double rescale = 7 [ default = 1.0, (help) = "amount to multiply pulse periods by" ];
|
||||
oneof dest {
|
||||
string fluxfile = 1 [(help) = "name of destination flux file"];
|
||||
HardwareFluxSinkProto drive = 2;
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
return std::make_unique<Fluxmap>(track.flux());
|
||||
}
|
||||
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
return std::make_unique<Fluxmap>();
|
||||
}
|
||||
|
||||
void recalibrate() {}
|
||||
|
||||
@@ -39,6 +39,7 @@ message Fl2FluxSourceProto {
|
||||
}
|
||||
|
||||
message FluxSourceProto {
|
||||
optional double rescale = 9 [ default = 1.0, (help) = "amount to divide pulse periods by" ];
|
||||
oneof source {
|
||||
string fluxfile = 1 [default = "name of source flux file"];
|
||||
HardwareFluxSourceProto drive = 2;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "image.h"
|
||||
#include "proto.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imagereader/imagereaderimpl.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -166,6 +165,21 @@ public:
|
||||
const Geometry& geometry = image->getGeometry();
|
||||
std::cout << fmt::format("D88: read {} tracks, {} sides\n",
|
||||
geometry.numTracks, geometry.numSides);
|
||||
|
||||
if (!config.has_heads())
|
||||
{
|
||||
auto* heads = config.mutable_heads();
|
||||
heads->set_start(0);
|
||||
heads->set_end(geometry.numSides - 1);
|
||||
}
|
||||
|
||||
if (!config.has_cylinders())
|
||||
{
|
||||
auto* cylinders = config.mutable_cylinders();
|
||||
cylinders->set_start(0);
|
||||
cylinders->set_end(geometry.numTracks - 1);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "image.h"
|
||||
#include "proto.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imagereader/imagereaderimpl.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -130,6 +129,8 @@ public:
|
||||
Error() << fmt::format("DIM: unknown media byte 0x%02x, could not determine write profile automatically", mediaByte);
|
||||
break;
|
||||
}
|
||||
|
||||
config.mutable_decoder()->mutable_ibm();
|
||||
}
|
||||
|
||||
image->calculateSize();
|
||||
@@ -137,6 +138,21 @@ public:
|
||||
std::cout << fmt::format("DIM: read {} tracks, {} sides, {} kB total\n",
|
||||
geometry.numTracks, geometry.numSides,
|
||||
((int)inputFile.tellg() - 256) / 1024);
|
||||
|
||||
if (!config.has_heads())
|
||||
{
|
||||
auto* heads = config.mutable_heads();
|
||||
heads->set_start(0);
|
||||
heads->set_end(geometry.numSides - 1);
|
||||
}
|
||||
|
||||
if (!config.has_cylinders())
|
||||
{
|
||||
auto* cylinders = config.mutable_cylinders();
|
||||
cylinders->set_start(0);
|
||||
cylinders->set_end(geometry.numTracks - 1);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include "sector.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "image.h"
|
||||
#include "proto.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imagereader/imagereaderimpl.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -76,11 +76,54 @@ public:
|
||||
trackCount++;
|
||||
}
|
||||
|
||||
if (config.encoder().format_case() == EncoderProto::FormatCase::FORMAT_NOT_SET)
|
||||
{
|
||||
auto ibm = config.mutable_encoder()->mutable_ibm();
|
||||
auto trackdata = ibm->add_trackdata();
|
||||
trackdata->set_clock_rate_khz(500);
|
||||
auto sectors = trackdata->mutable_sectors();
|
||||
switch (fddType) {
|
||||
case 0x90:
|
||||
std::cout << "FDI: automatically setting format to 1.2MB (1024 byte sectors)\n";
|
||||
config.mutable_cylinders()->set_end(76);
|
||||
trackdata->set_track_length_ms(167);
|
||||
trackdata->set_sector_size(1024);
|
||||
for (int i = 0; i < 9; i++)
|
||||
sectors->add_sector(i);
|
||||
break;
|
||||
case 0x30:
|
||||
std::cout << "FDI: automatically setting format to 1.44MB\n";
|
||||
trackdata->set_track_length_ms(200);
|
||||
trackdata->set_sector_size(512);
|
||||
for (int i = 0; i < 18; i++)
|
||||
sectors->add_sector(i);
|
||||
break;
|
||||
default:
|
||||
Error() << fmt::format("FDI: unknown fdd type 0x%02x, could not determine write profile automatically", fddType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
image->calculateSize();
|
||||
const Geometry& geometry = image->getGeometry();
|
||||
std::cout << fmt::format("FDI: read {} tracks, {} sides, {} kB total\n",
|
||||
geometry.numTracks, geometry.numSides,
|
||||
((int)inputFile.tellg() - headerSize) / 1024);
|
||||
|
||||
if (!config.has_heads())
|
||||
{
|
||||
auto* heads = config.mutable_heads();
|
||||
heads->set_start(0);
|
||||
heads->set_end(geometry.numSides - 1);
|
||||
}
|
||||
|
||||
if (!config.has_cylinders())
|
||||
{
|
||||
auto* cylinders = config.mutable_cylinders();
|
||||
cylinders->set_start(0);
|
||||
cylinders->set_end(geometry.numTracks - 1);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
|
||||
case ImageReaderProto::kD64:
|
||||
return ImageReader::createD64ImageReader(config);
|
||||
|
||||
case ImageReaderProto::kNfd:
|
||||
return ImageReader::createNFDImageReader(config);
|
||||
|
||||
case ImageReaderProto::kNsi:
|
||||
return ImageReader::createNsiImageReader(config);
|
||||
|
||||
@@ -65,6 +68,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
|
||||
{".imd", [&]() { proto->mutable_imd(); }},
|
||||
{".img", [&]() { proto->mutable_img(); }},
|
||||
{".st", [&]() { proto->mutable_img(); }},
|
||||
{".nfd", [&]() { proto->mutable_nfd(); }},
|
||||
{".nsi", [&]() { proto->mutable_nsi(); }},
|
||||
{".td0", [&]() { proto->mutable_td0(); }},
|
||||
{".vgi", [&]() { proto->mutable_img(); }},
|
||||
@@ -87,20 +91,3 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
|
||||
ImageReader::ImageReader(const ImageReaderProto& config):
|
||||
_config(config)
|
||||
{}
|
||||
|
||||
void getTrackFormat(const ImgInputOutputProto& config,
|
||||
ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const ImgInputOutputProto::TrackdataProto& f : config.trackdata())
|
||||
{
|
||||
if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track())))
|
||||
continue;
|
||||
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
|
||||
continue;
|
||||
if (f.has_side() && (f.side() != side))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
static std::unique_ptr<ImageReader> createDimImageReader(const ImageReaderProto& config);
|
||||
static std::unique_ptr<ImageReader> createFdiImageReader(const ImageReaderProto& config);
|
||||
static std::unique_ptr<ImageReader> createD88ImageReader(const ImageReaderProto& config);
|
||||
static std::unique_ptr<ImageReader> createNFDImageReader(const ImageReaderProto& config);
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<Image> readImage() = 0;
|
||||
|
||||
@@ -3,6 +3,12 @@ syntax = "proto2";
|
||||
import "lib/common.proto";
|
||||
|
||||
message ImgInputOutputProto {
|
||||
enum Order {
|
||||
UNDEFINED = 0;
|
||||
CHS = 1;
|
||||
HCS = 2;
|
||||
}
|
||||
|
||||
message SectorsProto {
|
||||
repeated int32 sector = 1 [(help) = "sector ID"];
|
||||
}
|
||||
@@ -30,6 +36,7 @@ message ImgInputOutputProto {
|
||||
optional int32 sides = 6 [default=0, (help) = "number of sides in image"];
|
||||
optional int32 physical_offset = 7 [default=0, (help) = "logical:physical track offset"];
|
||||
optional int32 physical_step = 8 [default=1, (help) = "logical:physical track step"];
|
||||
optional Order order = 9 [default=CHS, (help) = "the order in which to emit tracks in the image"];
|
||||
}
|
||||
|
||||
message DiskCopyInputProto {}
|
||||
@@ -41,6 +48,7 @@ message Td0InputProto {}
|
||||
message DimInputProto {}
|
||||
message FdiInputProto {}
|
||||
message D88InputProto {}
|
||||
message NFDInputProto {}
|
||||
|
||||
message ImageReaderProto {
|
||||
optional string filename = 1 [(help) = "filename of input sector image"];
|
||||
@@ -55,6 +63,7 @@ message ImageReaderProto {
|
||||
DimInputProto dim = 9;
|
||||
FdiInputProto fdi = 10;
|
||||
D88InputProto d88 = 11;
|
||||
NFDInputProto nfd = 12;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef IMAGEREADERIMPL_H
|
||||
#define IMAGEREADERIMPL_H
|
||||
|
||||
extern void getTrackFormat(const ImgInputOutputProto& config,
|
||||
ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "image.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imagereader/imagereaderimpl.h"
|
||||
#include "imginputoutpututils.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -27,34 +27,31 @@ public:
|
||||
Error() << "IMG: bad configuration; did you remember to set the tracks, sides and trackdata fields?";
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
int trackCount = 0;
|
||||
for (int track = 0; track < _config.img().tracks(); track++)
|
||||
{
|
||||
for (const auto& p : getTrackOrdering(_config.img()))
|
||||
{
|
||||
int track = p.first;
|
||||
int side = p.second;
|
||||
|
||||
if (inputFile.eof())
|
||||
break;
|
||||
int physicalCylinder = track * _config.img().physical_step() + _config.img().physical_offset();
|
||||
|
||||
for (int side = 0; side < _config.img().sides(); side++)
|
||||
{
|
||||
ImgInputOutputProto::TrackdataProto trackdata;
|
||||
getTrackFormat(_config.img(), trackdata, track, side);
|
||||
ImgInputOutputProto::TrackdataProto trackdata;
|
||||
getTrackFormat(_config.img(), trackdata, track, side);
|
||||
|
||||
for (int sectorId : getSectors(trackdata))
|
||||
{
|
||||
Bytes data(trackdata.sector_size());
|
||||
inputFile.read((char*) data.begin(), data.size());
|
||||
for (int sectorId : getSectors(trackdata))
|
||||
{
|
||||
Bytes data(trackdata.sector_size());
|
||||
inputFile.read((char*) data.begin(), data.size());
|
||||
|
||||
const auto& sector = image->put(physicalCylinder, side, sectorId);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = track;
|
||||
sector->physicalCylinder = physicalCylinder;
|
||||
sector->logicalSide = sector->physicalHead = side;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
trackCount++;
|
||||
const auto& sector = image->put(physicalCylinder, side, sectorId);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = track;
|
||||
sector->physicalCylinder = physicalCylinder;
|
||||
sector->logicalSide = sector->physicalHead = side;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
|
||||
image->calculateSize();
|
||||
|
||||
149
lib/imagereader/nfdimagereader.cc
Normal file
149
lib/imagereader/nfdimagereader.cc
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "sector.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "image.h"
|
||||
#include "proto.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
// reader based on this partial documentation of the D88 format:
|
||||
// https://www.pc98.org/project/doc/d88.html
|
||||
|
||||
class NFDImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
NFDImageReader(const ImageReaderProto& config):
|
||||
ImageReader(config)
|
||||
{}
|
||||
|
||||
std::unique_ptr<Image> readImage()
|
||||
{
|
||||
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
Bytes fileId(14); // read first entry of track table as well
|
||||
inputFile.read((char*) fileId.begin(), fileId.size());
|
||||
|
||||
if (fileId == Bytes("T98FDDIMAGE.R1")) {
|
||||
Error() << "NFD: r1 images are not currently supported";
|
||||
}
|
||||
if (fileId != Bytes("T98FDDIMAGE.R0")) {
|
||||
Error() << "NFD: could not find NFD header";
|
||||
}
|
||||
|
||||
Bytes header(0x10a10);
|
||||
inputFile.seekg( 0, std::ios::beg );
|
||||
inputFile.read((char*) header.begin(), header.size());
|
||||
|
||||
ByteReader headerReader(header);
|
||||
|
||||
char heads = headerReader.seek(0x115).read_8();
|
||||
if (heads != 2) {
|
||||
Error() << "NFD: unsupported number of heads";
|
||||
}
|
||||
|
||||
if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET)
|
||||
std::cout << "NFD: overriding configured format";
|
||||
|
||||
auto ibm = config.mutable_encoder()->mutable_ibm();
|
||||
config.mutable_cylinders()->set_end(0);
|
||||
std::cout << "NFD: HD 1.2MB mode\n";
|
||||
if (config.flux_sink().dest_case() == FluxSinkProto::DestCase::kDrive) {
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(true);
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
for (int track = 0; track < 163; track++)
|
||||
{
|
||||
auto trackdata = ibm->add_trackdata();
|
||||
trackdata->set_clock_rate_khz(500);
|
||||
trackdata->set_track_length_ms(167);
|
||||
auto sectors = trackdata->mutable_sectors();
|
||||
int currentTrackCylinder = -1;
|
||||
int currentTrackHead = -1;
|
||||
int trackSectorSize = -1;
|
||||
|
||||
for (int sectorInTrack = 0; sectorInTrack < 26; sectorInTrack++){
|
||||
headerReader.seek(0x120 + track * 26 * 16 + sectorInTrack * 16);
|
||||
int cylinder = headerReader.read_8();
|
||||
int head = headerReader.read_8();
|
||||
int sectorId = headerReader.read_8();
|
||||
int sectorSize = 128 << headerReader.read_8();
|
||||
int mfm = headerReader.read_8();
|
||||
int ddam = headerReader.read_8();
|
||||
int status = headerReader.read_8();
|
||||
headerReader.skip(9); // skip ST0, ST1, ST2, PDA, reserved(5)
|
||||
if (cylinder == 0xFF)
|
||||
continue;
|
||||
if (ddam != 0)
|
||||
Error() << "NFD: nonzero ddam currently unsupported";
|
||||
if (status != 0)
|
||||
Error() << "NFD: nonzero fdd status codes are currently unsupported";
|
||||
if (currentTrackCylinder < 0) {
|
||||
currentTrackCylinder = cylinder;
|
||||
currentTrackHead = head;
|
||||
} else if (currentTrackCylinder != cylinder) {
|
||||
Error() << "NFD: all sectors in a track must belong to the same cylinder";
|
||||
} else if (currentTrackHead != head) {
|
||||
Error() << "NFD: all sectors in a track must belong to the same head";
|
||||
}
|
||||
if (trackSectorSize < 0) {
|
||||
trackSectorSize = sectorSize;
|
||||
// this is the first sector we've read, use it settings for per-track data
|
||||
trackdata->set_cylinder(cylinder);
|
||||
trackdata->set_head(head);
|
||||
trackdata->set_sector_size(sectorSize);
|
||||
trackdata->set_use_fm(!mfm);
|
||||
if (!mfm) {
|
||||
trackdata->set_gap_fill_byte(0xffff);
|
||||
trackdata->set_idam_byte(0xf57e);
|
||||
trackdata->set_dam_byte(0xf56f);
|
||||
}
|
||||
// create timings to approximately match N88-BASIC
|
||||
if (sectorSize <= 128) {
|
||||
trackdata->set_gap0(0x1b);
|
||||
trackdata->set_gap2(0x09);
|
||||
trackdata->set_gap3(0x1b);
|
||||
} else if (sectorSize <= 256) {
|
||||
trackdata->set_gap0(0x36);
|
||||
trackdata->set_gap3(0x36);
|
||||
}
|
||||
} else if (trackSectorSize != sectorSize) {
|
||||
Error() << "NFD: multiple sector sizes per track are currently unsupported";
|
||||
}
|
||||
Bytes data(sectorSize);
|
||||
inputFile.read((char*) data.begin(), data.size());
|
||||
const auto& sector = image->put(cylinder, head, sectorId);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = cylinder;
|
||||
sector->physicalCylinder = cylinder;
|
||||
sector->logicalSide = sector->physicalHead = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
|
||||
sectors->add_sector(sectorId);
|
||||
if (config.cylinders().end() < cylinder)
|
||||
config.mutable_cylinders()->set_end(cylinder);
|
||||
}
|
||||
}
|
||||
|
||||
image->calculateSize();
|
||||
const Geometry& geometry = image->getGeometry();
|
||||
std::cout << fmt::format("NFD: read {} tracks, {} sides\n",
|
||||
geometry.numTracks, geometry.numSides);
|
||||
return image;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::createNFDImageReader(
|
||||
const ImageReaderProto& config)
|
||||
{
|
||||
return std::unique_ptr<ImageReader>(new NFDImageReader(config));
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
|
||||
case ImageWriterProto::kNsi:
|
||||
return ImageWriter::createNsiImageWriter(config);
|
||||
|
||||
case ImageWriterProto::kRaw:
|
||||
return ImageWriter::createRawImageWriter(config);
|
||||
|
||||
default:
|
||||
Error() << "bad output image config";
|
||||
return std::unique_ptr<ImageWriter>();
|
||||
@@ -45,8 +48,9 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st
|
||||
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
|
||||
{".img", [&]() { proto->mutable_img(); }},
|
||||
{".ldbs", [&]() { proto->mutable_ldbs(); }},
|
||||
{".st", [&]() { proto->mutable_img(); }},
|
||||
{".nsi", [&]() { proto->mutable_nsi(); }},
|
||||
{".raw", [&]() { proto->mutable_raw(); }},
|
||||
{".st", [&]() { proto->mutable_img(); }},
|
||||
{".vgi", [&]() { proto->mutable_img(); }},
|
||||
{".xdf", [&]() { proto->mutable_img(); }},
|
||||
};
|
||||
|
||||
@@ -14,16 +14,12 @@ public:
|
||||
static std::unique_ptr<ImageWriter> create(const ImageWriterProto& config);
|
||||
static void updateConfigForFilename(ImageWriterProto* proto, const std::string& filename);
|
||||
|
||||
static std::unique_ptr<ImageWriter> createImgImageWriter(
|
||||
const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createLDBSImageWriter(
|
||||
const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createD64ImageWriter(
|
||||
const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(
|
||||
const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createNsiImageWriter(
|
||||
const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createD64ImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createImgImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createLDBSImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createNsiImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createRawImageWriter(const ImageWriterProto& config);
|
||||
|
||||
public:
|
||||
void printMap(const Image& sectors);
|
||||
|
||||
@@ -29,6 +29,7 @@ message LDBSOutputProto {
|
||||
|
||||
message DiskCopyOutputProto {}
|
||||
message NsiOutputProto {}
|
||||
message RawOutputProto {}
|
||||
|
||||
message ImageWriterProto {
|
||||
optional string filename = 1 [(help) = "filename of output sector image"];
|
||||
@@ -38,6 +39,7 @@ message ImageWriterProto {
|
||||
LDBSOutputProto ldbs = 4;
|
||||
DiskCopyOutputProto diskcopy = 5;
|
||||
NsiOutputProto nsi = 6;
|
||||
RawOutputProto raw = 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "image.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imagereader/imagereaderimpl.h"
|
||||
#include "imginputoutpututils.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -28,31 +28,31 @@ public:
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
for (int track = 0; track < tracks; track++)
|
||||
for (const auto& p : getTrackOrdering(_config.img(), tracks, sides))
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
int track = p.first;
|
||||
int side = p.second;
|
||||
|
||||
ImgInputOutputProto::TrackdataProto trackdata;
|
||||
getTrackFormat(_config.img(), trackdata, track, side);
|
||||
|
||||
auto sectors = getSectors(trackdata, geometry.numSectors);
|
||||
if (sectors.empty())
|
||||
{
|
||||
ImgInputOutputProto::TrackdataProto trackdata;
|
||||
getTrackFormat(_config.img(), trackdata, track, side);
|
||||
int maxSector = geometry.firstSector + geometry.numSectors - 1;
|
||||
for (int i=geometry.firstSector; i<=maxSector; i++)
|
||||
sectors.push_back(i);
|
||||
}
|
||||
|
||||
auto sectors = getSectors(trackdata, geometry.numSectors);
|
||||
if (sectors.empty())
|
||||
{
|
||||
int maxSector = geometry.firstSector + geometry.numSectors - 1;
|
||||
for (int i=geometry.firstSector; i<=maxSector; i++)
|
||||
sectors.push_back(i);
|
||||
}
|
||||
int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize;
|
||||
|
||||
int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : geometry.sectorSize;
|
||||
|
||||
for (int sectorId : sectors)
|
||||
{
|
||||
const auto& sector = image.get(track, side, sectorId);
|
||||
if (sector)
|
||||
sector->data.slice(0, sectorSize).writeTo(outputFile);
|
||||
else
|
||||
outputFile.seekp(sectorSize, std::ios::cur);
|
||||
}
|
||||
for (int sectorId : sectors)
|
||||
{
|
||||
const auto& sector = image.get(track, side, sectorId);
|
||||
if (sector)
|
||||
sector->data.slice(0, sectorSize).writeTo(outputFile);
|
||||
else
|
||||
outputFile.seekp(sectorSize, std::ios::cur);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
73
lib/imagewriter/rawimagewriter.cc
Normal file
73
lib/imagewriter/rawimagewriter.cc
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "sector.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "decoders/decoders.h"
|
||||
#include "image.h"
|
||||
#include "arch/northstar/northstar.h"
|
||||
#include "lib/imagewriter/imagewriter.pb.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class RawImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
RawImageWriter(const ImageWriterProto& config):
|
||||
ImageWriter(config)
|
||||
{}
|
||||
|
||||
void writeImage(const Image& image)
|
||||
{
|
||||
const Geometry& geometry = image.getGeometry();
|
||||
|
||||
size_t trackSize = geometry.numSectors * geometry.sectorSize;
|
||||
|
||||
if (geometry.numTracks * trackSize == 0) {
|
||||
std::cout << "RAW: no sectors in output; skipping image file generation." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << fmt::format("RAW: writing {} cylinders, {} sides\n",
|
||||
geometry.numTracks, geometry.numSides);
|
||||
|
||||
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "RAW: cannot open output file";
|
||||
|
||||
unsigned sectorFileOffset;
|
||||
for (int track = 0; track < geometry.numTracks * geometry.numSides; track++)
|
||||
{
|
||||
int side = (track < geometry.numTracks) ? 0 : 1;
|
||||
|
||||
std::vector<std::shared_ptr<Record>> records;
|
||||
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(track % geometry.numTracks, side, sectorId);
|
||||
if (sector)
|
||||
records.insert(records.end(), sector->records.begin(), sector->records.end());
|
||||
}
|
||||
|
||||
std::sort(records.begin(), records.end(),
|
||||
[&](std::shared_ptr<Record> left, std::shared_ptr<Record> right) {
|
||||
return left->startTime < right->startTime;
|
||||
});
|
||||
|
||||
for (const auto& record : records)
|
||||
{
|
||||
record->rawData.writeTo(outputFile);
|
||||
Bytes(3).writeTo(outputFile);
|
||||
}
|
||||
Bytes(1).writeTo(outputFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createRawImageWriter(
|
||||
const ImageWriterProto& config)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new RawImageWriter(config));
|
||||
}
|
||||
|
||||
57
lib/imginputoutpututils.cc
Normal file
57
lib/imginputoutpututils.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "globals.h"
|
||||
#include "lib/imagereader/imagereader.pb.h"
|
||||
#include "imginputoutpututils.h"
|
||||
|
||||
std::vector<std::pair<int, int>> getTrackOrdering(const ImgInputOutputProto& config,
|
||||
unsigned numTracks, unsigned numSides)
|
||||
{
|
||||
int tracks = config.has_tracks() ? config.tracks() : numTracks;
|
||||
int sides = config.has_sides() ? config.sides() : numSides;
|
||||
|
||||
std::vector<std::pair<int, int>> ordering;
|
||||
switch (config.order())
|
||||
{
|
||||
case ImgInputOutputProto::CHS:
|
||||
{
|
||||
for (int track = 0; track < tracks; track++)
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
ordering.push_back(std::make_pair(track, side));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ImgInputOutputProto::HCS:
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
{
|
||||
for (int track = 0; track < tracks; track++)
|
||||
ordering.push_back(std::make_pair(track, side));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Error() << "IMG: invalid track ordering";
|
||||
}
|
||||
|
||||
return ordering;
|
||||
}
|
||||
|
||||
void getTrackFormat(const ImgInputOutputProto& config,
|
||||
ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side)
|
||||
{
|
||||
trackdata.Clear();
|
||||
for (const ImgInputOutputProto::TrackdataProto& f : config.trackdata())
|
||||
{
|
||||
if (f.has_track() && f.has_up_to_track() && ((track < f.track()) || (track > f.up_to_track())))
|
||||
continue;
|
||||
if (f.has_track() && !f.has_up_to_track() && (track != f.track()))
|
||||
continue;
|
||||
if (f.has_side() && (f.side() != side))
|
||||
continue;
|
||||
|
||||
trackdata.MergeFrom(f);
|
||||
}
|
||||
}
|
||||
|
||||
12
lib/imginputoutpututils.h
Normal file
12
lib/imginputoutpututils.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef IMGINPUTOUTPUTUTILS_H
|
||||
#define IMGINPUTOUTPUTUTILS_H
|
||||
|
||||
extern std::vector<std::pair<int, int>> getTrackOrdering(const ImgInputOutputProto& config,
|
||||
unsigned numTracks = 0, unsigned numSides = 0);
|
||||
|
||||
extern void getTrackFormat(const ImgInputOutputProto& config,
|
||||
ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ static std::shared_ptr<Fluxmap> readFluxmap(FluxSource& fluxsource, unsigned cyl
|
||||
{
|
||||
std::cout << fmt::format("{0:>3}.{1}: ", cylinder, head) << std::flush;
|
||||
std::shared_ptr<Fluxmap> fluxmap = fluxsource.readFlux(cylinder, head);
|
||||
fluxmap->rescale(1.0/config.flux_source().rescale());
|
||||
std::cout << fmt::format(
|
||||
"{0:.0} ms in {1} bytes\n",
|
||||
fluxmap->duration()/1e6,
|
||||
@@ -52,7 +53,7 @@ static std::set<std::shared_ptr<Sector>> collect_sectors(std::set<std::shared_pt
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"\n multiple conflicting copies of sector {} seen; ",
|
||||
std::get<0>(sectorid), std::get<1>(sectorid), std::get<2>(sectorid));
|
||||
std::get<2>(sectorid));
|
||||
replacing->status = replacement->status = Sector::CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
13
lib/sector.h
13
lib/sector.h
@@ -4,6 +4,8 @@
|
||||
#include "bytes.h"
|
||||
#include "fluxmap.h"
|
||||
|
||||
class Record;
|
||||
|
||||
/*
|
||||
* Note that sectors here used zero-based numbering throughout (to make the
|
||||
* maths easier); traditionally floppy disk use 0-based track numbering and
|
||||
@@ -32,12 +34,13 @@ public:
|
||||
nanoseconds_t headerEndTime = 0;
|
||||
nanoseconds_t dataStartTime = 0;
|
||||
nanoseconds_t dataEndTime = 0;
|
||||
int physicalCylinder = 0;
|
||||
int physicalHead = 0;
|
||||
int logicalTrack = 0;
|
||||
int logicalSide = 0;
|
||||
int logicalSector = 0;
|
||||
unsigned physicalCylinder = 0;
|
||||
unsigned physicalHead = 0;
|
||||
unsigned logicalTrack = 0;
|
||||
unsigned logicalSide = 0;
|
||||
unsigned logicalSector = 0;
|
||||
Bytes data;
|
||||
std::vector<std::shared_ptr<Record>> records;
|
||||
|
||||
std::tuple<int, int, int, Status> key() const
|
||||
{ return std::make_tuple(logicalTrack, logicalSide, logicalSector, status); }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "fluxmap.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "lib/usb/usb.pb.h"
|
||||
#include "greaseweazle.h"
|
||||
#include "serial.h"
|
||||
|
||||
@@ -61,8 +62,9 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
GreaseWeazleUsb(const std::string& port):
|
||||
_serial(SerialPort::openSerialPort(port))
|
||||
GreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config):
|
||||
_serial(SerialPort::openSerialPort(port)),
|
||||
_config(config)
|
||||
{
|
||||
int version = getVersion();
|
||||
if (version >= 29)
|
||||
@@ -79,7 +81,7 @@ public:
|
||||
|
||||
/* Configure the hardware. */
|
||||
|
||||
do_command({ CMD_SET_BUS_TYPE, 3, BUS_IBMPC });
|
||||
do_command({ CMD_SET_BUS_TYPE, 3, (uint8_t)config.bus_type() });
|
||||
}
|
||||
|
||||
int getVersion()
|
||||
@@ -129,7 +131,7 @@ public:
|
||||
.write_8(CMD_READ_FLUX)
|
||||
.write_8(cmd.size())
|
||||
.write_le32(0) //ticks default value (guessed)
|
||||
.write_le32(2);//guessed
|
||||
.write_le16(2);//revolutions
|
||||
do_command(cmd);
|
||||
}
|
||||
}
|
||||
@@ -276,14 +278,13 @@ public:
|
||||
if (hardSectorThreshold != 0)
|
||||
Error() << "hard sectors are currently unsupported on the GreaseWeazel";
|
||||
|
||||
int revolutions = (readTime+_revolutions-1) / _revolutions;
|
||||
|
||||
do_command({ CMD_HEAD, 3, (uint8_t)side });
|
||||
|
||||
switch (_version)
|
||||
{
|
||||
case V22:
|
||||
{
|
||||
int revolutions = (readTime+_revolutions-1) / _revolutions;
|
||||
Bytes cmd(4);
|
||||
cmd.writer()
|
||||
.write_8(CMD_READ_FLUX)
|
||||
@@ -300,8 +301,8 @@ public:
|
||||
cmd.writer()
|
||||
.write_8(CMD_READ_FLUX)
|
||||
.write_8(cmd.size())
|
||||
.write_le32(0) //ticks default value (guessed)
|
||||
.write_le32(revolutions + (synced ? 1 : 0));
|
||||
.write_le32((readTime + (synced ? _revolutions : 0)) / _clock)
|
||||
.write_le32(0);
|
||||
do_command(cmd);
|
||||
}
|
||||
}
|
||||
@@ -384,14 +385,15 @@ private:
|
||||
};
|
||||
|
||||
std::unique_ptr<SerialPort> _serial;
|
||||
const GreaseWeazleProto& _config;
|
||||
int _version;
|
||||
nanoseconds_t _clock;
|
||||
nanoseconds_t _revolutions;
|
||||
};
|
||||
|
||||
USB* createGreaseWeazleUsb(const std::string& port)
|
||||
USB* createGreaseWeazleUsb(const std::string& port, const GreaseWeazleProto& config)
|
||||
{
|
||||
return new GreaseWeazleUsb(port);
|
||||
return new GreaseWeazleUsb(port, config);
|
||||
}
|
||||
|
||||
// vim: sw=4 ts=4 et
|
||||
|
||||
@@ -46,7 +46,15 @@
|
||||
commtimeouts.ReadIntervalTimeout = 100;
|
||||
SetCommTimeouts(_handle, &commtimeouts);
|
||||
|
||||
PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
|
||||
if (!EscapeCommFunction(_handle, CLRDTR))
|
||||
Error() << fmt::format("Couldn't clear DTR: {}",
|
||||
get_last_error_string());
|
||||
Sleep(200);
|
||||
if (!EscapeCommFunction(_handle, SETDTR))
|
||||
Error() << fmt::format("Couldn't set DTR: {}",
|
||||
get_last_error_string());
|
||||
|
||||
PurgeComm(_handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR);
|
||||
}
|
||||
|
||||
~SerialPortImpl() override
|
||||
@@ -113,6 +121,7 @@
|
||||
|
||||
#else
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
class SerialPortImpl : public SerialPort
|
||||
{
|
||||
@@ -138,6 +147,15 @@
|
||||
t.c_cc[VMIN] = 1;
|
||||
cfsetspeed(&t, 9600);
|
||||
tcsetattr(_fd, TCSANOW, &t);
|
||||
|
||||
/* Toggle DTR to reset the device. */
|
||||
|
||||
int flag = TIOCM_DTR;
|
||||
if (ioctl(_fd, TIOCMBIC, &flag) == -1)
|
||||
Error() << fmt::format("cannot clear DTR on serial port: {}", strerror(errno));
|
||||
usleep(200000);
|
||||
if (ioctl(_fd, TIOCMBIS, &flag) == -1)
|
||||
Error() << fmt::format("cannot set DTR on serial port: {}", strerror(errno));
|
||||
}
|
||||
|
||||
~SerialPortImpl() override
|
||||
|
||||
115
lib/usb/usb.cc
115
lib/usb/usb.cc
@@ -13,70 +13,87 @@
|
||||
|
||||
static USB* usb = NULL;
|
||||
|
||||
USB::~USB()
|
||||
{}
|
||||
USB::~USB() {}
|
||||
|
||||
static std::unique_ptr<CandidateDevice> selectDevice()
|
||||
{
|
||||
auto candidates = findUsbDevices({ FLUXENGINE_ID, GREASEWEAZLE_ID });
|
||||
if (candidates.size() == 0)
|
||||
Error() << "no devices found (is one plugged in? Do you have the appropriate "
|
||||
"permissions?";
|
||||
auto candidates = findUsbDevices({FLUXENGINE_ID, GREASEWEAZLE_ID});
|
||||
if (candidates.size() == 0)
|
||||
Error() << "no devices found (is one plugged in? Do you have the "
|
||||
"appropriate permissions?";
|
||||
|
||||
if (config.usb().has_serial())
|
||||
{
|
||||
auto wantedSerial = config.usb().serial();
|
||||
for (auto& c : candidates)
|
||||
{
|
||||
if (c->serial == wantedSerial)
|
||||
return std::move(c);
|
||||
}
|
||||
Error() << "serial number not found (try without one to list or autodetect devices)";
|
||||
}
|
||||
if (config.usb().has_serial())
|
||||
{
|
||||
auto wantedSerial = config.usb().serial();
|
||||
for (auto& c : candidates)
|
||||
{
|
||||
if (c->serial == wantedSerial)
|
||||
return std::move(c);
|
||||
}
|
||||
Error() << "serial number not found (try without one to list or "
|
||||
"autodetect devices)";
|
||||
}
|
||||
|
||||
if (candidates.size() == 1)
|
||||
return std::move(candidates[0]);
|
||||
if (candidates.size() == 1)
|
||||
return std::move(candidates[0]);
|
||||
|
||||
std::cerr << "More than one device detected; use --usb.serial=<serial> to select one:\n";
|
||||
for (const auto& c : candidates)
|
||||
{
|
||||
std::cerr << " ";
|
||||
switch (c->id)
|
||||
{
|
||||
case FLUXENGINE_ID:
|
||||
std::cerr << fmt::format("FluxEngine: {}\n", c->serial);
|
||||
break;
|
||||
std::cerr << "More than one device detected; use --usb.serial=<serial> to "
|
||||
"select one:\n";
|
||||
for (const auto& c : candidates)
|
||||
{
|
||||
std::cerr << " ";
|
||||
switch (c->id)
|
||||
{
|
||||
case FLUXENGINE_ID:
|
||||
std::cerr << fmt::format("FluxEngine: {}\n", c->serial);
|
||||
break;
|
||||
|
||||
case GREASEWEAZLE_ID:
|
||||
std::cerr << fmt::format("GreaseWeazle: {} on {}\n", c->serial, c->serialPort);
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
case GREASEWEAZLE_ID:
|
||||
std::cerr << fmt::format(
|
||||
"GreaseWeazle: {} on {}\n", c->serial, c->serialPort);
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
USB* get_usb_impl()
|
||||
{
|
||||
auto candidate = selectDevice();
|
||||
switch (candidate->id)
|
||||
{
|
||||
case FLUXENGINE_ID:
|
||||
std::cerr << fmt::format("Using FluxEngine {}\n", candidate->serial);
|
||||
return createFluxengineUsb(candidate->device);
|
||||
/* Special case for certain configurations. */
|
||||
|
||||
case GREASEWEAZLE_ID:
|
||||
std::cerr << fmt::format("Using GreaseWeazle {} on {}\n", candidate->serial, candidate->serialPort);
|
||||
return createGreaseWeazleUsb(candidate->serialPort);
|
||||
if (config.usb().has_greaseweazle() &&
|
||||
config.usb().greaseweazle().has_port())
|
||||
{
|
||||
const auto& conf = config.usb().greaseweazle();
|
||||
std::cerr << fmt::format(
|
||||
"Using GreaseWeazle on serial port {}\n", conf.port());
|
||||
return createGreaseWeazleUsb(conf.port(), conf);
|
||||
}
|
||||
|
||||
default:
|
||||
Error() << "internal";
|
||||
}
|
||||
/* Otherwise, select a device by USB ID. */
|
||||
|
||||
auto candidate = selectDevice();
|
||||
switch (candidate->id)
|
||||
{
|
||||
case FLUXENGINE_ID:
|
||||
std::cerr << fmt::format(
|
||||
"Using FluxEngine {}\n", candidate->serial);
|
||||
return createFluxengineUsb(candidate->device);
|
||||
|
||||
case GREASEWEAZLE_ID:
|
||||
std::cerr << fmt::format("Using GreaseWeazle {} on {}\n",
|
||||
candidate->serial,
|
||||
candidate->serialPort);
|
||||
return createGreaseWeazleUsb(
|
||||
candidate->serialPort, config.usb().greaseweazle());
|
||||
|
||||
default: Error() << "internal";
|
||||
}
|
||||
}
|
||||
|
||||
USB& getUsb()
|
||||
{
|
||||
if (!usb)
|
||||
usb = get_usb_impl();
|
||||
return *usb;
|
||||
if (!usb)
|
||||
usb = get_usb_impl();
|
||||
return *usb;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "flags.h"
|
||||
|
||||
class Fluxmap;
|
||||
class GreaseWeazleProto;
|
||||
namespace libusbp { class device; }
|
||||
|
||||
class USB
|
||||
@@ -33,7 +34,7 @@ protected:
|
||||
extern USB& getUsb();
|
||||
|
||||
extern USB* createFluxengineUsb(libusbp::device& device);
|
||||
extern USB* createGreaseWeazleUsb(const std::string& serialPort);
|
||||
extern USB* createGreaseWeazleUsb(const std::string& serialPort, const GreaseWeazleProto& config);
|
||||
|
||||
static inline int usbGetVersion() { return getUsb().getVersion(); }
|
||||
static inline void usbRecalibrate() { getUsb().recalibrate(); }
|
||||
|
||||
@@ -2,9 +2,26 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message GreaseWeazleProto {
|
||||
enum BusType {
|
||||
BUSTYPE_INVALID = 0;
|
||||
IBMPC = 1;
|
||||
SHUGART = 2;
|
||||
};
|
||||
|
||||
optional string port = 1
|
||||
[(help) = "GreaseWeazle serial port to use"];
|
||||
optional BusType bus_type = 2
|
||||
[(help) = "which FDD bus type is in use", default = IBMPC];
|
||||
}
|
||||
|
||||
message UsbProto {
|
||||
oneof device {
|
||||
string serial = 1
|
||||
[(help) = "serial number of FluxEngine or GreaseWeazle device to use"];
|
||||
}
|
||||
|
||||
oneof config {
|
||||
GreaseWeazleProto greaseweazle = 2 [(help) = "GreaseWeazle-specific options"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,41 +9,42 @@
|
||||
|
||||
static const std::string get_serial_number(const libusbp::device& device)
|
||||
{
|
||||
try
|
||||
{
|
||||
return device.get_serial_number();
|
||||
}
|
||||
catch (const libusbp::error& e)
|
||||
{
|
||||
if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER))
|
||||
return "n/a";
|
||||
throw;
|
||||
}
|
||||
try
|
||||
{
|
||||
return device.get_serial_number();
|
||||
}
|
||||
catch (const libusbp::error& e)
|
||||
{
|
||||
if (e.has_code(LIBUSBP_ERROR_NO_SERIAL_NUMBER))
|
||||
return "n/a";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<CandidateDevice>> findUsbDevices(const std::set<uint32_t>& ids)
|
||||
std::vector<std::unique_ptr<CandidateDevice>> findUsbDevices(
|
||||
const std::set<uint32_t>& ids)
|
||||
{
|
||||
std::vector<std::unique_ptr<CandidateDevice>> candidates;
|
||||
for (const auto& it : libusbp::list_connected_devices())
|
||||
{
|
||||
auto candidate = std::make_unique<CandidateDevice>();
|
||||
candidate->device = it;
|
||||
std::vector<std::unique_ptr<CandidateDevice>> candidates;
|
||||
for (const auto& it : libusbp::list_connected_devices())
|
||||
{
|
||||
auto candidate = std::make_unique<CandidateDevice>();
|
||||
candidate->device = it;
|
||||
|
||||
uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id();
|
||||
if (ids.contains(id))
|
||||
{
|
||||
candidate->id = id;
|
||||
candidate->serial = get_serial_number(it);
|
||||
uint32_t id = (it.get_vendor_id() << 16) | it.get_product_id();
|
||||
if (ids.find(id) != ids.end())
|
||||
{
|
||||
candidate->id = id;
|
||||
candidate->serial = get_serial_number(it);
|
||||
|
||||
if (id == GREASEWEAZLE_ID)
|
||||
{
|
||||
libusbp::serial_port port(candidate->device);
|
||||
candidate->serialPort = port.get_name();
|
||||
}
|
||||
if (id == GREASEWEAZLE_ID)
|
||||
{
|
||||
libusbp::serial_port port(candidate->device);
|
||||
candidate->serialPort = port.get_name();
|
||||
}
|
||||
|
||||
candidates.push_back(std::move(candidate));
|
||||
}
|
||||
}
|
||||
candidates.push_back(std::move(candidate));
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ void writeTracks(
|
||||
}
|
||||
else
|
||||
{
|
||||
fluxmap->rescale(config.flux_sink().rescale());
|
||||
/* Precompensation actually seems to make things worse, so let's leave
|
||||
* it disabled for now. */
|
||||
//fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
|
||||
@@ -77,6 +78,7 @@ void writeTracksAndVerify(
|
||||
}
|
||||
else
|
||||
{
|
||||
fluxmap->rescale(config.flux_sink().rescale());
|
||||
std::sort(sectors.begin(), sectors.end(), sectorPointerSortPredicate);
|
||||
|
||||
for (int retry = 0;; retry++)
|
||||
|
||||
@@ -469,7 +469,10 @@ buildlibrary libbackend.a \
|
||||
lib/imagewriter/imagewriter.cc \
|
||||
lib/imagewriter/imgimagewriter.cc \
|
||||
lib/imagewriter/ldbsimagewriter.cc \
|
||||
lib/imagereader/nfdimagereader.cc \
|
||||
lib/imagewriter/nsiimagewriter.cc \
|
||||
lib/imagewriter/rawimagewriter.cc \
|
||||
lib/imginputoutpututils.cc \
|
||||
lib/ldbs.cc \
|
||||
lib/proto.cc \
|
||||
lib/reader.cc \
|
||||
@@ -528,8 +531,9 @@ FORMATS="\
|
||||
northstar350 \
|
||||
northstar87 \
|
||||
tids990 \
|
||||
victor9k_ss \
|
||||
vgi \
|
||||
victor9k_ss \
|
||||
victor9k_ds \
|
||||
zilogmcz \
|
||||
"
|
||||
|
||||
@@ -641,6 +645,7 @@ encodedecodetest mac800 scripts/mac800_test.textpb
|
||||
encodedecodetest n88basic
|
||||
encodedecodetest tids990
|
||||
encodedecodetest victor9k_ss
|
||||
encodedecodetest victor9k_ds
|
||||
|
||||
# vim: sw=4 ts=4 et
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <fstream>
|
||||
|
||||
static FlagGroup flags;
|
||||
static bool verify = true;
|
||||
|
||||
static StringFlag sourceImage(
|
||||
{ "--input", "-i" },
|
||||
@@ -55,6 +56,13 @@ static StringFlag destHeads(
|
||||
setRange(config.mutable_heads(), value);
|
||||
});
|
||||
|
||||
static ActionFlag noVerifyFlag(
|
||||
{ "--no-verify", "-n" },
|
||||
"skip verification of write",
|
||||
[]{
|
||||
verify = false;
|
||||
});
|
||||
|
||||
int mainWrite(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
@@ -68,7 +76,7 @@ int mainWrite(int argc, const char* argv[])
|
||||
std::unique_ptr<FluxSink> fluxSink(FluxSink::create(config.flux_sink()));
|
||||
|
||||
std::unique_ptr<AbstractDecoder> decoder;
|
||||
if (config.has_decoder())
|
||||
if (config.has_decoder() && verify)
|
||||
decoder = AbstractDecoder::create(config.decoder());
|
||||
|
||||
std::unique_ptr<FluxSource> fluxSource;
|
||||
|
||||
@@ -17,7 +17,17 @@ image_reader {
|
||||
|
||||
image_writer {
|
||||
filename: "brother120.img"
|
||||
img {}
|
||||
img {
|
||||
tracks: 39
|
||||
sides: 1
|
||||
trackdata {
|
||||
sector_size: 256
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -17,7 +17,17 @@ image_reader {
|
||||
|
||||
image_writer {
|
||||
filename: "brother240.img"
|
||||
img {}
|
||||
img {
|
||||
tracks: 78
|
||||
sides: 1
|
||||
trackdata {
|
||||
sector_size: 256
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
459
src/formats/victor9k_ds.textpb
Normal file
459
src/formats/victor9k_ds.textpb
Normal file
@@ -0,0 +1,459 @@
|
||||
comment: 'Victor 9000 / Sirius One 1224kB DSHD GCR variable sector)'
|
||||
|
||||
image_reader {
|
||||
filename: "victor9k_ds.img"
|
||||
img {
|
||||
tracks: 80
|
||||
sides: 2
|
||||
trackdata {
|
||||
sector_size: 512
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 0
|
||||
up_to_track: 3
|
||||
sector_range {
|
||||
sector_count: 19
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 4
|
||||
up_to_track: 15
|
||||
sector_range {
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 16
|
||||
up_to_track: 26
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 27
|
||||
up_to_track: 37
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 38
|
||||
up_to_track: 47
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 48
|
||||
up_to_track: 59
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 60
|
||||
up_to_track: 70
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 71
|
||||
up_to_track: 79
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 0
|
||||
up_to_track: 7
|
||||
sector_range {
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 8
|
||||
up_to_track: 18
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 19
|
||||
up_to_track: 29
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 30
|
||||
up_to_track: 39
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 40
|
||||
up_to_track: 51
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 52
|
||||
up_to_track: 62
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 63
|
||||
up_to_track: 74
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 75
|
||||
up_to_track: 79
|
||||
sector_range {
|
||||
sector_count: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image_writer {
|
||||
filename: "victor9k_ds.img"
|
||||
img {
|
||||
tracks: 80
|
||||
sides: 2
|
||||
trackdata {
|
||||
sector_size: 512
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 0
|
||||
up_to_track: 3
|
||||
sector_range {
|
||||
sector_count: 19
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 4
|
||||
up_to_track: 15
|
||||
sector_range {
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 16
|
||||
up_to_track: 26
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 27
|
||||
up_to_track: 37
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 38
|
||||
up_to_track: 47
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 48
|
||||
up_to_track: 59
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 60
|
||||
up_to_track: 70
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 0
|
||||
track: 71
|
||||
up_to_track: 79
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 0
|
||||
up_to_track: 7
|
||||
sector_range {
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 8
|
||||
up_to_track: 18
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 19
|
||||
up_to_track: 29
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 30
|
||||
up_to_track: 39
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 40
|
||||
up_to_track: 51
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 52
|
||||
up_to_track: 62
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 63
|
||||
up_to_track: 74
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
side: 1
|
||||
track: 75
|
||||
up_to_track: 79
|
||||
sector_range {
|
||||
sector_count: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encoder {
|
||||
victor9k {
|
||||
trackdata {
|
||||
original_data_rate_khz: 500
|
||||
post_index_gap_us: 1000.0
|
||||
pre_header_sync_bits: 120
|
||||
post_header_gap_bits: 48
|
||||
pre_data_sync_bits: 40
|
||||
post_data_gap_bits: 200
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 0
|
||||
max_cylinder: 3
|
||||
original_period_ms: 237.9
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
sector_count: 19
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 4
|
||||
max_cylinder: 15
|
||||
original_period_ms: 224.5
|
||||
sector_range {
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 16
|
||||
max_cylinder: 26
|
||||
original_period_ms: 212.2
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 27
|
||||
max_cylinder: 37
|
||||
original_period_ms: 199.9
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 38
|
||||
max_cylinder: 47
|
||||
original_period_ms: 187.6
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 48
|
||||
max_cylinder: 59
|
||||
original_period_ms: 175.3
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 60
|
||||
max_cylinder: 70
|
||||
original_period_ms: 163.0
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 71
|
||||
max_cylinder: 79
|
||||
original_period_ms: 149.6
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 0
|
||||
max_cylinder: 7
|
||||
original_period_ms: 224.5
|
||||
sector_range {
|
||||
start_sector: 0
|
||||
sector_count: 18
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 8
|
||||
max_cylinder: 18
|
||||
original_period_ms: 212.2
|
||||
sector_range {
|
||||
sector_count: 17
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 19
|
||||
max_cylinder: 29
|
||||
original_period_ms: 199.9
|
||||
sector_range {
|
||||
sector_count: 16
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 30
|
||||
max_cylinder: 39
|
||||
original_period_ms: 187.6
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 40
|
||||
max_cylinder: 51
|
||||
original_period_ms: 175.3
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 52
|
||||
max_cylinder: 62
|
||||
original_period_ms: 163.0
|
||||
sector_range {
|
||||
sector_count: 13
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 63
|
||||
max_cylinder: 74
|
||||
original_period_ms: 149.6
|
||||
sector_range {
|
||||
sector_count: 12
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
head: 1
|
||||
min_cylinder: 75
|
||||
max_cylinder: 79
|
||||
original_period_ms: 144.0
|
||||
sector_range {
|
||||
sector_count: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decoder {
|
||||
victor9k {}
|
||||
}
|
||||
|
||||
cylinders {
|
||||
start: 0
|
||||
end: 79
|
||||
}
|
||||
|
||||
heads {
|
||||
start: 0
|
||||
end: 1
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ image_reader {
|
||||
}
|
||||
trackdata {
|
||||
track: 38
|
||||
up_to_track: 48
|
||||
up_to_track: 47
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
track: 49
|
||||
track: 48
|
||||
up_to_track: 59
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
@@ -111,13 +111,13 @@ image_writer {
|
||||
}
|
||||
trackdata {
|
||||
track: 38
|
||||
up_to_track: 48
|
||||
up_to_track: 47
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
}
|
||||
}
|
||||
trackdata {
|
||||
track: 49
|
||||
track: 48
|
||||
up_to_track: 59
|
||||
sector_range {
|
||||
sector_count: 14
|
||||
@@ -190,7 +190,7 @@ encoder {
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 38
|
||||
max_cylinder: 48
|
||||
max_cylinder: 47
|
||||
original_period_ms: 187.6
|
||||
sector_range {
|
||||
sector_count: 15
|
||||
@@ -198,7 +198,7 @@ encoder {
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
min_cylinder: 49
|
||||
min_cylinder: 48
|
||||
max_cylinder: 59
|
||||
original_period_ms: 175.3
|
||||
sector_range {
|
||||
|
||||
Reference in New Issue
Block a user