Compare commits

...

45 Commits

Author SHA1 Message Date
David Given
d6ae373fa8 Merge pull request #92 from davidgiven/d64
Add write-only support for D64 disk images.
2019-08-15 20:46:02 +02:00
David Given
a626d5f9a0 Add write-only support for D64 disk images. 2019-08-15 20:30:07 +02:00
David Given
29db67528d Merge pull request #90 from davidgiven/write
Add support for writing LDBS image files.
2019-08-10 21:40:56 +02:00
David Given
31d7477c6a Add LDBS documentation. 2019-08-10 21:35:21 +02:00
David Given
56af9eaf18 The LDBS ST1 field is now populated correctly on error. 2019-08-10 21:25:07 +02:00
David Given
5de0636fe7 First attempt at writing LDBS files. Not quite right. 2019-08-09 23:21:55 +02:00
David Given
f9117b8d11 Added a simple library for reading and writing LDBS files. 2019-08-09 22:41:07 +02:00
David Given
10d385375f Merge pull request #87 from davidgiven/write
Add support for pluggable input and output formats.
2019-08-09 21:22:03 +02:00
David Given
2f72c3f8f0 Mac images now put the metadata second. 2019-08-09 21:13:29 +02:00
David Given
54edff9b94 Amiga disks can now optionally emit the metadata. 2019-08-09 21:04:48 +02:00
David Given
112377f885 Add pluggable image readers, plus some documentation. 2019-08-09 20:56:06 +02:00
David Given
87e29fc386 Merge from write branch (because we want the new image code). 2019-08-08 23:27:11 +02:00
David Given
b1db5c48b1 Ignore more temporary files. 2019-08-08 23:26:42 +02:00
David Given
38fab7edcb Refactor all the image writing stuff into a ImageWriter subclass hierarchy with
a factory based on extension.
2019-08-08 00:27:41 +02:00
David Given
d8172154c3 Output images now get geometry specs to indicate what kind of file to write. 2019-08-07 23:23:07 +02:00
David Given
eb924780ab Refactor dataspecs to allow them to be used for other things too. 2019-08-06 23:50:02 +02:00
David Given
28e0ef0463 Merge pull request #84 from davidgiven/cleanup
Mechanical refactor to rearrange the source files into a more pleasing order.
2019-08-06 22:33:05 +02:00
David Given
4b07c38782 Mechanical refactor to rearrange the source files into a more pleasing order. 2019-08-06 22:25:11 +02:00
David Given
e0256adf77 Merge pull request #83 from davidgiven/write
Write code refactoring.
2019-08-06 22:24:40 +02:00
David Given
5748f017dd Refactor the write code to make it easier to add new encoders. 2019-08-06 22:17:58 +02:00
David Given
973f4c2c2d Skeleton work to factor out the encoder logic. 2019-08-03 22:30:30 +02:00
David Given
8e1774c69f Show the clock rate in kHz as well, because that's more useful. 2019-07-12 23:09:50 +02:00
David Given
56a36072f7 Sampler state machine cleanup; more debugging tools for the logic analyser. 2019-07-12 21:09:53 +02:00
David Given
8755d108ed Sanity check Mac sectors --- reject anything with an out-of-bounds sector ID. 2019-07-11 23:16:02 +02:00
David Given
ea40cd73d1 Merge pull request #78 from davidgiven/mx
Bugfixing and documentation for MX disks.
2019-07-11 22:02:24 +02:00
David Given
0e28899b72 Add the page on the MX disk format. Mac disks are now unicorns. 2019-07-11 21:34:01 +02:00
David Given
eee30db981 Honour the logical track numbering in MX disks (allows reading 40-track disks). 2019-07-11 21:03:54 +02:00
David Given
6959d18017 Merge pull request #77 from davidgiven/build
Build cleanups
2019-07-11 12:18:59 +02:00
David Given
9f92ce0ef7 Typo fix. 2019-07-11 12:10:36 +02:00
David Given
7658c1d774 Move the fluxtoau tool into the main client. 2019-07-11 12:00:25 +02:00
David Given
31dc3504e6 Move the fluxtovcd file into the main client. 2019-07-11 11:52:38 +02:00
David Given
af0c9d4261 Move cwftoflux into the main client. 2019-07-11 11:45:15 +02:00
David Given
155b9daef6 Clean build scripts a bit. 2019-07-11 11:25:29 +02:00
David Given
a2fdbc5c73 Merge pull request #76 from davidgiven/sampling
Minor fixes and debugging tooling
2019-07-11 00:10:18 +02:00
David Given
1e3581c5f3 Turns out I was using the wrong error threshold flags for Mac disks. ND disks
work fine now.
2019-07-10 23:55:16 +02:00
David Given
a1d345856e Add a tool for converting flux files directly to mono or stereo au files,
suitable for comparison in Audacity.
2019-07-10 22:56:39 +02:00
David Given
7a775afaea Make sure that CounterClock is actually 12MHz, and not 12-ish MHz. Doesn't
help.
2019-07-10 20:26:17 +02:00
David Given
c27c4fe312 Synchronise input pins and set LVTTL levels. More correct, but doesn't help the
read issues.
2019-07-10 19:48:03 +02:00
David Given
ad295c683c The sampler pulse conversion now uses BUS_CLK as the sample clock. 2019-07-09 22:43:46 +02:00
David Given
3f8fdaa27a Fix missing flag initialisation. 2019-07-09 22:38:35 +02:00
David Given
9f5d01787f Fix missing flag initialisation. 2019-07-09 22:38:35 +02:00
David Given
3c4487c42e Add the tool to export flux files as VCD, so that they can be read into
pulseview.
2019-07-09 00:48:35 +02:00
David Given
e5c2168a35 Merge. 2019-07-08 23:32:53 +02:00
David Given
98ea5e9600 Connect up pins 2.3, 2.4 and 2.5 for debugging with a logic analyser. 2019-07-08 01:08:05 +02:00
David Given
ce6077fa22 Apply Denis Kushch's timing fixes to the schematic. No more warnings on builds! 2019-07-08 00:52:59 +02:00
103 changed files with 1821 additions and 584 deletions

View File

@@ -15,7 +15,7 @@ install:
build_script:
- make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe cwftoflux.exe
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe
artifacts:
- path: fluxengine.zip

View File

@@ -3,6 +3,10 @@ streams
.*\.flux
.*\.img
.*\.raw
.*\.orig
.vscode
remote
FluxEngine.cydsn/CortexM3
FluxEngine.cydsn/Generated_Source
FluxEngine.cydsn/codegentemp

View File

@@ -28,13 +28,37 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_3" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_3" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_FREQ" />
<Data key="desired_freq" value="24" />
<Data key="desired_freq" value="12" />
<Data key="desired_unit" value="6" />
<Data key="divider" value="1" />
<Data key="divider" value="2" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
@@ -50,7 +74,7 @@
<Data key="src_clk_name" value="IMO" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
<Data key="user_set_domain" value="True" />
</Group>
<Group key="24cd38f7-f472-4403-837f-86807c8f5333">
<Data key="check_tolerance" value="True" />
@@ -193,6 +217,54 @@
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="75187c05-9501-4450-b306-6ccdd3bb77db">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_5" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_5" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="b762c287-7f87-4b21-982e-84be01dc5115">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
<Data key="derive_type" value="NAMED_DIVIDER" />
<Data key="desired_freq" value="0" />
<Data key="desired_unit" value="15" />
<Data key="divider" value="0" />
<Data key="domain" value="DIGITAL" />
<Data key="enabled" value="True" />
<Data key="minus_accuracy" value="0.25" />
<Data key="minus_tolerance" value="5" />
<Data key="name" value="Clock_2" />
<Data key="named_src_direct_connect" value="True" />
<Data key="netlist_name" value="Clock_2" />
<Data key="placement" value="AUTO" />
<Data key="plus_accuracy" value="0.25" />
<Data key="plus_tolerance" value="5" />
<Data key="scope" value="LOCAL" />
<Data key="src_clk_id" value="75C2148C-3656-4d8a-846D-0CAE99AB6FF7" />
<Data key="src_clk_name" value="BUS_CLK" />
<Data key="start_on_reset" value="True" />
<Data key="sync_with_bus_clk" value="True" />
<Data key="user_set_domain" value="False" />
</Group>
<Group key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358">
<Data key="check_tolerance" value="True" />
<Data key="clock_version" value="v1" />
@@ -537,6 +609,7 @@
<Group key="DWRInstGuidMapping">
<Group key="Clock">
<Data key="0b2f9bbb-00ce-4115-a788-ffb9d046a9e5" value="Clock_4" />
<Data key="4eef02b9-8ad1-43c4-85f1-b3335faa5fc4" value="Clock_3" />
<Data key="06c4d5d4-f15f-4b29-a1d0-c24b2e38b1ec" value="CounterClock" />
<Data key="24cd38f7-f472-4403-837f-86807c8f5333" value="PULSE_CLOCK" />
<Data key="63ed4137-0b09-4256-8a27-35c9a2653f1a" value="Clock_2" />
@@ -544,6 +617,8 @@
<Data key="349ffa20-8576-4ac3-9a6f-34ef606de6cf" value="Clock_1" />
<Data key="6616e828-6611-4893-a674-66c861d79d6c" value="SignalSamplingClock" />
<Data key="12664fc6-9d70-44b1-8a49-887a292e1b7f" value="Clock_3" />
<Data key="75187c05-9501-4450-b306-6ccdd3bb77db" value="Clock_5" />
<Data key="b762c287-7f87-4b21-982e-84be01dc5115" value="Clock_2" />
<Data key="b0162966-0060-4af5-82d1-fcb491ad7619/be0a0e37-ad17-42ca-b5a1-1a654d736358" value="UART_IntClock" />
<Data key="cb7e877c-9fb4-4fc1-a708-f1e48eb5a68c" value="CounterClock" />
<Data key="e4a53a4c-40e1-4747-a72a-10193ffdf31c" value="Clock_1" />
@@ -553,6 +628,7 @@
<Data key="4a398466-709f-4228-9500-96178658e13e" value="RDATA" />
<Data key="5a3407c1-b434-4438-a7b4-b9dfd2280495" value="MOTEA" />
<Data key="8d318d8b-cf7b-4b6b-b02c-ab1c5c49d0ba" value="SW1" />
<Data key="12e00eac-69b5-4717-85c8-25ef6b224d4c" value="DEBUG_PINS" />
<Data key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51" value="REDWC" />
<Data key="264be2d3-9481-494b-8d9c-c1905a45e9cc" value="FDD" />
<Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" />
@@ -561,6 +637,7 @@
<Data key="1425177d-0d0e-4468-8bcc-e638e5509a9b" value="UartRx" />
<Data key="a5d987c6-e45b-45b9-ad93-656fab06d720" value="TRK00" />
<Data key="a93ef5b3-00f4-42c0-8fad-0e275a7e2537" value="MOTEB" />
<Data key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a" value="DEBUG_PINS" />
<Data key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f" value="DEBUG_PINS" />
<Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/8b77a6c4-10a0-4390-971c-672353e2a49c" value="USBFS_Dm" />
<Data key="beca5e2d-f70f-4900-a4db-7eca1ed3126e/618a72fc-5ddd-4df5-958f-a3d55102db42" value="USBFS_Dp" />
@@ -571,7 +648,7 @@
<Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" />
<Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" />
<Data key="ed092b9b-d398-4703-be89-cebf998501f6" value="UartTx" />
<Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="SW_Tx_UART_1_tx" />
<Data key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6" value="UART_tx" />
<Data key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b" value="DRVSA" />
<Data key="fff78075-035e-43d7-8577-bc5be4d21926" value="WGATE" />
</Group>
@@ -3675,6 +3752,20 @@
<Data key="Port Format" value="2,2" />
</Group>
</Group>
<Group key="12e00eac-69b5-4717-85c8-25ef6b224d4c">
<Group key="0">
<Data key="Port Format" value="2,2" />
</Group>
<Group key="1">
<Data key="Port Format" value="2,3" />
</Group>
<Group key="2">
<Data key="Port Format" value="2,4" />
</Group>
<Group key="3">
<Data key="Port Format" value="2,0" />
</Group>
</Group>
<Group key="41e2d8ed-5494-4d8c-8ff7-f4f789cece51">
<Group key="0">
<Data key="Port Format" value="2,7" />
@@ -3757,6 +3848,17 @@
<Data key="Port Format" value="12,1" />
</Group>
</Group>
<Group key="b8380fb7-fdb8-449f-bd8d-c4ca96cdf55a">
<Group key="0">
<Data key="Port Format" value="2,5" />
</Group>
<Group key="1">
<Data key="Port Format" value="2,4" />
</Group>
<Group key="2">
<Data key="Port Format" value="2,3" />
</Group>
</Group>
<Group key="bc5d52a1-1b25-4aa0-9ba9-3f81d122772f">
<Group key="0">
<Data key="Port Format" value="0,5" />
@@ -3812,7 +3914,7 @@
</Group>
<Group key="fbd1f839-40f9-498e-a48b-5f3048ea5c3d/52f31aa9-2f0a-497d-9a1f-1424095e13e6">
<Group key="0">
<Data key="Port Format" value="2,0" />
<Data key="Port Format" value="2,5" />
</Group>
</Group>
<Group key="fede1767-f3fd-4021-b3d7-8f9d88f36f9b">
@@ -3840,7 +3942,7 @@
<Data key="CYDEV_ECC_ENABLE" value="False" />
<Data key="CYDEV_HEAP_SIZE" value="0x80" />
<Data key="CYDEV_INSTRUCT_CACHE_ENABLED" value="True" />
<Data key="CYDEV_PROTECTION_ENABLE" value="False" />
<Data key="CYDEV_PROTECTION_ENABLE" value="True" />
<Data key="CYDEV_STACK_SIZE" value="0x0800" />
<Data key="CYDEV_TEMPERATURE" value="0C - 85/125C" />
<Data key="CYDEV_TRACE_ENABLED" value="False" />

View File

@@ -2310,13 +2310,13 @@
<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="DEBUG_PINS" persistent="">
<Hidden v="True" />
<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="DEBUG_PINS_aliases.h" persistent="Generated_Source\PSoC5\DEBUG_PINS_aliases.h">
<Hidden v="True" />
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
@@ -3249,6 +3249,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="CAPTURE_CONTROL" 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="CAPTURE_CONTROL.h" persistent="Generated_Source\PSoC5\CAPTURE_CONTROL.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="CAPTURE_CONTROL.c" persistent="Generated_Source\PSoC5\CAPTURE_CONTROL.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="CAPTURE_CONTROL_PM.c" persistent="Generated_Source\PSoC5\CAPTURE_CONTROL_PM.c">
<Hidden v="False" />
</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>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
@@ -3667,6 +3700,6 @@
</ignored_deps>
</CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37>
<boot_component v="" />
<current_generation v="52" />
<current_generation v="60" />
</CyGuid_fec8f9e8-2365-4bdb-96d3-a4380222e01b>
</CyXmlSerializer>

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -306,6 +306,7 @@ static void cmd_read(struct read_frame* f)
dma_underrun = false;
int count = 0;
SAMPLER_CONTROL_Write(0); /* !reset */
CAPTURE_CONTROL_Write(1);
CyDmaChSetInitialTd(dma_channel, td[dma_writing_to_td]);
CyDmaClearPendingDrq(dma_channel);
CyDmaChEnable(dma_channel, 1);
@@ -364,7 +365,8 @@ static void cmd_read(struct read_frame* f)
}
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
}
abort:
abort:;
CAPTURE_CONTROL_Write(0);
CyDmaChSetRequest(dma_channel, CY_DMA_CPU_TERM_CHAIN);
while (CyDmaChGetRequest(dma_channel))
;

View File

@@ -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

View File

@@ -89,7 +89,7 @@ people who've had it work).
| [Brother 120kB](doc/disk-brother.md) | 🦄 | | |
| [Brother 240kB](doc/disk-brother.md) | 🦄 | 🦄 | |
| [Brother FB-100](doc/disk-fb100.md) | 🦖 | | Tandy Model 100, Husky Hunter, knitting machines |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦖 | | and probably the 400kB too |
| [Macintosh 800kB](doc/disk-macintosh.md) | 🦄 | | and probably the 400kB too |
| [TRS-80](doc/disk-trs80.md) | 🦖 | | a minor variation of the IBM scheme |
{: .datatable }
@@ -105,6 +105,7 @@ at least, check the CRC so what data's there is probably good.
|:-----------------------------------------|:-----:|:------:|-------|
| [AES Superplus / No Problem](doc/disk-aeslanier.md) | 🦖 | | hard sectors! |
| [Durango F85](doc/disk-durangof85.md) | 🦖 | | 5.25" |
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8-inch |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8-inch _and_ hard sectors |
{: .datatable }
@@ -189,7 +190,7 @@ Jonathan Müller (`foonathan <https://github.com/foonathan>`) with
contributions from many other people. It is licensed under the terms of the
BSD license. Please see the contents of the directory for the full text.
As an exception, `dep/fnmatchemu` contains parts of the OpenBSD C library
As an exception, `dep/emu` contains parts of the OpenBSD C library
code, Todd Miller and William A. Rowe (and probably others). It is licensed
under the terms of the 3-clause BSD license. Please see the contents of the
directory for the full text. It's been lightly modified by me.

View File

View File

@@ -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;
}

View File

@@ -9,6 +9,9 @@
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
#define BROTHER_TRACKS_PER_DISK 78
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
class Fluxmap;
@@ -22,9 +25,15 @@ public:
void decodeDataRecord();
};
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector);
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
const Bytes& data);
class BrotherEncoder : public AbstractEncoder
{
public:
virtual ~BrotherEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup brotherEncoderFlags;
#endif

View File

@@ -3,6 +3,7 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "record.h"
#include "brother.h"
#include "sector.h"

167
arch/brother/encoder.cc Normal file
View File

@@ -0,0 +1,167 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "brother.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup brotherEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
3.83);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
1.0);
static DoubleFlag sectorSpacingMs(
{ "--sector-spacing" },
"Time between successive sector headers (milliseconds).",
16.2);
static DoubleFlag postHeaderSpacingMs(
{ "--post-header-spacing" },
"Time between a sector's header and data records (milliseconds).",
0.69);
static StringFlag sectorSkew(
{ "--sector-skew" },
"Order in which to write sectors.",
"05a3816b4927");
static int encode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
static void write_sector_header(std::vector<bool>& bits, unsigned& cursor,
int track, int sector)
{
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
}
static void write_sector_data(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
uint16_t fifo = 0;
int width = 0;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
for (uint8_t byte : data)
write_byte(byte);
uint32_t realCrc = crcbrother(data);
write_byte(realCrc>>16);
write_byte(realCrc>>8);
write_byte(realCrc);
write_byte(0x58); /* magic */
write_byte(0xd4);
while (width != 0)
write_byte(0);
}
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|| (physicalSide != 0))
return std::unique_ptr<Fluxmap>();
int bitsPerRevolution = 200000.0 / clockRateUs;
const std::string& skew = sectorSkew.get();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorCount=0; sectorCount<BROTHER_SECTORS_PER_TRACK; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
unsigned headerCursor = headerMs*1e3 / clockRateUs;
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, physicalTrack, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
write_sector_data(bits, cursor, sectorData->data);
}
if (cursor > bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

@@ -153,6 +153,9 @@ void MacintoshDecoder::decodeSectorRecord()
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _track->physicalTrack;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
@@ -177,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));
}

View File

@@ -37,7 +37,7 @@ AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
readRawBits(32); /* skip the track number */
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
}
else if (_currentSector == 10)
{
@@ -67,7 +67,7 @@ void MxDecoder::decodeSectorRecord()
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
_sector->logicalTrack = _track->physicalTrack;
_sector->logicalTrack = _logicalTrack;
_sector->logicalSide = _track->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);

View File

@@ -15,6 +15,7 @@ public:
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
#endif

View File

@@ -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
-----------------

View File

@@ -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
-----------------

View File

@@ -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
-----------------

65
doc/disk-mx.md Normal file
View File

@@ -0,0 +1,65 @@
Disk: DVK MX
============
The DVK (in Russian, ДВК, Диалоговый вычислительный комплекс or Dialogue
Computing Complex) was a late 1970s Soviet personal computer, a cut-down
version of the professional SM EVM (СМ ЭВМ, abbreviation of Система Малых ЭВМ
--- literally System of Mini Computers), which _itself_ was an unlicensed
clone of the PDP-11. The MX board was an early floppy drive controller board
for it.
<div style="text-align: center">
<a href="http://www.leningrad.su/museum/show_big.php?n=1006"><img src="dvk3m.jpg" style="max-width: 60%" alt="A Durango F85, held precariously"></a>
</div>
The MX format is interesting in that it has to be read a track at a time. The
format contains the usual ID prologue at the beginning of the track, then
eleven data blocks and checksums, then the epilogue, then it stops. The
actual encoding is normal FM. There were four different disk variants, in all
combinations of single- and double-sided and 40- and 80-tracked; but every
track contained eleven 256-byte sectors.
The format varies subtly depending on whether you're using the 'new' driver
or the 'old' driver. FluxEngine should read both.
A track is:
* 8 x 0x0000 words (FM encoded as 01010101...)
* 1 x 0x00F3 --- start of track
* 1 x 0xnnnn --- track number
* 11 of:
* 128 words (256 bytes) of data
* 16 bit checksum
* **if 'new' format:**
* 3 x 0x83nn --- `n = (track_number<<1) + side_number`
* **if 'old' format:**
* 3 x 0x8301
The checksum is just the unsigned integer sum of all the words in the sector.
Words are all stored little-endian.
Reading discs
-------------
```
fluxengine read mx
```
You should end up with an `mx.img` which will vary in length depending on the format. The default is double-sided 80-track. For the other formats, use:
* single-sided 40-track: `-s :s=0:t=0-79x2`
* double-sided 40-track: `-s :s=0-1:t=0-79x2`
* single-sided 40-track: `-s :s=0:t=0-79`
* double-sided 40-track: `-s :s=0-1:t=0-79`
Useful references
-----------------
- [The Soviet Digital Electronics
Museum](http://www.leningrad.su/museum/main.php) (source of the image
above)
- [a random post on the HxC2001 support
forum](http://torlus.com/floppy/forum/viewtopic.php?t=1384) with lots of
information on the format

BIN
doc/dvk3m.jpg Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@@ -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

View File

@@ -1,92 +0,0 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "brother.h"
#include "crc.h"
static int encode_header_gcr(uint16_t word)
{
switch (word)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "header_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static int encode_data_gcr(uint8_t data)
{
switch (data)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
};
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
cursor += width;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
if (pos < bits.size())
bits[pos] = data & 1;
data >>= 1;
}
}
void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector)
{
write_bits(bits, cursor, 0xffffffff, 31);
write_bits(bits, cursor, BROTHER_SECTOR_RECORD, 32);
write_bits(bits, cursor, encode_header_gcr(track), 16);
write_bits(bits, cursor, encode_header_gcr(sector), 16);
write_bits(bits, cursor, encode_header_gcr(0x2f), 16);
}
void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor, const Bytes& data)
{
write_bits(bits, cursor, 0xffffffff, 32);
write_bits(bits, cursor, BROTHER_DATA_RECORD, 32);
uint16_t fifo = 0;
int width = 0;
if (data.size() != BROTHER_DATA_RECORD_PAYLOAD)
Error() << "unsupported sector size";
auto write_byte = [&](uint8_t byte)
{
fifo |= (byte << (8 - width));
width += 8;
while (width >= 5)
{
uint8_t quintet = fifo >> 11;
fifo <<= 5;
width -= 5;
write_bits(bits, cursor, encode_data_gcr(quintet), 8);
}
};
for (uint8_t byte : data)
write_byte(byte);
uint32_t realCrc = crcbrother(data);
write_byte(realCrc>>16);
write_byte(realCrc>>8);
write_byte(realCrc);
write_byte(0x58); /* magic */
write_byte(0xd4);
while (width != 0)
write_byte(0);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

18
lib/encoders/encoders.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef ENCODERS_H
#define ENCODERS_H
class Fluxmap;
class SectorSet;
class AbstractEncoder
{
public:
virtual ~AbstractEncoder() {}
public:
virtual std::unique_ptr<Fluxmap> encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors) = 0;
};
#endif

View File

@@ -21,6 +21,7 @@ public:
public:
nanoseconds_t duration() const { return _duration; }
unsigned ticks() const { return _ticks; }
size_t bytes() const { return _bytes.size(); }
const Bytes& rawBytes() const { return _bytes; }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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

View File

@@ -0,0 +1,31 @@
#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"
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());
}
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
{
const auto& filename = spec.filename;
if (ends_with(filename, ".img") || ends_with(filename, ".adf"))
return createImgImageReader(spec);
Error() << "unrecognised image filename extension";
return std::unique_ptr<ImageReader>();
}
ImageReader::ImageReader(const ImageSpec& spec):
spec(spec)
{}

View File

@@ -0,0 +1,27 @@
#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);
private:
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
public:
virtual SectorSet readImage() = 0;
protected:
ImageSpec spec;
};
#endif

View 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));
}

View 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));
}

View File

@@ -0,0 +1,109 @@
#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"
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());
}
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec)
{
const auto& filename = spec.filename;
if (ends_with(filename, ".img") || ends_with(filename, ".adf"))
return createImgImageWriter(sectors, spec);
else if (ends_with(filename, ".ldbs"))
return createLDBSImageWriter(sectors, spec);
else if (ends_with(filename, ".d64"))
return createD64ImageWriter(sectors, spec);
Error() << "unrecognised image filename extension";
return std::unique_ptr<ImageWriter>();
}
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;
}
}

View File

@@ -0,0 +1,34 @@
#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);
private:
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);
public:
virtual void adjustGeometry();
void printMap();
virtual void writeImage() = 0;
protected:
const SectorSet& sectors;
ImageSpec spec;
};
#endif

View 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));
}

View 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
View 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
View 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

View File

@@ -23,6 +23,11 @@ static DataSpecFlag source(
"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",
@@ -52,6 +57,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 +81,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 +102,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;
@@ -140,8 +150,10 @@ 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);
bool failures = false;
SectorSet allSectors;
auto tracks = readTracks();
@@ -160,8 +172,9 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
track->rawrecords.size(),
track->sectors.size());
if (track->sectors.size() > 0)
std::cout << fmt::format("{:.2f}us clock; ",
track->sectors.begin()->clock / 1000.0);
std::cout << fmt::format("{:.2f}us clock ({:.0f}kHz); ",
track->sectors.begin()->clock / 1000.0,
1000000.0 / track->sectors.begin()->clock);
for (auto& sector : track->sectors)
{
@@ -238,8 +251,7 @@ void readDiskCommand(AbstractDecoder& decoder, const std::string& outputFilename
std::cout << size << " bytes decoded." << std::endl;
}
Geometry geometry = guessGeometry(allSectors);
writeSectorsToFile(allSectors, geometry, outputFilename);
writeSectorsToFile(allSectors, outputSpec);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
}

View File

@@ -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

View File

@@ -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());
}
}
}

View File

@@ -17,8 +17,9 @@ 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;
void calculateSize(
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
unsigned& sectorSize) const;
private:
std::map<const key_t, std::unique_ptr<Sector>> _data;

View File

@@ -6,9 +6,14 @@
#include "protocol.h"
#include "usb.h"
#include "dataspec.h"
#include "encoders/encoders.h"
#include "fluxsource/fluxsource.h"
#include "fluxsink/fluxsink.h"
#include "fmt/format.h"
#include "record.h"
#include "image.h"
#include "sector.h"
#include "sectorset.h"
FlagGroup writerFlags { &hardwareFluxSourceFlags };
@@ -17,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");
@@ -28,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);
@@ -60,10 +75,12 @@ void writeTracks(
{
if (!outdb)
{
std::cout << "erasing" << std::endl;
std::cout << "erasing\n";
usbSeek(location.track);
usbErase(location.side);
}
else
std::cout << "skipping\n";
}
else
{
@@ -96,3 +113,14 @@ void fillBitmapTo(std::vector<bool>& bitmap,
}
}
void writeDiskCommand(AbstractEncoder& encoder)
{
const ImageSpec spec(input);
SectorSet allSectors = readSectorsFromFile(spec);
writeTracks(
[&](int track, int side) -> std::unique_ptr<Fluxmap>
{
return encoder.encode(track, side, allSectors);
}
);
}

View File

@@ -6,8 +6,11 @@
extern FlagGroup writerFlags;
class Fluxmap;
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);
@@ -15,5 +18,6 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(AbstractEncoder& encoder);
#endif

View File

@@ -89,6 +89,33 @@ buildprogram() {
echo build $prog$EXTENSION : strip $prog-debug$EXTENSION
}
buildsimpleprogram() {
local prog
prog=$1
shift
local flags
flags=
while true; do
case $1 in
-*)
flags="$flags $1"
shift
;;
*)
break
esac
done
local src
src=$1
shift
buildlibrary lib$prog.a $flags $src
buildprogram $prog lib$prog.a "$@"
}
runtest() {
local prog
prog=$1
@@ -110,22 +137,33 @@ 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 \
lib/decoders/decoders.cc \
lib/decoders/fluxmapreader.cc \
lib/decoders/fmmfm.cc \
lib/encoder.cc \
lib/f85/decoder.cc \
lib/fb100/decoder.cc \
lib/encoders/encoders.cc \
lib/flags.cc \
lib/fluxmap.cc \
lib/fluxsink/fluxsink.cc \
@@ -138,21 +176,20 @@ 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/writer.cc \
lib/zilogmcz/decoder.cc \
buildlibrary libfrontend.a \
src/fe-cwftoflux.cc \
src/fe-erase.cc \
src/fe-fluxtoau.cc \
src/fe-fluxtovcd.cc \
src/fe-inspect.cc \
src/fe-readadfs.cc \
src/fe-readaeslanier.cc \
@@ -183,32 +220,23 @@ buildprogram fluxengine \
libbackend.a \
libfmt.a \
buildlibrary libbrother120tool.a \
-Idep/emu \
tools/brother120tool.cc \
buildlibrary libemu.a \
dep/emu/fnmatch.c
buildprogram brother120tool \
libbrother120tool.a \
buildsimpleprogram brother120tool \
-Idep/emu \
tools/brother120tool.cc \
libbackend.a \
libemu.a \
libfmt.a \
buildlibrary libcwftoflux.a \
tools/cwftoflux.cc \
buildprogram cwftoflux \
libcwftoflux.a \
libbackend.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

View File

@@ -37,7 +37,7 @@ static double clockRate;
static void syntax()
{
std::cout << "Syntax: cwftoflux <cwffile> <fluxfile>\n";
std::cout << "Syntax: fluxengine convert cwftoflux <cwffile> <fluxfile>\n";
exit(0);
}
@@ -116,7 +116,7 @@ static void read_track()
sqlWriteFlux(outputDb, track_number, trackheader.side, fluxmap);
}
int main(int argc, const char* argv[])
int mainConvertCwfToFlux(int argc, const char* argv[])
{
if (argc != 3)
syntax();

101
src/fe-fluxtoau.cc Normal file
View File

@@ -0,0 +1,101 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "dataspec.h"
#include "fluxsource/fluxsource.h"
#include "decoders/fluxmapreader.h"
#include "fmt/format.h"
#include <fstream>
static FlagGroup flags { &hardwareFluxSourceFlags };
static DataSpecFlag source(
{ "--source", "-s" },
"source for data",
":d=0:t=0:s=0");
static StringFlag output(
{ "--output", "-o" },
"output AU file to write",
"output.au");
static SettableFlag highDensityFlag(
{ "--high-density", "--hd" },
"set the drive to high density mode");
static SettableFlag withIndex(
{ "--with-index" },
"place index markers in the right hand channel");
int mainConvertFluxToAu(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
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(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
unsigned totalTicks = fluxmap->ticks() + 2;
unsigned channels = withIndex ? 2 : 1;
std::cerr << "Writing output file...\n";
std::ofstream of(output, std::ios::out | std::ios::binary);
if (!of.is_open())
Error() << "cannot open output file";
/* Write header */
{
Bytes header;
header.resize(24);
ByteWriter bw(header);
bw.write_be32(0x2e736e64);
bw.write_be32(24);
bw.write_be32(totalTicks * channels);
bw.write_be32(2); /* 8-bit PCM */
bw.write_be32(TICK_FREQUENCY);
bw.write_be32(channels); /* channels */
of.write((const char*) header.cbegin(), header.size());
}
/* Write data */
{
Bytes data;
data.resize(totalTicks * channels);
memset(data.begin(), 0x80, data.size());
FluxmapReader fmr(*fluxmap);
unsigned timestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int op = fmr.readOpcode(ticks);
if (op == -1)
break;
timestamp += ticks;
if (op == F_OP_PULSE)
data[timestamp*channels + 0] = 0x7f;
if (withIndex && (op == F_OP_INDEX))
data[timestamp*channels + 1] = 0x7f;
}
of.write((const char*) data.cbegin(), data.size());
}
std::cerr << "Done. Warning: do not play this file, or you will break your speakers"
" and/or ears!\n";
return 0;
}

84
src/fe-fluxtovcd.cc Normal file
View File

@@ -0,0 +1,84 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sql.h"
#include "bytes.h"
#include "protocol.h"
#include "dataspec.h"
#include "fluxsource/fluxsource.h"
#include "decoders/fluxmapreader.h"
#include "fmt/format.h"
#include <fstream>
static FlagGroup flags { &hardwareFluxSourceFlags };
static DataSpecFlag source(
{ "--source", "-s" },
"source for data",
":d=0:t=0:s=0");
static StringFlag output(
{ "--output", "-o" },
"output VCD file to write",
"output.vcd");
static SettableFlag highDensityFlag(
{ "--high-density", "--hd" },
"set the drive to high density mode");
int mainConvertFluxToVcd(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
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(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
std::cerr << "Writing destination VCD...\n";
std::ofstream of(output, std::ios::out);
if (!of.is_open())
Error() << "cannot open output file";
of << "$timescale 1ns $end\n"
<< "$var wire 1 i index $end\n"
<< "$var wire 1 p pulse $end\n"
<< "$upscope $end\n"
<< "$enddefinitions $end\n"
<< "$dumpvars 0i 0p $end\n";
FluxmapReader fmr(*fluxmap);
unsigned timestamp = 0;
unsigned lasttimestamp = 0;
while (!fmr.eof())
{
unsigned ticks;
int op = fmr.readOpcode(ticks);
if (op == -1)
break;
unsigned newtimestamp = timestamp + ticks;
if (newtimestamp != lasttimestamp)
{
of << fmt::format("\n#{} 0i 0p\n", (uint64_t)((lasttimestamp+1) * NS_PER_TICK));
timestamp = newtimestamp;
of << fmt::format("#{} ", (uint64_t)(timestamp * NS_PER_TICK));
}
if (op == F_OP_PULSE)
of << "1p ";
if (op == F_OP_INDEX)
of << "1i ";
lasttimestamp = timestamp;
}
of << "\n";
return 0;
}

View File

@@ -12,7 +12,7 @@
#include "track.h"
#include "fmt/format.h"
static FlagGroup flags;
static FlagGroup flags { &readerFlags };
static SettableFlag dumpFluxFlag(
{ "--dump-flux", "-F" },

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -3,6 +3,7 @@
#include "reader.h"
#include "fluxmap.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "brother/brother.h"
#include "sector.h"
#include "sectorset.h"
@@ -13,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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -12,11 +12,6 @@
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.",
@@ -25,10 +20,11 @@ static IntFlag sectorIdBase(
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);
readDiskCommand(decoder);
return 0;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -14,7 +14,8 @@ int mainRpm(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
usbSetDrive(source.get().drive, false);
FluxSpec spec(source);
usbSetDrive(spec.drive, false);
nanoseconds_t period = usbGetRotationalPeriod();
std::cout << "Rotational period is " << period/1000 << " ms (" << 60e6/period << " rpm)" << std::endl;

View File

@@ -1,108 +1,23 @@
#include "globals.h"
#include "flags.h"
#include "fluxmap.h"
#include "sector.h"
#include "sectorset.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "brother/brother.h"
#include "image.h"
#include "writer.h"
#include "fmt/format.h"
#include "image.h"
#include <fstream>
#include <ctype.h>
static FlagGroup flags { &writerFlags };
static StringFlag inputFilename(
{ "--input", "-i" },
"The input image file to read from.",
"brother.img");
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
3.83);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
1.0);
static DoubleFlag sectorSpacingMs(
{ "--sector-spacing" },
"Time between successive sector headers (milliseconds).",
16.2);
static DoubleFlag postHeaderSpacingMs(
{ "--post-header-spacing" },
"Time between a sector's header and data records (milliseconds).",
0.69);
static StringFlag sectorSkew(
{ "--sector-skew" },
"Order in which to write sectors.",
"05a3816b4927");
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static FlagGroup flags { &writerFlags, &brotherEncoderFlags };
int mainWriteBrother(int argc, const char* argv[])
{
setWriterDefaultInput(":c=78:h=1:s=12:b=256");
setWriterDefaultDest(":d=0:t=0-77:s=0");
flags.parseFlags(argc, argv);
SectorSet allSectors;
Geometry geometry = {78, 1, 12, 256};
readSectorsFromFile(allSectors, geometry, inputFilename);
int bitsPerRevolution = 200000.0 / clockRateUs;
std::cerr << bitsPerRevolution << " bits per 200ms revolution" << std::endl
<< fmt::format("post-index gap: {:.3f}ms\n", (double)postIndexGapMs);
const std::string& skew = sectorSkew;
writeTracks(
[&](int track, int side) -> std::unique_ptr<Fluxmap>
{
if ((track < 0) || (track > 77) || (side != 0))
return std::unique_ptr<Fluxmap>();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorCount=0; sectorCount<geometry.sectors; sectorCount++)
{
int sectorId = charToInt(skew.at(sectorCount));
double headerMs = postIndexGapMs + sectorCount*sectorSpacingMs;
unsigned headerCursor = headerMs*1e3 / clockRateUs;
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
auto& sectorData = allSectors.get(track, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
writeBrotherSectorHeader(bits, cursor, track, sectorId);
fillBitmapTo(bits, cursor, dataCursor, { true, false });
writeBrotherSectorData(bits, cursor, sectorData->data);
}
if (cursor > bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
// The pre-index gap is not normally reported.
// std::cerr << "pre-index gap " << 200.0 - (double)cursor*clockRateUs/1e3 << std::endl;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
);
BrotherEncoder encoder;
writeDiskCommand(encoder);
return 0;
}

Some files were not shown because too many files have changed in this diff Show More