mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Compare commits
45 Commits
FluxEngine
...
FluxEngine
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9670e205e | ||
|
|
658e2b7295 | ||
|
|
7b4a8d6de2 | ||
|
|
e8f7b51aef | ||
|
|
9d6bc57a5f | ||
|
|
73766f92b4 | ||
|
|
80badf3b54 | ||
|
|
116529f85a | ||
|
|
5a2b2bc07a | ||
|
|
41070395c0 | ||
|
|
4304d1eede | ||
|
|
46f1b0aef4 | ||
|
|
9923d67a7c | ||
|
|
99335a84fd | ||
|
|
c266779433 | ||
|
|
bdcc12cd53 | ||
|
|
7988d0fe24 | ||
|
|
27f5c294b1 | ||
|
|
b9a53e0d1c | ||
|
|
f8b6d5e6fb | ||
|
|
04ff31c348 | ||
|
|
77b4aebd1b | ||
|
|
4056364300 | ||
|
|
60bfe050d3 | ||
|
|
28d0ce765e | ||
|
|
4954d33307 | ||
|
|
55f3354287 | ||
|
|
d6ae373fa8 | ||
|
|
a626d5f9a0 | ||
|
|
29db67528d | ||
|
|
31d7477c6a | ||
|
|
56af9eaf18 | ||
|
|
5de0636fe7 | ||
|
|
f9117b8d11 | ||
|
|
10d385375f | ||
|
|
2f72c3f8f0 | ||
|
|
54edff9b94 | ||
|
|
112377f885 | ||
|
|
87e29fc386 | ||
|
|
b1db5c48b1 | ||
|
|
38fab7edcb | ||
|
|
d8172154c3 | ||
|
|
eb924780ab | ||
|
|
28e0ef0463 | ||
|
|
4b07c38782 |
@@ -15,7 +15,7 @@ install:
|
||||
|
||||
build_script:
|
||||
- make
|
||||
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe
|
||||
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
|
||||
|
||||
artifacts:
|
||||
- path: fluxengine.zip
|
||||
|
||||
@@ -3,6 +3,10 @@ streams
|
||||
.*\.flux
|
||||
.*\.img
|
||||
.*\.raw
|
||||
.*\.orig
|
||||
.vscode
|
||||
remote
|
||||
FluxEngine.cydsn/CortexM3
|
||||
FluxEngine.cydsn/Generated_Source
|
||||
FluxEngine.cydsn/codegentemp
|
||||
|
||||
|
||||
4626
FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
Normal file
4626
FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
Normal file
File diff suppressed because it is too large
Load Diff
@@ -530,31 +530,38 @@
|
||||
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
|
||||
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED" persistent="">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
|
||||
<dependencies>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_aliases.h" persistent="Generated_Source\PSoC5\LED_aliases.h">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.c" persistent="Generated_Source\PSoC5\LED.c">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.h" persistent="Generated_Source\PSoC5\LED.h">
|
||||
<Hidden v="False" />
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PM.c" persistent="Generated_Source\PSoC5\LED_PM.c">
|
||||
<Hidden v="True" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
@@ -3282,6 +3289,39 @@
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
|
||||
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN" persistent="">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
|
||||
<dependencies>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN_aliases.h" persistent="Generated_Source\PSoC5\LED_PIN_aliases.h">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN.c" persistent="Generated_Source\PSoC5\LED_PIN.c">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="SOURCE_C;CortexM3;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
|
||||
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN.h" persistent="Generated_Source\PSoC5\LED_PIN.h">
|
||||
<Hidden v="False" />
|
||||
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
|
||||
<build_action v="HEADER;;;;" />
|
||||
<PropertyDeltas />
|
||||
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
<filters />
|
||||
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
|
||||
</dependencies>
|
||||
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
|
||||
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
|
||||
@@ -3700,6 +3740,6 @@
|
||||
</ignored_deps>
|
||||
</CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37>
|
||||
<boot_component v="" />
|
||||
<current_generation v="60" />
|
||||
<current_generation v="68" />
|
||||
</CyGuid_fec8f9e8-2365-4bdb-96d3-a4380222e01b>
|
||||
</CyXmlSerializer>
|
||||
Binary file not shown.
Binary file not shown.
6
Makefile
6
Makefile
@@ -1,8 +1,8 @@
|
||||
PACKAGES = zlib sqlite3 libusb-1.0
|
||||
|
||||
export CFLAGS = -O3 -g --std=c++14 \
|
||||
export CFLAGS = -Os -g --std=c++14 \
|
||||
-ffunction-sections -fdata-sections
|
||||
export LDFLAGS = -O3
|
||||
export LDFLAGS = -Os
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
export CXX = /mingw32/bin/g++
|
||||
@@ -22,7 +22,7 @@ export LIBS = $(shell pkg-config --libs $(PACKAGES))
|
||||
export EXTENSION =
|
||||
endif
|
||||
|
||||
CFLAGS += -Ilib -Idep/fmt
|
||||
CFLAGS += -Ilib -Idep/fmt -Iarch
|
||||
|
||||
export OBJDIR = .obj
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ void AmigaDecoder::decodeSectorRecord()
|
||||
|
||||
uint32_t wanteddatachecksum = deinterleave(ptr, 4).reader().read_be32();
|
||||
uint32_t gotdatachecksum = checksum(rawbytes.slice(62, 1024));
|
||||
_sector->data = deinterleave(ptr, 512);
|
||||
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(deinterleave(ptr, 512)).append(recoveryinfo);
|
||||
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
}
|
||||
@@ -134,6 +134,9 @@ void IbmDecoder::decodeSectorRecord()
|
||||
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
|
||||
if (wantCrc == gotCrc)
|
||||
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (_ignoreSideByte)
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
}
|
||||
|
||||
void IbmDecoder::decodeDataRecord()
|
||||
@@ -31,8 +31,9 @@ struct IbmIdam
|
||||
class IbmDecoder : public AbstractDecoder
|
||||
{
|
||||
public:
|
||||
IbmDecoder(unsigned sectorBase):
|
||||
_sectorBase(sectorBase)
|
||||
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false):
|
||||
_sectorBase(sectorBase),
|
||||
_ignoreSideByte(ignoreSideByte)
|
||||
{}
|
||||
|
||||
RecordType advanceToNextRecord();
|
||||
@@ -41,6 +42,7 @@ public:
|
||||
|
||||
private:
|
||||
unsigned _sectorBase;
|
||||
bool _ignoreSideByte;
|
||||
unsigned _currentSectorSize;
|
||||
unsigned _currentHeaderLength;
|
||||
};
|
||||
@@ -180,5 +180,7 @@ void MacintoshDecoder::decodeDataRecord()
|
||||
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
|
||||
|
||||
_sector->status = Sector::BAD_CHECKSUM;
|
||||
_sector->data = decode_crazy_data(inputbuffer, _sector->status);
|
||||
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
|
||||
_sector->data.clear();
|
||||
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
|
||||
}
|
||||
@@ -103,7 +103,43 @@ the unconnected pins and solder a short piece of wire to a GND pin on the
|
||||
board. Alternatively you'll need to splice it into your drive's power supply
|
||||
cable somehow. (The black one.)
|
||||
|
||||
## Building the firmware
|
||||
## Programming the board
|
||||
|
||||
You've got two options here. You can either use the precompiled firmware
|
||||
supplied with the source, or else install the Cypress SDK and build it
|
||||
yourself. If you want to hack the firmware source you need the latter, but
|
||||
if you trust me to do it for you use the precompiled firmware. In either
|
||||
case you'll need Windows and have to install some Cypress stuff.
|
||||
|
||||
**Before you read this:** If you're on Windows, good news! You can download a
|
||||
precompiled version of the FluxEngine client and precompiled firmware [from
|
||||
the GitHub releases
|
||||
page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
|
||||
it somewhere and run the `.exe` files from a `cmd` window (or other shell).
|
||||
Follow the instructions below to program the board with the firmware.
|
||||
|
||||
### Using the precompiled firmware
|
||||
|
||||
On your Windows machine, [install the PSoC
|
||||
Programmer](https://www.cypress.com/products/psoc-programming-solutions).
|
||||
**Note:** _not_ the Cypress Programmer, which is for a different board!
|
||||
Cypress will make you register.
|
||||
|
||||
Once done, run it. Plug the blunt end of the FluxEngine board into a USB
|
||||
port (the end which is a USB connector). The programmer should detect it
|
||||
and report it as a KitProg. You may be prompted to upgrade the programmer
|
||||
hardware; if so, follow the instructions and do it.
|
||||
|
||||
Now go to File -> File Load and open
|
||||
`FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex` in the
|
||||
project. If you're on Windows, the precompiled zipfile also contains a copy
|
||||
of this file. Press the Program button (the one in the toolbar marked with a
|
||||
down arrow). Stuff will happen and you should be left with three green boxes
|
||||
in the status bar and 'Programming Succeeded' at the top of the log window.
|
||||
|
||||
You're done. You can unplug the board and close the programmer.
|
||||
|
||||
### Building the firmware yourself
|
||||
|
||||
On your Windows machine, [install the Cypress SDK and CY8CKIT-059
|
||||
BSP](http://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and).
|
||||
@@ -118,7 +154,7 @@ tutorial and making the LED on your board flash. It'll tell you where all the
|
||||
controls are and how to program the board. Remember that the big end of the
|
||||
board plugs into your computer for programming.
|
||||
|
||||
When you're ready, open the `FluxEngine.cydsn/FluxEngine.cywrk` workspace,
|
||||
When you're ready, open the `FluxEngine.cydsn/FluxEngine.cyprj` project,
|
||||
pick 'Program' from the menu, and the firmware should compile and be
|
||||
programmed onto your board.
|
||||
|
||||
@@ -139,11 +175,6 @@ the port and proceed normally.
|
||||
|
||||
## Building the client
|
||||
|
||||
**Before you read this:** If you're on Windows, good news! You can download a
|
||||
*precompiled version of the FluxEngine client [from the GitHub releases
|
||||
*page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
|
||||
*it somewhere and run it from a `cmd` window (or other shell).
|
||||
|
||||
The client software is where the intelligence, such as it is, is. It's pretty
|
||||
generic libusb stuff and should build and run on Windows, Linux and OSX as
|
||||
well, although on Windows it'll need MSYS2 and mingw32. You'll need to
|
||||
|
||||
@@ -23,6 +23,17 @@ You should end up with an `amiga.adf` which is 901120 bytes long (for a
|
||||
normal DD disk) --- it ought to be a perfectly normal ADF file which you can
|
||||
use in an emulator.
|
||||
|
||||
If you want the metadata as well, specify a 528 byte sector size for the
|
||||
output image:
|
||||
|
||||
```
|
||||
fluxengine read amiga -o amiga.adf:b=528
|
||||
```
|
||||
|
||||
You will end up with a 929280 byte long image which you probably _can't_ use
|
||||
in an emulator; each sector will contain the 512 bytes of user payload
|
||||
followed by the 16 bytes of metadata.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ computer](https://ilesj.wordpress.com/2014/05/14/1541-why-so-complicated/) of
|
||||
300 bytes per second (!). (The drive itself could transfer data reasonably
|
||||
quickly.)
|
||||
|
||||
A standard 1541 disk has 35 tracks of 17 to 20 sectors, each 256 bytes long.
|
||||
A standard 1541 disk has 35 tracks of 17 to 21 sectors, each 256 bytes long.
|
||||
|
||||
Reading discs
|
||||
-------------
|
||||
@@ -34,15 +34,14 @@ Just do:
|
||||
fluxengine read c64
|
||||
```
|
||||
|
||||
You should end up with an `c64.img` which is 187136 bytes long (for a normal
|
||||
1541 disk).
|
||||
You should end up with an `c64.d64` file which is 174848 bytes long. You can
|
||||
load this straight into a Commodore 64 emulator such as
|
||||
[VICE](http://vice-emu.sourceforge.net/).
|
||||
|
||||
**Big warning!** The image may not work in an emulator. Commodore 64 disk images are
|
||||
**Big warning!** Commodore 64 disk images are
|
||||
complicated due to the way the tracks are different sizes and the odd sector
|
||||
size. FluxEngine chooses to store them in a simple 256 x 20 x 35 layout,
|
||||
with holes where missing sectors should be. This was easiest. If anyone can
|
||||
suggest a better way, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
size, so you need the special D64 or LDBS output formats to represent them
|
||||
sensibly. Don't use IMG unless you know what you're doing.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
@@ -50,6 +50,10 @@ with holes where missing sectors should be. This was easiest. If anyone can
|
||||
suggest a better way, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
The 12 bytes of metadata _follow_ the 512 bytes of user payload in the sector
|
||||
image. If you don't want it, specify a geometry in the output file with a
|
||||
512-byte sectore size like `-o mac.img:c=80:h=1:s=12:b=512`.
|
||||
|
||||
Useful references
|
||||
-----------------
|
||||
|
||||
|
||||
89
doc/using.md
89
doc/using.md
@@ -112,6 +112,51 @@ sensible for the command you're using.
|
||||
**Important note:** FluxEngine _always_ uses zero-based units (even if the
|
||||
*disk format says otherwise).
|
||||
|
||||
### Input and output specifiers
|
||||
|
||||
These use a very similar syntax to the source and destination specifiers
|
||||
(because they're based on the same microformat library!) but are used for
|
||||
input and output _images_: i.e. nicely lined up arrays of sectors which you
|
||||
can actually do something with.
|
||||
|
||||
Use `--input` (`-i`) or `--output` (`-o`) as appropriate to tell FluxEngine
|
||||
where you want to read from or write to. The actual format is autodetected
|
||||
based on the extension:
|
||||
|
||||
- `.img` or `.adf`: raw sector images in CHS order. Append
|
||||
`:c=80:h=2:s=9:b=512` to set the geometry; that specifies 80 cylinders, 2
|
||||
heads, 9 sectors, 512 bytes per sector. For output files (`--output`) the
|
||||
geometry will be autodetected if left unspecified. For input files you
|
||||
normally have to specify it.
|
||||
|
||||
- `.ldbs`: John Elliott's [LDBS disk image
|
||||
format](http://www.seasip.info/Unix/LibDsk/ldbs.html), which is
|
||||
consumable by the [libdsk](http://www.seasip.info/Unix/LibDsk/) suite of
|
||||
tools. This allows things like variable numbers of sectors per track
|
||||
(e.g. Macintosh or Commodore 64) and also provides information about
|
||||
whether sectors were read correctly. You can use libdsk to convert this
|
||||
to other formats, using a command like this:
|
||||
|
||||
```
|
||||
$ dsktrans out.ldbs -otype tele out.td0
|
||||
```
|
||||
|
||||
...to convert to TeleDisk format. (Note you have to use dsktrans rather
|
||||
than dskconv due to a minor bug in the geometry hadnling.)
|
||||
|
||||
FluxEngine's LDBS support is currently limited to write only, and
|
||||
it doesn't store a lot of the more esoteric LDBS features like format
|
||||
types, timings, and data rates.
|
||||
|
||||
- `.d64`: the venerable Commodore 64 disk image format as used by the 1540,
|
||||
1541, etc. This is a special-purpose format due to the weird layout of
|
||||
1540 disks and while you can use this for non-Commodore disks the result
|
||||
will be gibberish. Use this to image Commodore 64 disks and load the
|
||||
result into an emulator.
|
||||
|
||||
FluxEngine's D64 support is currently limited to write only. It will work
|
||||
with up to 40 logical tracks.
|
||||
|
||||
### High density disks
|
||||
|
||||
High density disks use a different magnetic medium to low and double density
|
||||
@@ -176,10 +221,39 @@ directory.
|
||||
file format in a non-backwards-compatible way; this tool will upgrade flux
|
||||
files to the new format.
|
||||
|
||||
- `fluxengine convert`: converts flux files from various formats to various
|
||||
other formats. You can use this to convert Catweasel flux files to
|
||||
FluxEngine's native format, FluxEngine flux files to various other formats
|
||||
useful for debugging (including VCD which can be loaded into
|
||||
[sigrok](http://sigrok.org)), and bidirectional conversion to and from
|
||||
Supercard Pro `.scp` format.
|
||||
|
||||
**Important SCP note:** import (`fluxengine convert scptoflux`) should be
|
||||
fairly robust, but export (`fluxengine convert fluxtoscp`) should only be
|
||||
done with great caution as FluxEngine files contain features which can't be
|
||||
represented very well in `.scp` format and they're probably pretty dubious.
|
||||
As ever, please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new) with any reports.
|
||||
|
||||
Commands which normally take `--source` or `--dest` get a sensible default if
|
||||
left unspecified. `fluxengine read ibm` on its own will read drive 0 and
|
||||
write an `ibm.img` file.
|
||||
|
||||
## Visualisation
|
||||
|
||||
When doing a read (either from a real disk or from a flux file) you can use
|
||||
`--write-svg=output.svg` to write out a graphical visualisation of where the
|
||||
sectors are on the disk. Here's a IBM PC 1232kB disk:
|
||||
|
||||

|
||||
|
||||
Blue represents data, light blue a header, and red is a bad sector. Side zero
|
||||
is on the left and side one is on the right.
|
||||
|
||||
The visualiser is extremely primitive and you have to explicitly tell it how
|
||||
big your disk is, in milliseconds. The default is 200ms (for a normal 3.5"
|
||||
disk). For a 5.25" disk, use `--visualiser-period=166`.
|
||||
|
||||
## Extra programs
|
||||
|
||||
Supplied with FluxEngine, but not part of FluxEngine, are some little tools I
|
||||
@@ -187,25 +261,24 @@ wrote to do useful things. These are built alongside FluxEngine.
|
||||
|
||||
- `brother120tool`: extracts files from a 120kB Brother filesystem image.
|
||||
|
||||
- `cwftoflux`: converts (one flavour of) CatWeasel flux file into a
|
||||
FluxEngine flux file.
|
||||
|
||||
## The recommended workflow
|
||||
|
||||
So you've just received, say, a huge pile of old Brother word processor disks containing valuable historical data, and you want to read them.
|
||||
So you've just received, say, a huge pile of old Brother word processor disks
|
||||
containing valuable historical data, and you want to read them.
|
||||
|
||||
Typically I do this:
|
||||
|
||||
```
|
||||
$ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux
|
||||
$ fluxengine read brother -s :d=0 -o brother.img --write-flux=brother.flux --write-svg=brother.svg
|
||||
```
|
||||
|
||||
This will read the disk in drive 0 and write out a filesystem image. It'll
|
||||
also copy the flux to brother.flux. If I then need to tweak the settings, I
|
||||
can rerun the decode without having to physically touch the disk like this:
|
||||
also copy the flux to brother.flux and write out an SVG visualisation. If I
|
||||
then need to tweak the settings, I can rerun the decode without having to
|
||||
physically touch the disk like this:
|
||||
|
||||
```
|
||||
$ fluxengine read brother -s brother.flux -o brother.img
|
||||
$ fluxengine read brother -s brother.flux -o brother.img --write-svg=brother.svg
|
||||
```
|
||||
|
||||
Apart from being drastically faster, this avoids touching the (potentially
|
||||
|
||||
1
doc/visualiser.svg
Normal file
1
doc/visualiser.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 394 KiB |
11
lib/bytes.cc
11
lib/bytes.cc
@@ -2,6 +2,7 @@
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "common/crunch.h"
|
||||
#include <fstream>
|
||||
#include <zlib.h>
|
||||
|
||||
static std::shared_ptr<std::vector<uint8_t>> createVector(unsigned size)
|
||||
@@ -280,6 +281,16 @@ Bytes Bytes::uncrunch() const
|
||||
return output;
|
||||
}
|
||||
|
||||
void Bytes::writeToFile(const std::string& filename) const
|
||||
{
|
||||
std::ofstream f(filename, std::ios::out | std::ios::binary);
|
||||
if (!f.is_open())
|
||||
Error() << fmt::format("cannot open output file '{}'", filename);
|
||||
|
||||
f.write((const char*) cbegin(), size());
|
||||
f.close();
|
||||
}
|
||||
|
||||
ByteReader Bytes::reader() const
|
||||
{
|
||||
return ByteReader(*this);
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
ByteReader reader() const;
|
||||
ByteWriter writer();
|
||||
|
||||
void writeToFile(const std::string& filename) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<uint8_t>> _data;
|
||||
unsigned _low;
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
MissingModifierException::MissingModifierException(const std::string& mod):
|
||||
mod(mod),
|
||||
std::runtime_error(fmt::format("missing mandatory modifier '{}'", mod))
|
||||
{}
|
||||
|
||||
std::vector<std::string> DataSpec::split(
|
||||
const std::string& s, const std::string& delimiter)
|
||||
{
|
||||
@@ -74,31 +79,108 @@ void DataSpec::set(const std::string& spec)
|
||||
filename = words[0];
|
||||
if (words.size() > 1)
|
||||
{
|
||||
locations.clear();
|
||||
|
||||
for (size_t i = 1; i < words.size(); i++)
|
||||
{
|
||||
auto mod = parseMod(words[i]);
|
||||
if ((mod.name != "t") && (mod.name != "s") && (mod.name != "d"))
|
||||
Error() << fmt::format("unknown data modifier '{}'", mod.name);
|
||||
modifiers[mod.name] = mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& drives = modifiers["d"].data;
|
||||
const DataSpec::Modifier& DataSpec::at(const std::string& mod) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return modifiers.at(mod);
|
||||
}
|
||||
catch (const std::out_of_range& e)
|
||||
{
|
||||
throw MissingModifierException(mod);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataSpec::has(const std::string& mod) const
|
||||
{
|
||||
return modifiers.find(mod) != modifiers.end();
|
||||
}
|
||||
|
||||
FluxSpec::FluxSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
locations.clear();
|
||||
|
||||
const auto& drives = spec.at("d").data;
|
||||
if (drives.size() != 1)
|
||||
Error() << "you must specify exactly one drive";
|
||||
drive = *drives.begin();
|
||||
|
||||
const auto& tracks = modifiers["t"].data;
|
||||
const auto& sides = modifiers["s"].data;
|
||||
const auto& tracks = spec.at("t").data;
|
||||
const auto& sides = spec.at("s").data;
|
||||
for (auto track : tracks)
|
||||
{
|
||||
for (auto side : sides)
|
||||
locations.push_back({ drive, track, side });
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "t") && (name != "s") && (name != "d"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in fluxspec '" << spec << "'";
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const DataSpec& spec)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = spec.filename;
|
||||
|
||||
if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b"))
|
||||
{
|
||||
cylinders = heads = sectors = bytes = 0;
|
||||
initialised = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cylinders = spec.at("c").only();
|
||||
heads = spec.at("h").only();
|
||||
sectors = spec.at("s").only();
|
||||
bytes = spec.at("b").only();
|
||||
initialised = true;
|
||||
}
|
||||
}
|
||||
catch (const MissingModifierException& e)
|
||||
{
|
||||
Error() << e.what() << " in imagespec '" << spec << "'";
|
||||
}
|
||||
|
||||
for (const auto& e : spec.modifiers)
|
||||
{
|
||||
const auto name = e.second.name;
|
||||
if ((name != "c") && (name != "h") && (name != "s") && (name != "b"))
|
||||
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
|
||||
}
|
||||
}
|
||||
|
||||
ImageSpec::ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes):
|
||||
filename(filename),
|
||||
cylinders(cylinders),
|
||||
heads(heads),
|
||||
sectors(sectors),
|
||||
bytes(bytes),
|
||||
initialised(true)
|
||||
{}
|
||||
|
||||
DataSpec::operator std::string(void) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -1,8 +1,57 @@
|
||||
#ifndef DATASPEC_H
|
||||
#define DATASPEC_H
|
||||
|
||||
class MissingModifierException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
MissingModifierException(const std::string& mod);
|
||||
const std::string mod;
|
||||
};
|
||||
|
||||
class DataSpec
|
||||
{
|
||||
public:
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
|
||||
unsigned only() const
|
||||
{
|
||||
if (data.size() != 1)
|
||||
Error() << "modifier " << name << " can only have one value";
|
||||
return *(data.begin());
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
const Modifier& at(const std::string& mod) const;
|
||||
bool has(const std::string& mod) const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
};
|
||||
|
||||
class FluxSpec
|
||||
{
|
||||
public:
|
||||
struct Location
|
||||
{
|
||||
@@ -17,36 +66,29 @@ public:
|
||||
{ return (drive != other.drive) || (track != other.track) || (side != other.side); }
|
||||
};
|
||||
|
||||
struct Modifier
|
||||
{
|
||||
std::string name;
|
||||
std::set<unsigned> data;
|
||||
std::string source;
|
||||
|
||||
bool operator == (const Modifier& other) const
|
||||
{ return (name == other.name) && (data == other.data); }
|
||||
|
||||
bool operator != (const Modifier& other) const
|
||||
{ return (name != other.name) || (data != other.data); }
|
||||
};
|
||||
public:
|
||||
FluxSpec(const DataSpec& dataspec);
|
||||
|
||||
public:
|
||||
static std::vector<std::string> split(
|
||||
const std::string& s, const std::string& delimiter);
|
||||
static Modifier parseMod(const std::string& spec);
|
||||
|
||||
public:
|
||||
DataSpec(const std::string& spec)
|
||||
{ set(spec); }
|
||||
|
||||
void set(const std::string& spec);
|
||||
operator std::string () const;
|
||||
|
||||
std::string filename;
|
||||
std::map<std::string, Modifier> modifiers;
|
||||
std::vector<Location> locations;
|
||||
unsigned drive;
|
||||
unsigned revolutions;
|
||||
};
|
||||
|
||||
class ImageSpec
|
||||
{
|
||||
public:
|
||||
ImageSpec(const DataSpec& dataspec);
|
||||
ImageSpec(const std::string filename,
|
||||
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes);
|
||||
|
||||
public:
|
||||
std::string filename;
|
||||
unsigned cylinders;
|
||||
unsigned heads;
|
||||
unsigned sectors;
|
||||
unsigned bytes;
|
||||
bool initialised : 1;
|
||||
};
|
||||
|
||||
static inline std::ostream& operator << (std::ostream& os, const DataSpec& dataSpec)
|
||||
|
||||
@@ -43,19 +43,25 @@ void AbstractDecoder::decodeToSectors(Track& track)
|
||||
|
||||
recordStart = fmr.tell();
|
||||
decodeSectorRecord();
|
||||
pushRecord(recordStart, fmr.tell());
|
||||
Fluxmap::Position recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
if (sector.status == Sector::DATA_MISSING)
|
||||
{
|
||||
/* The data is in a separate record. */
|
||||
|
||||
sector.headerStartTime = recordStart.ns();
|
||||
sector.headerEndTime = recordEnd.ns();
|
||||
r = advanceToNextRecord();
|
||||
if (r == DATA_RECORD)
|
||||
{
|
||||
recordStart = fmr.tell();
|
||||
decodeDataRecord();
|
||||
pushRecord(recordStart, fmr.tell());
|
||||
recordEnd = fmr.tell();
|
||||
pushRecord(recordStart, recordEnd);
|
||||
}
|
||||
}
|
||||
sector.dataStartTime = recordStart.ns();
|
||||
sector.dataEndTime = recordEnd.ns();
|
||||
|
||||
if (sector.status != Sector::MISSING)
|
||||
track.sectors.push_back(sector);
|
||||
|
||||
88
lib/flags.cc
88
lib/flags.cc
@@ -29,7 +29,7 @@ void FlagGroup::addFlag(Flag* flag)
|
||||
_flags.push_back(flag);
|
||||
}
|
||||
|
||||
void FlagGroup::parseFlags(int argc, const char* argv[])
|
||||
std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[])
|
||||
{
|
||||
if (_initialised)
|
||||
throw std::runtime_error("called parseFlags() twice");
|
||||
@@ -66,6 +66,7 @@ void FlagGroup::parseFlags(int argc, const char* argv[])
|
||||
|
||||
/* Now actually parse them. */
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
int index = 1;
|
||||
while (index < argc)
|
||||
{
|
||||
@@ -76,52 +77,73 @@ void FlagGroup::parseFlags(int argc, const char* argv[])
|
||||
std::string value;
|
||||
bool usesthat = false;
|
||||
|
||||
if ((thisarg.size() == 0) || (thisarg[0] != '-'))
|
||||
Error() << "non-option parameter " << thisarg << " seen (try --help)";
|
||||
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
|
||||
if (thisarg.size() == 0)
|
||||
{
|
||||
/* Long option. */
|
||||
|
||||
auto equals = thisarg.rfind('=');
|
||||
if (equals != std::string::npos)
|
||||
{
|
||||
key = thisarg.substr(0, equals);
|
||||
value = thisarg.substr(equals+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = thisarg;
|
||||
value = thatarg;
|
||||
usesthat = true;
|
||||
}
|
||||
/* Ignore this argument. */
|
||||
}
|
||||
else if (thisarg[0] != '-')
|
||||
{
|
||||
/* This is a filename. */
|
||||
filenames.push_back(thisarg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Short option. */
|
||||
/* This is a flag. */
|
||||
|
||||
if (thisarg.size() > 2)
|
||||
if ((thisarg.size() > 1) && (thisarg[1] == '-'))
|
||||
{
|
||||
key = thisarg.substr(0, 2);
|
||||
value = thisarg.substr(2);
|
||||
/* Long option. */
|
||||
|
||||
auto equals = thisarg.rfind('=');
|
||||
if (equals != std::string::npos)
|
||||
{
|
||||
key = thisarg.substr(0, equals);
|
||||
value = thisarg.substr(equals+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = thisarg;
|
||||
value = thatarg;
|
||||
usesthat = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key = thisarg;
|
||||
value = thatarg;
|
||||
usesthat = true;
|
||||
/* Short option. */
|
||||
|
||||
if (thisarg.size() > 2)
|
||||
{
|
||||
key = thisarg.substr(0, 2);
|
||||
value = thisarg.substr(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = thisarg;
|
||||
value = thatarg;
|
||||
usesthat = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto flag = flags_by_name.find(key);
|
||||
if (flag == flags_by_name.end())
|
||||
Error() << "unknown flag '" << key << "'; try --help";
|
||||
|
||||
flag->second->set(value);
|
||||
if (usesthat && flag->second->hasArgument())
|
||||
index++;
|
||||
}
|
||||
|
||||
auto flag = flags_by_name.find(key);
|
||||
if (flag == flags_by_name.end())
|
||||
Error() << "unknown flag '" << key << "'; try --help";
|
||||
|
||||
flag->second->set(value);
|
||||
|
||||
index++;
|
||||
if (usesthat && flag->second->hasArgument())
|
||||
index++;
|
||||
}
|
||||
|
||||
return filenames;
|
||||
}
|
||||
|
||||
void FlagGroup::parseFlags(int argc, const char* argv[])
|
||||
{
|
||||
auto filenames = parseFlagsWithFilenames(argc, argv);
|
||||
if (!filenames.empty())
|
||||
Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)";
|
||||
}
|
||||
|
||||
void FlagGroup::checkInitialised() const
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
|
||||
public:
|
||||
void parseFlags(int argc, const char* argv[]);
|
||||
std::vector<std::string> parseFlagsWithFilenames(int argc, const char* argv[]);
|
||||
void addFlag(Flag* flag);
|
||||
void checkInitialised() const;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const DataSpec& spec)
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const FluxSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define FLUXSINK_H
|
||||
|
||||
class Fluxmap;
|
||||
class DataSpec;
|
||||
class FluxSpec;
|
||||
|
||||
class FluxSink
|
||||
{
|
||||
@@ -14,7 +14,7 @@ private:
|
||||
static std::unique_ptr<FluxSink> createHardwareFluxSink(unsigned drive);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSink> create(const DataSpec& spec);
|
||||
static std::unique_ptr<FluxSink> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;
|
||||
|
||||
@@ -10,7 +10,7 @@ static bool ends_with(const std::string& value, const std::string& ending)
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::create(const DataSpec& spec)
|
||||
std::unique_ptr<FluxSource> FluxSource::create(const FluxSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
extern FlagGroup hardwareFluxSourceFlags;
|
||||
|
||||
class Fluxmap;
|
||||
class DataSpec;
|
||||
class FluxSpec;
|
||||
|
||||
class FluxSource
|
||||
{
|
||||
@@ -19,7 +19,7 @@ private:
|
||||
static std::unique_ptr<FluxSource> createStreamFluxSource(const std::string& path);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSource> create(const DataSpec& spec);
|
||||
static std::unique_ptr<FluxSource> create(const FluxSpec& spec);
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void recalibrate() {}
|
||||
|
||||
private:
|
||||
const std::string& _path;
|
||||
const std::string _path;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createStreamFluxSource(const std::string& path)
|
||||
|
||||
@@ -22,7 +22,7 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
|
||||
class Error
|
||||
{
|
||||
public:
|
||||
~Error()
|
||||
[[ noreturn ]] ~Error()
|
||||
{
|
||||
std::cerr << "Error: " << _stream.str() << std::endl;
|
||||
exit(1);
|
||||
|
||||
147
lib/image.cc
147
lib/image.cc
@@ -1,150 +1,25 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
Geometry guessGeometry(const SectorSet& sectors)
|
||||
SectorSet readSectorsFromFile(const ImageSpec& spec)
|
||||
{
|
||||
Geometry g;
|
||||
sectors.calculateSize(g.tracks, g.heads, g.sectors, g.sectorSize);
|
||||
return g;
|
||||
return ImageReader::create(spec)->readImage();
|
||||
}
|
||||
|
||||
void readSectorsFromFile(SectorSet& sectors, const Geometry& geometry,
|
||||
const std::string& filename)
|
||||
void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
std::ifstream inputFile(filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
size_t headSize = geometry.sectors * geometry.sectorSize;
|
||||
size_t trackSize = headSize * geometry.heads;
|
||||
|
||||
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
geometry.tracks, geometry.heads,
|
||||
geometry.sectors, geometry.sectorSize,
|
||||
geometry.tracks * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
inputFile.seekg(track*trackSize + head*headSize + sectorId*geometry.sectorSize, std::ios::beg);
|
||||
|
||||
Bytes data(geometry.sectorSize);
|
||||
inputFile.read((char*) data.begin(), geometry.sectorSize);
|
||||
|
||||
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writeSectorsToFile(const SectorSet& sectors, const Geometry& geometry,
|
||||
const std::string& filename)
|
||||
{
|
||||
/* Emit the map. */
|
||||
|
||||
int badSectors = 0;
|
||||
int missingSectors = 0;
|
||||
int totalSectors = 0;
|
||||
std::cout << "H.SS Tracks --->" << std::endl;
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
std::cout << fmt::format("{}.{:2} ", head, sectorId);
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
Sector* sector = sectors.get(track, head, sectorId);
|
||||
if (!sector)
|
||||
{
|
||||
std::cout << 'X';
|
||||
missingSectors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sector->status)
|
||||
{
|
||||
case Sector::OK:
|
||||
std::cout << '.';
|
||||
break;
|
||||
|
||||
case Sector::BAD_CHECKSUM:
|
||||
std::cout << 'B';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
case Sector::CONFLICT:
|
||||
std::cout << 'C';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalSectors++;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
int goodSectors = totalSectors - missingSectors - badSectors;
|
||||
if (totalSectors == 0)
|
||||
std::cout << "No sectors in output; skipping analysis" << std::endl;
|
||||
else
|
||||
{
|
||||
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
|
||||
<< " (" << (100*goodSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
|
||||
<< " (" << (100*missingSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
|
||||
<< " (" << (100*badSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
size_t headSize = geometry.sectors * geometry.sectorSize;
|
||||
size_t trackSize = headSize * geometry.heads;
|
||||
|
||||
std::cout << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
geometry.tracks, geometry.heads,
|
||||
geometry.sectors, geometry.sectorSize,
|
||||
geometry.tracks * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
std::ofstream outputFile(filename, std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
for (int track = 0; track < geometry.tracks; track++)
|
||||
{
|
||||
for (int head = 0; head < geometry.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < geometry.sectors; sectorId++)
|
||||
{
|
||||
auto sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*geometry.sectorSize, std::ios::beg);
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
|
||||
writer->adjustGeometry();
|
||||
writer->printMap();
|
||||
writer->writeImage();
|
||||
}
|
||||
|
||||
21
lib/image.h
21
lib/image.h
@@ -2,26 +2,13 @@
|
||||
#define IMAGE_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class Geometry
|
||||
{
|
||||
public:
|
||||
int tracks;
|
||||
int heads;
|
||||
int sectors;
|
||||
int sectorSize;
|
||||
};
|
||||
|
||||
extern Geometry guessGeometry(const SectorSet& sectors);
|
||||
|
||||
extern void readSectorsFromFile(
|
||||
SectorSet& sectors,
|
||||
const Geometry& geometry,
|
||||
const std::string& filename);
|
||||
extern SectorSet readSectorsFromFile(
|
||||
const ImageSpec& filename);
|
||||
|
||||
extern void writeSectorsToFile(
|
||||
const SectorSet& sectors,
|
||||
const Geometry& geometry,
|
||||
const std::string& filename);
|
||||
const ImageSpec& filename);
|
||||
|
||||
#endif
|
||||
|
||||
52
lib/imagereader/imagereader.cc
Normal file
52
lib/imagereader/imagereader.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
std::map<std::string, ImageReader::Constructor> ImageReader::formats =
|
||||
{
|
||||
{".adf", ImageReader::createImgImageReader},
|
||||
{".d81", ImageReader::createImgImageReader},
|
||||
{".img", ImageReader::createImgImageReader},
|
||||
};
|
||||
|
||||
static bool ends_with(const std::string& value, const std::string& ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
for (const auto& e : formats)
|
||||
{
|
||||
if (ends_with(filename, e.first))
|
||||
return e.second;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
|
||||
{
|
||||
verifyImageSpec(spec);
|
||||
return findConstructor(spec)(spec);
|
||||
}
|
||||
|
||||
void ImageReader::verifyImageSpec(const ImageSpec& spec)
|
||||
{
|
||||
if (!findConstructor(spec))
|
||||
Error() << "unrecognised image filename extension";
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const ImageSpec& spec):
|
||||
spec(spec)
|
||||
{}
|
||||
|
||||
38
lib/imagereader/imagereader.h
Normal file
38
lib/imagereader/imagereader.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef IMAGEREADER_H
|
||||
#define IMAGEREADER_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class ImageReader
|
||||
{
|
||||
public:
|
||||
ImageReader(const ImageSpec& spec);
|
||||
virtual ~ImageReader() {};
|
||||
|
||||
public:
|
||||
static std::unique_ptr<ImageReader> create(const ImageSpec& spec);
|
||||
static void verifyImageSpec(const ImageSpec& spec);
|
||||
|
||||
private:
|
||||
typedef
|
||||
std::function<
|
||||
std::unique_ptr<ImageReader>(const ImageSpec& spec)
|
||||
>
|
||||
Constructor;
|
||||
|
||||
static std::map<std::string, Constructor> formats;
|
||||
|
||||
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
|
||||
|
||||
static Constructor findConstructor(const ImageSpec& spec);
|
||||
|
||||
public:
|
||||
virtual SectorSet readImage() = 0;
|
||||
|
||||
protected:
|
||||
ImageSpec spec;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
66
lib/imagereader/imgimagereader.cc
Normal file
66
lib/imagereader/imgimagereader.cc
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagereader/imagereader.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class ImgImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
ImgImageReader(const ImageSpec& spec):
|
||||
ImageReader(spec)
|
||||
{}
|
||||
|
||||
SectorSet readImage()
|
||||
{
|
||||
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
|
||||
size_t headSize = spec.sectors * spec.bytes;
|
||||
size_t trackSize = headSize * spec.heads;
|
||||
|
||||
std::cout << fmt::format("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
spec.cylinders, spec.heads,
|
||||
spec.sectors, spec.bytes,
|
||||
spec.cylinders * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
SectorSet sectors;
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
|
||||
{
|
||||
inputFile.seekg(track*trackSize + head*headSize + sectorId*spec.bytes, std::ios::beg);
|
||||
|
||||
Bytes data(spec.bytes);
|
||||
inputFile.read((char*) data.begin(), spec.bytes);
|
||||
|
||||
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
|
||||
sector.reset(new Sector);
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = sector->physicalTrack = track;
|
||||
sector->logicalSide = sector->physicalSide = head;
|
||||
sector->logicalSector = sectorId;
|
||||
sector->data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageReader> ImageReader::createImgImageReader(
|
||||
const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageReader>(new ImgImageReader(spec));
|
||||
}
|
||||
|
||||
64
lib/imagewriter/d64imagewriter.cc
Normal file
64
lib/imagewriter/d64imagewriter.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ldbs.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
static int sectors_per_track(int track)
|
||||
{
|
||||
if (track < 17)
|
||||
return 21;
|
||||
if (track < 24)
|
||||
return 19;
|
||||
if (track < 30)
|
||||
return 18;
|
||||
return 17;
|
||||
}
|
||||
|
||||
class D64ImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
D64ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
std::cout << "writing D64 triangular image\n";
|
||||
|
||||
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
uint32_t offset = 0;
|
||||
for (int track = 0; track < 40; track++)
|
||||
{
|
||||
int sectorCount = sectors_per_track(track);
|
||||
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, 0, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(offset);
|
||||
outputFile.write((const char*) sector->data.cbegin(), 256);
|
||||
}
|
||||
|
||||
offset += 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new D64ImageWriter(sectors, spec));
|
||||
}
|
||||
|
||||
128
lib/imagewriter/imagewriter.cc
Normal file
128
lib/imagewriter/imagewriter.cc
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
|
||||
{
|
||||
{".adf", ImageWriter::createImgImageWriter},
|
||||
{".d64", ImageWriter::createD64ImageWriter},
|
||||
{".d81", ImageWriter::createImgImageWriter},
|
||||
{".img", ImageWriter::createImgImageWriter},
|
||||
{".ldbs", ImageWriter::createLDBSImageWriter},
|
||||
};
|
||||
|
||||
static bool ends_with(const std::string& value, const std::string& ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
ImageWriter::Constructor ImageWriter::findConstructor(const ImageSpec& spec)
|
||||
{
|
||||
const auto& filename = spec.filename;
|
||||
|
||||
for (const auto& e : formats)
|
||||
{
|
||||
if (ends_with(filename, e.first))
|
||||
return e.second;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
verifyImageSpec(spec);
|
||||
return findConstructor(spec)(sectors, spec);
|
||||
}
|
||||
|
||||
void ImageWriter::verifyImageSpec(const ImageSpec& spec)
|
||||
{
|
||||
if (!findConstructor(spec))
|
||||
Error() << "unrecognised image filename extension";
|
||||
}
|
||||
|
||||
ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
sectors(sectors),
|
||||
spec(spec)
|
||||
{}
|
||||
|
||||
void ImageWriter::adjustGeometry()
|
||||
{
|
||||
if (!spec.initialised)
|
||||
{
|
||||
sectors.calculateSize(spec.cylinders, spec.heads, spec.sectors, spec.bytes);
|
||||
spec.initialised = true;
|
||||
std::cout << "Autodetecting output geometry\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::printMap()
|
||||
{
|
||||
int badSectors = 0;
|
||||
int missingSectors = 0;
|
||||
int totalSectors = 0;
|
||||
std::cout << "H.SS Tracks --->" << std::endl;
|
||||
for (int head = 0; head < spec.heads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
|
||||
{
|
||||
std::cout << fmt::format("{}.{:2} ", head, sectorId);
|
||||
for (int track = 0; track < spec.cylinders; track++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (!sector)
|
||||
{
|
||||
std::cout << 'X';
|
||||
missingSectors++;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sector->status)
|
||||
{
|
||||
case Sector::OK:
|
||||
std::cout << '.';
|
||||
break;
|
||||
|
||||
case Sector::BAD_CHECKSUM:
|
||||
std::cout << 'B';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
case Sector::CONFLICT:
|
||||
std::cout << 'C';
|
||||
badSectors++;
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cout << '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
totalSectors++;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
int goodSectors = totalSectors - missingSectors - badSectors;
|
||||
if (totalSectors == 0)
|
||||
std::cout << "No sectors in output; skipping analysis" << std::endl;
|
||||
else
|
||||
{
|
||||
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
|
||||
<< " (" << (100*goodSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
|
||||
<< " (" << (100*missingSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
|
||||
<< " (" << (100*badSectors/totalSectors) << "%)"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
45
lib/imagewriter/imagewriter.h
Normal file
45
lib/imagewriter/imagewriter.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef IMAGEWRITER_H
|
||||
#define IMAGEWRITER_H
|
||||
|
||||
class SectorSet;
|
||||
class ImageSpec;
|
||||
|
||||
class ImageWriter
|
||||
{
|
||||
public:
|
||||
ImageWriter(const SectorSet& sectors, const ImageSpec& spec);
|
||||
virtual ~ImageWriter() {};
|
||||
|
||||
public:
|
||||
static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec);
|
||||
static void verifyImageSpec(const ImageSpec& filename);
|
||||
|
||||
private:
|
||||
typedef
|
||||
std::function<
|
||||
std::unique_ptr<ImageWriter>(const SectorSet& sectors, const ImageSpec& spec)
|
||||
>
|
||||
Constructor;
|
||||
|
||||
static std::map<std::string, Constructor> formats;
|
||||
|
||||
static std::unique_ptr<ImageWriter> createImgImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createLDBSImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
static std::unique_ptr<ImageWriter> createD64ImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec);
|
||||
|
||||
static Constructor findConstructor(const ImageSpec& spec);
|
||||
|
||||
public:
|
||||
virtual void adjustGeometry();
|
||||
void printMap();
|
||||
virtual void writeImage() = 0;
|
||||
|
||||
protected:
|
||||
const SectorSet& sectors;
|
||||
ImageSpec spec;
|
||||
};
|
||||
|
||||
#endif
|
||||
62
lib/imagewriter/imgimagewriter.cc
Normal file
62
lib/imagewriter/imgimagewriter.cc
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class ImgImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
ImgImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
unsigned numCylinders = spec.cylinders;
|
||||
unsigned numHeads = spec.heads;
|
||||
unsigned numSectors = spec.sectors;
|
||||
unsigned numBytes = spec.bytes;
|
||||
|
||||
size_t headSize = numSectors * numBytes;
|
||||
size_t trackSize = headSize * numHeads;
|
||||
|
||||
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
|
||||
numCylinders, numHeads,
|
||||
numSectors, numBytes,
|
||||
numCylinders * trackSize / 1024)
|
||||
<< std::endl;
|
||||
|
||||
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
for (int track = 0; track < numCylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < numHeads; head++)
|
||||
{
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
outputFile.seekp(sector->logicalTrack*trackSize + sector->logicalSide*headSize + sector->logicalSector*numBytes, std::ios::beg);
|
||||
outputFile.write((const char*) sector->data.cbegin(), sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createImgImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new ImgImageWriter(sectors, spec));
|
||||
}
|
||||
107
lib/imagewriter/ldbsimagewriter.cc
Normal file
107
lib/imagewriter/ldbsimagewriter.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "flags.h"
|
||||
#include "dataspec.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ldbs.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
class LDBSImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
LDBSImageWriter(const SectorSet& sectors, const ImageSpec& spec):
|
||||
ImageWriter(sectors, spec)
|
||||
{}
|
||||
|
||||
void writeImage()
|
||||
{
|
||||
LDBS ldbs;
|
||||
|
||||
unsigned numCylinders = spec.cylinders;
|
||||
unsigned numHeads = spec.heads;
|
||||
unsigned numSectors = spec.sectors;
|
||||
unsigned numBytes = spec.bytes;
|
||||
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector",
|
||||
numCylinders, numHeads,
|
||||
numSectors, numBytes)
|
||||
<< std::endl;
|
||||
|
||||
Bytes trackDirectory;
|
||||
ByteWriter trackDirectoryWriter(trackDirectory);
|
||||
int trackDirectorySize = 0;
|
||||
trackDirectoryWriter.write_le16(0);
|
||||
|
||||
for (int track = 0; track < numCylinders; track++)
|
||||
{
|
||||
for (int head = 0; head < numHeads; head++)
|
||||
{
|
||||
Bytes trackHeader;
|
||||
ByteWriter trackHeaderWriter(trackHeader);
|
||||
|
||||
int actualSectors = 0;
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
actualSectors++;
|
||||
}
|
||||
|
||||
trackHeaderWriter.write_le16(0x000C); /* offset of sector headers */
|
||||
trackHeaderWriter.write_le16(0x0012); /* length of each sector descriptor */
|
||||
trackHeaderWriter.write_le16(actualSectors);
|
||||
trackHeaderWriter.write_8(0); /* data rate unknown */
|
||||
trackHeaderWriter.write_8(0); /* recording mode unknown */
|
||||
trackHeaderWriter.write_8(0); /* format gap length */
|
||||
trackHeaderWriter.write_8(0); /* filler byte */
|
||||
trackHeaderWriter.write_le16(0); /* approximate track length */
|
||||
|
||||
for (int sectorId = 0; sectorId < numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = sectors.get(track, head, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
uint32_t sectorLabel = (('S') << 24) | ((track & 0xff) << 16) | (head << 8) | sectorId;
|
||||
uint32_t sectorAddress = ldbs.put(sector->data, sectorLabel);
|
||||
|
||||
trackHeaderWriter.write_8(track);
|
||||
trackHeaderWriter.write_8(head);
|
||||
trackHeaderWriter.write_8(sectorId);
|
||||
trackHeaderWriter.write_8(0); /* power-of-two size */
|
||||
trackHeaderWriter.write_8((sector->status == Sector::OK) ? 0x00 : 0x20); /* 8272 status 1 */
|
||||
trackHeaderWriter.write_8(0); /* 8272 status 2 */
|
||||
trackHeaderWriter.write_8(1); /* number of copies */
|
||||
trackHeaderWriter.write_8(0); /* filler byte */
|
||||
trackHeaderWriter.write_le32(sectorAddress);
|
||||
trackHeaderWriter.write_le16(0); /* trailing bytes */
|
||||
trackHeaderWriter.write_le16(0); /* approximate offset */
|
||||
trackHeaderWriter.write_le16(sector->data.size());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t trackLabel = (('T') << 24) | ((track & 0xff) << 16) | ((track >> 8) << 8) | head;
|
||||
uint32_t trackHeaderAddress = ldbs.put(trackHeader, trackLabel);
|
||||
trackDirectoryWriter.write_be32(trackLabel);
|
||||
trackDirectoryWriter.write_le32(trackHeaderAddress);
|
||||
trackDirectorySize++;
|
||||
}
|
||||
}
|
||||
|
||||
trackDirectoryWriter.seek(0);
|
||||
trackDirectoryWriter.write_le16(trackDirectorySize);
|
||||
|
||||
uint32_t trackDirectoryAddress = ldbs.put(trackDirectory, LDBS_TRACK_BLOCK);
|
||||
Bytes data = ldbs.write(trackDirectoryAddress);
|
||||
data.writeToFile(spec.filename);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createLDBSImageWriter(
|
||||
const SectorSet& sectors, const ImageSpec& spec)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new LDBSImageWriter(sectors, spec));
|
||||
}
|
||||
81
lib/ldbs.cc
Normal file
81
lib/ldbs.cc
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "globals.h"
|
||||
#include <string.h>
|
||||
#include "bytes.h"
|
||||
#include "ldbs.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
LDBS::LDBS()
|
||||
{}
|
||||
|
||||
uint32_t LDBS::put(const Bytes& data, uint32_t type)
|
||||
{
|
||||
uint32_t address = top;
|
||||
Block& block = blocks[address];
|
||||
block.type = type;
|
||||
block.data = data;
|
||||
|
||||
top += data.size() + 20;
|
||||
return address;
|
||||
}
|
||||
|
||||
uint32_t LDBS::read(const Bytes& data)
|
||||
{
|
||||
ByteReader br(data);
|
||||
|
||||
br.seek(0);
|
||||
if ((br.read_be32() != LDBS_FILE_MAGIC)
|
||||
|| (br.read_be32() != LDBS_FILE_TYPE))
|
||||
Error() << "not a valid LDBS file";
|
||||
|
||||
uint32_t address = br.read_le32();
|
||||
br.skip(4);
|
||||
uint32_t trackDirectory = br.read_le32();
|
||||
|
||||
while (address)
|
||||
{
|
||||
br.seek(address);
|
||||
if (br.read_be32() != LDBS_BLOCK_MAGIC)
|
||||
Error() << fmt::format("invalid block at address 0x{:x}", address);
|
||||
|
||||
Block& block = blocks[address];
|
||||
block.type = br.read_be32();
|
||||
|
||||
uint32_t size = br.read_le32();
|
||||
br.skip(4);
|
||||
address = br.read_le32();
|
||||
|
||||
block.data.writer().append(br.read(size));
|
||||
}
|
||||
|
||||
top = data.size();
|
||||
return trackDirectory;
|
||||
}
|
||||
|
||||
const Bytes LDBS::write(uint32_t trackDirectory)
|
||||
{
|
||||
Bytes data(top);
|
||||
ByteWriter bw(data);
|
||||
|
||||
uint32_t previous = 0;
|
||||
for (const auto& e : blocks)
|
||||
{
|
||||
bw.seek(e.first);
|
||||
bw.write_be32(LDBS_BLOCK_MAGIC);
|
||||
bw.write_be32(e.second.type);
|
||||
bw.write_le32(e.second.data.size());
|
||||
bw.write_le32(e.second.data.size());
|
||||
bw.write_le32(previous);
|
||||
bw.append(e.second.data);
|
||||
|
||||
previous = e.first;
|
||||
}
|
||||
|
||||
bw.seek(0);
|
||||
bw.write_be32(LDBS_FILE_MAGIC);
|
||||
bw.write_be32(LDBS_FILE_TYPE);
|
||||
bw.write_le32(previous);
|
||||
bw.write_le32(0);
|
||||
bw.write_le32(trackDirectory);
|
||||
|
||||
return data;
|
||||
}
|
||||
41
lib/ldbs.h
Normal file
41
lib/ldbs.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef LDBS_H
|
||||
#define LDBS_H
|
||||
|
||||
class Bytes;
|
||||
|
||||
/* A very simple interface to John Elliott's LDBS image format:
|
||||
* http://www.seasip.info/Unix/LibDsk/ldbs.html
|
||||
*/
|
||||
|
||||
#define LDBS_FILE_MAGIC 0x4C425301 /* "LBS\01" */
|
||||
#define LDBS_FILE_TYPE 0x44534B02 /* "DSK\02" */
|
||||
#define LDBS_BLOCK_MAGIC 0x4C444201 /* "LDB\01" */
|
||||
#define LDBS_TRACK_BLOCK 0x44495201 /* "DIR\01" */
|
||||
|
||||
class LDBS
|
||||
{
|
||||
public:
|
||||
LDBS();
|
||||
|
||||
public:
|
||||
const Bytes& get(uint32_t address) const
|
||||
{ return blocks.at(address).data; }
|
||||
|
||||
uint32_t put(const Bytes& data, uint32_t type);
|
||||
|
||||
public:
|
||||
const Bytes write(uint32_t trackDirectory);
|
||||
uint32_t read(const Bytes& bytes);
|
||||
|
||||
private:
|
||||
struct Block
|
||||
{
|
||||
uint32_t type;
|
||||
Bytes data;
|
||||
};
|
||||
|
||||
std::map<uint32_t, Block> blocks;
|
||||
unsigned top = 20;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -9,25 +9,37 @@
|
||||
#include "decoders/decoders.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "visualiser.h"
|
||||
#include "record.h"
|
||||
#include "image.h"
|
||||
#include "bytes.h"
|
||||
#include "decoders/rawbits.h"
|
||||
#include "track.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags };
|
||||
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags };
|
||||
|
||||
static DataSpecFlag source(
|
||||
{ "--source", "-s" },
|
||||
"source for data",
|
||||
":t=0-79:s=0-1:d=0");
|
||||
|
||||
static DataSpecFlag output(
|
||||
{ "--output", "-o" },
|
||||
"output image file to write to",
|
||||
"");
|
||||
|
||||
static StringFlag destination(
|
||||
{ "--write-flux", "-f" },
|
||||
"write the raw magnetic flux to this file",
|
||||
"");
|
||||
|
||||
static StringFlag visualise(
|
||||
{ "--write-svg" },
|
||||
"write a visualisation of the disk to this file",
|
||||
"");
|
||||
|
||||
static SettableFlag justRead(
|
||||
{ "--just-read" },
|
||||
"just read the disk and do no further processing");
|
||||
@@ -52,6 +64,11 @@ void setReaderDefaultSource(const std::string& source)
|
||||
::source.set(source);
|
||||
}
|
||||
|
||||
void setReaderDefaultOutput(const std::string& output)
|
||||
{
|
||||
::output.set(output);
|
||||
}
|
||||
|
||||
void setReaderRevolutions(int revolutions)
|
||||
{
|
||||
setHardwareFluxSourceRevolutions(revolutions);
|
||||
@@ -71,9 +88,9 @@ void Track::readFluxmap()
|
||||
|
||||
std::vector<std::unique_ptr<Track>> readTracks()
|
||||
{
|
||||
const DataSpec& dataSpec = source;
|
||||
const FluxSpec spec(source);
|
||||
|
||||
std::cout << "Reading from: " << dataSpec << std::endl;
|
||||
std::cout << "Reading from: " << source << std::endl;
|
||||
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
|
||||
@@ -92,10 +109,10 @@ std::vector<std::unique_ptr<Track>> readTracks()
|
||||
);
|
||||
}
|
||||
|
||||
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(dataSpec);
|
||||
std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
|
||||
|
||||
std::vector<std::unique_ptr<Track>> tracks;
|
||||
for (const auto& location : dataSpec.locations)
|
||||
for (const auto& location : spec.locations)
|
||||
{
|
||||
auto track = std::make_unique<Track>(location.track, location.side);
|
||||
track->fluxsource = fluxSource;
|
||||
@@ -132,7 +149,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!replacing || (replacing->status != Sector::OK))
|
||||
if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
|
||||
{
|
||||
if (!replacing)
|
||||
replacing.reset(new Sector);
|
||||
@@ -140,8 +157,11 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
|
||||
}
|
||||
}
|
||||
|
||||
void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename)
|
||||
void readDiskCommand(AbstractDecoder& decoder)
|
||||
{
|
||||
const ImageSpec outputSpec(output);
|
||||
ImageWriter::verifyImageSpec(outputSpec);
|
||||
|
||||
bool failures = false;
|
||||
SectorSet allSectors;
|
||||
auto tracks = readTracks();
|
||||
@@ -239,8 +259,10 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
|
||||
std::cout << size << " bytes decoded." << std::endl;
|
||||
}
|
||||
|
||||
Geometry geometry = guessGeometry(allSectors);
|
||||
writeSectorsToFile(allSectors, geometry, outputFilename);
|
||||
if (!visualise.get().empty())
|
||||
visualiseSectorsToFile(allSectors, visualise.get());
|
||||
|
||||
writeSectorsToFile(allSectors, outputSpec);
|
||||
if (failures)
|
||||
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ class Track;
|
||||
extern FlagGroup readerFlags;
|
||||
|
||||
extern void setReaderDefaultSource(const std::string& source);
|
||||
extern void setReaderDefaultOutput(const std::string& output);
|
||||
extern void setReaderRevolutions(int revolutions);
|
||||
|
||||
extern std::vector<std::unique_ptr<Track>> readTracks();
|
||||
|
||||
extern void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename);
|
||||
extern void readDiskCommand(AbstractDecoder& decoder);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,10 @@ public:
|
||||
Status status = Status::INTERNAL_ERROR;
|
||||
Fluxmap::Position position;
|
||||
nanoseconds_t clock = 0;
|
||||
nanoseconds_t headerStartTime = 0;
|
||||
nanoseconds_t headerEndTime = 0;
|
||||
nanoseconds_t dataStartTime = 0;
|
||||
nanoseconds_t dataEndTime = 0;
|
||||
int physicalTrack = 0;
|
||||
int physicalSide = 0;
|
||||
int logicalTrack = 0;
|
||||
|
||||
@@ -18,8 +18,9 @@ Sector* SectorSet::get(int track, int head, int sector) const
|
||||
return i->second.get();
|
||||
}
|
||||
|
||||
void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
int& sectorSize) const
|
||||
void SectorSet::calculateSize(
|
||||
unsigned& numTracks, unsigned& numHeads,
|
||||
unsigned& numSectors, unsigned& sectorSize) const
|
||||
{
|
||||
numTracks = numHeads = numSectors = sectorSize = 0;
|
||||
|
||||
@@ -28,10 +29,10 @@ void SectorSet::calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
auto& sector = i.second;
|
||||
if (sector)
|
||||
{
|
||||
numTracks = std::max(numTracks, sector->logicalTrack+1);
|
||||
numHeads = std::max(numHeads, sector->logicalSide+1);
|
||||
numSectors = std::max(numSectors, sector->logicalSector+1);
|
||||
sectorSize = std::max(sectorSize, (int)sector->data.size());
|
||||
numTracks = std::max(numTracks, (unsigned)sector->logicalTrack+1);
|
||||
numHeads = std::max(numHeads, (unsigned)sector->logicalSide+1);
|
||||
numSectors = std::max(numSectors, (unsigned)sector->logicalSector+1);
|
||||
sectorSize = std::max(sectorSize, (unsigned)sector->data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@ public:
|
||||
std::unique_ptr<Sector>& get(int track, int head, int sector);
|
||||
Sector* get(int track, int head, int sector) const;
|
||||
|
||||
void calculateSize(int& numTracks, int& numHeads, int& numSectors,
|
||||
int& sectorSize) const;
|
||||
const std::map<const key_t, std::unique_ptr<Sector>>& get() const
|
||||
{ return _data; }
|
||||
|
||||
void calculateSize(
|
||||
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
|
||||
unsigned& sectorSize) const;
|
||||
|
||||
private:
|
||||
std::map<const key_t, std::unique_ptr<Sector>> _data;
|
||||
|
||||
95
lib/visualiser.cc
Normal file
95
lib/visualiser.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "globals.h"
|
||||
#include "image.h"
|
||||
#include "sector.h"
|
||||
#include "sectorset.h"
|
||||
#include "visualiser.h"
|
||||
#include "fmt/format.h"
|
||||
#include "flags.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <math.h>
|
||||
|
||||
FlagGroup visualiserFlags;
|
||||
|
||||
static IntFlag period(
|
||||
{ "--visualiser-period" },
|
||||
"rotational period for use by the visualiser (milliseconds)",
|
||||
200);
|
||||
|
||||
static const int SIZE = 480;
|
||||
static const int BORDER = 10;
|
||||
static const int RADIUS = (SIZE/2) - (BORDER/2);
|
||||
static const int CORE = 50;
|
||||
static const int TRACKS = 83;
|
||||
static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS;
|
||||
|
||||
void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename)
|
||||
{
|
||||
std::cout << "writing visualisation\n";
|
||||
std::ofstream f(filename, std::ios::out);
|
||||
if (!f.is_open())
|
||||
Error() << "cannot open visualisation file";
|
||||
|
||||
f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">",
|
||||
0, 0, SIZE*2, SIZE);
|
||||
|
||||
const double radians_per_ns = 2.0*M_PI / (period*1e6);
|
||||
|
||||
auto drawSide = [&](int side)
|
||||
{
|
||||
f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2);
|
||||
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS);
|
||||
|
||||
for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++)
|
||||
{
|
||||
double radius = CORE + physicalTrack*TRACK_SPACING;
|
||||
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius);
|
||||
|
||||
auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour)
|
||||
{
|
||||
start %= period*1000000;
|
||||
end %= period*1000000;
|
||||
if (end < start)
|
||||
end += period*1000000;
|
||||
|
||||
double theta1 = start * radians_per_ns;
|
||||
double theta2 = end * radians_per_ns;
|
||||
int large = (theta2 - theta1) >= M_PI;
|
||||
|
||||
f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2);
|
||||
f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour);
|
||||
f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius);
|
||||
f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large);
|
||||
f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>",
|
||||
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
|
||||
start/1e6, end/1e6);
|
||||
};
|
||||
|
||||
/* Sadly, SectorSets aren't indexable by physical track. */
|
||||
for (const auto& e : sectors.get())
|
||||
{
|
||||
const auto& sector = e.second;
|
||||
if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack))
|
||||
{
|
||||
const char* colour = "#f00";
|
||||
if (sector->status == Sector::OK)
|
||||
colour = "#00f";
|
||||
if (sector->headerStartTime && sector->headerEndTime)
|
||||
drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff");
|
||||
if (sector->dataStartTime && sector->dataEndTime)
|
||||
drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f << "</g>";
|
||||
};
|
||||
|
||||
f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE);
|
||||
|
||||
drawSide(0);
|
||||
drawSide(1);
|
||||
|
||||
f << "</svg>";
|
||||
}
|
||||
12
lib/visualiser.h
Normal file
12
lib/visualiser.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef VISUALISER_H
|
||||
#define VISUALISER_H
|
||||
|
||||
#include "flags.h"
|
||||
|
||||
class SectorSet;
|
||||
|
||||
extern FlagGroup visualiserFlags;
|
||||
|
||||
extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename);
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,11 @@ static DataSpecFlag dest(
|
||||
"destination for data",
|
||||
":d=0:t=0-79:s=0-1");
|
||||
|
||||
static DataSpecFlag input(
|
||||
{ "--input", "-i" },
|
||||
"input image file to read from",
|
||||
"");
|
||||
|
||||
static SettableFlag highDensityFlag(
|
||||
{ "--high-density", "-H" },
|
||||
"set the drive to high density mode");
|
||||
@@ -33,12 +38,17 @@ void setWriterDefaultDest(const std::string& dest)
|
||||
::dest.set(dest);
|
||||
}
|
||||
|
||||
void setWriterDefaultInput(const std::string& input)
|
||||
{
|
||||
::input.set(input);
|
||||
}
|
||||
|
||||
void writeTracks(
|
||||
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
|
||||
{
|
||||
const DataSpec& spec = dest;
|
||||
const FluxSpec spec(dest);
|
||||
|
||||
std::cout << "Writing to: " << spec << std::endl;
|
||||
std::cout << "Writing to: " << dest << std::endl;
|
||||
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
setHardwareFluxSinkDensity(highDensityFlag);
|
||||
@@ -103,12 +113,10 @@ void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
}
|
||||
}
|
||||
|
||||
void writeDiskCommand(
|
||||
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename)
|
||||
void writeDiskCommand(AbstractEncoder& encoder)
|
||||
{
|
||||
SectorSet allSectors;
|
||||
|
||||
readSectorsFromFile(allSectors, geometry, inputFilename);
|
||||
const ImageSpec spec(input);
|
||||
SectorSet allSectors = readSectorsFromFile(spec);
|
||||
writeTracks(
|
||||
[&](int track, int side) -> std::unique_ptr<Fluxmap>
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ class AbstractEncoder;
|
||||
class Geometry;
|
||||
|
||||
extern void setWriterDefaultDest(const std::string& dest);
|
||||
extern void setWriterDefaultInput(const std::string& input);
|
||||
|
||||
extern void writeTracks(const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer);
|
||||
|
||||
@@ -17,7 +18,6 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
unsigned& cursor, unsigned terminateAt,
|
||||
const std::vector<bool>& pattern);
|
||||
|
||||
extern void writeDiskCommand(
|
||||
AbstractEncoder& encoder, const Geometry& geometry, const std::string& inputFilename);
|
||||
extern void writeDiskCommand(AbstractEncoder& encoder);
|
||||
|
||||
#endif
|
||||
|
||||
49
mkninja.sh
49
mkninja.sh
@@ -137,13 +137,26 @@ buildlibrary libfmt.a \
|
||||
dep/fmt/posix.cc \
|
||||
|
||||
buildlibrary libbackend.a \
|
||||
lib/aeslanier/decoder.cc \
|
||||
lib/amiga/decoder.cc \
|
||||
lib/apple2/decoder.cc \
|
||||
lib/brother/decoder.cc \
|
||||
lib/brother/encoder.cc \
|
||||
lib/imagereader/imagereader.cc \
|
||||
lib/imagereader/imgimagereader.cc \
|
||||
lib/imagewriter/d64imagewriter.cc \
|
||||
lib/imagewriter/imagewriter.cc \
|
||||
lib/imagewriter/imgimagewriter.cc \
|
||||
lib/imagewriter/ldbsimagewriter.cc \
|
||||
arch/aeslanier/decoder.cc \
|
||||
arch/amiga/decoder.cc \
|
||||
arch/apple2/decoder.cc \
|
||||
arch/brother/decoder.cc \
|
||||
arch/brother/encoder.cc \
|
||||
arch/c64/decoder.cc \
|
||||
arch/f85/decoder.cc \
|
||||
arch/fb100/decoder.cc \
|
||||
arch/ibm/decoder.cc \
|
||||
arch/macintosh/decoder.cc \
|
||||
arch/mx/decoder.cc \
|
||||
arch/victor9k/decoder.cc \
|
||||
arch/zilogmcz/decoder.cc \
|
||||
lib/bytes.cc \
|
||||
lib/c64/decoder.cc \
|
||||
lib/common/crunch.c \
|
||||
lib/crc.cc \
|
||||
lib/dataspec.cc \
|
||||
@@ -151,8 +164,6 @@ buildlibrary libbackend.a \
|
||||
lib/decoders/fluxmapreader.cc \
|
||||
lib/decoders/fmmfm.cc \
|
||||
lib/encoders/encoders.cc \
|
||||
lib/f85/decoder.cc \
|
||||
lib/fb100/decoder.cc \
|
||||
lib/flags.cc \
|
||||
lib/fluxmap.cc \
|
||||
lib/fluxsink/fluxsink.cc \
|
||||
@@ -165,23 +176,21 @@ buildlibrary libbackend.a \
|
||||
lib/fluxsource/streamfluxsource.cc \
|
||||
lib/globals.cc \
|
||||
lib/hexdump.cc \
|
||||
lib/ibm/decoder.cc \
|
||||
lib/image.cc \
|
||||
lib/macintosh/decoder.cc \
|
||||
lib/mx/decoder.cc \
|
||||
lib/ldbs.cc \
|
||||
lib/reader.cc \
|
||||
lib/sector.cc \
|
||||
lib/sectorset.cc \
|
||||
lib/sql.cc \
|
||||
lib/usb.cc \
|
||||
lib/victor9k/decoder.cc \
|
||||
lib/visualiser.cc \
|
||||
lib/writer.cc \
|
||||
lib/zilogmcz/decoder.cc \
|
||||
|
||||
buildlibrary libfrontend.a \
|
||||
src/fe-cwftoflux.cc \
|
||||
src/fe-erase.cc \
|
||||
src/fe-fluxtoau.cc \
|
||||
src/fe-fluxtoscp.cc \
|
||||
src/fe-fluxtovcd.cc \
|
||||
src/fe-inspect.cc \
|
||||
src/fe-readadfs.cc \
|
||||
@@ -200,6 +209,7 @@ buildlibrary libfrontend.a \
|
||||
src/fe-readvictor9k.cc \
|
||||
src/fe-readzilogmcz.cc \
|
||||
src/fe-rpm.cc \
|
||||
src/fe-scptoflux.cc \
|
||||
src/fe-seek.cc \
|
||||
src/fe-testbulktransport.cc \
|
||||
src/fe-upgradefluxfile.cc \
|
||||
@@ -223,12 +233,13 @@ buildsimpleprogram brother120tool \
|
||||
libemu.a \
|
||||
libfmt.a \
|
||||
|
||||
runtest bitaccumulator-test tests/bitaccumulator.cc
|
||||
runtest bytes-test tests/bytes.cc
|
||||
runtest compression-test tests/compression.cc
|
||||
runtest crunch-test tests/crunch.cc
|
||||
runtest dataspec-test tests/dataspec.cc
|
||||
runtest flags-test tests/flags.cc
|
||||
runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest bitaccumulator-test tests/bitaccumulator.cc
|
||||
runtest kryoflux-test tests/kryoflux.cc
|
||||
runtest compression-test tests/compression.cc
|
||||
runtest bytes-test tests/bytes.cc
|
||||
runtest crunch-test tests/crunch.cc
|
||||
runtest fluxpattern-test tests/fluxpattern.cc
|
||||
runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest kryoflux-test tests/kryoflux.cc
|
||||
runtest ldbs-test tests/ldbs.cc
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "fluxmap.h"
|
||||
#include "writer.h"
|
||||
|
||||
static FlagGroup flags;
|
||||
static FlagGroup flags { &writerFlags };
|
||||
|
||||
int mainErase(int argc, const char* argv[])
|
||||
{
|
||||
|
||||
@@ -34,14 +34,15 @@ int mainConvertFluxToAu(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
const auto& locations = source.get().locations;
|
||||
FluxSpec spec(source);
|
||||
const auto& locations = spec.locations;
|
||||
if (locations.size() != 1)
|
||||
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
|
||||
const auto& location = *(locations.begin());
|
||||
|
||||
std::cerr << "Reading source flux...\n";
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
|
||||
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
|
||||
unsigned totalTicks = fluxmap->ticks() + 2;
|
||||
unsigned channels = withIndex ? 2 : 1;
|
||||
|
||||
188
src/fe-fluxtoscp.cc
Normal file
188
src/fe-fluxtoscp.cc
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "sql.h"
|
||||
#include "bytes.h"
|
||||
#include "protocol.h"
|
||||
#include "dataspec.h"
|
||||
#include "fmt/format.h"
|
||||
#include "decoders/fluxmapreader.h"
|
||||
#include "scp.h"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
static FlagGroup flags { };
|
||||
|
||||
static SettableFlag fortyTrackMode(
|
||||
{ "--48", "-4" },
|
||||
"set 48 tpi mode; only every other physical track is emitted"
|
||||
);
|
||||
|
||||
static SettableFlag singleSided(
|
||||
{ "--single-sided", "-s" },
|
||||
"only emit side 0"
|
||||
);
|
||||
|
||||
static IntFlag diskType(
|
||||
{ "--disk-type" },
|
||||
"sets the SCP disk type byte",
|
||||
0xff
|
||||
);
|
||||
|
||||
static sqlite3* inputDb;
|
||||
|
||||
static void syntax()
|
||||
{
|
||||
std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void write_le32(uint8_t dest[4], uint32_t v)
|
||||
{
|
||||
dest[0] = v;
|
||||
dest[1] = v >> 8;
|
||||
dest[2] = v >> 16;
|
||||
dest[3] = v >> 24;
|
||||
}
|
||||
|
||||
static int strackno(int track, int side)
|
||||
{
|
||||
if (fortyTrackMode)
|
||||
track /= 2;
|
||||
if (singleSided)
|
||||
return track;
|
||||
else
|
||||
return (track << 1) | side;
|
||||
}
|
||||
|
||||
int mainConvertFluxToScp(int argc, const char* argv[])
|
||||
{
|
||||
auto filenames = flags.parseFlagsWithFilenames(argc, argv);
|
||||
if (filenames.size() != 2)
|
||||
syntax();
|
||||
|
||||
inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY);
|
||||
auto tracks = sqlFindFlux(inputDb);
|
||||
|
||||
int maxTrack = 0;
|
||||
int maxSide = 0;
|
||||
for (auto p : tracks)
|
||||
{
|
||||
if (singleSided && (p.second == 1))
|
||||
continue;
|
||||
maxTrack = std::max(maxTrack, (int)p.first);
|
||||
maxSide = std::max(maxSide, (int)p.second);
|
||||
}
|
||||
int maxStrack = strackno(maxTrack, maxSide);
|
||||
|
||||
std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n",
|
||||
fortyTrackMode ? "48 tpi" : "96 tpi",
|
||||
singleSided ? "single sided" : "double sided",
|
||||
maxStrack + 1
|
||||
);
|
||||
|
||||
ScpHeader fileheader = {0};
|
||||
fileheader.file_id[0] = 'S';
|
||||
fileheader.file_id[1] = 'C';
|
||||
fileheader.file_id[2] = 'P';
|
||||
fileheader.file_id[3] = 0x18; /* Version 1.8 of the spec */
|
||||
fileheader.type = diskType;
|
||||
fileheader.revolutions = 5;
|
||||
fileheader.start_track = 0;
|
||||
fileheader.end_track = maxStrack;
|
||||
fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI);
|
||||
fileheader.cell_width = 0;
|
||||
fileheader.heads = singleSided ? 1 : 0;
|
||||
|
||||
Bytes trackdata;
|
||||
ByteWriter trackdataWriter(trackdata);
|
||||
|
||||
int trackstep = 1 + fortyTrackMode;
|
||||
int maxside = singleSided ? 0 : 1;
|
||||
for (int track = 0; track <= maxTrack; track += trackstep)
|
||||
{
|
||||
for (int side = 0; side <= maxside; side++)
|
||||
{
|
||||
int strack = strackno(track, side);
|
||||
std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush;
|
||||
|
||||
auto fluxmap = sqlReadFlux(inputDb, track, side);
|
||||
ScpTrack trackheader = {0};
|
||||
trackheader.track_id[0] = 'T';
|
||||
trackheader.track_id[1] = 'R';
|
||||
trackheader.track_id[2] = 'K';
|
||||
trackheader.strack = strack;
|
||||
|
||||
FluxmapReader fmr(*fluxmap);
|
||||
Bytes fluxdata;
|
||||
ByteWriter fluxdataWriter(fluxdata);
|
||||
|
||||
int revolution = 0;
|
||||
unsigned revTicks = 0;
|
||||
unsigned totalTicks = 0;
|
||||
unsigned ticksSinceLastPulse = 0;
|
||||
uint32_t startOffset = 0;
|
||||
while (revolution < 5)
|
||||
{
|
||||
unsigned ticks;
|
||||
int opcode = fmr.readOpcode(ticks);
|
||||
if (ticks)
|
||||
{
|
||||
ticksSinceLastPulse += ticks;
|
||||
totalTicks += ticks;
|
||||
revTicks += ticks;
|
||||
}
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case -1: /* end of flux, treat like an index marker */
|
||||
case F_OP_INDEX:
|
||||
{
|
||||
auto* revheader = &trackheader.revolution[revolution];
|
||||
write_le32(revheader->offset, startOffset + sizeof(ScpTrack));
|
||||
write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2);
|
||||
write_le32(revheader->index, revTicks * NS_PER_TICK / 25);
|
||||
revolution++;
|
||||
revheader++;
|
||||
revTicks = 0;
|
||||
startOffset = fluxdataWriter.pos;
|
||||
break;
|
||||
}
|
||||
|
||||
case F_OP_PULSE:
|
||||
{
|
||||
unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25;
|
||||
while (t >= 0x10000)
|
||||
{
|
||||
fluxdataWriter.write_be16(0);
|
||||
t -= 0x10000;
|
||||
}
|
||||
fluxdataWriter.write_be16(t);
|
||||
ticksSinceLastPulse = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader));
|
||||
trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader));
|
||||
trackdataWriter += fluxdata;
|
||||
|
||||
std::cout << fmt::format("{} ms in {} bytes\n",
|
||||
totalTicks * MS_PER_TICK,
|
||||
fluxdata.size());
|
||||
}
|
||||
}
|
||||
|
||||
sqlClose(inputDb);
|
||||
|
||||
std::cout << "Writing output file...\n";
|
||||
std::ofstream of(filenames[1], std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
Error() << "cannot open output file";
|
||||
of.write((const char*) &fileheader, sizeof(fileheader));
|
||||
of.write((const char*) trackdata.begin(), trackdata.size());
|
||||
of.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -30,14 +30,15 @@ int mainConvertFluxToVcd(int argc, const char* argv[])
|
||||
{
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
const auto& locations = source.get().locations;
|
||||
const FluxSpec spec(source);
|
||||
const auto& locations = spec.locations;
|
||||
if (locations.size() != 1)
|
||||
Error() << "the source dataspec must contain exactly one track (two sides count as two tracks)";
|
||||
const auto& location = *(locations.begin());
|
||||
|
||||
std::cerr << "Reading source flux...\n";
|
||||
setHardwareFluxSourceDensity(highDensityFlag);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(source);
|
||||
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
|
||||
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
|
||||
|
||||
std::cerr << "Writing destination VCD...\n";
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"adfs.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,10 +20,11 @@ static IntFlag sectorIdBase(
|
||||
int mainReadADFS(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("adfs.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@ static StringFlag outputFilename(
|
||||
int mainReadAESLanier(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("aeslanier.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
AesLanierDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"amiga.adf");
|
||||
|
||||
int mainReadAmiga(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("amiga.adf:c=80:h=2:s=11:b=512");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
AmigaDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"ampro.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
|
||||
int mainReadAmpro(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("ampro.adf");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"apple2.img");
|
||||
|
||||
int mainReadApple2(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("apple2.adf");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Apple2Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -14,19 +14,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"brother.img");
|
||||
|
||||
int mainReadBrother(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-81:s=0");
|
||||
setReaderDefaultOutput("brother.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
BrotherDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"c64.img");
|
||||
|
||||
int mainReadC64(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79x2:s=0");
|
||||
setReaderDefaultOutput("c64.d64");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Commodore64Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"dfs.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
@@ -25,11 +20,12 @@ static IntFlag sectorIdBase(
|
||||
int mainReadDFS(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("dfs.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"f85.img");
|
||||
|
||||
int mainReadF85(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("f85.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
DurangoF85Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"fb100.img");
|
||||
|
||||
int mainReadFB100(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79x2:s=0");
|
||||
setReaderDefaultOutput("fb100.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Fb100Decoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,23 +12,24 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"ibm.img");
|
||||
|
||||
static IntFlag sectorIdBase(
|
||||
{ "--sector-id-base" },
|
||||
"Sector ID of the first sector.",
|
||||
1);
|
||||
|
||||
static BoolFlag ignoreSideByte(
|
||||
{ "--ignore-side-byte" },
|
||||
"Ignore the side byte in the sector ID, and use the physical side instead.",
|
||||
false);
|
||||
|
||||
int mainReadIBM(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("ibm.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
IbmDecoder decoder(sectorIdBase);
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
IbmDecoder decoder(sectorIdBase, ignoreSideByte);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"mac.img");
|
||||
|
||||
int mainReadMac(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("mac.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
MacintoshDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,18 +12,14 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"mx.img");
|
||||
|
||||
int mainReadMx(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0-1");
|
||||
setReaderDefaultOutput("mx.img");
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
MxDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,19 +13,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"victor9k.img");
|
||||
|
||||
int mainReadVictor9K(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-79:s=0");
|
||||
setReaderDefaultOutput("victor9k.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
Victor9kDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,15 @@
|
||||
|
||||
static FlagGroup flags { &readerFlags };
|
||||
|
||||
static StringFlag outputFilename(
|
||||
{ "--output", "-o" },
|
||||
"The output image file to write to.",
|
||||
"zilogmcz.img");
|
||||
|
||||
int mainReadZilogMCZ(int argc, const char* argv[])
|
||||
{
|
||||
setReaderDefaultSource(":t=0-76:s=0");
|
||||
setReaderDefaultOutput("zilogmcz.img");
|
||||
setReaderRevolutions(2);
|
||||
flags.parseFlags(argc, argv);
|
||||
|
||||
ZilogMczDecoder decoder;
|
||||
readDiskCommand(decoder, outputFilename);
|
||||
readDiskCommand(decoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user