Compare commits

...

704 Commits

Author SHA1 Message Date
David Given
f9b7c7f6c1 Briefly experiment with area sizing. 2021-12-15 22:36:11 +01:00
David Given
855d346b8a More UI tweaking. Begining to think that libui doesn't quite do what we want
(don't seem to be able to set the size of an Area?).
2021-12-15 22:31:37 +01:00
David Given
473e81a2da Fixup after merge. 2021-12-15 20:20:42 +01:00
David Given
d925391d57 Merge from master. 2021-12-15 20:17:11 +01:00
David Given
63a5954dfa Merge pull request #390 from davidgiven/victor9k
Victor 9000 fixes
2021-12-15 15:08:41 +00:00
David Given
897931f273 Merge pull request #386 from davidgiven/pulsefix
Various firmware fixes
2021-12-14 21:07:06 +00:00
David Given
03e80b2f1c Fix the format so it no longer discards sector 0. 2021-12-14 21:10:38 +01:00
David Given
53c8ec864c Clean up the GreaseWeazle bandwidth tester. Sometimes it doesn't read the right
amount of data from the device?
2021-12-14 18:01:53 +00:00
David Given
5d3002f118 Announce the device serial number on connection again. 2021-12-13 23:24:02 +01:00
David Given
5eeb52660c Update documentation for the USB autodetection. 2021-12-13 23:19:16 +01:00
David Given
3dfafaa278 GreaseWeazle autodetection now works (at least on Linux). 2021-12-13 23:09:33 +01:00
David Given
462bd9ae5e Rewrite the sampler pulse detection... again. 2021-12-12 23:13:23 +00:00
David Given
dab0fcc7c0 Hopefully fix Linux build. 2021-12-12 21:15:30 +01:00
David Given
b8a3e8085e Fix after merge. 2021-12-12 19:58:19 +00:00
David Given
c9d1d72ba3 Merge in the libusbp changes. 2021-12-12 19:50:33 +00:00
David Given
ccf5d513d2 Merge from master. 2021-12-12 19:49:32 +00:00
David Given
d157b7b05d Make libusbp work on Windows; rework the Microsoft-specific bits of the
firmware to match. This does at least allow us to get rid of the patcher.
2021-12-12 17:25:24 +00:00
dg
05981ac46c Make work on OSX. 2021-12-12 15:40:12 +00:00
David Given
08615a5954 Make libusbp build, at least on Linux; port fluxengineusb to use it. 2021-12-12 16:29:51 +01:00
David Given
b6dbe26c7e Raw import of libusbp. 2021-12-11 18:59:44 +01:00
David Given
8a460b3e56 Add tabs. 2021-12-11 12:43:53 +01:00
David Given
012f1a872b Merge from master. 2021-12-11 12:34:51 +01:00
David Given
4f0d61b379 Merge pull request #388 from davidgiven/supercardpro
Tell the various IBM decoders which sectors to expect, for more reliable retries.
2021-12-11 11:26:51 +00:00
David Given
9e5e494f88 Merge from master. 2021-12-11 12:18:06 +01:00
David Given
b15fd05e8d Add support in the IBM decoder for trackdata protos, and then configure the IBM
profiles to know about which sectors it should see in the result image to allow
retries if sectors aren't found.
2021-12-11 12:17:32 +01:00
David Given
faabc28d1b Merge pull request #387 from davidgiven/supercardpro
Refactor the serial port code.
2021-12-10 23:45:49 +00:00
David Given
4b815846ee ...and fix for OSX. 2021-12-10 23:35:58 +00:00
David Given
fe8be18c4c Fix the serial port code on Windows. 2021-12-10 23:29:20 +00:00
David Given
519c30321d Split the serial port code off into its own file so we can use it for the SCP
driver.
2021-12-11 00:06:14 +01:00
David Given
77be88a5bb Add a package that the tests need. 2021-12-10 21:25:00 +01:00
David Given
8d04931f9f Turns out the high density pin is asserted when _double_ density. Set it
correctly.
2021-12-10 20:07:02 +00:00
David Given
3d1ee7a43e Don't spin in an infinite loop if the decoder tries to find a data record and
the matcher fails to find anything, leaving the seek point unchanged.
2021-12-10 19:56:00 +00:00
David Given
2584a25527 Update components and binary. 2021-12-10 19:55:06 +00:00
David Given
0647a82d38 The tests will run on Windows now. 2021-12-10 19:54:31 +00:00
David Given
c56459c98c Add back the pulse converter, as a 12MHz sample clock only allows pulses which
are 83ns long, which I think is too big. We now sample the index signal at
64MHz which allows pulses down to 16ns.
2021-12-10 17:40:58 +00:00
David Given
3347d057fe Merge from master. 2021-12-09 23:26:58 +01:00
David Given
7b0a4a057d Merge pull request #376 from tdaede/scpfix2
Fix SCP writer to always write complete revolutions.
2021-12-09 22:26:36 +00:00
David Given
eb0bb2cc2f Rework container properties to be more elegant. More UI stuff. 2021-12-09 23:00:37 +01:00
David Given
5ff7651218 Don't use sem_open on Windows. 2021-12-09 17:32:22 +00:00
David Given
dcb3761c9b Work on some menu stuff. 2021-12-09 00:28:34 +01:00
David Given
6fa83f4dcb Miscellaneous tidying and fix a crash on exit. 2021-12-08 23:22:33 +01:00
David Given
001c65dba8 Add the list of configs to the GUI. 2021-12-08 22:51:48 +01:00
David Given
969a08bd9a Turns out I don't need component builders any more. 2021-12-08 22:01:11 +01:00
David Given
3c3e520594 Merge pull request #378 from tdaede/ibm_n88basic
Add ibm_n88basic format.
2021-12-08 19:56:35 +00:00
dg
ed9ffc9091 Headers for OSX. 2021-12-08 19:40:19 +00:00
David Given
5d0750e835 Make the threading stuff actually work. 2021-12-08 20:34:06 +01:00
David Given
3283d6e8c0 Switch to named semaphores, because OSX. 2021-12-08 19:46:47 +01:00
David Given
1b96f60b2a Merge from master. 2021-12-08 19:39:59 +01:00
David Given
a15d78c040 Add some threading primitives. 2021-12-07 22:50:03 +00:00
David Given
40b7f57116 Add a Windows manifest and rc file to make the GUI run there. 2021-12-07 22:49:49 +00:00
Thomas Daede
68314cd44d Add n88basic format.
This isn't really intended to be used directly (bare images are
normally .d88 etc) but is useful for testing the FM encoder.
2021-12-07 14:39:31 -08:00
David Given
17787b97d4 Merge pull request #371 from tdaede/d88hd
Set media density automatically for D88 format.
2021-12-07 21:54:58 +00:00
David Given
da84297b2c Merge pull request #380 from tdaede/last_fluxmap
SCP: Only write the last continuous fluxmap.
2021-12-07 21:47:54 +00:00
David Given
e3e3bb770d Merge pull request #379 from tdaede/no_disk_sector_check
Remove per-track sector check from D88.
2021-12-07 21:45:47 +00:00
David Given
9d4f180741 Merge pull request #381 from hharte/fixnorthstar
Fix North Star after decoder update
2021-12-07 21:44:26 +00:00
dg
91281bb58b Make OSX happy. 2021-12-07 21:02:30 +00:00
David Given
4ddd7d04b5 Overhaul the C++ GUI API so it actually looks reasonable. 2021-12-07 22:00:41 +01:00
David Given
316836d9f6 Merge pull request #382 from darkstar/protofix
Fix a small typo in proto.cc
2021-12-07 17:24:24 +00:00
Michael D
aa3b0a117a Fix a small typo in proto.cc
Also add long-forms "yes" and "no" as valid options
2021-12-07 18:10:53 +01:00
Howard M. Harte
0f7df3281f NorthStar: Fix nsiimagewriter image size in informational message. 2021-12-06 17:57:08 -10:00
Howard M. Harte
4a91a35799 Fix the North Star decoder after the PLL upgrade. 2021-12-06 17:55:54 -10:00
dg
36c2263675 Fix the Micropolis decoder after the PLL upgrade. 2021-12-06 22:20:18 +00:00
David Given
28068d8d31 Merge pull request #375 from davidgiven/fl2
Make the new FL2 format the default.
2021-12-06 22:14:33 +00:00
dg
97cc54a07a Some C++ abstractions for the UI code. 2021-12-06 21:29:07 +00:00
Thomas Daede
0c3c08db36 SCP: Only write the last continuous fluxmap. 2021-12-06 11:47:42 -08:00
Thomas Daede
bb5d62fe69 Remove per-track sector check from D88.
It's no longer needed to be this strict.
2021-12-06 10:13:30 -08:00
David Given
376270dd53 Merge pull request #377 from tdaede/d88trackfill
Set FM gap fill byte in D88.
2021-12-06 16:21:13 +00:00
Thomas Daede
9056b23eaa Set FM gap fill byte in D88. 2021-12-06 08:10:02 -08:00
Thomas Daede
634be4ae51 Fix SCP writer to always write complete revolutions.
There were a couple of problems that needed to be fixed:

- Fluxmaps can be produced with zero index pulses by encoders.
This is somewhat ambiguous but it's assumed that this means there
is exactly one revolution.

- The SCP unindexed mode wasn't properly supported, as it should
create equal-length revolutions not aligned to indexes. However,
fluxmaps don't contain any information on rotation speed. Therefore,
drop the ability to create unindexed SCPs and slightly change the
meaning of the option to not include pre-index data.

- Real flux reads can potentially produce duplicate index pulses.
Avoid creating zero-length revs.

- Fix off-by-one error for rev count.
2021-12-06 07:41:16 -08:00
dtrg
99901f365e Fix package name. 2021-12-06 12:57:54 +00:00
David Given
422620f4fe Merge pull request #374 from davidgiven/ibm
Make the gap fill byte configurable.
2021-12-05 22:27:45 +00:00
dg
dd3cb93ed9 Finally do some drawing. 2021-12-05 21:50:13 +00:00
David Given
dbb422a410 Typo fix. 2021-12-05 21:18:24 +00:00
David Given
68712b8be7 Try adding a drawable to see what happens. 2021-12-05 21:16:43 +00:00
David Given
da7e2b1b54 Add a menu. 2021-12-05 20:41:20 +00:00
David Given
8cff85ad3d Make the GUI prototype build on Windows. 2021-12-05 20:28:11 +00:00
dg
9ffde65bb0 Update dependencies. 2021-12-05 20:03:46 +00:00
dg
5759bc602f Tweak so that GTK builds again. 2021-12-05 20:02:17 +00:00
dg
4bb6fdc79a Make the GUI example build on OSX. 2021-12-05 19:54:19 +00:00
dg
bd019d5d70 Create new branch named "libui" 2021-12-05 18:31:59 +00:00
dg
0457282d47 Smoke test of the GUI, at last on Linux. 2021-12-05 18:26:56 +00:00
dg
06d8609200 Raw import of libui. 2021-12-05 18:00:47 +00:00
David Given
fa975b5511 Merge 194b9b1193 into 5f5f22c82b 2021-12-05 17:51:27 +00:00
dg
194b9b1193 Modify the flux upgrader to convert from sqlite flux files to fl2 flux files. 2021-12-05 17:46:23 +00:00
dg
27fceb3f41 Make the new FL2 flux format the default. 2021-12-05 17:09:02 +00:00
dg
930e0bcd67 Let the tests run on OSX. 2021-12-05 17:04:35 +00:00
dg
d81e0a3ed4 Merge from master. 2021-12-05 16:44:15 +00:00
dg
ebb5c17be4 Make the IBM format gap fill byte configurable. 2021-12-05 16:43:45 +00:00
dg
1e99fe5b04 Merge from master. 2021-12-05 16:37:04 +00:00
David Given
5f5f22c82b Merge pull request #373 from davidgiven/fl2
Add a new much simpler file format.
2021-12-05 16:29:21 +00:00
David Given
709e300960 More typo fixing. 2021-12-05 17:17:03 +01:00
David Given
d2f677d84e Typo fix. 2021-12-05 17:00:28 +01:00
dg
18d90c44dd Rework the flux decoders to add a desync opcode for separating multiple reads
in a flux stream. Finally add a unit test for the flux decoder.
2021-12-05 14:42:57 +00:00
dg
298f77f52e Add the FL2 reader. 2021-12-05 11:33:19 +00:00
David Given
70b2f7efb4 Merge pull request #372 from davidgiven/osx
Add a warning if you use a /dev/tty file on OSX
2021-12-04 21:14:46 +00:00
dg
78a070bd99 Add a simple warning if you're using OSX with a GreaseWeazle and select a
/dev/tty file rather than a /dev/cu one (the /dev/tty files usually don't work).
2021-12-04 20:57:53 +00:00
dg
c88ff973b1 Merge from master. 2021-12-04 19:23:41 +00:00
Thomas Daede
66f82f764d Set media density automatically for D88 format. 2021-12-04 10:31:17 -08:00
dg
d36a18c17a Add the experimental FL2 encoder. 2021-12-04 12:39:36 +00:00
David Given
8efd95346a Merge pull request #369 from tdaede/d88plus
Use per-track encoding specified in D88 files.
2021-12-04 11:44:28 +00:00
Thomas Daede
04b91377c9 Use per-track encoding specified in D88 files.
This supports varying sector sizes, counts, and modulation on
a per-track basis. It also supports writing sectors in the
original order.
2021-12-04 03:36:06 -08:00
David Given
f5d04d989c Merge pull request #366 from davidgiven/victor9k
Add a Victor 9000 encoder.
2021-12-03 23:03:46 +00:00
David Given
06a497f346 As the victor9k profile is single-sided only, rename it. 2021-12-03 23:43:29 +01:00
David Given
b1cf706a8f Update Victor9K documentation. 2021-12-03 23:41:53 +01:00
David Given
91f04e36e9 Bash the Victor9K encoder into producing images that look almost plausible. 2021-12-03 23:39:06 +01:00
David Given
5d76eec0f6 Merge pull request #361 from tdaede/autodim
Automatically configure format for DIM when no format is specified.
2021-12-03 21:38:47 +00:00
Thomas Daede
7822325866 Automatically configure format for DIM when no format is specified. 2021-12-03 13:34:35 -08:00
David Given
521123f139 Merge from master. 2021-12-03 22:34:31 +01:00
David Given
b3f511b025 Merge pull request #364 from davidgiven/scp
Improve SCP handling.
2021-12-03 21:28:43 +00:00
David Given
ff63840beb Run the encodedecode tests with scp as well as flux. 2021-12-03 22:12:33 +01:00
David Given
1fbcf4aa5f More Victor9K encoder. 2021-12-03 22:04:25 +01:00
dtrg
8d6a811204 Correctly write the number of revolutions in SCP files. 2021-12-03 18:13:17 +00:00
dtrg
08fe2a9e27 Merge from master. 2021-12-03 13:59:03 +00:00
David Given
d8465f4374 Merge pull request #357 from tdaede/d88
Add minimal D88 image format reader.
2021-12-03 11:59:28 +00:00
Thomas Daede
8a3cd14723 Add minimal D88 image format reader.
This implements a subset of D88, only supporting the first floppy
in an image. It only supports images with equal sector sizes,
equal numbers of sectors per track, and no bad sectors.
2021-12-03 02:18:29 -08:00
David Given
91e794add1 Merge pull request #358 from davidgiven/imageformats
Refactor to allow image readers to specify the encoder/decoder formats, if desired.
2021-12-02 22:55:23 +00:00
dg
9c98caf21d Remove the prototype IMD automatic encoder code, because it needs a lot more
work.
2021-12-02 22:44:33 +00:00
dg
f97c42017f Instead of preloading the image, just explicitly load it in fe-write and change
the writeDiskCommand interface to take an Image rather than an ImageReader.
2021-12-01 19:58:24 +00:00
dg
3033a2cb95 Add semi-functioning prototype code to set the encoder and decoder
automatically with IMD files. Also make the IMD reader work properly again.
2021-12-01 18:18:48 +00:00
dg
bcf6f48d46 In fe-read, load the image before constructing the encoder, decoder etc so as
to allow the image reader to modify the config if necessary.
2021-12-01 17:55:05 +00:00
David Given
4c97f15a65 Merge pull request #355 from tdaede/lowercase
Make filename endings case insensitive.
2021-12-01 17:23:08 +01:00
David Given
5dc6349818 Merge pull request #356 from tdaede/fix_sizes
Fix reported size of DIM and FDI images.
2021-12-01 17:22:08 +01:00
Thomas Daede
6ab5c4012a Make filename endings case insensitive. 2021-12-01 06:35:15 -08:00
Thomas Daede
1d50e45777 Fix reported size of DIM and FDI images.
The computation for size accidentally included header size.
2021-12-01 06:32:18 -08:00
David Given
3cbf420acd Merge pull request #354 from tdaede/fdi
Add FDI format reader.
2021-12-01 13:00:41 +01:00
Thomas Daede
ea407b2182 Add FDI format reader.
This format is common for NEC PC-98 disk images.
2021-12-01 03:44:18 -08:00
David Given
5228885760 Merge pull request #348 from tdaede/dim
Add DIM format reader.
2021-12-01 12:01:33 +01:00
Thomas Daede
676845aaf3 Add DIM format reader.
This format is common for Japanese PC floppy images.
2021-12-01 00:07:02 -08:00
David Given
61af9b3810 Merge pull request #351 from tdaede/xdf
Add alias for XDF image files.
2021-11-30 12:42:33 +01:00
Thomas Daede
406a433c3f Add alias for XDF image files.
This is a common extension for Sharp X68000 bare disk images.
2021-11-30 00:40:54 -08:00
David Given
2abd413c08 Merge pull request #350 from tdaede/gw_density
Fix inverted density select in Greaseweazle driver.
2021-11-29 13:25:03 +01:00
Thomas Daede
ad96c9bb9f Fix inverted density select in Greaseweazle driver.
Pin 2 should be set high when high density is selected.
2021-11-28 19:31:26 -08:00
David Given
b412d54998 Do the framework and hopefully a lot of the maths of the Victor 9K encoder. 2021-11-27 23:38:57 +01:00
David Given
6247d3c5e6 Don't print lots of digits after the decimal point in the amount of read data. 2021-11-27 23:38:25 +01:00
David Given
b7ee513dfd Set up the config for the Victor 9000 format. 2021-11-27 20:49:36 +01:00
David Given
3795abc19e Add the Victor9k boilerplate. 2021-11-27 19:44:53 +01:00
David Given
1d32a4d984 Merge pull request #346 from davidgiven/victor9k
Rewrite the flux decoder to use a PLL-driven algorithm.
2021-11-26 22:43:58 +01:00
David Given
a20a78cd64 Merge from trunk. 2021-11-26 21:37:25 +01:00
David Given
a2f51b36ec Check in missing file... 2021-11-26 21:36:22 +01:00
David Given
910ccb273a Added the PLL phase adjustment stuff to the decoder. Much better decodes. 2021-11-26 21:27:58 +01:00
David Given
2cbe39e553 Fix bugs to make the IBM decoder work again. 2021-11-26 21:17:58 +01:00
David Given
994c8fc02d Merge pull request #347 from tdaede/jpc
Add Japanese PC 1.2MB format.
2021-11-26 12:56:47 +01:00
Thomas Daede
57dcd6d520 Add Japanese PC 1.2MB format.
This format is used by the PC-98 and Sharp X68000 series.

3.5" disks are spun at 360rpm to match the clock rate of 5.25".
2021-11-26 03:50:44 -08:00
David Given
20ade1de7b Replace the flux decoder with a PLL-based one stolen from samdisk. It almost
works.
2021-11-25 00:00:51 +01:00
David Given
28aff78469 Make victor9k disk images triangular. 2021-11-21 21:41:14 +01:00
David Given
056055bf0b Merge pull request #341 from davidgiven/ibm
Minor fixes to do the the imgwriter and Acorn formats.
2021-11-04 20:17:50 +01:00
David Given
368d329459 Set enable_side_byte for Acorn formats, because the machines don't always write
it.
2021-11-03 14:16:26 +01:00
David Given
2fe1ffeaf1 Add an ignore_track_byte setting. 2021-11-03 14:15:51 +01:00
David Given
e252e8eb1d If no sector_count is specified in a sector_range, autodetect it. 2021-11-03 14:15:25 +01:00
David Given
e160bcc7d5 Merge pull request #333 from ejona86/micropolis-write
Add Micropolis encoder
2021-10-31 10:33:24 +01:00
David Given
41bd035690 Merge pull request #312 from davidgiven/teledisk
Add read support for Teledisk images, plus add a format for the HP 9121 disk drive
2021-10-30 23:58:20 +02:00
David Given
b0003fc7e5 Merge from master. 2021-10-30 22:19:37 +02:00
David Given
8e1be97589 Merge pull request #335 from ejona86/scp-revs-crash
scpfluxsource: Support more than 5 revolutions
2021-10-30 20:39:45 +02:00
Eric Anderson
181f2f38d8 s/ScpTrackStart/ScpTrackHeader/ and use in ScpTrack 2021-10-30 10:29:17 -07:00
David Given
661940114e Merge pull request #338 from davidgiven/atari
Switch the Atari ST formats to use 1-based sector IDs.
2021-10-30 13:30:57 +02:00
David Given
9874d4bec5 Update compiler flags to make newer compilers happy. 2021-10-30 13:22:00 +02:00
David Given
7d5fedf35f Switch the Atari ST formats to use 1-based sector IDs. 2021-10-28 19:40:16 +02:00
Eric Anderson
69ad36a9ae scpfluxsource: Support more than 5 revolutions
This prevents uninitialized memory reads for files with many
revolutions. See #322.
2021-10-08 20:40:23 -07:00
Eric Anderson
a00137d742 Remove unused fe-writemicropolis.cc 2021-10-02 17:37:33 -07:00
Eric Anderson
e002842640 Add Micropolis writing documentation snippet 2021-10-02 17:26:46 -07:00
Eric Anderson
41e9c46cba Port Micropolis encoder to protobuf-based config 2021-10-02 16:24:38 -07:00
David Given
bc2d58bee3 Merge pull request #332 from ejona86/redir-typo
Redirect dd to stdout instead of file '1'
2021-10-02 20:28:00 +00:00
Eric Anderson
c54de27503 Merge branch 'master' into micropolis-write
Let's not act like this actually compiles.
2021-10-02 12:45:56 -07:00
Eric Anderson
d9bfd77fba Redirect dd to stdout instead of file '1'
Looks like a typo, that would have had mostly the intended effect.
2021-10-02 11:41:06 -07:00
David Given
336cc0077c Merge pull request #331 from davidgiven/proto
Assorted minor fixes.
2021-09-25 21:06:40 +00:00
David Given
0be673b210 Clarify documentation in the building doc. 2021-09-25 22:43:19 +02:00
David Given
028e2498fb Update references to --input.flux and --output.flux. 2021-09-25 22:35:02 +02:00
David Given
249f1dea9d Merge from master. 2021-09-25 22:24:14 +02:00
David Given
07c7b22669 Allow setting enum proto values from the command line. 2021-09-19 22:59:47 +02:00
David Given
6623058a18 Merge pull request #321 from wybren1971/patch-3
Update commodore1541.textpb
2021-08-19 13:22:10 +02:00
Wybren van Duinen
68e4d569d0 Update commodore1541.textpb
Changed to default .d64 for use with emulators
2021-08-17 15:00:56 +02:00
David Given
7186b25a69 Merge pull request #317 from davidgiven/tests
Fix a bunch of C++ incompatibilities.
2021-08-04 20:39:06 +02:00
David Given
42cacb18bd Lots of warning fixes. Can we switch to C++ 2A? 2021-08-04 20:29:04 +02:00
David Given
d09c03f4d2 Update fmt::format to version 7.1.3 (the last before module support is needed). 2021-08-04 20:14:53 +02:00
David Given
ad240b150e Merge pull request #316 from davidgiven/sectorids
Switch to using sector IDs rather than zero-index sector numbers.
2021-08-03 22:45:02 +02:00
David Given
1177ef6f8d Allow the LDBS data rate and recording mode to be specified, or (crudely)
guessed if not.
2021-08-03 22:31:33 +02:00
David Given
2ff50c0c56 Fix merge issues. 2021-08-01 20:46:48 +02:00
David Given
4f4ed1307f Merge from master. 2021-08-01 19:42:04 +02:00
David Given
6d5b7cb64e Merge pull request #315 from davidgiven/tests
Overhaul the tests.
2021-08-01 19:37:15 +02:00
David Given
00fc4e3890 Merge from master. 2021-08-01 19:26:39 +02:00
David Given
7344ee4402 Fix binary extension on Windows. 2021-08-01 19:12:16 +02:00
David Given
740eacc7ac real_*_oneof() is too new for GI's proto library. 2021-08-01 15:29:34 +02:00
David Given
8b1bcf21ee Don't run the encodedecode tests on non-Linux platforms. 2021-08-01 15:29:15 +02:00
David Given
b0a5174c0a Wire up the (available) tests. 2021-08-01 14:58:46 +02:00
David Given
b7cca1b95b Add up_to_track in the image reader/writer trackdata proto. Abstract out the
routine which fetches the trackdata for more consistent (and correct) results.
2021-08-01 13:57:35 +02:00
David Given
44c51f1246 Don't crash when given a missing sector. 2021-08-01 13:56:51 +02:00
David Given
516d43d7a8 Correctly parse extra config textpbs as textpbs, not binarypbs. 2021-08-01 13:56:31 +02:00
David Given
86d49d563e Write the correct values to the sector headers! 2021-08-01 13:30:26 +02:00
David Given
a2911a9585 Allow setting oneof message fields to the default value. 2021-07-31 14:24:49 +02:00
David Given
9a12b651f9 Remember to wire up the tids990 encoder. 2021-07-31 00:44:07 +02:00
David Given
9fa631acca Fix the Brother encoder. 2021-07-31 00:42:25 +02:00
David Given
8df7998a83 Don't use ninja -v. 2021-07-31 00:41:46 +02:00
David Given
c81c1926c0 Wire up the encode/decode tests. Hey, look, failures! 2021-07-31 00:38:13 +02:00
David Given
7ab1288424 Pretty sure the Atari formats don't need swap_sides at all. 2021-07-31 00:37:55 +02:00
David Given
3ee88adfa9 Add a simple tool for doing round-trip encode/decode tests. 2021-07-30 00:10:54 +02:00
David Given
29e8c99b4f Hopefully get the imagereader/imagewriter and IBM encoder/decoders using sector
IDs.
2021-07-29 00:02:46 +02:00
David Given
8b115f8156 Convert the IBM encoder/decoder to use lists of sector IDs rather than
sector_skew strings.
2021-07-28 21:51:12 +02:00
David Given
11c546f113 Merge pull request #313 from davidgiven/ibm
swap_sides isn't symmetrical for the encoder and the decoder
2021-07-28 20:47:46 +02:00
David Given
c087539eb7 swap_sides isn't symmetrical for the encoder and the decoder. On the encoder,
it affects which logical side is being written to which physical side. On the
decoder, it modifies the logical side bit in the sector header itself.
2021-07-28 20:33:07 +02:00
David Given
27f7cbb892 Allow .TD0 and well as .td0 extensions. 2021-07-24 00:09:56 +02:00
David Given
11ffb09b66 Attempt to fix build error on OSX. 2021-07-24 00:03:22 +02:00
David Given
114f9f522d Add a completely guessed-at HP 9121 format. Tidy up the Teledisk reader
somewhat.
2021-07-23 23:52:30 +02:00
David Given
91dc51d59a Just skip missing sectors rather than wiping the entire track. 2021-07-23 23:51:59 +02:00
David Given
11b3922b02 Remember to calculate the disk geometry! 2021-07-23 23:22:12 +02:00
David Given
05552cc3e5 Add a basic and largely untested TD0 reader. 2021-07-23 23:21:30 +02:00
David Given
3db7964509 Fix a horrifying Bytes.slice bug which was causing memory corruption. 2021-07-23 23:17:19 +02:00
David Given
2a06adcabb Merge pull request #310 from davidgiven/ibm
The 1581 format doesn't need swap_sides.
2021-07-22 00:20:42 +02:00
David Given
8cec3354ea The 1581 format doesn't need swap_sides. 2021-07-22 00:01:07 +02:00
David Given
f9df728f45 Merge pull request #309 from davidgiven/ibm
Fix swap_sides on the IBM encoder/decoder.
2021-07-21 21:12:36 +02:00
David Given
46eead34c4 Warning fix. 2021-07-21 21:00:57 +02:00
David Given
786b35fee2 Fix swap_sides on the ibm encoder/decoder to work; remember to specify it for
both the encoder and decoder.
2021-07-21 20:57:43 +02:00
David Given
5c6c609991 Merge pull request #308 from davidgiven/protobuf
Properly merge in the default settings.
2021-07-20 21:15:49 +02:00
David Given
0be8fedf26 MergeFromString() is apparently too new for the version of protobuf on GI's
Linux distro.
2021-07-20 20:42:33 +02:00
David Given
1f9aaf1ac0 Properly merge in the default settings. 2021-07-20 20:24:18 +02:00
David Given
3926de3fa1 Merge pull request #306 from davidgiven/verifier
Verify disks when writing them
2021-07-19 22:48:05 +02:00
David Given
9086f18413 Update documentation. 2021-07-19 22:39:14 +02:00
David Given
ad2576bc76 Hopefully, add support for 400kB Mac disks. 2021-07-19 22:39:07 +02:00
David Given
4523407d9d Fix the C64 encoder to work again. 2021-07-19 22:38:49 +02:00
David Given
53162ac934 Ensure that the 1200kB 5.25" IBM format uses high_density: true. 2021-07-19 22:12:50 +02:00
David Given
1dac51d4ba Don't try to verify when writing to files. 2021-07-19 21:52:16 +02:00
David Given
13deef3416 Plumb the verifier stuff through the rest of the encoders; fix it to actually
work and not crash, god I hate C++.
2021-07-19 21:46:48 +02:00
David Given
b887bebb26 First largely untested draft of a working verifier. 2021-07-19 00:13:06 +02:00
David Given
d51160babb Convert all the readables to formats. 2021-07-16 23:41:22 +02:00
David Given
44b9e7a398 Convert all the writables into formats. 2021-07-16 22:46:45 +02:00
David Given
065cd113c1 Rework to support combined format definitions, although with only the Amiga
format enabled.
2021-07-16 22:39:38 +02:00
David Given
a5f618b7e4 Merge pull request #304 from davidgiven/verifier
Massive refactor to simplify the logic.
2021-07-16 19:04:55 +01:00
David Given
a06e4e862d Remove junk files. 2021-07-16 19:56:07 +02:00
David Given
084c858446 Fix seg fault when writing non-existent sectors. 2021-07-16 19:55:55 +02:00
David Given
5d889d4d95 Remove Track, which is now obsolete. 2021-07-16 00:00:11 +02:00
David Given
f589e094b2 Remove RawRecord, which is now obsolete. 2021-07-15 23:55:29 +02:00
David Given
2eff798d74 Remove SectorSet, which is now obsolete. 2021-07-15 23:51:02 +02:00
David Given
239cecff9e Convert all the encoders and imagereaders to the new scheme. 2021-07-15 23:43:07 +02:00
David Given
fa36af454e The encoders and decoders all build now. 2021-07-15 23:13:59 +02:00
David Given
458d0f7a1b Another mahoosive refactor: move away from using protos for the disk output in
favour of pod objects, which allows STL contains. Lots of shared_ptrs
everywhere. Much simpler and more effective code. The reader works with one of
the decoders; still have to do the rest of them and the encoders.
2021-07-15 22:50:29 +02:00
David Given
f1f27ffd33 The protoified reader now builds and runs, although doesn't work properly. 2021-07-11 23:53:50 +02:00
David Given
4a2e09e8eb Merge from master. 2021-07-11 17:38:09 +02:00
David Given
4b3fada646 Archival checkin (WIP). 2021-07-11 17:36:42 +02:00
David Given
690befc98b Merge pull request #302 from davidgiven/cleanup
More refactoring and cleanup, this time of the encoders.
2021-07-11 16:34:56 +01:00
David Given
c6b3c0f9eb Remove stray flags from the Macintosh encoder. 2021-07-11 17:03:34 +02:00
David Given
b423a71b38 Refactor the encoders the same way that's been done for the decoders. 2021-07-11 16:55:18 +02:00
David Given
e226773066 Merge pull request #301 from davidgiven/cleanup
Refactor the decoders
2021-07-11 15:19:14 +01:00
David Given
98918d160a Convert the ZilogMcz decoder. 2021-07-11 16:12:34 +02:00
David Given
969298fb58 Convert the Victor9k decoder. 2021-07-11 16:10:26 +02:00
David Given
bc60f3b45a Convert the Northstar decoder. 2021-07-11 16:08:16 +02:00
David Given
f7a4785d22 Convert the Tids990 decoder. 2021-07-11 16:04:44 +02:00
David Given
1bf41cbfd7 Convert the MX decoder. 2021-07-11 16:02:41 +02:00
David Given
e9d80423ae Convert the Micropolis decoder. 2021-07-11 16:00:46 +02:00
David Given
928ffbd7af Convert the Macintosh decoder. 2021-07-11 15:58:40 +02:00
David Given
25ebad2448 Convert the IBM decoder. 2021-07-11 15:56:20 +02:00
David Given
dd951dc0ed Convert the Fb100 decoder (which I think got lost in the last big refactor). 2021-07-11 15:54:04 +02:00
David Given
aed9e44b6b Convert the Durango F85 decoder. 2021-07-11 15:50:35 +02:00
David Given
1fa2547aff Convert the C64 decoder. 2021-07-11 13:04:07 +02:00
David Given
164ceb845e Convert the Brother decoder. 2021-07-11 13:01:56 +02:00
David Given
ce463686dc Convert the Apple2 decoder. 2021-07-11 12:57:41 +02:00
David Given
92aa28cac2 Convert the AES Lanier decoder. 2021-07-11 12:55:05 +02:00
David Given
f8674230ed Add prototypes for all the decoders because it's easier to do them in one go... 2021-07-11 12:52:34 +02:00
David Given
326969e488 Clean up the Amiga decoder. 2021-07-11 12:34:40 +02:00
David Given
155e8c2a4b Merge pull request #296 from hharte/northstar_mixed_density
Northstar: Handle mixed-density disks
2021-07-09 22:47:55 +01:00
David Given
7f95f6b43f Merge pull request #299 from davidgiven/greaseweazle
Support GreaseWeazle versions above 24.
2021-07-08 10:08:59 +01:00
David Given
b2d7ba1a65 Treat all GreaseWeazle versions from 24 or above the same, as the USB protocol
has been frozen.
2021-07-08 10:50:31 +02:00
David Given
b550bbbd08 Don't link against libudev any more. 2021-07-08 10:49:57 +02:00
Howard M. Harte
cc79977ac0 Northstar: Handle mixed-density disks
North Star DOS provided an upgrade path for disks formatted as
single-density to hold double-density data without reformatting.  In
this case, the four directory blocks will be single-density but other
areas of the disk are double-density.  This cannot be accurately
represented using a .nsi file, so in these cases, we pad the sector to
512-bytes, filling with spaces.
2021-07-06 17:34:53 -07:00
David Given
4cbb75df60 Merge pull request #273 from davidgiven/amiga
Fix the Amiga encoder after the protobuf rewrite.
2021-07-06 23:05:45 +01:00
David Given
7d8926659e Merge pull request #292 from wybren1971/patch-2
Fix distorted picture
2021-07-06 14:34:18 +01:00
David Given
37ca3f74c7 Make the Amiga encoder a unicorn. 2021-07-05 23:16:37 +02:00
David Given
56cbf39d59 Finally make the Amiga encoder work properly. Do some FM/MFM/bits refactoring. 2021-07-05 23:16:03 +02:00
Wybren van Duinen
a783e7f500 fix typo 2021-07-05 17:41:37 +02:00
Wybren van Duinen
2f4ac42684 Fix distorted picture
modify available space (width and height) with subtraction of two times the border (up en down, and left and right)
2021-07-05 16:56:57 +02:00
David Given
ec689e100e Merge from trunk. 2021-07-04 23:04:02 +02:00
David Given
6f0909d992 Merge pull request #289 from davidgiven/c64
Fix writing C64 disks
2021-07-04 11:21:20 +01:00
David Given
0092dec49e Hopefully make writing C64 disks work again by fixing the logical:physical
track mapping.
2021-07-03 12:09:10 +02:00
David Given
3447689c19 Don't crash when trying to encode tracks with no data. 2021-07-03 11:38:45 +02:00
David Given
d4563fd842 Merge pull request #282 from davidgiven/osx
Fix some OSX build inelegancies.
2021-06-19 13:28:44 +02:00
David Given
d2ecec6009 Typo fixes. 2021-06-19 12:54:06 +02:00
David Given
26f26aec50 Try to fix ranlib on Linux and Windows. 2021-06-19 12:35:18 +02:00
David Given
34dfc2767f Fix some OSX build inelegancies. 2021-06-19 12:24:47 +02:00
David Given
bc3d3cabce Merge pull request #276 from davidgiven/greaseweazle
Fix GreaseWeazle support.
2021-06-04 23:53:45 +02:00
David Given
23d80b93ae Update GreaseWeazle documentation for the new version. 2021-06-04 23:42:23 +02:00
David Given
0d59d3d195 Typo fix on the Linux/OSX side of things. 2021-06-04 23:36:25 +02:00
David Given
192427cf80 Finally figure out why Windows wasn't working --- the Win32 ReadFile / ::read
call waits until _all_ bytes are read, rather than returning as soon as any
bytes are read. Fixed.
2021-06-04 22:03:44 +01:00
David Given
98e4094d70 Set up the GreaseWeazle termios properly. 2021-06-02 22:51:57 +02:00
David Given
e88b939866 Allow multiple fallback parameters using =. 2021-06-02 22:51:41 +02:00
David Given
aed5a02ee1 Remove flag references from the Amiga encoder. 2021-05-28 16:58:28 +02:00
David Given
9f1e1bb2b6 Merge pull request #271 from hharte/rpm-retry
Perform retries when calculating drive RPM.
2021-05-27 11:29:51 +02:00
David Given
15e9383d0b Merge pull request #269 from hharte/fix-doc
Add mingw-w64-i686-protobuf to list of packages
2021-05-27 11:21:43 +02:00
David Given
4ec056900c Merge pull request #270 from hharte/track-ruler
Add ruler for track numbers.
2021-05-27 11:21:24 +02:00
David Given
983feb59b0 Merge pull request #272 from hharte/fix-typo
Fix typo.
2021-05-27 11:21:00 +02:00
Howard M. Harte
8b2ce33f83 Fix typo. 2021-05-26 23:12:09 -07:00
Howard M. Harte
44b452b30b Perform retries when calculating drive RPM.
The drive RPM measurement fails about 3% of the time.  Retry up to
five times until it succeeds, and exit with an error if it doesn't.
2021-05-26 23:02:34 -07:00
Howard M. Harte
f60c9981b8 Add mingw-w64-i686-protobuf to list of packages
When building under Windows, the mingw-w64-i686-protobuf support
package is required.
2021-05-26 22:54:20 -07:00
Howard M. Harte
7c3df93580 Add ruler for track numbers. 2021-05-26 22:50:13 -07:00
David Given
601d7dfcf8 Merge pull request #268 from davidgiven/windows
Make the Windows build static again.
2021-05-26 20:37:25 +02:00
David Given
41cb53a550 We neeed update:true to get the version of the protobuf package with the static
library in it.
2021-05-26 20:23:59 +02:00
David Given
87c13ae20c Make the Windows binary static again. 2021-05-26 18:51:33 +01:00
David Given
c30482af66 Merge pull request #266 from davidgiven/hharte-northstar
Merge in hharte's Northstar changes
2021-05-25 20:25:27 +02:00
David Given
5f4f2f10d7 Update FluxEngine components and rebuild firmware. 2021-05-25 19:11:32 +01:00
David Given
ae630a0e18 Merge pull request #267 from hharte/hharte-northstar
Northstar: Fix configurations.
2021-05-25 18:55:29 +02:00
Howard M. Harte
c66931cb12 Northstar: Fix configurations.
Tested read/write with 350K, 175K, and 87K disks.
2021-05-24 20:40:17 -07:00
David Given
fb2dbe25cd Add missing file. 2021-05-24 23:33:40 +02:00
David Given
bf4831be9f Migrate the Northstar code to the new framework. 2021-05-24 23:20:59 +02:00
David Given
0f83082cf0 Merge branch 'northstar' of https://github.com/hharte/fluxengine into hharte-northstar 2021-05-24 21:39:51 +02:00
David Given
fbfde604e7 Merge pull request #264 from davidgiven/salfter-local
Overhaul profiles and merge in salfter's IBM formats
2021-05-24 12:24:21 +02:00
David Given
5e2dfbed73 Add the 9-sector IBM formats. 2021-05-24 12:10:21 +02:00
David Given
571602d3de Go back to only having a single Atari ST read profile. 2021-05-24 12:10:01 +02:00
David Given
85693f2577 Show the profile comment in the help. 2021-05-24 11:15:21 +02:00
David Given
60bc8ad888 Add comment fields to all the configs; fix some documentation. 2021-05-23 23:57:38 +02:00
David Given
9bf309eb5c Add physical mapping options. 2021-05-23 23:57:22 +02:00
David Given
eeddfa87b6 Merge branch 'local' of https://github.com/salfter/fluxengine into salfter-local 2021-05-23 21:18:58 +02:00
David Given
861fe2466c Merge pull request #263 from davidgiven/hpingel-greaseweazle_v024_compat
Update the GreaseWeazle and USB support.
2021-05-23 21:12:03 +02:00
David Given
3a2cce78ca Update incorrect reference to usb.device. 2021-05-23 20:49:34 +02:00
David Given
4e01f1de0d Update GreaseWeazle documentation. 2021-05-23 20:41:10 +02:00
David Given
4f0a5e854c Attempt to rework the USB handling to make the GreaseWeazle work on Windows and
Mac --- we abandon autodetection for anything other that FluxEngines as libusb
is painfully inconsistent with serial devices.
2021-05-23 18:53:25 +01:00
David Given
c669a9c808 Add libudev autodetect for GreaseWeazle devices. 2021-05-23 17:44:01 +02:00
David Given
e4d827256f Add test code for platform detection. 2021-05-23 12:42:49 +02:00
David Given
1c1ad2725e Port the Greaseweazle driver to use the serial port rather than raw libusb. 2021-05-23 12:39:41 +02:00
David Given
d18e54c5a3 Add Greaseweazle support for firmwares 22 and 24. 2021-05-23 12:01:29 +02:00
David Given
7a885e23d3 Merge branch 'greaseweazle_v024_compat' of https://github.com/hpingel/fluxengine into hpingel-greaseweazle_v024_compat 2021-05-23 10:50:35 +02:00
David Given
33dde3a465 Merge pull request #262 from davidgiven/wybren1971-Commodore-64-encoder
Merge in wybren1971's 1541 encoder and D64 reader.
2021-05-22 23:42:52 +02:00
David Given
8d4ac59f03 Try and fix that intermittent dependency problem with the test protos. 2021-05-22 23:22:24 +02:00
David Given
4fbfc492ce Mark the C64 format as a unicorn. 2021-05-22 20:59:54 +02:00
David Given
ac1fb71bc5 Note the limitation when writing 1541 disks. 2021-05-22 20:58:28 +02:00
David Given
01d6b37b83 Try to update the dev build with the current date. 2021-05-22 10:36:23 +02:00
David Given
2284b4dce1 Upgrade to checkout@v2. 2021-05-22 10:21:43 +02:00
David Given
b92f80e6be And try to fix it again. 2021-05-22 00:01:00 +02:00
David Given
bd5596fa30 Fix the autorelease script. 2021-05-21 23:57:39 +02:00
David Given
4614b63c30 Convert wybren1971's D64 reader and 1541 encoder to work with the new
architecture.
2021-05-21 23:34:28 +02:00
David Given
cf41b6cbb2 Merge branch 'Commodore-64-encoder' of https://github.com/wybren1971/fluxengine into wybren1971-Commodore-64-encoder 2021-05-21 23:02:04 +02:00
David Given
87f3e0e291 Merge pull request #253 from davidgiven/protobuf
Rework the configuration system to use protobufs.
2021-05-21 22:36:58 +02:00
David Given
1997abcde6 Warning fix pass. 2021-05-21 22:12:14 +02:00
David Given
8b761298ee Using a tool which wants a profile with no parameters now lists the available
profiles.
2021-05-21 21:39:00 +02:00
David Given
df0356b841 Update documentation. 2021-05-21 21:27:42 +02:00
David Given
dc158ebff4 Add a documentation page for the Eco1. 2021-05-21 21:15:51 +02:00
David Given
f8192b90f4 Add README banner about the command line change. 2021-05-21 20:42:50 +02:00
David Given
5237049e2c Fix fe-inspect to work. 2021-05-21 20:40:52 +02:00
David Given
9d70fb261e Remove some obsolete tools. 2021-05-21 20:40:42 +02:00
David Given
8370b8a743 Adjust documentation formatting. 2021-05-20 23:00:28 +02:00
David Given
25656d81a1 Update documentation for the new version. 2021-05-20 22:41:03 +02:00
David Given
be97791428 Set sane defaults for rpm and seek. Adjust the aeslanier cylinder range. 2021-05-20 22:40:51 +02:00
David Given
18b683d22e Add drive:N as a valid flux source and sink specifier; remove the --drive
option. Also, a bunch of option cleanup.
2021-05-20 22:14:22 +02:00
David Given
34e9742f71 Use .st for the default Atari ST image filenames. 2021-05-20 19:07:15 +02:00
David Given
087eb7777e Bizarrely, we need to install vim to get xxd. 2021-05-19 23:03:16 +01:00
David Given
c469c5e3a1 Set the right msystem? 2021-05-19 22:56:34 +01:00
David Given
485c29bb49 Install maybe the right packages? 2021-05-19 22:53:53 +01:00
David Given
aa0cf150bd Apparently I've been using the wrong compiler all along. 2021-05-19 22:50:29 +01:00
David Given
91c52e91cd Merge. 2021-05-19 23:02:08 +02:00
David Given
a1f4014738 Rework the ibm encoder to allow per-track configurations. Rename everything for
consistency.
2021-05-19 23:01:32 +02:00
David Given
8a136ac4f6 Allow imgimagewriter to write mixed-format disks; add an eco1 disk format for the VDS Eco1. 2021-05-19 22:12:23 +02:00
David Given
ea43b3dc6a Apply Windows compatability hack. 2021-05-18 22:46:57 +01:00
David Given
57fc787819 You can load config files now. 2021-05-18 22:44:41 +02:00
David Given
a0164b8de3 Rip out dataspecs everywhere. 2021-05-18 22:22:06 +02:00
David Given
9df35c1814 Fix a bunch of documentation strings. 2021-05-18 21:10:59 +02:00
David Given
9d2a5fee86 Range ends default to range start. 2021-05-18 21:09:31 +02:00
David Given
a6b2e932fa copy-flux-to works. 2021-05-18 20:13:03 +02:00
David Given
05aaa2634b Don't hard-code references to the global config in the
updateConfigForFilename() methods.
2021-05-18 19:57:23 +02:00
David Given
2c98f5c542 Convert cwftoflux to a proper flux source. 2021-05-18 19:37:43 +02:00
David Given
d246fca9df Convert scptoflux to a proper flux source. 2021-05-18 01:09:55 +02:00
David Given
c79feb405c Switch to resource path type thingies for specifying flux file types, where
appropriate. Much better.
2021-05-18 00:40:16 +02:00
David Given
b1145f8da3 Flux sink type is now detected from file extension. 2021-05-18 00:23:38 +02:00
David Given
3588d681a2 Convert fluxscp to a proper flux sink. 2021-05-17 23:28:30 +02:00
David Given
55d894ae1f Convert fluxtovcd to a proper flux sink. 2021-05-17 22:12:44 +02:00
David Given
0509caf432 Allow the .au writer to write multiple tracks. 2021-05-17 21:53:15 +02:00
David Given
5d1d807e78 Convert fluxtoau to a real flux sink. 2021-05-17 21:42:25 +02:00
David Given
22ba38b2e0 Add rawread. 2021-05-17 01:06:09 +02:00
Howard M. Harte
42f858267c Hard Sectors: Fix index_irq assertion.
The index_irq could trigger one sector too late, in the case where
index_irq was reset just before the sector pulse was received.

Check the duration of the time between pulses before propagating the
hardsec_index_irq_primed to index_irq.

While this change is required for reading and writing North Star
floppies (due to lack of positioning information in the sector header)
it is also helpful for reading Micropolis disks, as currently,
occasionally the first sector will be missed, and will be found on the
next rotation of the disk.  This change is required for reliably writing
Micropolis disks though, when that functionality becomes available.
2021-05-16 15:41:55 -07:00
Howard M. Harte
5b1a3173f8 Add support for North Star hard-sectored floppies.
North Star Floppy disks use 10-sector hard sectored disks with either FM
or MFM encoding.  They may be single- or double-sided.  Each of the 10
sectors contains 256 (FM) or 512 (MFM) bytes of data.  The disk has 35
cylinders, with tracks 0-34 on side 0, and tracks 35-69 on side 1.
Tracks on side 1 are numbered "backwards" in that track 35 corresponds
to cylinder 34, side 1, and track 69 corresponds to cylinder 0, side 1.

The North Star sector format does not include any head positioning
information.  As such, reads from North Star floppies need to be
synchronized with the index pulse, in order to properly identify the
sector being read.  While there is a command line option:
--sync-with-index, that does this, the North Star reader forces this
behavior by default.

MFM sectors have 32 bytes of 00's followed by two sync characters,
specified in the North Star MDS manual as 0xFBFB.

This is true for most disks; however, I found a few disks, including an
original North Star DOS/BASIC v2.2.1 DQ disk, that uses 0xFBnn, where
nn is an incrementing pattern.

While searching for the start of a sector header, seekToPattern()
ignores the sector pulses.  If a sector header cannot be decoded for any
reason, seekToPattern() will advance past one or more sector pulses.
For this reason, the _hardSectorId is calulated after the sector header
is found.

Due to the nature of the track ordering on side 1, an .nsi image reader
and writer are provided for double-sided disks.  The .nsi image format
supports both single- and double-sided disks; however, single-sided .nsi
images are equivalent to .img images.
2021-05-16 15:41:55 -07:00
David Given
97822bd9a8 analysedriveresponse works. 2021-05-16 23:01:03 +02:00
David Given
b926f5a692 Setting boolean config fields works. 2021-05-16 23:00:34 +02:00
David Given
191dfdf13f Make seek work. 2021-05-16 20:13:02 +02:00
David Given
a100f1fe1e Merge. 2021-05-16 18:23:30 +02:00
David Given
471ccf281c Allow setting cylinder and head ranges. 2021-05-16 18:11:40 +02:00
David Given
6c959be188 Remove reference to dead code. 2021-05-16 00:13:17 +01:00
David Given
010887f18f Try and make build on Windows again. 2021-05-16 00:36:32 +02:00
David Given
e4b5e4c502 Make Kryoflux streams again. 2021-05-16 00:25:41 +02:00
David Given
2de3b4f92e Make rawwrite work (which replaces erase and writeflux). 2021-05-15 23:30:58 +02:00
David Given
225a93330d Rename more config stuff. 2021-05-15 22:37:29 +02:00
David Given
41b36649a9 Rename a bunch of config fields for clarity. 2021-05-15 22:23:42 +02:00
David Given
55c26ab1c4 Make rpm work. 2021-05-15 21:48:42 +02:00
David Given
8b1e60a1fd Remove unused dataspecs. 2021-05-15 21:35:38 +02:00
David Given
ed0f38748b Add helper flags to fe-read and fe-write for setting common parameters. 2021-05-15 21:28:02 +02:00
David Given
7d75a720ca Remove dependent FlagGroups, to make sure that we can't use them by mistake. 2021-05-15 19:00:45 +02:00
David Given
5a38db166f Remove reader flags. 2021-05-15 18:56:30 +02:00
David Given
3c9fb79263 The -Wl,--no-as-needed option isn't needed any more and doesn't work on OSX. 2021-05-15 18:30:49 +02:00
David Given
98d5a2dad9 Add missing file. 2021-05-15 18:07:05 +02:00
David Given
4ab8b4984d Rename all InputProto and OutputProtos for clarity. 2021-05-15 18:05:53 +02:00
David Given
f741ad058e Convert the ZilogMcz decoder. 2021-05-15 17:40:20 +02:00
David Given
2512a4fc32 Convert the Victor9k decoder. 2021-05-15 17:29:59 +02:00
David Given
5554d5e608 Convert the TIDS990 encoder and decoder. 2021-05-15 17:24:20 +02:00
David Given
48d5ed2ff9 Convert the MX decoder. 2021-05-15 17:06:28 +02:00
David Given
2632668d0e Convert (hopefully correctly) the IBM writables. 2021-05-15 14:26:06 +02:00
David Given
f46e444aa2 Convert the Micropolis decoder. 2021-05-15 14:10:08 +02:00
David Given
1149ad86a2 Convert the Apple2 decoder. 2021-05-15 14:04:44 +02:00
David Given
e1398d98b0 Remove some stray files. 2021-05-15 13:50:13 +02:00
David Given
8133e2b7aa Convert the Atari ST configuration. 2021-05-15 13:49:31 +02:00
David Given
ebe678b18b Fix IBM sector base default; convert the ampro configuration. 2021-05-15 13:47:34 +02:00
David Given
509217606c Remove sqlite flux sink flags. 2021-05-15 13:42:30 +02:00
David Given
6fb694669c Remove the fluxmapreader flags. 2021-05-15 13:34:26 +02:00
David Given
5a63172a86 Remove the hardware source/sink flags. 2021-05-15 13:06:53 +02:00
David Given
93dcc7e242 Convert the image readers and Mac encoder and decoder. 2021-05-15 12:33:22 +02:00
David Given
243eea33e9 Convert the FB100 decoder. 2021-05-15 12:11:11 +02:00
David Given
38a8367f62 Convert the F85 decoder. 2021-05-15 12:05:31 +02:00
David Given
a02953cccc Fix the last fix. 2021-05-15 11:56:50 +02:00
David Given
f13f96967e Try to fix the dependency issue with protobufs. 2021-05-15 11:09:31 +02:00
David Given
f7c31281e0 Convert the C64 decoder to the new configuration scheme. Also convert all the
imagewriters.
2021-05-15 00:25:32 +02:00
David Given
ac34a43d9b Add the missing Brother 120kB format. 2021-05-15 00:08:49 +02:00
David Given
c8d0950979 Convert the Amiga encoder and decoder to the new system. 2021-05-15 00:06:39 +02:00
David Given
a4ff59eccb Remember to actually hook the aeslanier decoder up. 2021-05-14 23:42:01 +02:00
David Given
c3a50f9442 Add missing file... 2021-05-14 23:39:47 +02:00
David Given
57e81ee72e Convert the AES Lanier decoder to the new configuration system. 2021-05-14 23:39:07 +02:00
David Given
05df0a37b1 Add (pretty basic) config dumping and documentation help. 2021-05-14 23:29:16 +02:00
David Given
25f2c3a8c1 Move the USB flags into the config file. 2021-05-13 23:35:05 +02:00
David Given
c3aa12db78 Add support for filename flags to the flags parser. Allow setting config values
from the command line.
2021-05-13 23:16:52 +02:00
David Given
a3bd7cc644 Programmatically create the readables and writables mappings. 2021-05-13 19:39:45 +02:00
David Given
5a186b6960 Rename all protos to end with 'Proto' to avoid name conflicts. 2021-05-13 18:05:08 +02:00
David Given
639588fa68 Hardware flux sources are configurable. 2021-05-13 17:39:49 +02:00
David Given
f9510c54b2 Split the encoder/decoder configuration away from inputs and outputs. Make the
test pattern creator a flux source.
2021-05-13 17:23:06 +02:00
David Given
3a8ddf8025 The writer now works with the new config system. 2021-05-13 15:55:05 +02:00
a2801ea88c add write support for various 5.25" formats 2021-05-12 17:28:59 -07:00
David Given
c2aae7ee18 fe-read now supports multiple readable formats. 2021-05-13 00:02:54 +02:00
David Given
9d0804eff4 Convert the IBM decoder to use the new config system. 2021-05-12 23:42:38 +02:00
David Given
6ff84b3693 Bash the imagewriter stuff into working with the new config system. 2021-05-12 23:08:17 +02:00
wybren1971
b641e0282b Update disk-c64.md
wrong quotes
2021-05-12 15:32:37 +02:00
wybren1971
37a467cabc updated the Doc 2021-05-12 15:24:35 +02:00
wybren1971
e01a7110ac Added format bytes (disk ID) to header from BAM 2021-05-12 14:55:32 +02:00
wybren1971
5dad5de548 removed a testmessage 2021-05-12 10:14:12 +02:00
wybren1971
295325a20b Added some checks on sector status 2021-05-12 10:11:51 +02:00
David Given
df0a9bac96 More config machinery: the reader now reads (but can't put the resulting image
anywhere).
2021-05-12 00:26:42 +02:00
David Given
cf9cef6f87 Added the machinery for including literal protobufs in the code. 2021-05-11 22:13:10 +02:00
wybren1971
f92814b24b Added option to write d64 images back to disk 2021-05-11 19:54:50 +02:00
David Given
331b59cd1e Merge pull request #256 from davidgiven/writeflux
Fix writeflux to actually work.
2021-05-11 00:04:10 +01:00
David Given
ed6d211aff Fix writeflux to actually work. 2021-05-11 00:22:19 +02:00
David Given
a8f1469d36 Flesh out the proto config stuff some more. 2021-05-10 22:38:04 +02:00
David Given
74cb332706 The dotted path setter should now work properly. 2021-05-08 23:56:45 +02:00
David Given
89165369b1 Import Snowhouse. 2021-05-08 23:01:40 +02:00
David Given
a54e3d33a6 Add a first draft of the dotted config file setter. 2021-05-08 22:58:29 +02:00
David Given
0f17984f41 Merge pull request #227 from hharte/ibm-presets
Add presets for 5.25" IBM PC floppies.
2021-05-08 15:41:45 +01:00
wybren1971
6527ceb913 Added comment 2021-05-08 12:41:11 +02:00
wybren1971
64a57ba837 Change sectorskew IMD starts numbering with 1 2021-05-08 12:39:28 +02:00
wybren1971
74da9330f8 IMD file expect sectornum to start with 1 not 0 2021-05-08 12:23:58 +02:00
David Given
4fa1dd6860 There aren't any static protobuf libraries, so we're going to have to build
dynamic Windows executables now.
2021-05-08 10:56:15 +01:00
Howard M. Harte
e55effc9ca Add presets for 5.25" IBM PC floppies.
For reading:
    --ibm-preset-360 - Read a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-360-96tpi - Read a 5.25" 360kB disk in a 96tpi
        drive.
    --ibm-preset-1200 - Read a 5.25" 1.2MB disk.

For writing:
    --ibm-preset-360 - Write a 5.25" 360kB disk in a 48tpi drive.
    --ibm-preset-1200 - Write a 5.25" 1.2MB disk in a 96tpi drive.

Test:
    Test reading and writing using a Teac FD-55GFR-571 96tpi drive.
    Test reading and writing using an Alps DFC222B05A 48tpi drive.

Fixes davidgiven/fluxengine#189
2021-05-07 23:20:58 -07:00
David Given
924b862f7c Adjust protobuf configuration. 2021-05-07 23:58:41 +02:00
David Given
5f077762d5 Hopefully set the right protoc compiler? 2021-05-07 23:45:43 +02:00
David Given
1b0ec50711 Add lots more protobuf machinery. 2021-05-07 23:38:31 +02:00
David Given
22f320f1c4 Merge from master. 2021-05-07 22:27:45 +02:00
David Given
9949476584 Merge pull request #252 from davidgiven/40track
Add support for possibly writing 40-track disks on an 80-track drive.
2021-05-07 22:21:02 +02:00
David Given
1b5b170557 Add support for possibly writing 40-track disks on an 80-track drive. 2021-05-07 22:01:20 +02:00
David Given
925a3a4bdb Merge pull request #251 from davidgiven/40track
Better 40-track support.
2021-05-07 21:16:19 +02:00
David Given
720fe9f95f Make sure that places which use usbSeek() honour --40-track. 2021-05-07 20:45:46 +02:00
David Given
7c4f8e1443 Added documentation on 40-track disks and drives. 2021-05-07 20:40:22 +02:00
David Given
79d24dff46 Add boilerplate for a protobuf definition. 2021-05-07 20:05:50 +02:00
wybren1971
928435a31d Added error catching to exit gracely on an error 2021-05-07 15:59:03 +02:00
wybren1971
26ac92eaa3 Standardized messages in classes 2021-05-07 13:25:25 +02:00
wybren1971
2974c08b08 Fix bug in printing sectorskew to screen 2021-05-07 13:11:43 +02:00
wybren1971
5a7b0b3050 Warning was commented out. Fixed 2021-05-07 13:03:41 +02:00
wybren1971
cc2d9bbdd1 Bugfix wrong sectorsize used 2021-05-07 12:54:54 +02:00
David Given
e912152784 Common out the high-density flag too. 2021-05-07 00:27:10 +02:00
David Given
d00681f623 Make the new 40-track flag common between source and sink. 2021-05-07 00:21:13 +02:00
wybren1971
52942c3d2a Fixed a bug with sectorskew for 1.44 diskettes 2021-05-06 17:40:57 +02:00
wybren1971
dedabdd8d8 Added possibility to write IMD images 2021-05-06 14:26:21 +02:00
wybren1971
3061499860 Small bugfixes and added a check on the sectorskew 2021-05-06 14:18:40 +02:00
wybren1971
20f18755ed added an example in the comment 2021-05-06 14:17:37 +02:00
wybren1971
333f2aba54 added a bool for FM or MFM coding 2021-05-06 14:17:05 +02:00
wybren1971
1cb673ed80 Added a bool for knowing FM or MFM use 2021-05-06 14:16:32 +02:00
wybren1971
57e0bc784a Fix bug in startsectorID 2021-05-01 09:12:38 +02:00
wybren1971
425afa93da Better Comment reading with string 2021-04-30 21:45:22 +02:00
wybren1971
259b2cebc7 Added optional cylindermap and sector head map 2021-04-30 20:33:39 +02:00
wybren1971
06589826c8 Small Tweak in gap3 value 2021-04-30 19:43:57 +02:00
wybren1971
2245cd982a Leave destination as is 2021-04-30 19:38:02 +02:00
wybren1971
065b50769f Update IMD writing based on geometry in image 2021-04-30 15:26:06 +02:00
wybren1971
d1e99852bc Merge pull request #2 from davidgiven/master
Update fork with master commits
2021-04-21 08:29:54 +02:00
David Given
bf8f6ae687 Add imagereader offset and step modes, and hardware forty-track modes. Not sure
this is the right way to do it.
2021-04-21 00:41:36 +02:00
David Given
4ad6805ea1 Change to house style. 2021-04-19 20:29:55 +02:00
David Given
4f4e3f0b89 Qualify LIF options, to allow multiple LIF formats. 2021-04-19 20:29:48 +02:00
David Given
b51f2c1ec8 Fix C++ standard issue. 2021-04-19 20:27:44 +02:00
David Given
1bec06fd75 Merge branch 'master' of https://github.com/wybren1971/fluxengine into wybren1971-master 2021-04-19 19:28:53 +02:00
David Given
451d2e2d1d Merge pull request #244 from davidgiven/visualiser
Update the visualiser to use AGG.
2021-04-19 16:56:47 +02:00
David Given
9cee12f9f4 Add index mark and sector alignment. 2021-04-19 15:31:24 +02:00
David Given
f5d6011a77 Merge from trunk. 2021-04-19 13:25:27 +02:00
David Given
64b2ff19ea Mark the new releases as not being prereleases. 2021-04-19 13:25:10 +02:00
David Given
9c17a64773 Don't fail the deletion if no assets exist. 2021-04-19 13:15:38 +02:00
David Given
69c877ff66 Adjust wording. 2021-04-19 12:58:21 +02:00
David Given
ac557ffedc Update git checkout action. 2021-04-19 12:24:04 +02:00
David Given
b1e41bc583 I think that the git tag command doesn't like newlines in the message. Fix. 2021-04-19 12:12:24 +02:00
David Given
a144395ec9 Syntax fix. 2021-04-19 11:49:07 +02:00
David Given
0cf9d05489 Try a different tag action. 2021-04-19 11:47:39 +02:00
David Given
0e6c0a41d0 Merge pull request #245 from davidgiven/github
Switch to github CI for Windows rather than Appveyor.
2021-04-19 11:30:46 +02:00
David Given
8a83652d08 Add the github autorelease script. 2021-04-19 11:24:06 +02:00
David Given
91ee72a8d6 Need to use the C++ compiler. 2021-04-19 01:29:06 +02:00
David Given
91b1c8c13c Getting there. Configure ar. 2021-04-19 01:17:20 +02:00
David Given
26effeabe6 More path tweaking. 2021-04-19 01:13:55 +02:00
David Given
611c9740ed Getting there. More path adjustment. 2021-04-19 01:08:26 +02:00
David Given
2a048c3228 Add diagnostics. 2021-04-19 01:04:19 +02:00
David Given
f4fd83d999 Adjust package list again. 2021-04-19 00:55:42 +02:00
David Given
cf68585808 Remove the appveyor script. 2021-04-19 00:52:12 +02:00
David Given
9f9e926cff Try explicitly building with the mingw compiler. 2021-04-19 00:51:49 +02:00
wybren1971
9dc0dd75fd Fix bug in ibm-preset-720 2021-04-18 22:51:10 +02:00
David Given
9f285710f8 Update msys version. 2021-04-18 22:08:35 +02:00
David Given
ee1c448327 Try to set up build correctly. 2021-04-18 22:00:14 +02:00
David Given
e85bf1713e Enable github CI for Windows. 2021-04-18 21:55:55 +02:00
David Given
7341c71277 Cleanup. 2021-04-18 21:42:10 +02:00
David Given
d579863311 Try and make build on Windows. 2021-04-18 21:41:32 +02:00
David Given
c79cfc19aa Update the visualiser documentation. 2021-04-18 21:05:22 +02:00
David Given
997a6be267 Update the visualiser to use the new bitmap library instead of emitting SVG. 2021-04-18 21:01:21 +02:00
David Given
762bb3006d Move the visualiser code into fe-analyselayout. 2021-04-18 13:09:49 +02:00
wybren1971
07f2bd8cab small fix in tracks info 2021-04-18 09:49:06 +02:00
David Given
daf83db9b3 The standalone visualiser functions. 2021-04-18 01:21:52 +02:00
wybren1971
55c6e19af4 add IMD file reader 2021-04-17 19:32:03 +02:00
wybren1971
9cadc94c5a add IMD file reader 2021-04-17 19:12:52 +02:00
wybren1971
cacdf9ef56 add IMD file reader 2021-04-17 19:06:56 +02:00
wybren1971
a3042fc6c0 typo fix 2021-04-17 18:53:11 +02:00
wybren1971
3efd492525 add IMD file reader 2021-04-17 18:52:41 +02:00
wybren1971
55a5cbc356 Merge pull request #1 from davidgiven/master
update fork
2021-04-17 17:49:52 +02:00
wybren1971
2887b024ab add HP-LIF preset 2021-04-17 17:01:49 +02:00
wybren1971
917303edb9 add IMD file reader 2021-04-17 16:55:42 +02:00
wybren1971
c712c15a30 add IMD file reader 2021-04-17 16:55:25 +02:00
wybren1971
0c541db8e0 add IMD file reader 2021-04-17 16:55:07 +02:00
wybren1971
603b1520d7 add IMD file reader 2021-04-17 16:49:41 +02:00
David Given
c7eb8ad5c8 Add CSV reader and skeleton layout analyser. 2021-04-17 00:08:06 +02:00
David Given
0b285aa7f4 Remove the visualiser integration from the reader. 2021-04-16 21:38:26 +02:00
David Given
e8665bd00c Build a cscope cross-reference (if cscope is installed). 2021-04-16 21:38:08 +02:00
David Given
fb4eaa4332 Merge pull request #241 from wybren1971/patch-1
Update fe-writeibm.cc
2021-04-13 01:14:49 +02:00
wybren1971
874a9eae76 Update fe-writeibm.cc 2021-04-12 21:39:40 +02:00
wybren1971
8230520956 Update fe-writeibm.cc 2021-04-12 20:40:23 +02:00
wybren1971
66da9675f1 Update fe-writeibm.cc
Added a HP-LIF preset to write HP-LIF floppy disks for old HP analyzers in need of LIF formatted disks with 76 tracks and sectors of 1024 bytes.
2021-04-12 20:37:19 +02:00
David Given
61ff48c005 Merge pull request #236 from erichelgeson/eric/typo
Fix minor typo
2021-02-20 22:02:46 +01:00
Eric Helgeson
5fc8a1e52a Fix minor typo 2021-02-20 14:42:02 -06:00
David Given
df1ac74069 Merge pull request #233 from davidgiven/ibm
Refactor the IBM readers and writers.
2021-02-16 23:47:50 +01:00
David Given
91f718bf38 Fold the Atari ST writer into write ibm. Update documentation. 2021-02-16 23:24:41 +01:00
David Given
46e987e393 Refactor the IBM readers so they just set flags and readibm.cc is doing all the
work.
2021-02-16 23:07:25 +01:00
David Given
a59b4f7be7 Document writing 1581 disks. 2021-02-16 21:04:40 +01:00
David Given
ca66e3c35c Merge pull request #218 from hpingel/ibm_enc_swapsides_preset_1581
Adding preset for Commodore 1581 (cbm1581) to IBM writer frontend
2021-02-16 21:01:19 +01:00
hpingel
31e2ad8cba Enforce usage of Greaseweazle firmware v0.24 after adapting CMD_READ_FLUX and CMD_WRITE_FLUX 2021-01-21 23:44:13 +01:00
hpingel
320f32895a adding preset cbm1581 to ibm writer frontend 2021-01-18 23:08:33 +01:00
David Given
d4db131d3c Merge pull request #217 from davidgiven/writes
Update sequencer to not lose one tick from every non-zero interval.
2021-01-18 11:22:02 +01:00
David Given
27c2c9045e Update sequencer to not lose one tick from every non-zero interval. 2021-01-18 00:27:00 +01:00
David Given
f97b7c7d62 Merge pull request #215 from davidgiven/ibm
Fix the timing when writing IBM 1440kB disks.
2021-01-17 19:39:30 +01:00
David Given
9eb33d31ac Merge from master. 2021-01-17 18:57:49 +01:00
David Given
6dd84d6fc2 Add swapSides parameter to the Atari ST writer. 2021-01-17 18:53:43 +01:00
David Given
daddd60581 Merge branch 'ibm_enc_swapsides' of https://github.com/hpingel/fluxengine into hpingel-ibm_enc_swapsides 2021-01-17 18:43:31 +01:00
David Given
e832723ee4 Merge pull request #203 from keithclark/atari-st-format
Write Atari ST formats
2021-01-17 18:34:40 +01:00
Keith Clark
35f4a63c0e Fix type in IBM disk documentation 2021-01-17 14:02:37 +00:00
Keith Clark
28478ea4ac Add Atari ST disk documentation 2021-01-17 14:02:37 +00:00
Keith Clark
0f93a68694 Add Atari ST disk reader front-end 2021-01-17 14:02:37 +00:00
Keith Clark
1a2d5d13b3 Add .st to list of supported extensions 2021-01-17 14:02:37 +00:00
Keith Clark
5f2894fc5b Add support for writing Atari ST floppy formats 2021-01-17 14:02:37 +00:00
hpingel
66cb39dce2 added parameter swapSides to IBM encoder 2021-01-17 12:54:14 +01:00
David Given
d44c871c54 Remove stray debug tracing. 2021-01-16 22:26:54 +01:00
David Given
dff0378ba8 Fix timings for 1440kB disks. 2021-01-16 22:26:45 +01:00
David Given
4f7b1b7140 Merge pull request #212 from davidgiven/analysis
Rework the disk response analyser to draw its own graphs
2021-01-15 10:52:44 +01:00
David Given
662514304b Merge from master. 2021-01-14 01:24:19 +01:00
David Given
0913e9e0c0 Allow saving the other image formats supported by libstb. 2021-01-13 20:34:21 +01:00
David Given
d403733627 Update graphs and documentation. 2021-01-13 20:05:13 +01:00
David Given
fae5b439d0 Fix buffer overrun bug. 2021-01-13 20:01:36 +01:00
David Given
bc66de6d85 Can't initialise variable-sized objects. 2021-01-13 10:43:16 +01:00
David Given
57e598156c Add support to analyse driveresponse for drawing its own graphs via AGG. 2021-01-13 01:46:48 +01:00
David Given
b570e44ee4 Merge pull request #211 from davidgiven/patch-1
Add missing zlib dependency.
2021-01-11 11:59:22 +01:00
David Given
b115d0b55b Add missing zlib dependency.
Fixes: #190
2021-01-11 11:57:59 +01:00
David Given
03fc1419de Archival checkin of partial work towards drawing my own graphs. 2021-01-11 00:56:09 +01:00
Eric Anderson
8b71c0d737 Add Micropolis encoder
Has not been tested on a Micropolis machine.
2021-01-10 15:18:00 -08:00
David Given
a833aa0a00 Import STB's image writer library and hook it up to AGG. 2021-01-10 16:38:49 +01:00
David Given
52332b04ac Import the AGG graphics library. 2021-01-10 14:45:32 +01:00
David Given
529488cab0 Typo fix. 2021-01-10 13:55:55 +01:00
David Given
b2429a7ca3 Typo fixes. 2021-01-10 12:34:55 +01:00
David Given
0bce12d3b4 Merge pull request #210 from davidgiven/analysis
Add the drive response documentation page.
2021-01-10 12:26:09 +01:00
David Given
75f557cb18 Add the drive response documentation page. 2021-01-10 12:15:37 +01:00
David Given
035dd1fad1 Merge pull request #209 from davidgiven/analyse
Add an analysis tool for checking drive response.
2021-01-10 02:01:33 +01:00
David Given
d2df79a665 Remember to get rid of the junk comments! 2021-01-10 01:09:35 +01:00
David Given
103e0a13bb Typo fix. 2021-01-10 01:06:13 +01:00
David Given
d1b5eec84a Add the analysis tool and Python script for drawing the results. 2021-01-10 00:51:43 +01:00
David Given
6b1e6b31ed Add initial version of the response analysis tool. 2021-01-09 13:42:37 +01:00
David Given
f6f6db913e Merge pull request #208 from davidgiven/greaseweazel
Add client support for the GreaseWeazle.
2021-01-09 01:12:10 +01:00
David Given
ec0a6416fd Update documentation. 2021-01-09 00:52:16 +01:00
David Given
1787402be9 ...and again. 2021-01-09 00:45:27 +01:00
David Given
5f6d99f138 Attempt that fix again. 2021-01-09 00:37:37 +01:00
David Given
d1e2b0d1f8 Fix an issue with old C++ compilers and designated initialisers. 2021-01-09 00:29:46 +01:00
David Given
c2c51bbe33 Fix after merge. 2021-01-09 00:02:14 +01:00
David Given
bb806e3853 Merge from master. 2021-01-08 23:23:50 +01:00
David Given
a11d0e75c8 Merge pull request #200 from ejona86/hard-sectors
Add firmware support for hard sectors
2021-01-08 23:20:40 +01:00
David Given
5406ff0ea3 Typo fix. 2021-01-08 23:14:51 +01:00
David Given
c88317b44a Document how to make things work on Windows. 2021-01-08 23:13:27 +01:00
David Given
6898062d66 Document Windows horror. 2021-01-08 21:37:44 +01:00
David Given
6e1f264e6a Make --sync-with-index and --revolutions work properly on the GreaseWeazle. 2021-01-08 21:36:50 +01:00
David Given
082be14232 Add GreaseWeazle support for --high-density. 2021-01-08 21:20:33 +01:00
David Given
231aa44d03 The clock compensation factor for Mac doesn't seem to be needed? 2021-01-08 01:14:39 +01:00
David Given
cdb12f35d4 Update documentation to mention the GreaseWeazle. 2021-01-07 23:03:34 +01:00
David Given
e831ee8b44 Add erase support. 2021-01-07 22:46:11 +01:00
David Given
40e9a6082f Remove that 'packed' thing. 2021-01-07 22:17:19 +01:00
David Given
53cec292d0 Refactor the GreaseWeazle converter to allow it to be tested. 2021-01-07 22:06:45 +01:00
David Given
3f85309ee5 Commit non-functioning GreaseWeazle write support. 2021-01-07 20:42:06 +01:00
David Given
70944f8521 Reading flux now correctly handles index markers. 2021-01-07 20:02:51 +01:00
David Given
2ab00c42ff Handle index pulses on read. 2021-01-07 01:05:23 +01:00
David Given
a572742caa I read my first disk using the GreaseWeazel! 2021-01-07 00:58:19 +01:00
David Given
400e5f8580 The bandwidth tester works. 2021-01-06 23:24:28 +01:00
David Given
74f0fd89b6 We can successfully seek on the GreaseWeazle. 2021-01-06 22:52:52 +01:00
David Given
09f9bea7a2 Add boilerplate for the GreaseWeazle driver. 2021-01-06 20:39:13 +01:00
David Given
8bffb38117 Fix bad merge. 2021-01-05 02:16:55 +01:00
David Given
eb5d545c35 Remember to unicornify the Mac formats. 2021-01-05 02:09:47 +01:00
David Given
a79a545730 Merge pull request #205 from davidgiven/writemac
Add Macintosh write support.
2021-01-05 02:08:52 +01:00
David Given
3863dab944 Try to fix an AppVeyor issue. 2021-01-05 01:56:49 +01:00
David Given
e53b7ecd8b Rebuild firmware. 2021-01-05 01:51:29 +01:00
David Given
7d88673ed5 Merge from trunk. 2021-01-05 01:36:54 +01:00
David Given
41f2da71e4 Merge. 2021-01-05 01:36:10 +01:00
David Given
cb4ee0fd74 Comment typo fix. 2021-01-05 01:36:00 +01:00
David Given
088381a5a6 Add a DiskCopy 4.2 image reader. We can now write files to floppy, and they
almost work.
2021-01-05 00:36:06 +01:00
David Given
629af2a697 You can now create Bytes from strings and vice versa. 2021-01-04 23:31:54 +01:00
David Given
884edfd497 Tweak the Mac encoder parameters to work. 2021-01-04 23:06:15 +01:00
David Given
83dd9e462e Fix sequencer bug where intervals of 0 would go horribly wrong. 2021-01-04 22:06:19 +01:00
David Given
70a6dfd98a Warning fix. 2021-01-04 20:16:36 +01:00
David Given
7f5d96382b Update PSoC components. 2021-01-04 20:16:15 +01:00
David Given
fd4d1c4bb7 Writing Mac disks now looks like it's about half working, but some of the
tracks are ending up mangled on disk --- maybe it's a firmware bug?
2021-01-04 19:16:09 +01:00
David Given
7eaf3de572 Mac encoder is code-complete. 2021-01-04 17:48:59 +01:00
David Given
4b608de3fb Merge pull request #186 from davidgiven/8inch
Add some utility features for 8" drives
2021-01-04 01:50:59 +01:00
David Given
b47e6e852b Merge pull request #188 from davidgiven/jv3
Add support for reading TRS-80 JV3 images.
2021-01-04 01:50:38 +01:00
David Given
a8a8ce4a36 Do the basic skeleton of the writer. 2021-01-04 01:49:15 +01:00
David Given
c61376d5a1 Add skeleton of Mac encoder. 2021-01-04 00:50:12 +01:00
Eric Anderson
d3a5bb08d3 Set drive before checking rotation speed for writing 2020-12-24 23:11:34 -08:00
Eric Anderson
f1506d0dbd Add firmware support for hard sectors 2020-12-24 17:39:52 -08:00
David Given
15e6d4959e Adjust Micropolis documentation. 2020-10-11 18:49:00 +02:00
David Given
41216fd1cd Merge pull request #191 from ejona86/micropolis
Add Micropolis decoder
2020-10-11 18:52:31 +02:00
Eric Anderson
b8786866db Fix carry handling in Micropolis checksum
Two bugs make a right, mostly. The very last carry should be ignored and
when adding the low and high bytes they commonly would commonly produce
a carry which was being ignored. On the test disks these two bugs
cancelled themselves out, except only for track 1, side 1, sector 0. I
had noticed it was suspicously off-by-one earlier for both test disks,
but the cause wasn't obvious.

On some old 1980s disks, with real data, it changed number of bad
sectors from 224 (out of 2464) to 5 for one disk and 81 to 3 for
another.
2020-10-04 19:55:25 -05:00
Eric Anderson
82bd1bead4 Add Micropolis decoder
Resolves #187
2020-10-03 13:05:06 -05:00
David Given
6e2bdcad79 Document the JV3 format. 2020-09-18 00:56:02 +02:00
David Given
ef3c9f3d03 Add the fe-image command and the TRS-80 JV3 file reader. 2020-09-18 00:53:39 +02:00
David Given
5427f24df2 Merge from master. 2020-09-13 19:35:47 +02:00
David Given
b374340303 Try multiplexing SIDE1 onto DIR. 2020-09-10 22:07:47 +02:00
David Given
c78ed2c6ad Add the TK43 pin, which goes low when seeking to track 43 or above. 2020-09-10 21:48:30 +02:00
David Given
3b02bc8cf1 Merge pull request #183 from davidgiven/ds990
Add support for TI DS990 disks.
2020-09-10 21:19:10 +02:00
David Given
c7e48a7e76 Unicorn the DS990 format. 2020-09-10 20:10:54 +02:00
David Given
77d125c03d Typo fix. 2020-08-22 12:38:10 +02:00
David Given
8aa52aeefd Add documentation for the DS990. 2020-08-22 12:37:11 +02:00
David Given
0bab038454 Make the DS990 encoder work, maybe? 2020-08-22 00:36:48 +02:00
David Given
6c3b49f4d0 Add the DS990 encoder skeleton. 2020-08-21 23:07:35 +02:00
David Given
03dd689f17 Make the DS990 decoder work. 2020-08-21 00:32:36 +02:00
David Given
c375c948c0 Add boilerplate for the TI DS990 decoder. 2020-08-20 22:41:14 +02:00
David Given
cbcf457ce3 Merge pull request #182 from davidgiven/usb
Add support for multiple simultaneously connected FluxEngines.
2020-08-10 23:32:16 +02:00
David Given
4855f825e2 Show serial number on device connection, and improve the device listing a bit. 2020-08-10 23:16:04 +02:00
David Given
85bc1637f2 Document the use of multiple FluxEngines. 2020-08-10 23:12:33 +02:00
David Given
73398b83a9 Add support for specifying which FluxEngine you want to use with the --devices
parameter.
2020-08-10 22:36:47 +02:00
David Given
2727e66d40 Allow multiple USB implementations. 2020-08-09 22:33:54 +02:00
David Given
8b6be5a501 Move usb.{cc,h} into its own directory. 2020-08-09 21:14:09 +02:00
David Given
4fee29307c Refactor the USB stuff to allow for multiple USB implementations. 2020-08-08 14:52:11 +01:00
David Given
35f8249c67 Merge pull request #178 from davidgiven/mac
Add support for exporting to DiskCopy 4.2 Mac disk image
2020-08-02 17:42:48 +01:00
David Given
d1467a14b8 Merge. 2020-08-02 18:24:15 +02:00
David Given
3e6b9eb74d Document the .diskcopy file format. 2020-08-02 18:22:30 +02:00
David Given
ce2e8fb4b5 First draft of the DiskCopy 4.2 image writer. 2020-07-30 20:21:49 +01:00
David Given
7eaa75c05d Merge pull request #177 from davidgiven/mac
Make Mac double-sided disks work.
2020-07-29 00:38:28 +02:00
David Given
e86de4483a Fix stupid bug where the mac decoder was looking at the wrong bit for the side
data.
2020-07-28 01:25:50 +02:00
David Given
203a74713f Merge pull request #175 from davidgiven/scp
Make SCP import and export properly handle single-sided images.
2020-06-30 11:19:48 +02:00
David Given
59ed2a6793 Make SCP import and export properly handle single-sided images. 2020-06-26 20:25:56 +02:00
David Given
a03283ce64 Merge pull request #174 from davidgiven/scp
Fix the SCP exporter.
2020-06-26 15:40:41 +02:00
David Given
984cdaeb03 Make nanoseconds_t a double to prevent overflow on very large numbers of
revolutions (I've just seen a flux file with 50).
2020-06-26 14:47:25 +02:00
David Given
a1ed4a9171 Fill out the SCP checksum correctly, and add a --indexed mode which causes data
prior to the first index mark to be discarded and sets the INDEX bit in the
header.
2020-06-26 12:20:49 +02:00
David Given
93caf8e549 Merge pull request #172 from davidgiven/b169
Fix a crash when decoding MX disks if advanceToNextRecord finds no records in a track.
2020-06-25 22:15:00 +02:00
David Given
3841942153 Fix a crash when decoding MX disks if advanceToNextRecord finds no records in a
track.
2020-06-25 21:56:33 +02:00
David Given
5706877b67 Merge pull request #171 from davidgiven/newsampler
Manually merge in jboone's new sampler from #164
2020-06-25 21:16:11 +02:00
David Given
d60900262b Remove the pulse converters from the sampler (the new sampler doesn't require
them). Update firmware.
2020-06-25 21:07:58 +02:00
David Given
54ea34400b Merge in jboone's updated sampler branch. 2020-06-25 21:01:35 +02:00
Jared Boone
db2ab8841a Update Sampler.v, moving clock domain crossing to FIFO interface.
Hopefully, I unscrewed the tab/space and line ending mismatches to minimize the diff.
2020-05-30 21:31:17 -07:00
David Given
adba93ae0a Merge pull request #163 from davidgiven/brother120
Untested Brother 120kB write support.
2020-05-24 01:32:28 +02:00
David Given
98587d04a7 Merge from trunk. 2020-05-24 00:56:17 +02:00
David Given
0051b64648 Merge pull request #167 from vanbogaertetienne/trs80fix
Typo fix in FM_TRS80DAM2_PATTERN
2020-05-24 00:47:12 +02:00
vanbogaertetienne
603009ba15 Typo fix in FM_TRS80DAM2_PATTERN 2020-05-22 20:46:48 +02:00
Jared Boone
adb9809692 Simplify Sampler. 2020-05-20 11:34:29 -07:00
David Given
06eb10d2a0 Merge. 2020-05-16 10:38:17 +02:00
David Given
2244299bd9 Add a bias parameter to allow the entire Brother format to be moved around on
the disk.
2020-05-16 10:37:48 +02:00
David Given
6ca06ecafb Fix several embarassingly stupid bugs in the brother 120kB encoder code path. 2020-05-14 10:41:25 +02:00
David Given
9a5958f80b Prevent OOB when appending no bytes to a fluxmap. 2020-05-14 10:40:49 +02:00
David Given
2b53ac057c Fix some bugs which allow erasing tracks with F_FRAME_WRITE_CMD to work again.
(F_FRAME_ERASE_CMD always worked.)
2020-05-13 23:45:58 +02:00
David Given
5deba8af41 Untested Brother 120kB write support. 2020-05-13 14:49:06 +02:00
David Given
3c54a663b8 Merge pull request #157 from davidgiven/amigacorruption
Fix some issues causing corruption when reading Amiga disks
2020-04-09 00:16:38 +02:00
David Given
1fd65452c4 Typo fix. 2020-04-08 23:37:08 +02:00
David Given
30646ccb07 Fix an Amiga decoder bug where truncated sectors would be considered valid (the
Amiga checksum algorithm is weak and zero bytes don't contribute to the
checksum).
2020-04-08 23:35:55 +02:00
David Given
5be7249a30 Merge pull request #155 from davidgiven/amigawriter
Fix stray bytes at the end of images
2020-04-07 23:13:23 +02:00
David Given
067af18103 When writing images, use the sector size in the spec rather than the actual
data size, to avoid problems with multipart formats like the Amiga.
2020-04-07 23:02:47 +02:00
David Given
8dbd2a72a7 Merge pull request #150 from davidgiven/sixbit
Fix the new sampler and sequencer
2020-04-03 23:54:29 +02:00
David Given
c29e131a3b Convert the IBM format back into a unicorn now I've fixed it. 2020-04-03 23:49:01 +02:00
David Given
a9e30c1e49 Fix an off-by-one error in the sequencer that should have it generating correct
sequences.
2020-04-03 22:58:51 +02:00
David Given
972c8c6b61 Fix off-by-one sampler error, so now the clock rates are right again. 2020-04-03 22:27:33 +02:00
David Given
2007ff7546 Fix merge wibble. 2020-04-03 21:49:26 +02:00
David Given
64694580cd Remember to bump the protocol number after the bytecode change. 2020-04-03 21:46:51 +02:00
David Given
deaab94494 Merge pull request #146 from davidgiven/sixbit
Switch to a simplified encoding with a six-bit timer.
2020-04-03 00:45:21 +02:00
David Given
1509e1f89d Document the new bytecode format. 2020-04-03 00:38:20 +02:00
David Given
29e1ddc2ff Bytecode upgrades always produce the latest version; we don't want to upgrade
1->2->3 (although that specific case is harmless, by accident).
2020-04-03 00:36:55 +02:00
David Given
1fe6434563 Something is wrong with the IBM PC writer, I don't know what. Mark it as a
dinosaur again.
2020-04-03 00:27:12 +02:00
David Given
0367b7e77d Merge from trunk. 2020-04-01 00:06:35 +02:00
David Given
e6da85bf64 Merge pull request #145 from davidgiven/writereport
Add a machine readable read report.
2020-04-01 00:04:54 +02:00
David Given
cd19fcdadd The CSV report now writes records for every sector in the output map, even the
missing ones.
2020-03-31 00:14:23 +02:00
David Given
1954f02cfb Don't reset the device on startup because it confuses Linux. 2020-03-30 22:23:18 +01:00
David Given
39b23200b0 Fix missing flag dependency. 2020-03-29 23:12:12 +02:00
David Given
0644d6d965 Remove some stray tracing (which was causing problems). Fix a potential problem
where sampleclock posedges could be lost in the sequencer.
2020-03-29 23:11:53 +02:00
David Given
a075694d8e Rewrite the sequencer to work with the new six bit bytecode. Fiddle with the
USB stuff a lot in an attempt to resolve the weird packet loss issue.
2020-03-29 15:10:35 +02:00
David Given
b1ea5a9a35 Rework the writer to use a fluxsink rather than just writing stuff directly. 2020-03-29 15:08:45 +02:00
David Given
00087cbb6b Fix a catastrophic DMA setup bug which was causing (probably) every other byte
of data to be mixed up with every other byte... but as every other byte was a
0x80 we never noticed up until now.
2020-03-20 00:06:58 +01:00
David Given
1b48ea20c4 Remove the cruncher. 2020-03-20 00:06:07 +01:00
David Given
3d0f019fc4 Replace the sampler with one using the new simplified bytecode. 2020-03-19 23:39:23 +01:00
David Given
a08bfc183f Display the tick value along with each interval in the histogram. 2020-03-19 22:16:07 +01:00
David Given
c5aef9b051 Annotate inspect to display USB bandwidth. 2020-03-15 13:47:17 +00:00
David Given
fc2655ecd6 Rework the bytecode format to use a much simplified setup: a six-bit timer with
the top two bits reserved for pulse and index state. This is actually smaller,
bandwidth-wise, than the old version, and may be smaller than the crunched
version.
2020-03-14 18:58:43 +00:00
David Given
a737c723d3 Make sure to update before installing packages. 2020-03-14 14:47:04 +00:00
David Given
37aa8b62b0 Add a --write-csv=X option to the reader to dump the sector status map as a
machine-readable file.
2020-03-14 14:35:19 +00:00
David Given
a401173f6d Teach the Amiga decoder how many sectors to expect on each track. 2020-03-09 12:54:29 +00:00
David Given
ce76dc4279 Merge pull request #140 from davidgiven/sectors
Fix a couple of annoying but minor issues
2020-02-28 00:08:11 +01:00
David Given
1025bd857b Don't crashloop if the USB's not connected, as it causes the drives to be
constantly reprobed (which runs the motor).
2020-02-27 22:32:27 +01:00
David Given
025802b2d0 Count required sectors correctly on mac disks. 2020-02-27 22:30:05 +01:00
951 changed files with 163647 additions and 12442 deletions

View File

@@ -1,41 +0,0 @@
version: '{branch}.{build}'
clone_depth: 1
skip_tags: true
environment:
MSYSTEM: MINGW32
init:
- git config --global core.autocrlf input
install:
- set PATH=c:\msys64\mingw32\bin;c:\msys64\usr\bin;c:\msys64\bin;%PATH%
- echo %PATH%
- pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
build_script:
- make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
artifacts:
- path: fluxengine.zip
name: fluxengine.zip
deploy:
release: FluxEngine Windows client version $(APPVEYOR_BUILD_NUMBER)
description: >
This is an automatically built version of the FluxEngine Windows client
which is generated whenever a significant checkin has happened. It's had
no testing whatsoever.
To use, download it, put it somewhere, and then run it from a cmd window
or other command line shell.
provider: GitHub
auth_token:
secure: dfJjj7fWCoDUz+Ni11OcNPB/U3TNJFwNA2AsL++ChFjniUsZLlC6SDWHiL/t4FZo
artifact: fluxengine.zip
draft: false
prerelease: false
on:
branch: master

View File

@@ -10,32 +10,46 @@ jobs:
with:
fetch-depth: 1
- name: apt
run: sudo apt install libusb-1.0-0-dev libsqlite3-dev ninja-build
run: sudo apt update && sudo apt install libudev-dev libsqlite3-dev ninja-build protobuf-compiler libgtk-3-dev
- name: make
run: make
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: brew
run: brew install sqlite pkg-config libusb ninja
run: brew install sqlite pkg-config libusb ninja protobuf truncate
- name: make
run: make
# build-windows:
# runs-on: windows-latest
# steps:
# - uses: numworks/setup-msys2@v1
# with:
# path-type: inherit
# - uses: actions/checkout@v1
# - name: pacman
# run: |
# msys2do pacman -S --noconfirm --needed make ninja mingw-w64-i686-libusb mingw-w64-i686-sqlite3 mingw-w64-i686-zlib mingw-w64-i686-gcc zip
# - name: build
# run: |
# msys2do make
build-windows:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
make
ninja
mingw-w64-i686-libusb
mingw-w64-i686-sqlite3
mingw-w64-i686-zlib
mingw-w64-i686-gcc
zip
mingw-w64-i686-protobuf
vim
diffutils
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: build
run: |
make

64
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Autorelease
on:
push:
branches:
- "master"
jobs:
dev-release:
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: MINGW32
install: >-
make
ninja
mingw-w64-i686-libusb
mingw-w64-i686-sqlite3
mingw-w64-i686-zlib
mingw-w64-i686-gcc
zip
mingw-w64-i686-protobuf
vim
diffutils
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build
run: |
make
- name: zip
run: |
zip -9 fluxengine.zip fluxengine.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
- name: date
run: |
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> ${GITHUB_ENV}
- name: tag
uses: EndBug/latest-tag@latest
with:
tag-name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: delete-old-assets
uses: mknejp/delete-release-assets@v1
with:
token: ${{ github.token }}
tag: dev
assets: |
fluxengine.zip
fail-if-no-assets: false
- name: release
uses: softprops/action-gh-release@v1
with:
name: Development build ${{ env.RELEASE_DATE }}
files: |
fluxengine.zip
tag_name: dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,254 +1,254 @@
:4000000000800020110000006112000061120000064A08B5136843F020031360044B1A6803F53F5302331A6001F038F9E8460040FA46004010B5054C237833B9044B13B10E
:400040000448AFF300800123237010BD6881FF1F00000000E03A0000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000027
:400080006C81FF1FE03A0000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1FA2
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080024B012200205A7293
:4001000002F072BA8881FF1F10B5C4B2204601F087FA0128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A6178
:400140005B69002B0CBF02230023002814BF184643F0010002F0B0FF62B608BD8881FF1F38B50446C5B2284602F0E0F9062002F0FDFB44F00200C0B202F0D8F9062002F0D1
:40018000F5FB284602F0D2F9BDE83840062002F0D7BB10B5642402F0C3F930B90120FFF7DFFF013CF7D1204610BD012010BD70B5C4B220460E4601F03BFA314605460246D9
:4001C000204601F0F7FA204601F02AFA0128FAD0284670BD38B5044D0024285D013402F075FB402CF9D138BDA081FF1F08B502F08FFD002002F098FD02F0AAFD02F0B4FDA8
:4002000080B208BD10B50446012002F0A7F9642002F096FBFFF7EAFF2080002002F09EF9642002F08DFBFFF7E1FF608010BD08B502F09AFE002002F0A3FE02F0B5FE02F0C4
:40024000BFFE80B208BD10B50446FFF7A2FF322002F076FBFFF7EBFF20800020FFF780FF322002F06DFBFFF7E2FF608010BD0FB400B593B014AB53F8042B402102A8019397
:4002800003F016F802A802F0B6F902F0C0F913B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF72FFF62782146BDE81040042001F00CBAF43A000007B50023BF
:4002C000ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8583043B1002002F014F9002002F0A7FE002384F8583010BD00BF8881FF1F63
:4003000038B5104D837895F85B2004469A4204D0FFF7E4FF002385F85E302368C5F859302279094B1A71A378002B14BF0220012002F086FEE07802F07DFE2079BDE83840D7
:4003400002F0B4BE8881FF1FE181FF1F38B50D4C94F8585065B904F15900FFF7D1FF012002F0D8F84FF47A7002F0EAFA84F85E50E3682366012384F85830BDE8384002F0AB
:400380001DBB00BF8881FF1FF8B51E4C0646FFF7DDFF94F85E3003B15EB91B48FFF767FFFFF7F7FE0120002384F85E00636602F0DDFA3246616E1548FFF759FF114D002794
:4003C000636E9E4216D002F0ABF800B16F66636E9E4205DD0020FFF7C3FE6B6E013305E005DA0120FFF7BCFE6B6E013B6B6602F0E5FAE5E7322002F0A3FABDE8F8400448B2
:40040000FFF735BF8881FF1F013B0000083B0000253B00002DE9F04F9BB062B602F038FB9B49042002F05CFB9A4802F085F89A4802F028FE994802F0B9F802F009FD02F0E5
:40044000DBFB002002F0FCFD02F0D4F80221002001F09CF803210846914C02F08FFA002384F85B30FFF772FFFFF793FE84F86800FFF734FF012384F85B30FFF767FFFFF7A9
:4004800088FE84F86900FFF729FF94F86800854B854994F869202546002A14BF0A461A46002808BF19468148FFF7E1FEA24602F085FA94F8583043B12A6EEB689B1A41F2D4
:4004C0008832934201D9FFF709FF01F097F818B97748FFF7CCFE04E001F096F80028F7D109E001F08BF80028FBD07248FFF7BFFE032001F0B3F9032001F092F80128D6D187
:400500006D48FFF7FDFE6D490320FFF750FE94F86A106B48FFF7ABFE94F86A30023B142B00F27684DFE813F0150074041E00740424007404480074046B007404D800740496
:40054000FE017404AC037404CC037404D3037404ED0303238DF828308DF829300C238DF82A30CAE394F86C00FFF70EFF554BC1E3FFF7ECFE00236372E068627A02F0FF018F
:4005800032B9EB681B1AB3F57A7FF6DD0B460AE04BB100228AF80920DAF80C10627A12B9EB685B1AFAE707228DF8282004228DF82920ADF82A30A0E30220FFF7A5FD0027C5
:4005C0000DF1280902F0FAF94FF480780026C8EB07039A1906F809200136402EF9D10220FFF792FD32464946022001F071F8B8F10108EBD10137402FE4D1334B42E04FF0AD
:400600000109002702F0DAF94FF00008012001F025F9012001F004F80128FAD10DF1280B4022594601F0C6F8012000F0F9FF0028FAD1064608EB07030593059B1BF80620B4
:400640003344DBB2934209D08DE80C003946334642461E48FFF70BFE4FF000090136402EEBD108F10108B8F5807FCFD10137402FC8D149461648FFF7FAFDB9F1000F00F0A8
:400680005981144B1B8809A8ADF8243036E300BF19010000F900000091000000C50000008881FF1F373B0000333B00003A3B0000523B0000653B0000E181FF1FF281FF1FB9
:4006C0006F3B0000E43A0000E63A00007E3B00009A3B0000E83A000094F86C0001F03EFF606EFFF751FE02F04FFD934B934F1A78002602F0FB021A701A7842F001021A70A9
:400700001A7802F0FE021A701A7802F0FE021A7002F03EFD0220FFF7F7FC41F6FF734FF480420121022002F091FC84F8AA0002F0B3F8B8550136402EF9D1DFF82082002616
:4007400008F1990B1FFA8BF70136402E14BF3246002218F8010F22440623127E402102F0CFF83A4646F2434198F8000002F0DAF84037402EBFB2E7D19AF86D3043B10023F2
:400780008AF80930637A002BFCD000238AF80930182200210AA802F057FD694B4FF0FF320C9340230D930023236062602372236894F8AA002344197E02F026F894F8AA00B1
:4007C00001F0E4FF012194F8AA0001F0B7FF2368002BFCD000239946CAF80430DAF80C200127059202F0EAF8059AE3689B1AB4F86E2093420ED36FB1042195F8AA0002F01E
:400800000FF894F8AA0002F01BF80028F9D107468AF80800237A03F0FF08002B48D16A682B689A4202D1002FDCD141E063680AA80BEB831343440A93C8F140030B9300F00A
:4008400001FB0B9B09F10109C3F1400398440D9B5FFA88F8E3B93B4E0220FFF755FCA6F1400EBEE80F000FC6BEE80F000FC6BEE80F000FC69EE80F0086E80F00A6F1300193
:400880004022022000F024FF703E40230C960D93B8F13F0FCAD962682B4B01321340002BBEBF03F1FF3363F03F030133636099E70AA8267A00F01CFB0220FFF725FC0D9BDA
:4008C000F6B2402B07D0022040221E4900F000FF0220FFF719FC0D9B022033F040021DBFC3F1400292B21749114600F0F1FE0220FFF70AFCFFF76EFC36B11448FFF7B7FCC2
:400900000220FFF7DBFC06E0114B09A81B88ADF82430FFF7C1FC627A4946237A0D48FFF7A6FC78E20C48FFF7A2FCD4F86E6016F03F0615D003206CE293640040A081FF1F9E
:400940003892FF1F7892FF1F3F000080A43B0000EA3A0000BE3B0000D13B00009F81FF1F012001F001FE95F86C0001F0F7FD02F00BFCB94BB94F1A7842F004021A701A7888
:4009800042F001021A701A7802F0FE021A701A7802F0FE021A7002F0FBFB686EFFF7F4FC01214FF4804341F6FF72084601F0E2FD85F8AA0001F070FFB8550136402EF9D1C7
:4009C000DFF8B882002708F199039EB207930137402F14BF3A46002218F8010F22440523127E402101F08CFF314646F2475298F8000001F097FF4036402FB6B2E7D1DAF8D3
:400A00006E3000269B09182231464FF0FF3B0AA8CAF800600593CAF804B0B1468AF80860B04602F011FC0D973746012000F016FF069601F0C3FFB8F1000F0AD14EB901201B
:400A400000F0EEFD012804D14022854900F0B2FE06462268834B01321340002BBCBF03F1FF3363F03F036168B8BF01338B4200F09080069B3BB1237A002B40F09B806B7A7C
:400A8000002B40F097800B9BBBBBB8F1000F09D0754A7F2199540133402BFBD1724A0B930A922AE04EB3012000F0BAFD28BBDFF8B8E13F2E0EF1400CBCE80F00AEE80F0034
:400AC000BCE80F00AEE80F00BCE80F00AEE80F009CE80F008EE80F00AEF130030A9307F101070B9607DD059BBB4204D0012000F0B5FE464601E04FF001080B9BDBB1236893
:400B0000079A0AA802EB83120D9BC3F1400313440C9300F0FCF90D9B6BB92A68514B01321340002BBEBF03F1FF3363F03F030133236040230D93636801332AD12B683F2BD2
:400B400027D14FF0000BC5F804B001F041FD85F808B06B6895F8AA002B44197E01F054FE95F8AA0001F012FE012195F8AA0001F0E5FD85F809B0637A002BFCD04FF0000B53
:400B800001208AF809B001F02FFD584601F0ECFC01E0069B2BB1237A63B96B7A53B90123069363685B453FF444AF09F10109D5F804B03EE761682D482268FFF758FB01F043
:400BC0000DFD012001F0D0FC002001F00DFD042194F8AA0001F024FE94F8AA0001F030FE0028F9D19AF8AA0001F0BEFD9AF809309AF808200293012303920193CDF80080A9
:400C00004B463A4605991A48FFF731FBB8F1000F16D1059BBB420AD0012000F001FD01280646F6D10E49FFF7C2FA3F2803DC012000F028FE04E0304600F010FE0137E8E73B
:400C4000FFF7C8FA0B48FFF712FB237A0BB10220DFE0094B16E500BF97650040A081FF1F7892FF1F3F0000803892FF1FDB3B0000E63B0000163C0000EC3A00009F81FF1FE7
:400C800094F86C0001F06AFC606EFFF77DFB6648FFF7EDFA00236372637A002BFCD0012001F0A2FC00238AF80930637A002BFCD0002001F099FC5D48FFF7D9FA5C4B19E03C
:400CC000002084F85E00FFF75FFB5A4B12E094F8683023B195F869200AB985F86C2094F869201AB113B9012385F86C305248FFF707FB524B1B88ADF828300AA8FFF7CCFA13
:400D000089E0FFF7EBFA02F021F9002002F0C4F82A2601F0EFFF002001F092FF324600210AA802F091FA17238DF828308DF8296001F044FE002001F0EDFB002002F080F920
:400D4000C82001F0FDFD0DEB0600FFF75BFA0DF13E00FFF778FA01F031FE012002F070F9322001F0EDFD0DF12E00FFF74BFA0DF14200FFF768FA012001F0CCFB4FF49670C9
:400D800001F0DEFD01F01AFE0DF13600FFF73AFA0DF14A00FFF757FA002001F0BBFB4FF4967001F0CDFD01F009FE022002F048F9322001F0C5FD0DF13200FFF723FA0DF113
:400DC0004600FFF740FA012001F0A4FB4FF4967001F0B6FD01F0F2FD0DF13A00FFF712FA0DF14E00FFF72FFA002001F093FB4FF4967001F0A5FD01F0E1FD002002F020F9E5
:400E0000002384F85E3002F023F801F0F5FE74E70120FFF753FA032000F020FD0848FFF726FAFFF744BB00BF253C0000333C0000EE3A0000F03A0000F281FF1FF23A0000AC
:400E4000403C000070B5002401254268002A4ED0C368002B4BD00368013A591C01601B784260802B01D145752FE013F0800F467D0269017D12D036B1D20042F004020331E8
:400E8000026101754475026903F00103D20042F004021A43037D0261033315E046B13F2B06D9D20042F004020331026101754475026943EA0223427D036112B143F0C00320
:400EC0000361037D447508330375037D072BBCD90269083B22FA03F38268511C81601370C368013BC360037D083B0375ADE770BD07B5027D42B102AA002102F8011D0260E5
:400F000001224260FFF79EFF03B05DF804FBF0B5012100244368002B43D0C268002A40D0427D4AB183685A1C8260827D1A70C3684475013BC360EDE7027D072A0BD806687C
:400F40000769751C05603578013B45EA07250832056143600275027D0369A2F10805EB40DBB203F0C006802E07D0C02E0ED103F03F0383754175802308E0C3F3401363F0F5
:400F80007F03033A03F08103027502E00575002BC0D08268551C85601370C368013BC360B8E7F0BD2DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E84B
:400FC00003008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EAE3
:40100000450502F0B9F8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F086
:401040000103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0E5F8072D90
:40108000F5D15E485E4E002550F8041F05F1105303F1520221F0FF075333C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A0023BE
:4010C00013609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A13781C
:4011000043F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4B4F
:40114000A2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0D3
:40118000030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7076
:4011C00094E80F0007C52B80BDE8F081004800404C0F00480F010049A146004025420040224200400440004006400040A2430040A0430040453C0000E8460040FCFFFF475F
:40120000A000004800760040540F0048F846004020760040580F004828760040035001400C0F0048C0510040180F0048200F00482C0F0048380F004832510040440F0048E6
:40124000CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C5FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC9B
:40128000086005E001F0B4FF0C2303604FF0FF33184608BDCC80FF1FC893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7CE
:4012C000114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F090FFFFF79AF8FEE700BF01000000143E0000124A134B10B51A60124A134C1368134843F4007389
:4013000013600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E07F
:4013400014ED00E0000000000080FF1F61120000BC760040C080FF1F08ED00E0F8B501F013FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E3525D
:401380001F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B304625
:4013C0001A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0CAFE07211720AB
:4014000001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062045
:4014400001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C203A
:4014800001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040ED92FF1F2B1D0000651B0000291D00005D1C0000891C00002C
:4014C000B91C0000F11C0000311D0000A51D0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A60E9
:401500004FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B8E
:4015400003221A70802203F8202C012001F014FE0D4B04221A7010BD0893FF1F0E93FF1F0C93FF1F0D93FF1F0993FF1FF892FF1F0B93FF1F8093FF1F00E100E09E60004099
:401580009C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BFB892FF1F0A4A002313700A4A13700A4A13700A4A7F
:4015C00013700A4A13700A4A13700A4A13700A4B03221A70802203F8202C70470E93FF1F0C93FF1F0D93FF1F0993FF1FF892FF1F0B93FF1F8093FF1F28600040014B187812
:40160000704700BF0D93FF1F044B1A7802F0FF001AB118780022C0B21A7070470C93FF1F024A0C2303FB0020407870471493FF1F431E072B0CD8074A064B00010344805CAD
:401640005B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032DDF
:4016800018D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034BF9
:4016C000185538BD1493FF1FE492FF1FED92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BF08
:40170000D588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F29
:40174000385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F0020353
:401780007355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BF1493FF1FFC5F0040ED92FF1FE492FF1F706000402DE9F0470446154688460029C2
:4017C00040D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F05C
:401800007FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F825
:40184000093002E00D4600E000252846BDE8F087ED92FF1FE492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BF1493FF1FDA
:40188000FE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B22A
:4018C000E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087009
:4019000030BD00BF8493FF1F8093FF1F00600040FC92FF1FF992FF1F0E93FF1F0A93FF1F8193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012004
:40194000137070470E93FF1F0A93FF1FF992FF1F8093FF1F8193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B1556828
:40198000215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000408493FF1F006000400D
:4019C000FC92FF1F8193FF1F0A93FF1FF992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BF0E93FF1FF992FF1F0A93FF1F8193FF1F054B9A6820
:401A00003AB19A68044910709A680988518000229A607047FC92FF1F8493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F08C
:401A40006003202B05D0402B06D043B900F012FC04E001F0A1FB01E000F046FD10B9034B03221A7008BD00BF28600040F992FF1F0060004008B5084A084B01201978138819
:401A80000B449BB21380064B00221A70FFF7B6FF044B03221A7008BD8493FF1F8093FF1F0E93FF1FF992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE8084082
:401AC000FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BF0E93FF1FF992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BF0E93FF1F47
:401B0000F992FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFF892FF1F086000400E93FF1FF992FF1FFD
:401B4000074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF70470E93FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF3F
:401B8000174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A084915461378207829
:401BC000DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040F992FF1F0A93FF1F8193FF1F29600040054A00231380054A916819B191680B709B
:401C000092685380704700BF8493FF1FFC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B29342E7
:401C400038BF0380FFF728FE012008BDFC92FF1F0E93FF1F0A93FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370479E
:401C80000B6000401493FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B6000401493FF1F0B4B04221A700F3BDD
:401CC00093F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B6000401493FF1F0B4B08221A700F3B93F84230094B93F854
:401D0000302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B6000401493FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF025
:401D4000000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425799D
:401D80002D0658BF84F801C090700133DBB24908D7E7F0BD9F6000401493FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5B6
:401DC000B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082A86
:401E0000C1700371417100F10400EAD108B070BD6F3C00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144BB
:401E4000B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D109
:401E8000054BFF221A70BDE8F08300BF1493FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC0035869002070470893FF1FD03C0000E7
:401EC0002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF868
:401F00000420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000FF4
:401F40000BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F844
:401F800008A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D28838
:401FC000C2F307224A71083394E7BDE8F88F00BF0D93FF1F1493FF1F0993FF1FFC5F004070600040FA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300C6
:40200000406908BD0D93FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B13
:402040001A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C3680E
:4020800003EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE7811
:4020C000BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C20083393427D
:4021000009D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8C9
:40214000F84FFFF767BEBDE8F88F00BF1493FF1FFA92FF1F8293FF1F0D93FF1F0B93FF1F1093FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F048
:40218000010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF006000401493FF1FFC5F004030B4194B1A7902F0D5
:4021C0007F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D1A6
:402200008C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF006000401493FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE7D
:4022400043681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BD0D93FF1F00600040FA92FF1F8293FF1F38B58A4A8A4C13780021DBB2CB
:4022800021801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B68F0
:4022C0006360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B2CD
:40230000854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD80F6
:402340001A78228018E0BDE8384000F025BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E702222280C0
:40238000514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB2F2
:4023C0005AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB247
:40240000D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012BCB
:402440002BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A7937
:402480005AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040FC92FF1F0893FF1FD03C0000343D0000BC3C0000A73D0000A093FF1F3B
:4024C0001493FF1FB992FF1F0B93FF1F0D93FF1FFA92FF1FF892FF1F0C93FF1F0993FF1F8293FF1F0F93FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A804B
:40250000FFF786BB0020704700600040FC92FF1F943C0000014B1870704700BF7F640040014B1878704700BF69640040014B1870704700BF78650040064A0123136002F65F
:4025400088321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF72640040014B1870704700BF7665004073B515461E460B4C052300221F
:40258000019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A11702F
:4025C000A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF78640040704738B505460078012428B1CF
:4026000000F062FD285D0134E4B2F8E738BD08B50D2000F059FDBDE808400A2000F054BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A46314621
:40264000207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF8294609
:40268000207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207842
:4026C00003B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1FB0
:40270000024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD875
:40274000084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C2548E
:4027800000207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C4900D0
:4027C00003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC28070
:40280000002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF147000408B
:4028400017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA021302
:40288000DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F075FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0E0
:4028C00067FC204610BD00BFE480FF1F030610B5044611D400F058FC084AE300117803F1804303F5F04319705378147001335370BDE8104000F04CBC10BD00BFE480FF1F56
:4029000030B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC28000200120704700000A
:4029400038B50446084DB4F5004F05D9286800F013FCA4F50044F6E7034B58686043BDE8384000F009BC00BFEC80FF1F024B1B7A584300F001BC00BFEC80FF1F0E4B00F024
:4029800003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B012275
:4029C0001A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0087
:402A000000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A99
:402A40001360084B4FF400421C60C3F8E82010BDBC92FF1FBD2A000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034AB9
:402A8000136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BFBC92FF1F024B1868C0F30040704700BF10E000E038B5FFF7E9
:402AC000F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BFC092FF1F024B03EB8003586859607047BC92FF1F134B144A1B78DBB20360127843EA02236E
:402B0000114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF2E
:402B40000301004904010049EC460040020100490101004900010049050100490601004910B500F011FB204A044613780A2043F002031370137C43F00203137412F80A3C45
:402B800043F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CB9
:402BC000A3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D4BAAB4300400E5900402F5B004080E200E008B500F0C5FA0F4A79
:402C0000137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0ABBA00BF0859004074
:402C4000044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F9101065
:402C80000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040803C00008A93FF1F8C93FF1F9093FF1F08B5102000F0A6F926
:402CC00007210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDB12D0000095900408893FF1F10B5054C23781BB9FFF7DCFFB5
:402D000001232370BDE81040FFF72ABFD892FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF79F
:402D400015FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF9093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B5E
:402D80001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270478A93FF1F8C93FF1F8893FF1F7047000010B500F0E7F9214A04461378DE
:402DC0000A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F040023F
:402E000003F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0A9B900BFED
:402E4000AB43004006590040275B004080E200E008B500F099F90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F8BC
:402E80000A3C937903F0FD039371BDE8084000F07FB900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E04223C0
:402EC00008380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470259004092
:402F00008A3C00009693FF1F9C93FF1F9493FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BDE5
:402F400009300000015900409893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFD992FF1F044B1A7802F0FB021A701A7842F001021A70704700590040D2
:402F800010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF9493FF1FA05B004031
:402FC0000E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270479693FF1FD3
:403000009C93FF1F9893FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF76640040D7
:40304000014B1870704700BF7C64004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDE6
:40308000FC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF74650040014B1870704700BFDF
:4030C0007764004000000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C4601258C
:40310000002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B7823407E
:4031400003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BFE7
:4031800001380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704751
:4031C000006000401493FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B577
:403200001E460E4C0025E41AA410A54204D056F8253098470135F8E700F0E2FD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BDEC3D0000EC3D000024
:40324000EC3D0000F43D000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF330293F9
:4032800005934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF77E
:4032C000CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BFB3
:403300001D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A38945
:403340004FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F08700009E
:403380002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB130233F
:4033C0008DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A080002373
:403400004FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A07B0
:4034400044BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB023201217E
:40348000F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB83
:4034C00001210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039B1D
:40350000073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB89BC
:403540005B0601D4099801E04FF0FF301DB0BDE8F08F00BFBB3D0000C13D0000C53D000000000000CD3200002DE9F04791461F460A698B6806469342B8BF1346C9F80030AD
:4035800091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD0013598
:4035C000E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430291
:4036000039463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D00123224639462F
:403640003046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A3E
:4036800040F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F151
:4036C0004205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E003
:4037000021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F4B
:4037400000F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F009
:40378000040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013C34
:4037C00005F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111D3A
:403800001960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF093
:40384000FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B984219
:40388000B8BF184605E00B7804F1420584F842308AE705B0BDE8F0836F3C0000CC3D000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431E9F
:4038C0000A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B7E
:4039000002F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B681218C2
:40394000226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018E0
:40398000824201BF106852680918216062605C602846BDE8384000F098B838BDE092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E015
:4039C00000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304696
:403A000000F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F8A0
:403A40000130EBD10C233360304600F03EF8002070BD00BFE092FF1FDC92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F88542C5
:403A80000ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7DCFB431C02D1236803B178
:403AC0002B6038BDC493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C79203078253032787F
:403B000000686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520E7
:403B4000303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E64203078253032780066614C
:403B8000696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636F25
:403BC000756E743D256420693D256420643D256400636D645F777269746500646F6E6520256420256400703D25642063723D25642063773D256420663D256420773D2564FF
:403C000020696E6465783D256420756E64657272756E3D25640077726974652066696E69736865640073746172742065726173696E670073746F702065726173696E670092
:403C400069646C650000510040100040510040300000000140001000140140000800400140000A004C014000020050014020003031323334353637383941424344454600E9
:403C8000000100000004000000100001000000040000001028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0080
:403CC0005300460054003100300030000100000001000000D83C000001000000A73D0000000000000000000001000000F03C000001000000793D000004000000123D000014
:403D0000000000000000000000000000103D0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500CE
:403D4000780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010036
:403D800080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B20AF
:403DC00000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E46704759000000ED120000F8B500BFF8BC08BC9E467047E7
:403E000035000000183E0000C880FF1FA0000000601200000000000000000000C893FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D46
:403E400000FA0000400000000090D003FF0000000000000000000000000000000000000000000000000000000000000000000000B93D0000000000000000000000000000B0
:403E80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081FF1F00000000000000000000000063
:4000000000800020110000009D1000009D100000064A08B5136843F020031360044B1A6803F53F5302331A6001F056F8E8460040FA46004010B5054C237833B9044B13B17D
:400040000448AFF300800123237010BD6881FF1F00000000E8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000021
:400080006C81FF1FE8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F9C
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080114BDA68196919B9FD
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F056B9704700BF60
:400140008881FF1F10B5C4B2204601F087F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BE5
:400180000CBF02230023002814BF184643F0010002F092FE62B608BD8881FF1F38B50446C5B2284602F0C2F8062002F0DFFA44F00200C0B202F0BAF8062002F0D7FA2846BD
:4001C00002F0B4F8BDE83840062002F0B9BA10B5642402F0A5F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F033F9012805D0204601F04CFAA8
:400200002846FFF79FFF204601F030F9314605460246204601F0ECF9204601F01FF90028FAD1284670BD000038B5044D0024285D013402F04BFA402CF9D138BDAC81FF1F30
:4002400008B502F065FC002002F06EFC02F080FC02F08AFC80B208BD10B50446012002F07DF8642002F06CFAFFF7EAFF2080002002F074F8642002F063FAFFF7E1FF60801C
:4002800010BD08B502F070FD002002F079FD02F08BFD02F095FD80B208BD10B50446FFF796FF322002F04CFAFFF7EBFF20800120FFF774FF322002F043FAFFF7E2FF608072
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F0FE02A802F08CF802F096F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF6278E6
:400300002146BDE81040042001F000B9043A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0EAFF85
:40034000002002F07DFD002384F8643010BD00BF8881FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF31
:400380000220012002F05CFDE078B0FA80F0400902F050FD2079BDE8384002F087BD00BF8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0AAFFDC
:4003C0004FF47A7002F0BCF984F86A50E368E366012384F86430BDE8384002F0EFB900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FECE
:400400000120002384F86A00236702F0AFF92A46216F1848FFF755FF144E0027236F9D4216D001F07DFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA002053
:40044000FFF7ACFE336F013B336702F0B7F9E5E7322002F075F92A2DCCBF0020012002F031FDBDE8F8400448FFF72BBF8881FF1F113A0000183A0000353A00002DE9F04F70
:4004800099B062B602F004FA9E49042002F028FA9D4801F051FF9D4802F0F4FC9C4801F085FF02F0D5FB02F0A7FA002002F0C8FC01F0A0FF0221002000F086FF954C012011
:4004C00001F0FEF8002384F86730FFF76DFFFFF77EFE84F87400FFF72BFF012384F86730FFF762FFFFF773FE84F87500FFF720FF894B94F87400894994F875202546002AA4
:4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F031F9264602F04EF994F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07EFF18B9794820
:40054000FFF7BFFE04E000F07DFF0028F7D10BE000F072FF10B902F031F9F9E77248FFF7B0FE032001F098F8032000F077FF0128D4D16E48FFF7EEFE6D490320FFF734FE85
:4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C
:4005C000D4034D0303238DF820308DF821300F238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD44
:400600000B4611E083B10022174696F87810F068277594F814E0BEF1000F02D1EB681B1AF7E701329142F3DA07228DF8202004228DF82120ADF82230F8E20220FFF782FD2F
:400640004FF000080DF1200A02F0B8F84FF480790027C9EB0803DA1907F80A200137402FF9D10220FFF76EFD3A465146022000F04DFFB9F10109EBD108F10108B8F1400F58
:40068000E2D12E4B38E04FF0010A4FF000080DF1200B02F093F84FF0000959460120FFF7A3FD08EB090300270493049B1BF807203B44DBB29A4209D08DE80C0041463B4641
:4006C0004A461F48FFF7FDFD4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400FD5D151461648FFF7EAFDBAF1000F00F01B81144B1B8807A8ADF81C30AD
:4007000095E200BF55010000F900000091000000C50000008881FF1F473A0000433A00004A3A0000623A0000753A0000ED81FF1FFE81FF1F7F3A0000EC380000EE380000BE
:400740008E3A0000AA3A0000F0380000206FFFF749FE94F8780001F001FE94F8780001F0E5FD02F015FCB94BDFF8FC821A78002702F0FB021A701A7842F001021A701A789D
:4007800002F0FE021A701A7802F0FE021A7002F003FC0220FFF7D6FC012141F6FF734FF48042084602F052FB84F8B60001F074FF08F807000137402FF8D1DFF8B0A200271A
:4007C0000AF195091FFA89F80137402F14BF3A4600221AF8010F2244062392F82420402101F08EFF424646F24B419AF8000001F099FF08F14008402F1FFA88F8E4D196F859
:40080000793053B196F87C30336100233375237D002BFCD000233375336100234FF0FF32236062602372236894F8B600234493F8241001F0E9FE94F8B60001F0A7FE01214A
:4008400094F8B60001F07AFE2368002BFCD0002398467360D6F80CA0012701F0AFFFE368B4F87A20CAEB030393420DD367B1042195F8B60001F0D4FE94F8B60001F0E0FEE1
:400880000028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF752FC6968402209EB8111022000F02FFE6A68674B01321340002BBEBF03F1FF3363F091
:4008C0003F03013308F101086360C6E70220277AFFF738FC00221146022000F017FE0220FFF730FCFFB2FFF79FFC002001F01EFD37B15848FFF7E5FC0220FFF709FD06E06E
:40090000554B08A81B88ADF82030FFF7EFFC227D4146237A5148FFF7D4FC15E25048FFF7D0FCD4F87A7017F03F0701D0032009E2286FFFF757FD95F8780001F00FFD95F829
:40094000780001F0F3FC012001F00EFD02F020FB444BDFF814811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F00FFB01214FF4804315
:4009800041F6FF72084601F0F5FC85F8B60001F083FE08F807000137402FF8D1DFF8CC90002709F195031FFA83F804930137402F14BF3A46002219F8010F2244052392F8D7
:4009C0002420402101F09CFE414646F2495299F8000001F0A7FE08F14008402F1FFA88F8E4D100274FF0FF33376098467360BB463B46D6F87A9037725FEA99190CBF4FF060
:400A0000010A4FF0000A2168114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426FD02BB1227A002A7AD12A7D002A77D12068049A059302EB8010BAF1000F36
:400A400016D040223F2102F003FB1CE09B6400403F000080B43A0000F2380000CE3A0000E13A000099650040AC81FF1FAB81FF1F014601370120FFF7B7FBC7EB0903D3F10C
:400A8000000A4AEB030A2168B34A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322ED12A683F2A2BD14FF00008C5F8048001F08CFC85F808806B6895F8DC
:400AC000B6002B4493F8241001F09EFD95F8B60001F05CFD012195F8B60001F02FFD95F87E302B6185F81480237D002BFCD04FF00008012086F8148001F076FC404601F0B4
:400B000033FC00E023B1237A5BB92B7D4BB90123626842453FF477AF0BF1010BD5F8048071E701F05BFC012001F01EFC002001F05BFC042194F8B60001F072FD94F8B60011
:400B400001F07EFD80460028F8D196F8B60001F00BFD337D327A0293012303920193CDF800A05B463A4649467C48FFF7AAFBC6F81080BAF1000F0BD0FFF756FB002001F028
:400B8000D5FB237A63B17648FFF79BFB0220D9E0B945F1D073490120FFF726FB0137F7E77148FFF78EFB714B3DE094F8780001F0D5FB206FFFF716FC6D48FFF782FB94F8A5
:400BC0007930236100232375237D002BFCD0012001F00AFC00233375237D002BFCD0002001F002FC002363483361FFF76AFB624B19E0002084F86A00FFF7F4FB5F4B12E003
:400C000094F8743023B195F875200AB985F8782094F875201AB113B9012385F878305848FFF798FB574B1B88ADF8203008A8FFF75DFB89E0FFF77CFB02F088F8002002F0FA
:400C40002BF82A2701F056FF002001F0F9FE3A46002108A802F0FCF917238DF820308DF8217001F0ABFD002001F054FB002002F0E7F8C82001F064FD0DF12200FFF7ECFA35
:400C80000DF13600FFF709FB01F098FD012002F0D7F8322001F054FD0DF12600FFF7DCFA0DF13A00FFF7F9FA012001F033FB4FF4967001F045FD01F081FD0DF12E00FFF7AC
:400CC000CBFA0DF14200FFF7E8FA002001F022FB4FF4967001F034FD01F070FD022002F0AFF8322001F02CFD0DEB0700FFF7B4FA0DF13E00FFF7D1FA012001F00BFB4FF4FE
:400D0000967001F01DFD01F059FD0DF13200FFF7A3FA0DF14600FFF7C0FA002001F0FAFA4FF4967001F00CFD01F048FD002002F087F8002384F86A3001F08AFF01F05CFE97
:400D400074E70120FFF7E4FA032000F0A5FC0E48FFF7B7FAFFF7E2BB3F000080EB3A00001B3B00004092FF1F253B0000F43800002D3B00003B3B0000F6380000F8380000ED
:400D8000FE81FF1FFA380000483B00000F4B1A78120616D55878C0B2012814D11B79DBB2042B02D0052B05D07047094A094B5A60282203E0084A074B5A60E0221A8000F04C
:400DC00045BE002070470120704700BF00600040FC380000C492FF1F243900002DE9F04172B68A4B61221A70A3F5F06301221A801924874A9C7092E803008033062283F8F7
:400E0000002283E80300522203F580731A70814B814A1B78814EDBB2137040F61802804B00251A8041F2512223F8022C33784FF4F07003F0010343EA450502F0A1F8013CE3
:400E400005F003052ED0032DF0D1764B4FF480721A8007221A70744A002548211570917002221D705D7103F8032C0422DA716F4A6F4C13786F4E43F00103137012F8013C9A
:400E8000062743F0030302F8013C2378012243F0800323705D4B1A70674A137843F02003137000E0FEE707FB056300219A881868013502F0CDF8072DF5D16048604E0025F3
:400EC00050F8041F05F1105303F14A0221F0FF074B33C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1534A00231360936013619361FD
:400F0000514B524F1A68524BDFF88C811A60514B1A6803F1784303F5D6431A604E4A137843F002031370137C43F0020313742378A2F5863243F040032370413A137843F047
:400F400010031370454A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A803F4A083307CA03C31A803E4A3E4BA2F5B0
:400F8000616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F0030326
:400FC00088F800302E4B48221A702E4A402313702D49937013729372082382F81F3220220A7048710A72294A0A20137001F0BEFB274B88F8006044223D70264D1A7094E8E4
:401000000F0007C52B80BDE8F08100BF004800401C0B00480F010049A146004025420040224200400440004006400040A2430040A04300404D3B0000E8460040FCFFFF470A
:401040009000004800760040240B0048F846004020760040280B004803500140DC0A0048C0510040E80A0048F00A0048FC0A0048080B004832510040140B0048CF01004921
:401080001D51004001590040235B0040585B004076580040B0430040F946004008B501F0ABFF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC086005E043
:4010C00001F09AFF0C2303604FF0FF33184608BDCC80FF1F9093FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7114653F88D
:40110000046C8C1AA64202D041F8045BF9E701381033E5E701F076FFFFF7B0F9FEE700BF01000000E03C0000124A134B10B51A60124A134C1368134843F40073136000238F
:40114000032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF739FEBDE8104001F0CFB900BF0004FA050CED00E014ED00E016
:40118000000000000080FF1F9D100000BC760040C080FF1F08ED00E0F8B501F0F9FE4B4A01271378022643F001031370137C484C43F001031374474B02F5E3521F700B3215
:4011C00003F8946C1378054603F07F031370002001F0CCFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0BDFA282001F0BAFA384B30461A7802F089
:401200007F021A701A7802F0BF021A7023783343237001F0ABFA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0B0FE0721172001F0DEFA5F
:401240002949172001F0CCFA0721182001F0D6FA2649182001F0C4FA0721152001F0CEFA2349152001F0BCFA0721052001F0C6FA2049052001F0B4FA0721062001F0BEFA17
:401280001D49062001F0ACFA0721084601F0B6FA1A49072001F0A4FA0721082001F0AEFA1749082001F09CFA0021162001F0A6FA1449162001F094FA07210C2001F09EFA0C
:4012C000BDE8F84010490C2001F08ABAA5430040944300409D60004012600040F851004084600040B592FF1F671B0000A1190000651B0000991A0000C51A0000F51A0000BA
:401300002D1B00006D1B0000E11B0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A604FF080729F
:401340001A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B03221A70D2
:40138000802203F8202C012001F0FAFD0D4B04221A7010BDD092FF1FD692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F00E100E09E6000409C600040AF
:4013C000286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF8092FF1F0A4A002313700A4A13700A4A13700A4A13700A4ADE
:4014000013700A4A13700A4A13700A4B03221A70802203F8202C7047D692FF1FD492FF1FD592FF1FD192FF1FC092FF1FD392FF1F4893FF1F28600040014B1878704700BFC1
:40144000D592FF1F044B1A7802F0FF001AB118780022C0B21A707047D492FF1F024A0C2303FB002040787047DC92FF1F431E072B0CD8074A064B00010344805C5B7800F0CD
:401480000F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032D18D8DFE8AD
:4014C00005F002070C110021084600F0FDFF0DE00021084600F0DCFF08E00021084600F0BBFF03E00021084600F09AFF054B1855EDB2072D03D801F0CFF8034B185538BDA0
:40150000DC92FF1FAC92FF1FB592FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BFD588DB7824
:4015400084BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4F385D00F016
:40158000F3FF11232946FE2218F8040001F0B8F806F5C04278321FFA89F118F8040001F0C1F8124D18F80410385D01F02DF80121385D00F0C3FF735D43F002037355735D92
:4015C00003F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFDC92FF1FFC5F0040B592FF1FAC92FF1F706000402DE9F047044615468846002940D0431E54
:40160000072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F09BFF1223FE222946305D01F061F807F557
:40164000C0411FFA88F27831305D01F06BF8DFF84490315D1AF8040000F0D6FF01211AF8040000F06BFF17F8093043F0020307F8093017F8093003F0FD0307F8093002E099
:401680000D4600E000252846BDE8F087B592FF1FAC92FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFDC92FF1FFE5F0040C3
:4016C000431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2E854506895
:401700000133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B1870087030BD00BF12
:401740004C93FF1F4893FF1F00600040C492FF1FC192FF1FD692FF1FD292FF1F4993FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012013707047C2
:40178000D692FF1FD292FF1FC192FF1F4893FF1F4993FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B15568215C013B85
:4017C000C9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404C93FF1F00600040C492FF1F4C
:401800004993FF1FD292FF1FC192FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFD692FF1FC192FF1FD292FF1F4993FF1F054B9A683AB19A682B
:40184000044910709A680988518000229A607047C492FF1F4C93FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F06003202BFD
:4018800005D0402B06D043B900F012FC04E001F087FB01E0FFF77AFA10B9034B03221A7008BD00BF28600040C192FF1F0060004008B5084A084B0120197813880B449BB208
:4018C0001380064B00221A70FFF7B6FF044B03221A7008BD4C93FF1F4893FF1FD692FF1FC192FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE80840FFF7D8BF34
:40190000BDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFD692FF1FC192FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFD692FF1FC192FF1FCE
:4019400008B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFC092FF1F08600040D692FF1FC192FF1F074B1B782C
:40198000DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047D692FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF174B1B782A
:4019C000190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078DBB200064D
:401A000005D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040C192FF1FD292FF1F4993FF1F29600040054A00231380054A916819B191680B7092685380CB
:401A4000704700BF4C93FF1FC492FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B2934238BF03806C
:401A8000FFF728FE012008BDC492FF1FD692FF1FD292FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A7370470B600040D9
:401AC000DC92FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040DC92FF1F0B4B04221A700F3B93F83230CF
:401B0000094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040DC92FF1F0B4B08221A700F3B93F84230094B93F8302002F0F9
:401B40000302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040DC92FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF0000C31B372
:401B8000CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB036425792D0658BF05
:401BC00084F801C090700133DBB24908D7E7F0BD9F600040DC92FF1F70600040FE5F004000F08EBC70B50446184B88B003AA03F11006154618685968083303C5B3422A46B4
:401C0000F7D11B782B70FCB12223237001AD03232846637000F06CFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082AC170037125
:401C4000417100F10400EAD108B070BD773B00002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F805902144B9F1000F62
:401C800007D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1054BFF2213
:401CC0001A70BDE8F08300BFDC92FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047D092FF1F9C3B00002DE9F84F64
:401D0000424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF80420597891
:401D400091425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000F0BDAB9F11C
:401D8000010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F808A0CAF330
:401DC000072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288C2F3072281
:401E00004A71083394E7BDE8F88F00BFD592FF1FDC92FF1FD192FF1FFC5F004070600040C292FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC300406908BDDA
:401E4000D592FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B1A78002AC0
:401E80007FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C36803EB020993
:401EC0004B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78BAF1000F12
:401F000006F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C200833934209D013F814
:401F4000081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE8F84FFFF732
:401F800067BEBDE8F88F00BFDC92FF1FC292FF1F4A93FF1FD592FF1FD392FF1FD892FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F00101917098
:401FC0000021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040DC92FF1FFC5F004030B4194B1A7902F07F02531EE1
:40200000072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D18C7006E077
:402040000D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040DC92FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE43681B791B
:402080008B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDD592FF1F00600040C292FF1F4A93FF1F38B5874A874C13780021DBB221801806BC
:4020C000517840F188800A2900F20081DFE811F05800FE00FE00FE00FE00FE000B00FE007900FE007D00D3787949012B09D17A4B1A787A4B03EBC2035B685B6863601223C5
:4021000010E0CB78022B12D18878FFF7E5FD002800F0DC80436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B21D16A4B00228878D5B2854203D3F6
:40214000634A92783AB910E0187801320028F7D018780344F0E75E4A62499278097C914203D16148FFF73EFD5F4B1A78002A00F0AD801A78228018E0BDE8384000F010BF77
:4021800013F0030313D0022B40F0A0802380504B0C211B7903F07F02544B01FB02339A78534BD2B21A7000225A706360BBE702222280504A11784E4AC9B211705370626062
:4021C000B1E7012323804C4BEFE70123238013794A4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378444ADBB25AE0937803F0FF0153B93E4B1A7892
:4022000091425FD019703F4B01201870FFF71AFE58E0481EC0B2FFF75FFD0028EED155E0FFF722FF002851D0294A374913791279DBB2D2B20A70354A3049D25CCB5C9A4236
:4022400040D0304B01221A70FFF758FD3AE003F00303012B2BD009D3022B37D11C4B9B78002B33D1BDE83840FFF7C4BE184B9B78012B2BD11F4A137803F0FD0315E003F0E5
:402280000303012B13D008D3022B1FD1104B9B78E3B9BDE83840FFF783BE0D4B9B78012B14D1144A137843F0020313700AE0084B1A795AB998781B791549DBB2CA5C22EAEF
:4022C0000002CA54BDE83840FFF7A0BA002038BD00600040C492FF1FD092FF1F9C3B0000003C0000733C00006893FF1FDC92FF1F8192FF1FD392FF1FD592FF1FC292FF1F96
:40230000C092FF1FD492FF1FD192FF1F4A93FF1FD792FF1F014B1870704700BF78650040014B1878704700BF6C640040014B1870704700BF78640040064A0123136002F698
:4023400088321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF7A650040014B1870704700BF7F64004073B515461E460B4C0523002210
:40238000019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A117031
:4023C000A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF7E640040704738B505460078012428B1CB
:4024000000F066FD285D0134E4B2F8E738BD08B50D2000F05DFDBDE808400A2000F058BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A46314617
:40244000207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF829460B
:40248000207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207844
:4024C00003B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1FB2
:40250000024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD877
:40254000084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25490
:4025800000207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C4900D2
:4025C00003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC28072
:40260000002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF147000408D
:4026400017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA021304
:40268000DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F079FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0DE
:4026C0006BFC204610BD00BFE480FF1F030610B5044611D400F05CFC084AE300117803F1804303F5F04319705378147001335370BDE8104000F050BC10BD00BFE480FF1F4C
:4027000030B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC28000200120704700000C
:4027400038B50446084DB4F5004F05D9286800F017FCA4F50044F6E7034B58686043BDE8384000F00DBC00BFEC80FF1F024B1B7A584300F005BC00BFEC80FF1F0E4B00F01A
:4027800003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B012277
:4027C0001A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0089
:4028000000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A9B
:402840001360084B4FF400421C60C3F8E82010BD8492FF1FBD28000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034AF5
:40288000136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF8492FF1F024B1868C0F30040704700BF10E000E038B5FFF723
:4028C000F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8892FF1F024B03EB80035868596070478492FF1F134B144A1B78DBB20360127843EA0223E0
:40290000114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BF30
:402940000301004904010049EC460040020100490101004900010049050100490601004910B500F015FB204A044613780A2043F002031370137C43F00203137412F80A3C43
:4029800043F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062CBB
:4029C000A3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D8BAAB4300400E5900402F5B004080E200E008B500F0C9FA0F4A73
:402A0000137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0AFBA00BF0859004072
:402A4000044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F9101067
:402A80000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040883B00005293FF1F5493FF1F5893FF1F08B5102000F0A6F9C9
:402AC00007210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDB12B0000095900405093FF1F10B5054C23781BB9FFF7DCFFF1
:402B000001232370BDE81040FFF72ABFA092FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7D9
:402B400015FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5893FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B98
:402B80001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475293FF1F5493FF1F5093FF1F7047000010B500F0EBF9214A0446137884
:402BC0000A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400241
:402C000003F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0ADB900BFEB
:402C4000AB43004006590040275B004080E200E008B500F09DF90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F8BA
:402C80000A3C937903F0FD039371BDE8084000F083B900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E04223BE
:402CC00008380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470259004094
:402D0000923B00005E93FF1F6493FF1F5C93FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD88
:402D4000092E0000015900406093FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BFA192FF1F044B1A7802F0FB021A701A7842F001021A7070470059004046
:402D800010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5C93FF1FA05B00406B
:402DC0000E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475E93FF1F0D
:402E00006493FF1F6093FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF7C64004043
:402E4000014B1870704700BF7B64004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDE9
:402E8000FC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF74640040014B1870704700BFE2
:402EC00075640040014B1870704700BF7D640040FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF814388346CB
:402F00000121C9430C460125002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D900200130013812
:402F4000013001200B78234003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C2010000000000FD
:402F80000230800803D000BF01380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A4A
:402FC00000E013790020704700600040DC92FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BFEC
:403000006081FF1F0E4B70B51E460E4C0025E41AA410A54204D056F8253098470135F8E700F044FE084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD98
:40304000B83C0000B83C0000B83C0000C03C000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBFA2
:40308000234604F1FF33029305934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A464B
:4030C000014603480068FFF7CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F5B9
:403100007B1C43449D4238BF1D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FAA0
:403140000C23CAF80030A3894FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E44F5
:403180002660BDE8F08700002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF86F
:4031C0002930DFF89CB130238DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B7803
:40320000002B00F0A08000234FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BFAF
:4032400020228DF853201A0744BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B51
:4032800003D800FB02320121F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130D0
:4032C000303A092A03D804FB01210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F9CF
:4033000088B1194B33B9039B073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B27
:403340005344099342E7AB895B0601D4099801E04FF0FF301DB0BDE8F08F00BF873C00008D3C0000913C000000000000D53000002DE9F04791461F460A698B680646934279
:40338000B8BF1346C9F8003091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E0012352463946304631
:4033C000C04701301AD00135E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF3091
:40340000BDE8F08704F1430239463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D45BF
:4034400009D00123224639463046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D833
:40348000002A00F0BB80582A40F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A87
:4034C0003AD0ADE01A6801F14205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D228D
:403500005B4284F8432037E021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F8452055
:403540002268186812F0800F00F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DF0
:40358000A56008DB206820F0040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42E0
:4035C000DEBF302305F8013C05F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E019600023236173
:40360000754616E01A68111D1960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF70C
:4036400097FE013002D14FF0FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42D0
:40368000F2DBE068039B9842B8BF184605E00B7804F1420584F842308AE705B0BDE8F083773B0000983C000010B5C9B202449042034605D01C7801308C42F8D1184610BD55
:4036C000002010BD10B5431E0A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD71
:40370000994204D011F8014B02F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A0188342ED
:4037400001BF18685B681218226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232A
:403780002B6009E021686018824201BF106852680918216062605C602846BDE8384000F098B838BDA892FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA9429D
:4037C00002D90C23336046E000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E70D
:40380000174C23681BB9304600F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD37
:40384000211A304600F034F80130EBD10C233360304600F03EF8002070BD00BFA892FF1FA492FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F5
:40388000F8BD00F027F885420ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7F6FB46
:4038C000431C02D1236803B12B6038BD8C93FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215022800000013
:40390000000104000100000000000000000157494E55534200003030303031000000000000000000E0000000000105000100D6000000070000002A00440065007600690041
:403940006300650049006E0074006500720066006100630065004700550049004400730000009E0000007B00330064003200370035006300660065002D003500340033000D
:4039800035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0000007B00330064003200370035001F
:4039C0006300660065002D0035003400330035002D0034006400640035002D0061006300630061002D003900660062003900390035006500320066003600330038007D0098
:403A0000000000007265706C792030782530327800686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E69736865642073652D
:403A4000656B00796573006E6F00647269766520303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900631F
:403A80006F6D6D616E6420307825303278006661696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E2061667479
:403AC0006572202564207061636B65747300636F756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D2564F8
:403B000020773D256420696E6465783D256420756E64657272756E3D256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702027
:403B400065726173696E670069646C650000510040100040510040300000000140001000140140000800400140000A004C01400002005001402000303132333435363738CF
:403B80003941424344454600000100000004000000100001000000040000001001000000A43B000001000000733C0000000000000000000001000000BC3B00000100000084
:403BC000453C000004000000DE3B0000000000000000000000000000DC3B0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF000203FC
:403C000004030904160346006C007500780045006E00670069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F006700690036
:403C4000650073000009022E0001010080320904000004FF00000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E016F
:403C800000020180014300232D302B2000686C4C00656667454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E4670475900000029110000DA
:403CC000F8B500BFF8BC08BC9E46704735000000E03C0000C880FF1FA000000028120000000000009093FF1FFF000000675000400C00000007000000FFFFFFFF7F8000006F
:403D00003F0000000000007D00FA0000400000000090D003FF0000000000000000000000000000000000000000000000000000000000000000000000853C0000000000006A
:403D400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081FF1F00000000A4
:403D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003
:403DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C3
:403E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082
:403E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042
:403E80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002
:403EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C2
:403F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081
:403F40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
@@ -4098,68 +4098,68 @@
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
:0200000480007A
:400000000145004008520040015B004001650040010101404D0201403D0301404C0401404D0501404D060140500701404B0801404B0901404B0A0140500B0140470C0140CB
:40004000500D0140520E0140350F014046140140541501404B160140561701404718014044190140591A0140571B01400A400140104101400E4201400943014004440140BE
:400080000D4501400A4601400F470140094801400F4901401B4C0140084D014008500140045101407E0208440905108011821801600C610A7C4027212A0A8C8000010109B1
:4000C0000202040805090808090F0B100C080D010F02100F1120140416011709180819101B0E1D011E01210422082301240829092A0E2C0D320F331F35203B083E043F1020
:400100005608580459045B045C905D905F01850287018C01900F9401960898249A089E07A008A101A222A302AA08AE10B01FB101B302B420B90ABE10BF05C202C70EC810AC
:40014000C9FFCAFFCBFFCF83D804D904DA04DB04DC09DD09DF01030204A10620074008010A060B200C800D100E400F2010421110150416821720180419011E10202021010F
:40018000262027202A012C042E023508376138083E583F0240045208588859215A025D4060026108621265806F0180068140874088408A088E209880C0F1C2FFC4FBCAA1EC
:4001C000CCF0CEF2D004D61FD81FE20CE6C5EE82010D04020601070109080C010D080E0E100F130E150F161019081C201D082101220F2302240125042604270128012908CA
:400200002A082C012F08301F3220330F3E043F045608580459045B045C095D905F01810182088302840885028A108B018C088D038F049102920194049701982099029A1FB5
:400240009C089D02A202A302A408A501A704A808A902AC1FAD03AE20B107B43FBF01D804D904DB04DC09DF0100400108048006A0070809880A400C810E080F20100511082C
:40028000130115251740185019011B011E081F20210827402AA02B902C052D022F20308031213208350136803728388039243B013D083E4A3F2158405C105D806180851076
:4002C00087029102938094A195349644972099809B209C0C9D089EBAA480A602A713A908AC03B010B520B601C0F5C2FDC4FFCAFFCCFFCEFFD638D808E20AE648E880EA03A0
:40030000EE820003010D050506010801090C0B020C010D0D1001111012021602170D1801190D1C011D0D2201230D2401250D280129092C012D012E022F02310C3510360355
:4003400037033E403F51580459045B045F0180208102820F8404860A882F89028D028EA09204948095039640970C98049A099E10A020A102A20FA480A802AC01AD0AAF0550
:40038000B060B108B21FB306B480B501B61FBE10BF01D804D904DB04DC99DF01000902080302048A0708082209880C800D100F9011501305158016A01710180919041A08AD
:4003C0001BD21D80210923042440254026102B422C042D012E402F20310836843721380239803A103D203E083F825D015F0169506AA06F0178017B807D027F8080048604BE
:4004000089108A028F01938094A19525964497219B209C0D9D0A9EA89F08A321A480A520A68AA790AD01B040B210C0FFC2FFC4FFCAF9CCF2CEFDE242E401EA01000103027E
:4004400004E0050108010A080B020CE00F02100F1280130814481602170218201B021E071F02200422482302261027022A082B042F10311F34E0361F382039023F015804DF
:4004800059045B045C095F0180038109840387318A018B098C038F06900391129309940395099712980399409E02A003A309A603A709A803A909AC03AD49AF24B338B40349
:4004C000B540B707BB88BE10D804D904DB04DC90DF010080012002800320048A052008180A010B400C020D080E0610021325172918081E061F802002215423252480284155
:4005000029202B202C872D202F203108321233013448350137A0380239883B103C803E043F115A806E408340860287048A0690029289935094019704981A99019B889C84CD
:400540009D209F20A0C2A108A280A328A401AB10B204C0FFC2FFC47FCAFFCCF7CEFFD608E080E620EA85EE2002800404050D064808200A400C0111011210144816021A07E2
:400580001B051C401E202608280129102A082C0F2D0A3020310C32C03303341F362037103A083E413F4056085804590B5B045C995D905F01822083018607870F88218A0825
:4005C0008B018C218F019210930E9428964297059AC89B039F01A02FA280A301A444A628A709A90FAA20AD0FB105B21FB4E0B503B709BA20BF51D804D904DC09DF010001EA
:400600000184030805A4078009090A060D080E881006121015141701180219811A051B481D041E081F20210422412501264027082B052C822F2432113408350137A03908B7
:400640003C823D043F144410450858805B10630A64086610780284018601911092289358950896019704980A9B819D249F20A0C2A108A280A401A761A840AF11B040B504F1
:40068000B702C0FFC27FC477CAFCCCF5CEF2D60CD80CDE01E202E640E880EA10EE4002080349077F08010A280B090C2F0F091001117F1408160217091A071B091E401F2962
:4006C000221023722709280429782A282B042F19310732603348341F352837183F55580459045B045C095F01802081048440850186A088418A088C4F8E808F04920794041B
:40070000966898689A029B019C41A208A610AA40AD02B01FB304B503B6E0BA80BF04D608D804D904DB04DC99DD90DF010128024003010521070109040A810B080D080E0633
:400740000F80128013181608170218821A801D081E041F14221423102601271A2B012D042E012F253210341036843721380239083A403C023D1A3E0A3F54584060026240FE
:4007800068026B016F0A831886408B048C018D089342951496019714980899019B809C109D209E0A9F21A0C0A284A402A509A74AB308B504C0DFC2FFC45ECAF8CCF4CEFBE6
:4007C000D608D808E010E280E644EE820002012F020103800402060107070A060B200C020E010FC81002120114071528174218021A011B101C021E012121220523082544DD
:400800002607272829212A012C0731E03207331F3B023E04580459045C905F0182698502861988198E198F119019924493049419962298229A199B089C19A20EA306A61923
:40084000A702A910AA19AB01AD01AE19AF10B070B208B301B407B510B607B70EB928BAA2BB80BE04BF14D804D904DB04DC99DF010082012402400521060207200810094941
:400880000B200C020D090E010F04104213141514164017801A201C821D011E281F012010210423C025442680270829202A082B402C802F1A30803108329034483614378162
:4008C000390A3B503D403F046D408C10C0FFC2FFC4FFCAF7CCA6CE5FE210010E04080508060409080C0D0E020F08110812091504180819081A021D081E04200423012702A7
:4009000029082D08300C320C330F34033A0A3F045608580459045B045C095D905F01801382048402871088028B1F8C088E018F1F901991019202951F96099A089B049C0273
:400940009D1FA001A20CA308A602A720A802AB02AC1BAD1FB210B40FB73FBB80BE14D804D904DC90DF010108021104020540060807200A040B810D110F2211021405161004
:400980001702190A1A041E201F20210425402680270229442A212C802E202F2130803120320433113545371039163A803B033D843F2058806002680D6A508101821084A1C0
:4009C000880289028B048E5090029134922893859508980599459AA99B219C229D0AA080A121A311A40CA690AA88AD80AF10B111B504B648C0F5C2FBC4F8CAFFCCFFCE7FAE
:400A0000D608D808E248E401E602E809EE030001062008010A7E0D010E08107F167F1A041E1020022101247E26012A402C7F3501367F3B303E40580459045B045C905F0179
:400A4000804484118708881189078C078E088F109211941195A5974298119B429C119F21A011A307A433A5C6A644A721A878AA02AC20AD84AE02B3E0B51FB67FB71FB9A0BC
:400A8000BE40C004C5E0C6C0C80AC9FFCAFFCBFFD004D601D804D904DA04DB04DC90DD09DF01E2C001160201030204020540071409A80B400C810D200F2010041141130440
:400AC00015A016A019801F80214023152510270428222B402F8030083250384039083B103D8041014B025210688069756B116C016E406F02705071E972A07348810A8204C4
:400B00008404850487048A108C509040910292A8938094A295A496409733980199049B209C269D0A9EB89F18A141A351A484A530A68CA808B210B540C0FFC2FFC4FFCA8B79
:400B4000CC0ECE1ED001E003E208E402EA40EE060601083109100D0C0E080F021040130116021702180319011A101E0420382304240325082608290A2B042C022E2130400E
:400B8000310F3207351036383A803E013F10580459045B045C995F018001830C8401870188018A028B078C018F019001930194019701980199079E019F01A001A108A302BA
:400BC000A601A701A801AB01AC01AE02AF01B507B603B708BE40BF50D804D904DF0100820201032A05480608081909800B400D080E461002128013241501174818011D4E2B
:400C00001E01218022102408260228082BA22C082E022F103011328436063710380239A83D103F806D5080048280850186058C208D01900291A89248934494019620981A26
:400C40009A029B609C809D20A043A284A328AB80B604C0EFC2FFC4BFCAEFCCEFCE3FE080E451E8C0EE02000801010201050108040B010C010D010E081101130217011901F5
:400C80001A011D01210123022401250129012A022D01300332043303340836083A023E543F0440434502480149FF4AFF4BFF4D204EF05110580459045A045C095D095F017F
:400CC0006108624063406480664067408004840885CC86018722881F8C048D01900493119401961E9722980499F19C019E10A004A344A588A604A824A9AAAA19AB44AC0134
:400D0000AE22B020B30FB50EB61FB7F0BE41BF10D804D90BDC90DF01002001020301058907080B040C200D090E011010120816801764181019221A0A1B801D021E0222083E
:400D400025602711280929022B202C082E022F10301232843606371039A83A013D884010410248107C0283408520881089088B308C088D448F4091EA920193049602972033
:400D8000981B99819A839B0C9D209F10A022A108A286A320A408A601A7C0A904AB04AD01AE01B080B18AB608B708C0FBC2F2C4F6CAEFCCEFCE5FD003DE80E0A0E240E47094
:400DC000E602E890ECC01B011F0231203302368037083B40C630CCF0CE103088364037043A023C2088108F0893809F08A520AE80AF41B340CCF0CE60EE4053405640840897
:400E00008C8093809A409E029F04A488A520A6C0B2C0D460E2408E0292029E02A520A6C0AB04AE01EA80EE4015027B02C404DC015940670882108E4191029720AF10B7020E
:400E4000D401D801E008E602EC021A4085408D049102966097289D40A201C608E60209440F4191029420962097299C409D40A201A544A810AB01B040C20FEA0126809202EF
:400E8000A320A520A680AE40B720C820EE405280572079407E01828085208D409202A320A520D460DC80DE20E210E62004200A200C400F401F1050805B405E025F8083408D
:400EC000894091029420962097209C409D40A201AF40B304C001C20DC601D405D605E001E801850188809102A480B3C0E401EA04EC04010109010B010D010F0111011B01BF
:400F00001D0100FF01AB020211050000BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B847004700000100008000000282008200D5
:400F400000000000000303000300000027001801270018010004000000050000000000000000000000000000000000000000000000000000000000000000000000000000DF
:400000000145004008520040015B004001640040020101400B0301403E04014041050140130701405508014056090140510A0140540B01405B0C0140540D0140470E01400D
:40004000380F01400A1501400B1701404B18014059190140521A01404E1B01400B40014012410140144201400C430140044401400D4501400946014011470140084801406A
:4000800011490140154C0140034D014006500140045101407E0208020941118218021901600C61067C4027212D0A8C40E62082608580908091029840AD01B040E008E224DC
:4000C000E680EE020008021003020405052006020B080F041110120117041A021B011C041F0420062107220123082610270428102A082B042F04300731203208330F361052
:40010000371038883A303B083E453F4156085808590A5B045C995D905F0189018B028D039601A002A102A301AC01B202B303B601BE04D608D804D904DB04DC99DD90DF0157
:40014000000A09900A400F10100811081302164119801AA01B221C101F802142231026082A252B012F80300231443220388039023B143F90584059105F80618063046640B5
:4001800068046B026D206E407A107B028110822088088A108E018F4090809102968098409A209D80A502B502C00CC22BC49ECA8FCC0FCE3FD61CD81CDE0AE204E60DEA04B2
:4001C000EE0181108D449010918092609D449E049F80A110A402A701A904AC01B208B720E202E60AEA05EE080004020804010602080409010AF90B140D010E020F2810030B
:4002000012FC134114041580164017011A041B4E1C801E042202234124042620274128042A102B412CFF2DF12F02310F32FF330F35F03B0A3E045804590B5B045C905F0188
:40024000800481078378840485088730880489808C038DFF8E0490019101920294029502970498029C029DA09F48A002A102A304A404A53FA802A908AB10AC05AF80B13F30
:40028000B207B33FB7C0BB80BE04D804D904DC90DF01008801020308048405020708084009220A110C040D090F0911011202132814801568170119041C202104224A230182
:4002C00025052714288629042C022F6430083102324035013714394A3B203F60422043045C405D206D106E407C028D01914293689420952D96909785980A99039A089B0820
:400300009C709D109E069FD5A042A108A280A31CA494A502A65DA702AB02AD10B220B380B610C0FFC2FFC4FFCAFFCCEBCE3FD630DE80E212E410E60AE840EA01EC80000C29
:4003400002F1040C06100A040E021080120C140C164018011A021C031EFC22022608280C2A202CFF34FF3E10560858045B045D905F0180088121830284C085218601870427
:4003800088088B108D2F8E04900291019204941A96209740980899209B019C109E209F2FA046A121A238A30EA408A808A921AB08AD0FAE80B23FB360B480B51FB640B71FD4
:4003C000B908BE44C203C60EC804C9FFCAFFCBFFCF83D804D904DA04DB04DC99DD09DF01000801020309050A070909080A820B040D0A0F091141120813081402152A192051
:400400001D041F9024022508268027082A082B102C402D402E10351437813CA03D0540804240462057405A015B415EA6600263026540661668026A406D90835089408B01C6
:400440008E409142920693489422953D96B197809808994B9A109CF29D309F14A040A31CA480A54AA604A702A814AC08B780C0FFC2FFC4FFCAB0CCF0CEF0D040D6F8D8F8B5
:40048000E280E66CE802EC1000200250061008400A3010621208146216041A031C801D01207C2201261C2A102E10306031013280341F3E043F01580B59045B045C095F01FF
:4004C000802381C8820C85C8860189C88A108C2F8D228FCC9130922F9302942395C8960899029A409C239F48A020A1C8A202A423A604A780A880A9D4AC0FAD80AF01B01FA9
:40050000B161B260B380B480B51FB808B902BE10BF14C062C702C810C9FFCAFFCBFFCD20CEF0D110D804D904DA04DB04DC99DD09DF01E108E240E340E480E640E7400040C6
:40054000012102200505070409220A110C800E040F6010481144148016221710180419021B0C1C201D011E0420C0212122092501281029022B052E202F04311A32603504B8
:4005800036443842392840044A0850085F80660267806B026E807C02810188108B028D018F40900891409348951096209780980899489A109CA29D019E029F14A351A54004
:4005C000A604A702AB80AF40B548C07FC2FFC4FFCA0FCC1FCE0FD004D204D610DE80E008E220E680EA81012002010340070308100A080B100F10100413101408161217100D
:400600001B1C1C1B1D401F20217C2301250227042B102D022F083001316032023418351F36043E40580B590B5B045C995F01804082308610888089018A308B028D028F0133
:4006400090E2920894E296049A109E03A0FCA201A420A6D0AE1CAF01B2E0B301B502B61FB928BBC0BF14D608D80BD918DB04DC99DD90DF0100110501071408800A600C019B
:400680000D100E0A0F60121013041480162218101A801B501D401E121F0C23442409250828812A202B182F02310132403314380139203B843D905C205F80640867026A40A1
:4006C0006D40780279407B017C027F0189088E10C07AC2FDC4B0CA1FCC0FCE3FD630D830DE91E63080028E409240A402AD40B301E204E640EC01EE0A878092409D409F8071
:40070000A402A701A810B614E202EA40EC010040021105C20724082009200A110BC00D020F4810F11202139014F11604179C18101AE11B901E0E1F0320F1220823902480D4
:40074000261127902B902DFC2F0134F035E0360F371F3A803B20580B590B5B045C995F018001821A8411850186068C059C01A703AC05AD03B101B20CB302B410B501B603BE
:40078000BE10BF11C006C6ECC80CC9FFCAFFCBFFD004D601D804D904DA04DB04DC99DD09DF01E2C00080010803800410050109800C420E080F201140166019681B801C02B3
:4007C0001E081F6020042106220223842510264029482C422F6435013640371439083A403B403D203E083F4045104604492050105180680869056A106B236F027040713001
:40080000728173087C127F04842091529280934894A4952596119784980299039A289B149C309D109E069FE1A042A10CA288A314A40CA522A655A722A920B4C8B640B7042C
:40084000C05DC2F1C431CAFACCF0CE72D204DE80E202E608EE10000101120324040706080760088009800A100C0F0D700EF00F01104011421280131014801620187019069A
:400880001A801C011D0420082104220424012708280829042A022E0F2F10318032FF3370350F39203E043F01580459045B045C905F018210838487848A108B848D168E10FE
:4008C0008F41901C920193E49402960497849A1C9B189E03A002A101A208A306A510A610A722AA10AB84ADE7AF08B5F8B61FB707BB80D80BD90BDC99DF01008001050208F7
:400900000410058109080A810B040C020E0A0F2010081202130A166819081E02200821142211232425802610290A2A802B042C022E222F20329235013614395A3C123D0820
:400940003E083F40458046024801498058105B206A407B307C028240860190109157928193409520980299039AE09B0A9C109E02A04BA10CA28EA314A404A611AB40AF2038
:40098000B001C0DFC2FFC47FCAFFCCEDCEFFDE80E402EA80EE021B011F0232203302350836803B408908C630CCF0CE1030013180344037043A013F80822088028B1097407A
:4009C0009D08A108A620AE80AF01CCF0CE60E6805108560883408508932097409A089C419D089F04A002A108A580A740AA09D460E220E610EA20EE808108844185809320A9
:400A000097409C41A002A580AB04E2C0E650EA8014407002C404DC015840601084028C108D20A40AA808B040D401D802E008E402EA011B01811087019A109C40A120A4082A
:400A4000AE10C6080A040B200E200F01861487108E0297019A109D10A120A408AB01B040C20FE408E8012620932097409902A002A108B502C82052015310550870027502C3
:400A80008E0193209902A002A108AA20AF40D4E0DC80DE20EC80EE40051008080C080F041F105120561059206202840887049A109D10A120A202A408C001C20DC601D407CD
:400AC000D80285209D20AF1001010B010D010F0111011D0100FF01AB02021105BF0000A09F001F000000000000000000100000004000000000000000C0000000FF0000B8F6
:400B00004700470000010000800000028200820000000000000707000700000027001801270018010004000000050000000000000000000000000000000000000000000002
:400B40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075
:400B80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035
:400BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F5
:400C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B4
:400C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074
:400C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034
:400CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F4
:400D000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B3
:400D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073
:400D80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033
:400DC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F3
:400E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B2
:400E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000072
:400E80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032
:400EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F2
:400F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B1
:400F40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000071
:400F80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031
:400FC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F1
:4010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:0200000005AB4E
:0200000090CF9F
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E321614AC
:0C00000000012E16106900002E30A138FF
:00000001FF

View File

@@ -814,9 +814,9 @@
</Group>
<Group key="Component">
<Group key="v1">
<Data key="cy_boot" value="cy_boot_v5_80" />
<Data key="cy_boot" value="cy_boot_v6_10" />
<Data key="Em_EEPROM_Dynamic" value="Em_EEPROM_Dynamic_v2_20" />
<Data key="LIN_Dynamic" value="LIN_Dynamic_v5_0" />
<Data key="LIN_Dynamic" value="LIN_Dynamic_v6_0" />
</Group>
</Group>
<Data key="DataVersionKey" value="2" />
@@ -848,6 +848,7 @@
<Data key="efd5f185-0c32-4824-ba72-3ceb5356f5a7" value="Clock_1" />
</Group>
<Group key="Pin">
<Data key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b" value="Pin_2" />
<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" />
@@ -858,6 +859,7 @@
<Data key="472f8fdb-f772-44fb-8897-cc690694237b" value="WDATA" />
<Data key="736cb12b-c863-43d4-a8f0-42f06023f8b5" value="SIDE1" />
<Data key="4249c923-fcff-453b-8629-bec6fddd00c1" value="STEP" />
<Data key="27315b0e-6a8c-4b7f-be77-73ab434fa803" value="Pin_1" />
<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" />
@@ -869,6 +871,7 @@
<Data key="c5367cde-21d5-4866-9a32-d16abfea0c61" value="WPT" />
<Data key="d19368c5-6855-41bb-a9ff-808938abef00" value="INDEX" />
<Data key="e9f14b5a-b2bf-49b8-98f3-d7b5a43ace8d" value="DRVSB" />
<Data key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d" value="LOW_CURRENT" />
<Data key="e851a3b9-efb8-48be-bbb8-b303b216c393" value="INDEX300" />
<Data key="e51063a9-4fad-40c7-a06b-7cc4b137dc18" value="DSKCHG" />
<Data key="ea7ee228-8b3f-426c-8bb8-cd7a81937769" value="DIR" />
@@ -3963,6 +3966,11 @@
</Group>
</Group>
<Group key="Pin2">
<Group key="3e1862bb-be82-47b0-9549-7ebfe76b6f7b">
<Group key="0">
<Data key="Port Format" value="0,6" />
</Group>
</Group>
<Group key="4a398466-709f-4228-9500-96178658e13e">
<Group key="0">
<Data key="Port Format" value="1,5" />
@@ -4064,6 +4072,11 @@
<Data key="Port Format" value="1,0" />
</Group>
</Group>
<Group key="27315b0e-6a8c-4b7f-be77-73ab434fa803">
<Group key="0">
<Data key="Port Format" value="0,7" />
</Group>
</Group>
<Group key="1425177d-0d0e-4468-8bcc-e638e5509a9b">
<Group key="0">
<Data key="Port Format" value="12,6" />
@@ -4149,6 +4162,11 @@
<Data key="Port Format" value="12,3" />
</Group>
</Group>
<Group key="e16b5ef8-00d3-40a4-bc1c-194983c8eb3d">
<Group key="0">
<Data key="Port Format" value="3,2" />
</Group>
</Group>
<Group key="e851a3b9-efb8-48be-bbb8-b303b216c393">
<Group key="0">
<Data key="Port Format" value="3,0" />

View File

@@ -26,33 +26,12 @@
<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="patcher.vbs" persistent="patcher.vbs">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="OTHER;;;;" />
<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="protocol.h" persistent="..\protocol.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="crunch.c" persistent="..\lib\common\crunch.c">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;;;;" />
<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="crunch.h" persistent="..\lib\common\crunch.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>
@@ -2795,6 +2774,138 @@
</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="Pin_2" persistent="">
<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="Pin_2_aliases.h" persistent="Generated_Source\PSoC5\Pin_2_aliases.h">
<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="Pin_2.c" persistent="Generated_Source\PSoC5\Pin_2.c">
<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="Pin_2.h" persistent="Generated_Source\PSoC5\Pin_2.h">
<Hidden v="True" />
</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>
<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="Pin_1" persistent="">
<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="Pin_1_aliases.h" persistent="Generated_Source\PSoC5\Pin_1_aliases.h">
<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="Pin_1.c" persistent="Generated_Source\PSoC5\Pin_1.c">
<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="Pin_1.h" persistent="Generated_Source\PSoC5\Pin_1.h">
<Hidden v="True" />
</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>
<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="TK43_REG" 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="TK43_REG.h" persistent="Generated_Source\PSoC5\TK43_REG.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="TK43_REG.c" persistent="Generated_Source\PSoC5\TK43_REG.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="TK43_REG_PM.c" persistent="Generated_Source\PSoC5\TK43_REG_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>
<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="TK43" 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="TK43_aliases.h" persistent="Generated_Source\PSoC5\TK43_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="TK43.c" persistent="Generated_Source\PSoC5\TK43.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="TK43.h" persistent="Generated_Source\PSoC5\TK43.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>
@@ -3183,7 +3294,7 @@
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Optimization Level" v="" />
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Link Time Optimization" v="" />
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@Linker@Optimization@SHARED Fat LTO objects" v="" />
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Pre Build Commands" v="cscript patcher.vbs" />
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Pre Build Commands" v="" />
<name_val_pair name="c9323d49-d323-40b8-9b59-cc008d68a989@Release@CortexM3@User Commands@General@Post Build Commands" v="" />
</name>
</platform>
@@ -3362,8 +3473,8 @@
</platforms>
<project_current_platform v="c9323d49-d323-40b8-9b59-cc008d68a989" />
<last_selected_tab v="Cypress" />
<WriteAppVersionLastSavedWith v="4.2.0.641" />
<WriteAppMarketingVersionLastSavedWith v=" 4.2" />
<WriteAppVersionLastSavedWith v="4.4.0.80" />
<WriteAppMarketingVersionLastSavedWith v=" 4.4" />
<project_id v="ff3eb327-f593-4eb3-a00f-72497469e963" />
<GenerateDescriptionFiles v="False" />
</CyGuid_49cfd574-032a-4a64-b7be-d4eeeaf25e43>
@@ -3375,11 +3486,9 @@
<library_dep persistent="${CyRoot}\psoc\content\default\CyAnnotationLibrary\CyAnnotationLibrary.cylib\CyAnnotationLibrary.cyprj" />
</library_deps>
<CyGuid_b0d670ad-d48f-47cb-9d0b-b1642bab195c type_name="CyDesigner.Common.Base.CyExprTypeMgr" version="1" />
<ignored_deps>
<library_dep persistent="C:\Users\dg\Documents\PSoC Creator\4.1\Downloads ( 4.1).cylib\Downloads ( 4.1).cyprj" />
</ignored_deps>
<ignored_deps />
</CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37>
<boot_component v="" />
<current_generation v="138" />
<current_generation v="150" />
</CyGuid_fec8f9e8-2365-4bdb-96d3-a4380222e01b>
</CyXmlSerializer>

View File

@@ -1,7 +1,6 @@
//`#start header` -- edit after this line, do not edit this line
`include "cypress.v"
`include "../SuperCounter/SuperCounter.v"
//`#end` -- edit above this line, do not edit this line
// Generated on 12/11/2019 at 21:18
@@ -9,7 +8,7 @@
module Sampler (
output [2:0] debug_state,
output reg [7:0] opcode,
output req,
output reg req,
input clock,
input index,
input rdata,
@@ -19,90 +18,76 @@ module Sampler (
//`#start body` -- edit after this line, do not edit this line
localparam STATE_RESET = 0;
localparam STATE_WAITING = 1;
localparam STATE_OPCODE = 2;
// NOTE: Reset pulse is used in both clock domains, and must be long enough
// to be detected in both.
reg [1:0] state;
reg [6:0] counter;
reg [5:0] counter;
reg oldsampleclock;
wire sampleclocked;
assign sampleclocked = !oldsampleclock && sampleclock;
reg index_edge;
reg rdata_edge;
reg oldindex;
wire indexed;
assign indexed = !oldindex && index;
reg req_toggle;
wire rdataed;
reg oldrdata;
assign rdataed = !oldrdata && rdata;
reg rdata_toggle;
reg old_rdata_toggle;
assign req = (state == STATE_OPCODE);
reg index_toggle;
reg old_index_toggle;
always @(posedge clock)
always @(posedge rdata)
begin
rdata_toggle <= ~rdata_toggle;
end
always @(posedge index)
begin
index_toggle <= ~index_toggle;
end
always @(posedge sampleclock)
begin
if (reset)
begin
state <= STATE_RESET;
opcode <= 0;
oldsampleclock <= 0;
oldindex <= 0;
oldrdata <= 0;
old_rdata_toggle <= 0;
old_index_toggle <= 0;
index_edge <= 0;
rdata_edge <= 0;
counter <= 0;
req_toggle <= 0;
end
else
case (state)
STATE_RESET:
state <= STATE_WAITING;
STATE_WAITING:
begin
/* If something has happened, emit any necessary interval byte. */
if ((rdataed || indexed) && (counter != 0))
begin
opcode <= {0, counter};
state <= STATE_OPCODE;
end
else if (indexed)
begin
oldindex <= 1;
opcode <= 8'h81;
state <= STATE_OPCODE;
end
else if (rdataed)
begin
oldrdata <= 1;
opcode <= 8'h80;
state <= STATE_OPCODE;
end
else if (sampleclocked)
begin
oldsampleclock <= 1;
if (counter == 7'h7f)
begin
opcode <= {0, counter};
state <= STATE_OPCODE;
end
counter <= counter + 1;
end
/* Reset state once we've done the thing. */
if (oldrdata && !rdata)
oldrdata <= 0;
if (oldindex && !index)
oldindex <= 0;
if (oldsampleclock && !sampleclock)
oldsampleclock <= 0;
end
STATE_OPCODE: /* opcode or interval byte sent here */
begin
state <= STATE_WAITING;
counter <= 0;
end
endcase
begin
/* If data_toggle or index_toggle have changed state, this means that they've
* gone high since the last sampleclock. */
index_edge <= index_toggle != old_index_toggle;
old_index_toggle <= index_toggle;
rdata_edge <= rdata_toggle != old_rdata_toggle;
old_rdata_toggle <= rdata_toggle;
if (rdata_edge || index_edge || (counter == 6'h3f)) begin
opcode <= { rdata_edge, index_edge, counter };
req_toggle <= ~req_toggle;
counter <= 1; /* remember to count this tick */
end else begin
counter <= counter + 1;
end
end
end
reg req_toggle_q;
always @(posedge clock)
begin
if (reset) begin
req_toggle_q <= 0;
req <= 0;
end else begin
req_toggle_q <= req_toggle;
req <= (req_toggle != req_toggle_q);
end
end
//`#end` -- edit above this line, do not edit this line

View File

@@ -19,19 +19,15 @@ module Sequencer (
//`#start body` -- edit after this line, do not edit this line
localparam STATE_LOAD = 0;
localparam STATE_WAITING = 1;
localparam STATE_PULSING = 2;
localparam STATE_INDEXING = 3;
localparam STATE_WRITING = 1;
localparam OPCODE_PULSE = 8'h80;
localparam OPCODE_INDEX = 8'h81;
reg [1:0] state;
reg [6:0] countdown;
reg state;
reg [5:0] countdown;
reg pulsepending;
assign req = (!reset && (state == STATE_LOAD));
assign wdata = (state == STATE_PULSING);
assign debug_state = state;
assign wdata = (!reset && (state == STATE_WRITING) && (countdown == 0) && pulsepending);
assign debug_state = 0;
reg olddataclock;
wire dataclocked;
@@ -52,49 +48,43 @@ begin
begin
state <= STATE_LOAD;
countdown <= 0;
pulsepending <= 0;
oldsampleclock <= 0;
end
else
begin
if (!oldsampleclock && sampleclock)
sampleclocked <= 1;
oldsampleclock <= sampleclock;
case (state)
STATE_LOAD:
/* Wait for a posedge on dataclocked, indicating an opcode has
begin
/* A posedge on dataclocked indicates that another opcode has
* arrived. */
if (dataclocked)
case (opcode)
OPCODE_PULSE:
state <= STATE_PULSING;
OPCODE_INDEX:
state <= STATE_INDEXING;
default:
begin
countdown <= opcode[6:0];
state <= STATE_WAITING;
end
endcase
begin
pulsepending <= opcode[7];
if (opcode[5:0] == 0)
countdown <= 0;
else
countdown <= opcode[5:0] - 1; /* compensate for extra tick in state machine */
state <= STATE_WRITING;
end
end
STATE_WAITING:
STATE_WRITING:
begin
if (sampleclocked)
begin
sampleclocked <= 0;
countdown <= countdown - 1;
/* Nasty fudge factor here to account for one to two
* sample ticks lost per pulse. */
if (countdown <= 2)
if (countdown == 0)
state <= STATE_LOAD;
else
countdown <= countdown - 1;
sampleclocked <= 0;
end
STATE_PULSING:
state <= STATE_LOAD;
STATE_INDEXING:
if (indexed)
state <= STATE_LOAD;
end
endcase
end
end

View File

Binary file not shown.

View File

@@ -5,7 +5,6 @@
#include <setjmp.h>
#include "project.h"
#include "../protocol.h"
#include "../lib/common/crunch.h"
#define MOTOR_ON_TIME 5000 /* milliseconds */
#define STEP_INTERVAL_TIME 6 /* ms */
@@ -14,14 +13,18 @@
#define DISKSTATUS_WPT 1
#define DISKSTATUS_DSKCHG 2
#define STEP_TOWARDS0 1
#define STEP_AWAYFROM0 0
#define STEP_TOWARDS0 0
#define STEP_AWAYFROM0 1
static bool drive0_present;
static bool drive1_present;
static volatile uint32_t clock = 0; /* ms */
static volatile bool index_irq = false;
/* Duration in ms. 0 causes every pulse to be an index pulse. Durations since
* last pulse greater than this value imply sector pulse. Otherwise is an index
* pulse. */
static volatile uint32_t hardsec_index_threshold = 0;
static bool motor_on = false;
static uint32_t motor_on_time = 0;
@@ -34,7 +37,6 @@ static struct set_drive_frame current_drive_flags;
static uint8_t td[BUFFER_COUNT];
static uint8_t dma_buffer[BUFFER_COUNT][BUFFER_SIZE] __attribute__((aligned()));
static uint8_t usb_buffer[BUFFER_SIZE] __attribute__((aligned()));
static uint8_t xfer_buffer[BUFFER_SIZE] __attribute__((aligned()));
static uint8_t dma_channel;
#define NEXT_BUFFER(b) (((b)+1) % BUFFER_COUNT)
@@ -71,12 +73,41 @@ static void system_timer_cb(void)
CY_ISR(index_irq_cb)
{
index_irq = true;
/* Hard sectored media has sector pulses at the beginning of every sector
* and the index pulse is an extra pulse in the middle of the last sector.
* When the extra pulse is seen, the next sector pulse is also the start of
* the track. */
static bool hardsec_index_irq_primed = false;
static uint32_t hardsec_last_pulse_time = 0;
uint32_t index_pulse_duration = clock - hardsec_last_pulse_time;
if (!hardsec_index_threshold)
{
index_irq = true;
hardsec_index_irq_primed = false;
}
else
{
/* It's only an index pulse if the previous pulse is less than
* the threshold.
*/
index_irq = (index_pulse_duration <= hardsec_index_threshold) ?
hardsec_index_irq_primed : false;
if (index_irq)
hardsec_index_irq_primed = false;
else
hardsec_index_irq_primed =
index_pulse_duration <= hardsec_index_threshold;
hardsec_last_pulse_time = clock;
}
/* Stop writing the instant the index pulse comes along; it may take a few
* moments for the main code to notice the pulse, and we don't want to overwrite
* the beginning of the track. */
ERASE_REG_Write(0);
if (index_irq)
ERASE_REG_Write(0);
}
CY_ISR(capture_dma_finished_irq_cb)
@@ -116,7 +147,7 @@ static void set_drive_flags(struct set_drive_frame* flags)
current_drive_flags = *flags;
DRIVESELECT_REG_Write(flags->drive ? 2 : 1); /* select drive 1 or 0 */
DENSITY_REG_Write(flags->high_density); /* density bit */
DENSITY_REG_Write(!flags->high_density); /* double density bit */
INDEX_REG_Write(flags->index_mode);
}
@@ -151,6 +182,12 @@ static void wait_until_writeable(int ep)
;
}
static void wait_until_readable(int ep)
{
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
;
}
static void send_reply(struct any_frame* f)
{
print("reply 0x%02x", f->f.type);
@@ -168,9 +205,15 @@ static void send_error(int code)
/* buffer must be big enough for a frame */
static int usb_read(int ep, uint8_t buffer[FRAME_SIZE])
{
if (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_FULL)
{
USBFS_EnableOutEP(ep);
wait_until_readable(ep);
}
int length = USBFS_GetEPCount(ep);
USBFS_ReadOutEP(ep, buffer, length);
while (USBFS_GetEPState(ep) == USBFS_OUT_BUFFER_FULL)
while (USBFS_GetEPState(ep) != USBFS_OUT_BUFFER_EMPTY)
;
return length;
}
@@ -239,6 +282,7 @@ static void seek_to(int track)
CyWdtClear();
}
CyDelay(STEP_SETTLING_TIME);
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
print("finished seek");
}
@@ -257,7 +301,7 @@ static void cmd_recalibrate(void)
send_reply(&r);
}
static void cmd_measure_speed(struct any_frame* f)
static void cmd_measure_speed(struct measurespeed_frame* f)
{
start_motor();
@@ -267,7 +311,7 @@ static void cmd_measure_speed(struct any_frame* f)
while (!index_irq)
{
elapsed = clock - start_clock;
if (elapsed > 1000)
if (elapsed > 1500)
{
elapsed = 0;
break;
@@ -276,10 +320,14 @@ static void cmd_measure_speed(struct any_frame* f)
if (elapsed != 0)
{
index_irq = false;
int target_pulse_count = f->hard_sector_count + 1;
start_clock = clock;
while (!index_irq)
elapsed = clock - start_clock;
for (int x=0; x<target_pulse_count; x++)
{
index_irq = false;
while (!index_irq)
elapsed = clock - start_clock;
}
}
DECLARE_REPLY_FRAME(struct speed_frame, F_FRAME_MEASURE_SPEED_REPLY);
@@ -319,13 +367,7 @@ static void cmd_bulk_read_test(struct any_frame* f)
CyWdtClear();
for (int y=0; y<256; y++)
{
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_FULL)
;
USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, buffer, sizeof(buffer));
while (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) != USBFS_OUT_BUFFER_EMPTY)
;
usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
for (unsigned z=0; z<sizeof(buffer); z++)
{
if (buffer[z] != (uint8)(x+y+z))
@@ -356,7 +398,7 @@ static void deinit_dma(void)
static void init_capture_dma(void)
{
dma_channel = SAMPLER_DMA_DmaInitialize(
2 /* bytes */,
1 /* bytes */,
true /* request per burst */,
HI16(CYDEV_PERIPH_BASE),
HI16(CYDEV_SRAM_BASE));
@@ -377,9 +419,9 @@ static void init_capture_dma(void)
static void cmd_read(struct read_frame* f)
{
SIDE_REG_Write(f->side);
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
/* Do slow setup *before* we go into the real-time bit. */
{
@@ -397,16 +439,14 @@ static void cmd_read(struct read_frame* f)
if (f->synced)
{
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
index_irq = false;
hardsec_index_threshold = 0;
}
crunch_state_t cs = {};
cs.outputptr = xfer_buffer;
cs.outputlen = BUFFER_SIZE;
dma_writing_to_td = 0;
dma_reading_from_td = -1;
dma_underrun = false;
@@ -463,54 +503,22 @@ static void cmd_read(struct read_frame* f)
{
/* Otherwise, there's a block waiting, so attempt to send it. */
uint8_t dma_buffer_usage = 0;
while (dma_buffer_usage < BUFFER_SIZE)
{
cs.inputptr = dma_buffer[dma_reading_from_td] + dma_buffer_usage;
cs.inputlen = BUFFER_SIZE - dma_buffer_usage;
crunch(&cs);
dma_buffer_usage += BUFFER_SIZE - cs.inputlen;
count++;
/* If there is no available space in the output buffer, flush the buffer via
* USB and go again. */
if (cs.outputlen == 0)
{
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
memcpy(usb_buffer, xfer_buffer, FRAME_SIZE);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
cs.outputptr = xfer_buffer;
cs.outputlen = BUFFER_SIZE;
}
}
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, dma_buffer[dma_reading_from_td], BUFFER_SIZE);
count++;
dma_reading_from_td = NEXT_BUFFER(dma_reading_from_td);
}
}
abort:;
bool saved_dma_underrun = dma_underrun;
donecrunch(&cs);
/* Terminate the transfer (all transfers are an exact number of fragments). */
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
/* If there's a complete packet waiting, send it. */
if (cs.outputlen != BUFFER_SIZE)
{
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
}
if ((cs.outputlen != 0) && (cs.outputlen != BUFFER_SIZE))
{
/* If there's a partial packet waiting, send it; this will also terminate the transfer. */
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, usb_buffer, BUFFER_SIZE-cs.outputlen);
}
else
{
/* Otherwise just terminate the transfer. */
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
}
USBFS_LoadInEP(FLUXENGINE_DATA_IN_EP_NUM, NULL, 0);
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
deinit_dma();
STEP_REG_Write(0);
if (saved_dma_underrun)
{
print("underrun after %d packets");
@@ -556,9 +564,10 @@ static void cmd_write(struct write_frame* f)
return;
}
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
{
uint8_t i = CyEnterCriticalSection();
REPLAY_FIFO_SET_LEVEL_MID;
@@ -566,41 +575,21 @@ static void cmd_write(struct write_frame* f)
REPLAY_FIFO_SINGLE_BUFFER_UNSET;
CyExitCriticalSection(i);
}
seek_to(current_track);
init_replay_dma();
bool writing = false; /* to the disk */
bool finished = false;
int packets = f->bytes_to_write / FRAME_SIZE;
bool finished = (packets == 0);
int count_written = 0;
int count_read = 0;
int packetwaiting = 0;
dma_writing_to_td = 0;
dma_reading_from_td = -1;
dma_underrun = false;
crunch_state_t cs = {};
cs.outputlen = BUFFER_SIZE;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
int old_reading_from_td = -1;
for (;;)
{
CyWdtClear();
/* Make sure that we always have a USB read in progress whenever possible. */
if (!finished && !packetwaiting)
{
/* There is no read in progress; has data arrived in the external USB buffer? */
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
{
/* Yes, data has arrived, so initiate the copy. */
packetwaiting = USBFS_ReadOutEP(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer, FRAME_SIZE);
}
}
//CyWdtClear();
/* Read data from USB into the buffers. */
@@ -609,59 +598,22 @@ static void cmd_write(struct write_frame* f)
if (writing && (dma_underrun || index_irq))
goto abort;
/* Read crunched data, if necessary. */
if (cs.inputlen == 0)
uint8_t* buffer = dma_buffer[dma_writing_to_td];
if (finished)
{
if (finished)
{
/* There's no more data to read, so fake some. */
for (int i=0; i<BUFFER_SIZE; i++)
xfer_buffer[i+0] = 0x7f;
cs.inputptr = xfer_buffer;
cs.inputlen = BUFFER_SIZE;
}
else if (packetwaiting)
{
/* There's a USB read into usb_buffer in progress, so check if it's finished. */
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_EMPTY)
{
/* It's done, so copy out the data. */
memcpy(xfer_buffer, usb_buffer, FRAME_SIZE);
cs.inputptr = xfer_buffer;
cs.inputlen = packetwaiting;
count_read++;
if ((packetwaiting < FRAME_SIZE) || (count_read == packets))
finished = true;
else
{
/* Wait for more USB data to show up. */
packetwaiting = 0;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
}
}
}
/* There's no more data to read, so fake some. */
memset(buffer, 0x3f, BUFFER_SIZE);
}
/* If there *is* data waiting in the buffer, uncrunch it. */
if (cs.inputlen != 0)
else
{
cs.outputptr = dma_buffer[dma_writing_to_td] + BUFFER_SIZE - cs.outputlen;
uncrunch(&cs);
if (cs.outputlen == 0)
{
/* Completed a DMA buffer; queue it for writing. */
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
cs.outputlen = BUFFER_SIZE;
}
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, buffer);
count_read++;
if (count_read == packets)
finished = true;
}
dma_writing_to_td = NEXT_BUFFER(dma_writing_to_td);
/* Once all the buffers are full, start writing. */
@@ -679,7 +631,7 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -701,7 +653,6 @@ static void cmd_write(struct write_frame* f)
}
}
abort:
print("done %d %d", dma_reading_from_td, dma_writing_to_td);
SEQUENCER_DMA_FINISHED_IRQ_Disable();
SEQUENCER_CONTROL_Write(1); /* reset */
@@ -715,33 +666,29 @@ abort:
}
print("p=%d cr=%d cw=%d f=%d w=%d index=%d underrun=%d", packets, count_read, count_written, finished, writing, index_irq, dma_underrun);
hardsec_index_threshold = 0;
if (!finished)
{
/* There's still some data to read, so just read and blackhole it ---
* easier than trying to terminate the connection. */
while (count_read != packets)
{
if (USBFS_GetEPState(FLUXENGINE_DATA_OUT_EP_NUM) == USBFS_OUT_BUFFER_FULL)
{
int length = usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
if (length < FRAME_SIZE)
break;
USBFS_EnableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
count_read++;
}
(void) usb_read(FLUXENGINE_DATA_OUT_EP_NUM, usb_buffer);
count_read++;
}
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
}
deinit_dma();
print("write finished");
STEP_REG_Write(0);
if (dma_underrun)
{
print("underrun!");
send_error(F_ERROR_UNDERRUN);
return;
}
print("success");
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_WRITE_REPLY);
send_reply((struct any_frame*) &r);
}
@@ -749,10 +696,11 @@ abort:
static void cmd_erase(struct erase_frame* f)
{
SIDE_REG_Write(f->side);
seek_to(current_track);
seek_to(current_track);
/* Disk is now spinning. */
print("start erasing");
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
;
@@ -761,6 +709,7 @@ static void cmd_erase(struct erase_frame* f)
while (!index_irq)
;
ERASE_REG_Write(0);
hardsec_index_threshold = 0;
print("stop erasing");
DECLARE_REPLY_FRAME(struct any_frame, F_FRAME_ERASE_REPLY);
@@ -886,7 +835,7 @@ static void handle_command(void)
break;
case F_FRAME_MEASURE_SPEED_CMD:
cmd_measure_speed(f);
cmd_measure_speed((struct measurespeed_frame*) f);
break;
case F_FRAME_BULK_WRITE_TEST_CMD:
@@ -926,6 +875,21 @@ static void handle_command(void)
}
}
static void detect_drives(void)
{
current_drive_flags.drive = 0;
start_motor();
drive0_present = home();
stop_motor();
current_drive_flags.drive = 1;
start_motor();
drive1_present = home();
stop_motor();
print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no");
}
int main(void)
{
CyGlobalIntEnable;
@@ -939,23 +903,11 @@ int main(void)
DRIVESELECT_REG_Write(0);
UART_Start();
USBFS_Start(0, USBFS_DWR_VDDD_OPERATION);
USBFS_DisableOutEP(FLUXENGINE_DATA_OUT_EP_NUM);
detect_drives();
CyWdtStart(CYWDT_1024_TICKS, CYWDT_LPMODE_DISABLED);
current_drive_flags.drive = 0;
start_motor();
drive0_present = home();
stop_motor();
current_drive_flags.drive = 1;
start_motor();
drive1_present = home();
stop_motor();
print("drive 0: %s drive 1: %s", drive0_present ? "yes" : "no", drive1_present ? "yes" : "no");
/* UART_PutString("GO\r"); */
for (;;)
{
CyWdtClear();
@@ -971,7 +923,7 @@ int main(void)
{
print("Waiting for USB...");
while (!USBFS_GetConfiguration())
;
CyWdtClear();
print("USB ready");
USBFS_EnableOutEP(FLUXENGINE_CMD_OUT_EP_NUM);
}
@@ -985,3 +937,75 @@ int main(void)
}
}
}
const uint8_t USBFS_MSOS_CONFIGURATION_DESCR[USBFS_MSOS_CONF_DESCR_LENGTH] = {
/* Length of the descriptor 4 bytes */ 0x28u, 0x00u, 0x00u, 0x00u,
/* Version of the descriptor 2 bytes */ 0x00u, 0x01u,
/* wIndex - Fixed:INDEX_CONFIG_DESCRIPTOR */ 0x04u, 0x00u,
/* bCount - Count of device functions. */ 0x01u,
/* Reserved : 7 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
/* bFirstInterfaceNumber */ 0x00u,
/* Reserved */ 0x01u,
/* compatibleId - "WINUSB\0\0" */ 'W', 'I', 'N', 'U', 'S', 'B', 0, 0,
/* subcompatibleID - "00001\0\0" */ '0', '0', '0', '0', '1',
0x00u, 0x00u, 0x00u,
/* Reserved : 6 bytes */ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u
};
const uint8_t USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[224] = {
/* Length; 4 bytes */ 224, 0, 0, 0,
/* Version; 2 bytes */ 0x00, 0x01, /* 1.0 */
/* wIndex */ 0x05, 0x00,
/* Number of sections */ 0x01, 0x00,
/* Property section size */ 214, 0, 0, 0,
/* Property data type */ 0x07, 0x00, 0x00, 0x00, /* 7 = REG_MULTI_SZ Unicode */
/* Property name length */ 42, 0,
/* Property name */ 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0,
'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0,
'a', 0, 'c', 0, 'e', 0, 'G', 0, 'U', 0, 'I', 0,
'D', 0, 's', 0, 0, 0,
/* Property data length */ 158, 0, 0, 0,
/* GUID #1 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
'8', 0, '}', 0, '\0', 0,
/* GUID #2 data */ '{', 0, '3', 0, 'd', 0, '2', 0, '7', 0, '5', 0,
'c', 0, 'f', 0, 'e', 0, '-', 0, '5', 0, '4', 0,
'3', 0, '5', 0, '-', 0, '4', 0, 'd', 0, 'd', 0,
'5', 0, '-', 0, 'a', 0, 'c', 0, 'c', 0, 'a', 0,
'-', 0, '9', 0, 'f', 0, 'b', 0, '9', 0, '9', 0,
'5', 0, 'e', 0, '2', 0, 'f', 0, '6', 0, '3', 0,
'8', 0, '}', 0, '\0', 0, '\0', 0
};
uint8 USBFS_HandleVendorRqst(void)
{
if (!(USBFS_bmRequestTypeReg & USBFS_RQST_DIR_D2H))
return false;
switch (USBFS_bRequestReg)
{
case USBFS_GET_EXTENDED_CONFIG_DESCRIPTOR:
switch (USBFS_wIndexLoReg)
{
case 4:
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_CONFIGURATION_DESCR[0u];
USBFS_currentTD.count = USBFS_MSOS_CONFIGURATION_DESCR[0u];
return USBFS_InitControlRead();
case 5:
USBFS_currentTD.pData = (volatile uint8 *) &USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
USBFS_currentTD.count = USBFS_MSOS_EXTENDED_PROPERTIES_DESCR[0u];
return USBFS_InitControlRead();
}
default:
break;
}
return true;
}

View File

@@ -1,18 +0,0 @@
const READ = 1
const WRITE = 2
filename = "Generated_Source\PSoC5\USBFS_descr.c"
set fso = CreateObject("Scripting.FileSystemObject")
set file = fso.OpenTextFile(filename, READ)
text = file.ReadAll
file.Close
set r = New RegExp
r.MultiLine = True
r.Pattern = "/\* +compatibleID.*\n.*"
text = r.replace(text, "'W', 'I', 'N', 'U', 'S', 'B', 0, 0,")
set file = fso.CreateTextFile(filename, True)
file.Write text
file.Close

View File

@@ -1,21 +1,45 @@
PACKAGES = zlib sqlite3 libusb-1.0
PACKAGES = zlib sqlite3 protobuf
ifeq ($(shell uname),Linux)
PACKAGES += gtk+-3.0
endif
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS =
export CFLAGS = \
-ffunction-sections -fdata-sections
export CXXFLAGS = $(CFLAGS) \
--std=gnu++2a \
-Wno-deprecated-enum-enum-conversion \
-Wno-deprecated-enum-float-conversion
export LDFLAGS = -pthread
export COPTFLAGS = -Os
export LDOPTFLAGS = -Os -s
export LDOPTFLAGS = -Os
export CDBGFLAGS = -O0 -g
export LDDBGFLAGS = -O0 -g
ifeq ($(OS), Windows_NT)
else
ifeq ($(shell uname),Darwin)
else
PACKAGES += libudev
endif
endif
ifeq ($(OS), Windows_NT)
export PROTOC = /mingw32/bin/protoc
export CC = /mingw32/bin/gcc
export CXX = /mingw32/bin/g++
export AR = /mingw32/bin/ar rcs
export AR = /mingw32/bin/ar rc
export RANLIB = /mingw32/bin/ranlib
export STRIP = /mingw32/bin/strip
export CFLAGS += -I/mingw32/include/libusb-1.0
export WINDRES = /mingw32/bin/windres
export CFLAGS += -I/mingw32/include/libusb-1.0 -I/mingw32/include
export LDFLAGS +=
export LIBS = -static -lz -lsqlite3 -lusb-1.0
export LIBS += -L/mingw32/lib -static -lz -lsqlite3 \
-lsetupapi -lwinusb -lole32 -lprotobuf -luuid
export GUILIBS += -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lmsimg32 \
-lcomdlg32 -ld2d1 -ldwrite -lole32 -loleaut32 -loleacc -luuid \
-lwindowscodecs
export EXTENSION = .exe
else
@@ -25,21 +49,41 @@ $(warning These pkg-config packages are installed: $(shell pkg-config --list-all
$(error You must have these pkg-config packages installed: $(PACKAGES))
endif
export PROTOC = protoc
export CC = gcc
export CXX = g++
export AR = ar rcs
export COBJC = clang
export AR = ar rc
export RANLIB = ranlib
export STRIP = strip
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS +=
export LIBS = $(shell pkg-config --libs $(PACKAGES))
export LIBS += $(shell pkg-config --libs $(PACKAGES)) -ldl
export EXTENSION =
ifeq ($(shell uname),Darwin)
AR = ar rcS
RANLIB += -c -no_warning_for_no_symbols
export CC = clang
export CXX = clang++
export COBJC = clang
export LDFLAGS += \
-framework Foundation \
-framework AppKit \
-framework IOKit \
-framework CoreFoundation
endif
endif
export XXD = xxd
CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja -v
@ninja -f .obj/build.ninja -k 0
@if command -v cscope > /dev/null; then cscope -bRq; fi
clean:
@echo CLEAN

View File

@@ -4,6 +4,11 @@ FluxEngine
(If you're reading this on GitHub, the formatting's a bit messed up. [Try the
version on cowlark.com instead.](http://cowlark.com/fluxengine/)
**Breaking news!** As of 2021-05-21, the command line environment has changed
_substantially_ (to make it more consistent and flexible, and allow some new
backend features like multi-format IBM scheme disks, which are popular with
CP/M). If things don't work the way you expect, please check the documentation.
What?
-----
@@ -24,17 +29,18 @@ Don't believe me? Watch the demo reel!
<iframe width="373" height="210" src="https://www.youtube.com/embed/m_s1iw8eW7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
**Important note.** On 2019-02-09 I did a hardware redesign and moved the pins on
the board. Sorry for the inconvenience, but it means you don't have to modify
the board any more to make it work. If you built the hardware prior to then,
you'll need to adjust it.
**New!** The FluxEngine client software now works with
[GreaseWeazle](https://github.com/keirf/Greaseweazle/wiki) hardware. So, if you
can't find a PSoC5 development kit, or don't want to use the Cypress Windows
tools for programming it, you can use one of these instead. Very nearly all
FluxEngine features are available with the GreaseWeazle and it works out-of-the
box. See the [dedicated GreaseWeazle documentation page](doc/greaseweazle.md)
for more information.
**Another important note.** On 2019-07-03 I've revamped the build process and
the (command line) user interface. It should be much nicer now, not least in
that there's a single client binary with all the functionality in it. The
interface is a little different, but not much. The build process is now
better (simpler). See [the building](doc/building.md) and
[using](doc/using.md) pages for more details.
**Important note.** On 2020-04-02 I changed the bytecode format (and firmware).
Flux files will need to be upgraded with `fluxengine upgradefluxfile`. The new
format should be more reliable and use way, way less bandwidth. Sorry for the
inconvenience.
Where?
------
@@ -59,8 +65,15 @@ following friendly articles:
- [Using a FluxEngine](doc/using.md) ∾ what to do with your new hardware ∾
flux files and image files ∾ knowing what you're doing
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact science ∾
the sector map ∾ clock detection and the histogram
- [Using GreaseWeazle hardware with the FluxEngine client
software](doc/greaseweazle.md) ∾ what works ∾ what doesn't work ∾ where to
go for help
- [Troubleshooting dubious disks](doc/problems.md) ∾ it's not an exact
science ∾ the sector map ∾ clock detection and the histogram
- [Checking your drive](doc/driveresponse.md) ∾ you can't do that with that ∾
measuring your drive's ability to work with exotic formats
Which?
------
@@ -77,20 +90,20 @@ people who've had it work).
### Old disk formats
| Format | Read? | Write? | Notes |
|:-----------------------------------------|:-----:|:------:|-------|
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | | |
| [Commodore 64 1541](doc/disk-c64.md) | 🦖 | | and probably the other GCR formats |
| [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 |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
| Format | Read? | Write? | Notes |
|:------------------------------------------|:-----:|:------:|-------|
| [IBM PC compatible](doc/disk-ibm.md) | 🦄 | 🦄 | and compatibles (like the Atari ST) |
| [Acorn ADFS](doc/disk-acornadfs.md) | 🦄 | 🦖* | single- and double- sided |
| [Acorn DFS](doc/disk-acorndfs.md) | 🦄 | 🦖* | |
| [Ampro Little Board](doc/disk-ampro.md) | 🦖 | 🦖* | |
| [Apple II DOS 3.3](doc/disk-apple2.md) | 🦄 | | doesn't do logical sector remapping |
| [Amiga](doc/disk-amiga.md) | 🦄 | 🦄 | |
| [Commodore 64 1541/1581](doc/disk-c64.md) | 🦄 | 🦄 | and probably the other formats |
| [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 |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
{: .datatable }
`*`: these formats are variations of the generic IBM format, and since the
@@ -110,8 +123,12 @@ 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 |
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
{: .datatable }
### Notes
@@ -195,6 +212,29 @@ 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/emu` contains parts of the OpenBSD C library
code, Todd Miller and William A. Rowe (and probably others). It is licensed
code, maintained by 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.
As an exception, `dep/agg` contains parts of the Anti-Grain Antialiasing
library, written by Maxim Semanarev (and 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.
As an exception, `dep/stb` contains parts of the libstb utility library,
written by Sean T Barett (and others). It is public domain/Unlicense/MIT
licensed, at your choice. Please see the contents of the directory for the full
text.
As an exception, `dep/snowhouse` contains the snowhouse assertion library,
taken from https://github.com/banditcpp/snowhouse. It is Boost Standard License
1.0 licensed. Please see the contents of the directory for the full text.
As an exception, `dep/libusbp` contains the libusbp library, taken from
https://github.com/pololu/libusbp. It is MIT licensed. Please see the contents
of the directory for the full text.
As an exception, `dep/libui` contains the libui GUI library, taken from
https://github.com/andlabs/libui. It is MIT licensed. Please see the contents
of the directory for the full text.

View File

@@ -5,16 +5,6 @@
#define AESLANIER_SECTOR_LENGTH 256
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5)
class Sector;
class Fluxmap;
class AesLanierDecoder : public AbstractDecoder
{
public:
virtual ~AesLanierDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config);
#endif

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message AesLanierDecoderProto {}

View File

@@ -6,7 +6,6 @@
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "bytes.h"
#include "record.h"
#include "fmt/format.h"
#include <string.h>
@@ -24,42 +23,57 @@ static Bytes reverse_bits(const Bytes& input)
return output;
}
AbstractDecoder::RecordType AesLanierDecoder::advanceToNextRecord()
class AesLanierDecoder : public AbstractDecoder
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
public:
AesLanierDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
void decodeSectorRecord()
{
/* Skip ID mark. */
readRawBits(16);
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE*16);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = reverse_bits(bytes);
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
}
/* Check data checksum, which also includes the header and is
* significantly better. */
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createAesLanierDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new AesLanierDecoder(config));
}
void AesLanierDecoder::decodeSectorRecord()
{
/* Skip ID mark. */
readRawBits(16);
const auto& rawbits = readRawBits(AESLANIER_RECORD_SIZE*16);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
const auto& reversed = reverse_bits(bytes);
_sector->logicalTrack = reversed[1];
_sector->logicalSide = 0;
_sector->logicalSector = reversed[2];
/* Check header 'checksum' (which seems far too simple to mean much). */
{
uint8_t wanted = reversed[3];
uint8_t got = reversed[1] + reversed[2];
if (wanted != got)
return;
}
/* Check data checksum, which also includes the header and is
* significantly better. */
_sector->data = reversed.slice(1, AESLANIER_SECTOR_LENGTH);
uint16_t wanted = reversed.reader().seek(0x101).read_le16();
uint16_t got = crc16ref(MODBUS_POLY_REF, _sector->data);
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,5 +1,4 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "amiga.h"
#include "bytes.h"

View File

@@ -9,29 +9,8 @@
#define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoder : public AbstractDecoder
{
public:
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
class AmigaEncoder : public AbstractEncoder
{
public:
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup amigaEncoderFlags;
extern std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config);
extern uint32_t amigaChecksum(const Bytes& bytes);
extern Bytes amigaInterleave(const Bytes& input);

13
arch/amiga/amiga.proto Normal file
View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message AmigaDecoderProto {}
message AmigaEncoderProto {
optional double clock_rate_us = 1
[default=2.00, (help)="Encoded data clock rate."];
optional double post_index_gap_ms = 2
[default=0.5, (help)="Post-index gap before first sector header."];
}

View File

@@ -2,12 +2,12 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "amiga.h"
#include "bytes.h"
#include "fmt/format.h"
#include "lib/decoders/decoders.pb.h"
#include <string.h>
#include <algorithm>
@@ -21,38 +21,65 @@
static const FluxPattern SECTOR_PATTERN(48, AMIGA_SECTOR_RECORD);
AbstractDecoder::RecordType AmigaDecoder::advanceToNextRecord()
class AmigaDecoder : public AbstractDecoder
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
public:
AmigaDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.amiga())
{}
RecordType advanceToNextRecord() override
{
_sector->clock = _fmr->seekToPattern(SECTOR_PATTERN);
if (_fmr->eof() || !_sector->clock)
return UNKNOWN_RECORD;
return SECTOR_RECORD;
}
void decodeSectorRecord() override
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
if (rawbits.size() < (AMIGA_RECORD_SIZE*16))
return;
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin() + 3;
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
Bytes data;
data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->data = data;
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return sectors;
}
private:
const AmigaDecoderProto& _config;
};
std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new AmigaDecoder(config));
}
void AmigaDecoder::decodeSectorRecord()
{
const auto& rawbits = readRawBits(AMIGA_RECORD_SIZE*16);
const auto& rawbytes = toBytes(rawbits).slice(0, AMIGA_RECORD_SIZE*2);
const auto& bytes = decodeFmMfm(rawbits).slice(0, AMIGA_RECORD_SIZE);
const uint8_t* ptr = bytes.begin() + 3;
Bytes header = amigaDeinterleave(ptr, 4);
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
_sector->logicalTrack = header[1] >> 1;
_sector->logicalSide = header[1] & 1;
_sector->logicalSector = header[2];
uint32_t wantedheaderchecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotheaderchecksum = amigaChecksum(rawbytes.slice(6, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(62, 1024));
_sector->data.clear();
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,23 +1,12 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "amiga.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
FlagGroup amigaEncoderFlags;
static DoubleFlag clockRateUs(
{ "--clock-rate" },
"Encoded data clock rate (microseconds).",
2.00);
static DoubleFlag postIndexGapMs(
{ "--post-index-gap" },
"Post-index gap before first sector header (milliseconds).",
0.5);
#include "image.h"
#include "arch/amiga/amiga.pb.h"
#include "lib/encoders/encoders.pb.h"
static bool lastBit;
@@ -33,13 +22,14 @@ static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vec
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = cursor - i - 1;
@@ -49,31 +39,44 @@ static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data,
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
assert(!(bytes.size() & 3));
Bytes interleaved = amigaInterleave(bytes);
encodeMfm(bits, cursor, interleaved, lastBit);
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static void write_interleaved_bytes(std::vector<bool>& bits, unsigned& cursor, uint32_t data)
{
Bytes b(4);
ByteWriter bw(b);
bw.write_be32(data);
write_interleaved_bytes(bits, cursor, b);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 528))
Error() << "unsupported sector size --- you must pick 512 or 528";
uint32_t checksum = 0;
auto write_interleaved_bytes = [&](const Bytes& bytes)
{
Bytes interleaved = amigaInterleave(bytes);
Bytes mfm = encodeMfm(interleaved, lastBit);
checksum ^= amigaChecksum(mfm);
checksum &= 0x55555555;
write_bits(bits, cursor, mfm);
};
auto write_interleaved_word = [&](uint32_t word)
{
Bytes b(4);
b.writer().write_be32(word);
write_interleaved_bytes(b);
};
write_bits(bits, cursor, AMIGA_SECTOR_RECORD, 6*8);
std::vector<bool> headerBits(20*16);
unsigned headerCursor = 0;
checksum = 0;
Bytes header =
{
0xff, /* Amiga 1.0 format byte */
@@ -81,49 +84,79 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
(uint8_t) sector->logicalSector,
(uint8_t) (AMIGA_SECTORS_PER_TRACK - sector->logicalSector)
};
write_interleaved_bytes(headerBits, headerCursor, header);
write_interleaved_bytes(header);
Bytes recoveryInfo(16);
if (sector->data.size() == 528)
recoveryInfo = sector->data.slice(512, 16);
write_interleaved_bytes(headerBits, headerCursor, recoveryInfo);
write_interleaved_bytes(recoveryInfo);
write_interleaved_word(checksum);
std::vector<bool> dataBits(512*16);
unsigned dataCursor = 0;
write_interleaved_bytes(dataBits, dataCursor, sector->data);
write_bits(bits, cursor, headerBits);
uint32_t headerChecksum = amigaChecksum(toBytes(headerBits));
write_interleaved_bytes(bits, cursor, headerChecksum);
uint32_t dataChecksum = amigaChecksum(toBytes(dataBits));
write_interleaved_bytes(bits, cursor, dataChecksum);
write_bits(bits, cursor, dataBits);
Bytes data = sector->data.slice(0, 512);
write_interleaved_word(amigaChecksum(encodeMfm(amigaInterleave(data), lastBit)));
write_interleaved_bytes(data);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class AmigaEncoder : public AbstractEncoder
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
public:
AmigaEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.amiga()) {}
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapMs * 1000 / clockRateUs, { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
std::vector<std::shared_ptr<Sector>> sectors;
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
if ((physicalTrack >= 0) && (physicalTrack < AMIGA_TRACKS_PER_DISK))
{
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_ms() * 1000 / _config.clock_rate_us(), { true, false });
lastBit = false;
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, _config.clock_rate_us()*1e3);
return fluxmap;
}
private:
const AmigaEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createAmigaEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config));
}

View File

@@ -7,19 +7,7 @@
#define APPLE2_SECTOR_LENGTH 256
#define APPLE2_ENCODED_SECTOR_LENGTH 342
class Sector;
class Fluxmap;
class Apple2Decoder : public AbstractDecoder
{
public:
virtual ~Apple2Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config);
#endif

4
arch/apple2/apple2.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Apple2DecoderProto {}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "apple2.h"
@@ -25,7 +24,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/7914a6083a3b3a8c243ae6c3b8cb50b023f21e0e/src/lib/formats/ap2_dsk.cpp
@@ -60,53 +59,68 @@ static Bytes decode_crazy_data(const uint8_t* inp, Sector::Status& status)
return output;
}
uint8_t combine(uint16_t word)
static uint8_t combine(uint16_t word)
{
return word & (word >> 7);
}
AbstractDecoder::RecordType Apple2Decoder::advanceToNextRecord()
class Apple2Decoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
public:
Apple2Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(8*8)).slice(0, 8);
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
/* Check ID. */
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
if (bytes.reader().read_be24() != APPLE2_DATA_RECORD)
return;
/* Read and decode data. */
unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength);
_sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
}
};
std::unique_ptr<AbstractDecoder> createApple2Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Apple2Decoder(config));
}
void Apple2Decoder::decodeSectorRecord()
{
/* Skip ID (as we know it's a APPLE2_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(8*8)).slice(0, 8);
ByteReader br(header);
uint8_t volume = combine(br.read_be16());
_sector->logicalTrack = combine(br.read_be16());
_sector->logicalSector = combine(br.read_be16());
uint8_t checksum = combine(br.read_be16());
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Apple2Decoder::decodeDataRecord()
{
/* Check ID. */
Bytes bytes = toBytes(readRawBits(3*8)).slice(0, 3);
if (bytes.reader().read_be24() != APPLE2_DATA_RECORD)
return;
/* Read and decode data. */
unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
bytes = toBytes(readRawBits(recordLength*8)).slice(0, recordLength);
_sector->status = Sector::BAD_CHECKSUM;
_sector->data = decode_crazy_data(&bytes[0], _sector->status);
}

View File

@@ -9,31 +9,11 @@
#define BROTHER_DATA_RECORD_CHECKSUM 3
#define BROTHER_DATA_RECORD_ENCODED_SIZE 415
#define BROTHER_TRACKS_PER_DISK 78
#define BROTHER_TRACKS_PER_240KB_DISK 78
#define BROTHER_TRACKS_PER_120KB_DISK 39
#define BROTHER_SECTORS_PER_TRACK 12
class Sector;
class Fluxmap;
class BrotherDecoder : public AbstractDecoder
{
public:
virtual ~BrotherDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
class BrotherEncoder : public AbstractEncoder
{
public:
virtual ~BrotherEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup brotherEncoderFlags;
extern std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config);
#endif

View File

@@ -0,0 +1,20 @@
syntax = "proto2";
message BrotherDecoderProto {}
enum BrotherFormat {
BROTHER240 = 0;
BROTHER120 = 1;
};
message BrotherEncoderProto {
optional double clock_rate_us = 1 [default = 3.83];
optional double post_index_gap_ms = 2 [default = 1.0];
optional double sector_spacing_ms = 3 [default = 16.2];
optional double post_header_spacing_ms = 4 [default = 0.69];
optional string sector_skew = 5 [default = "05a3816b4927"];
optional BrotherFormat format = 6 [default = BROTHER240];
optional int32 bias = 7 [default = 0];
}

View File

@@ -1,28 +0,0 @@
#ifndef BROTHER_H
#define BROTHER_H
/* Brother word processor format (or at least, one of them) */
#define BROTHER_SECTOR_RECORD 0xFFFFFD57
#define BROTHER_DATA_RECORD 0xFFFFFDDB
#define BROTHER_DATA_RECORD_PAYLOAD 256
#define BROTHER_DATA_RECORD_CHECKSUM 3
class Sector;
class Fluxmap;
class BrotherDecoder : public AbstractDecoder
{
public:
virtual ~BrotherDecoder() {}
SectorVector decodeToSectors(const RawRecordVector& rawRecords);
int recordMatcher(uint64_t fifo) const;
};
extern void writeBrotherSectorHeader(std::vector<bool>& bits, unsigned& cursor,
int track, int sector);
extern void writeBrotherSectorData(std::vector<bool>& bits, unsigned& cursor,
const std::vector<uint8_t>& data);
#endif

View File

@@ -4,7 +4,6 @@
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "record.h"
#include "brother.h"
#include "sector.h"
#include "bytes.h"
@@ -40,7 +39,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
static int decode_header_gcr(uint16_t word)
{
@@ -52,58 +51,73 @@ static int decode_header_gcr(uint16_t word)
#undef GCR_ENTRY
}
return -1;
}
class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
_sector->status = Sector::DATA_MISSING;
}
void decodeDataRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
AbstractDecoder::RecordType BrotherDecoder::advanceToNextRecord()
std::unique_ptr<AbstractDecoder> createBrotherDecoder(const DecoderProto& config)
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
return std::unique_ptr<AbstractDecoder>(new BrotherDecoder(config));
}
void BrotherDecoder::decodeSectorRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(32);
const auto& bytes = toBytes(rawbits).slice(0, 4);
ByteReader br(bytes);
_sector->logicalTrack = decode_header_gcr(br.read_be16());
_sector->logicalSector = decode_header_gcr(br.read_be16());
/* Sanity check the values read; there's no header checksum and
* occasionally we get garbage due to bit errors. */
if (_sector->logicalSector > 11)
return;
if (_sector->logicalTrack > 79)
return;
_sector->status = Sector::DATA_MISSING;
}
void BrotherDecoder::decodeDataRecord()
{
readRawBits(32);
const auto& rawbits = readRawBits(BROTHER_DATA_RECORD_ENCODED_SIZE*8);
const auto& rawbytes = toBytes(rawbits).slice(0, BROTHER_DATA_RECORD_ENCODED_SIZE);
Bytes bytes;
ByteWriter bw(bytes);
BitWriter bitw(bw);
for (uint8_t b : rawbytes)
{
uint32_t nibble = decode_data_gcr(b);
bitw.push(nibble, 5);
}
bitw.flush();
_sector->data = bytes.slice(0, BROTHER_DATA_RECORD_PAYLOAD);
uint32_t realCrc = crcbrother(_sector->data);
uint32_t wantCrc = bytes.reader().seek(BROTHER_DATA_RECORD_PAYLOAD).read_be24();
_sector->status = (realCrc == wantCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,38 +1,12 @@
#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");
#include "image.h"
#include "arch/brother/brother.pb.h"
#include "lib/encoders/encoders.pb.h"
static int encode_header_gcr(uint16_t word)
{
@@ -44,7 +18,7 @@ static int encode_header_gcr(uint16_t word)
#undef GCR_ENTRY
}
return -1;
};
}
static int encode_data_gcr(uint8_t data)
{
@@ -56,7 +30,7 @@ static int encode_data_gcr(uint8_t data)
#undef GCR_ENTRY
}
return -1;
};
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint32_t data, int width)
{
@@ -126,42 +100,113 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class BrotherEncoder : public AbstractEncoder
{
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_DISK)
|| (physicalSide != 0))
return std::unique_ptr<Fluxmap>();
public:
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{}
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++)
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
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;
std::vector<std::shared_ptr<Sector>> sectors;
const auto& sectorData = allSectors.get(physicalTrack, 0, sectorId);
int logicalTrack;
if (physicalSide != 0)
return sectors;
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return sectors;
logicalTrack = physicalTrack/2;
break;
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);
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return sectors;
logicalTrack = physicalTrack;
break;
}
for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++)
{
const auto& sector = image.get(logicalTrack, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _config.bias();
switch (_config.format())
{
case BROTHER120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
// 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;
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
}
int bitsPerRevolution = 200000.0 / _config.clock_rate_us();
const std::string& skew = _config.sector_skew();
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 = _config.post_index_gap_ms() + sectorCount*_config.sector_spacing_ms();
unsigned headerCursor = headerMs*1e3 / _config.clock_rate_us();
double dataMs = headerMs + _config.post_header_spacing_ms();
unsigned dataCursor = dataMs*1e3 / _config.clock_rate_us();
const auto& sectorData = image.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, logicalTrack, 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, _config.clock_rate_us()*1e3);
return fluxmap;
}
private:
const BrotherEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createBrotherEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config));
}

View File

@@ -1,21 +1,33 @@
#ifndef C64_H
#define C64_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define C64_SECTOR_RECORD 0xffd49
#define C64_DATA_RECORD 0xffd57
#define C64_SECTOR_LENGTH 256
class Sector;
class Fluxmap;
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
5. Data block 55...4A (325 GCR bytes)
6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
1. Header sync (SYNC for the next sector)
*/
#define C64_HEADER_DATA_SYNC 0xFF
#define C64_HEADER_BLOCK_ID 0x08
#define C64_DATA_BLOCK_ID 0x07
#define C64_HEADER_GAP 0x55
#define C64_INTER_SECTOR_GAP 0x55
#define C64_PADDING 0x0F
class Commodore64Decoder : public AbstractDecoder
{
public:
virtual ~Commodore64Decoder() {}
#define C64_TRACKS_PER_DISK 40
#define C64_BAM_TRACK 17
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config);
#endif

13
arch/c64/c64.proto Normal file
View File

@@ -0,0 +1,13 @@
syntax = "proto2";
import "lib/common.proto";
message Commodore64DecoderProto {}
message Commodore64EncoderProto {
optional double post_index_gap_us = 1 [default=0.0,
(help) = "post-index gap before first sector header."];
optional double clock_compensation_factor = 2 [default=1.0,
(help) = "scale the output clock by this much."];
}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "c64.h"
@@ -26,7 +25,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -52,41 +51,56 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Commodore64Decoder::advanceToNextRecord()
class Commodore64Decoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
public:
Commodore64Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
readRawBits(20);
const auto& bits = readRawBits(5*10);
const auto& bytes = decode(bits).slice(0, 5);
uint8_t checksum = bytes[0];
_sector->logicalSector = bytes[1];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
readRawBits(20);
const auto& bits = readRawBits(259*10);
const auto& bytes = decode(bits).slice(0, 259);
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
uint8_t gotChecksum = xorBytes(_sector->data);
uint8_t wantChecksum = bytes[256];
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createCommodore64Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Commodore64Decoder(config));
}
void Commodore64Decoder::decodeSectorRecord()
{
readRawBits(20);
const auto& bits = readRawBits(5*10);
const auto& bytes = decode(bits).slice(0, 5);
uint8_t checksum = bytes[0];
_sector->logicalSector = bytes[1];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[2] - 1;
if (checksum == xorBytes(bytes.slice(1, 4)))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Commodore64Decoder::decodeDataRecord()
{
readRawBits(20);
const auto& bits = readRawBits(259*10);
const auto& bytes = decode(bits).slice(0, 259);
_sector->data = bytes.slice(0, C64_SECTOR_LENGTH);
uint8_t gotChecksum = xorBytes(_sector->data);
uint8_t wantChecksum = bytes[256];
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

381
arch/c64/encoder.cc Normal file
View File

@@ -0,0 +1,381 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "c64.h"
#include "crc.h"
#include "sector.h"
#include "writer.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/c64/c64.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
/*
* Track # Sectors/Track Speed Zone bits/rotation
* 1 17 21 3 61,538.4
* 18 24 19 2 57,142.8
* 25 30 18 1 53,333.4
* 31 35 17 0 50,000.0
*/
if (track < 17)
return 200000.0/61538.4;
if (track < 24)
return 200000.0/57142.8;
if (track < 30)
return 200000.0/53333.4;
return 200000.0/50000.0;
}
static unsigned sectorsForTrack(unsigned track)
{
/*
* Track Sectors/track # Sectors Storage in Bytes
* ----- ------------- --------- ----------------
* 1-17 21 357 7820
* 18-24 19 133 7170
* 25-30 18 108 6300
* 31-40(*) 17 85 6020
* ---
* 683 (for a 35 track image)
*/
if (track < 17)
return 21;
if (track < 24)
return 19;
if (track < 30)
return 18;
return 17;
}
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, const std::vector<bool>& src)
{
for (bool bit : src) //Range-based for loop
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_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 bindump(std::ostream& stream, std::vector<bool>& buffer)
{
size_t pos = 0;
while ((pos < buffer.size()) and (pos <520))
{
stream << fmt::format("{:5d} : ", pos);
for (int i=0; i<40; i++)
{
if ((pos+i) < buffer.size())
stream << fmt::format("{:01b}", (buffer[pos+i]));
else
stream << "-- ";
if ((((pos + i + 1) % 8) == 0) and i != 0)
stream << " ";
}
stream << std::endl;
pos += 40;
}
}
static std::vector<bool> encode_data(uint8_t input)
{
/*
* Four 8-bit data bytes are converted to four 10-bit GCR bytes at a time by
* the 1541 DOS. RAM is only an 8-bit storage device though. This hardware
* limitation prevents a 10-bit GCR byte from being stored in a single
* memory location. Four 10-bit GCR bytes total 40 bits - a number evenly
* divisible by our overriding 8-bit constraint. Commodore sub- divides the
* 40 GCR bits into five 8-bit bytes to solve this dilemma. This explains
* why four 8-bit data bytes are converted to GCR form at a time. The
* following step by step example demonstrates how this bit manipulation is
* performed by the DOS.
*
* STEP 1. Four 8-bit Data Bytes
* $08 $10 $00 $12
*
* STEP 2. Hexadecimal to Binary Conversion
* 1. Binary Equivalents
* $08 $10 $00 $12
* 00001000 00010000 00000000 00010010
*
* STEP 3. Binary to GCR Conversion
* 1. Four 8-bit Data Bytes
* 00001000 00010000 00000000 00010010
* 2. High and Low Nybbles
* 0000 1000 0001 0000 0000 0000 0001 0010
* 3. High and Low Nybble GCR Equivalents
* 01010 01001 01011 01010 01010 01010 01011 10010
* 4. Four 10-bit GCR Bytes
* 0101001001 0101101010 0101001010 0101110010
*
* STEP 4. 10-bit GCR to 8-bit GCR Conversion
* 1. Concatenate Four 10-bit GCR Bytes
* 0101001001010110101001010010100101110010
* 2. Five 8-bit Subdivisions
* 01010010 01010110 10100101 00101001 01110010
*
* STEP 5. Binary to Hexadecimal Conversion
* 1. Hexadecimal Equivalents
* 01010010 01010110 10100101 00101001 01110010
* $52 $56 $A5 $29 $72
*
* STEP 6. Four 8-bit Data Bytes are Recorded as Five 8-bit GCR Bytes
* $08 $10 $00 $12
*
* are recorded as
* $52 $56 $A5 $29 $72
*/
std::vector<bool> output(10, false);
uint8_t hi = 0;
uint8_t lo = 0;
uint8_t lo_GCR = 0;
uint8_t hi_GCR = 0;
//Convert the byte in high and low nibble
lo = input >> 4; //get the lo nibble shift the bits 4 to the right
hi = input & 15; //get the hi nibble bij masking the lo bits (00001111)
lo_GCR = encode_data_gcr(lo); //example value: 0000 GCR = 01010
hi_GCR = encode_data_gcr(hi); //example value: 1000 GCR = 01001
//output = [0,1,2,3,4,5,6,7,8,9]
//value = [0,1,0,1,0,0,1,0,0,1]
// 01010 01001
int b = 4;
for (int i = 0; i < 10; i++)
{
if (i < 5) //01234
{ //i = 0 op
output[4-i] = (lo_GCR & 1); //01010
//01010 -> & 00001 -> 00000 output[4] = 0
//00101 -> & 00001 -> 00001 output[3] = 1
//00010 -> & 00001 -> 00000 output[2] = 0
//00001 -> & 00001 -> 00001 output[1] = 1
//00000 -> & 00001 -> 00000 output[0] = 0
lo_GCR >>= 1;
} else
{
output[i+b] = (hi_GCR & 1); //01001
//01001 -> & 00001 -> 00001 output[9] = 1
//00100 -> & 00001 -> 00000 output[8] = 0
//00010 -> & 00001 -> 00000 output[7] = 0
//00001 -> & 00001 -> 00001 output[6] = 1
//00000 -> & 00001 -> 00000 output[5] = 0
hi_GCR >>= 1;
b = b-2;
}
}
return output;
}
class Commodore64Encoder : public AbstractEncoder
{
public:
Commodore64Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.c64())
{}
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
if (physicalSide == 0)
{
int logicalTrack = physicalTrack / 2;
unsigned numSectors = sectorsForTrack(logicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sector = image.get(logicalTrack, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
* it is written in every header of every sector and track. headers are not
* stored in a d64 disk image so we have to get it from track 18 which
* contains the BAM.
*/
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
const auto& sectorData = image.get(C64_BAM_TRACK*2, 0, 0); //Read de BAM to get the DISK ID bytes
if (sectorData)
{
ByteReader br(sectorData->data);
br.seek(162); //goto position of the first Disk ID Byte
_formatByte1 = br.read_8();
_formatByte2 = br.read_8();
}
else
_formatByte1 = _formatByte2 = 0;
int logicalTrack = physicalTrack / 2;
double clockRateUs = clockRateUsForTrack(logicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
for (const auto& sector : sectors)
writeSector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
private:
void writeSector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector) const
{
/* Source: http://www.unusedino.de/ec64/technical/formats/g64.html
* 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR)
* 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
* 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
* 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR)
* 5. Data block 55...4A (325 GCR bytes)
* 6. Inter-sector gap 55 55 55 55...55 55 (4 to 12 bytes, never read)
* 1. Header sync (SYNC for the next sector)
*/
if ((sector->status == Sector::OK) or (sector->status == Sector::BAD_CHECKSUM))
{
// There is data to encode to disk.
if ((sector->data.size() != C64_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector->data.size());
// 1. Write header Sync (not GCR)
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 2. Write Header info 10 GCR bytes
/*
* The 10 byte header info (#2) is GCR encoded and must be decoded to
* it's normal 8 bytes to be understood. Once decoded, its breakdown is
* as follows:
*
* Byte $00 - header block ID ($08)
* 01 - header block checksum 16 (EOR of $02-$05)
* 02 - Sector
* 03 - Track
* 04 - Format ID byte #2
* 05 - Format ID byte #1
* 06-07 - $0F ("off" bytes)
*/
uint8_t encodedTrack = ((sector->logicalTrack) + 1); // C64 track numbering starts with 1. Fluxengine with 0.
uint8_t encodedSector = sector->logicalSector;
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
// uint8_t formatByte2 = C64_FORMAT_ID_BYTE2;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ _formatByte1 ^ _formatByte2);
write_bits(bits, cursor, encode_data(C64_HEADER_BLOCK_ID));
write_bits(bits, cursor, encode_data(headerChecksum));
write_bits(bits, cursor, encode_data(encodedSector));
write_bits(bits, cursor, encode_data(encodedTrack));
write_bits(bits, cursor, encode_data(_formatByte2));
write_bits(bits, cursor, encode_data(_formatByte1));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
// 3. Write header GAP not GCR
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_HEADER_GAP, 1*8); /* header gap */
// 4. Write Data sync not GCR
for (int i=0; i<6; i++)
write_bits(bits, cursor, C64_HEADER_DATA_SYNC, 1*8); /* sync */
// 5. Write data block 325 GCR bytes
/*
* The 325 byte data block (#5) is GCR encoded and must be decoded to its
* normal 260 bytes to be understood. The data block is made up of the following:
*
* Byte $00 - data block ID ($07)
* 01-100 - 256 bytes data
* 101 - data block checksum (EOR of $01-100)
* 102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
*/
write_bits(bits, cursor, encode_data(C64_DATA_BLOCK_ID));
uint8_t dataChecksum = xorBytes(sector->data);
ByteReader br(sector->data);
int i = 0;
for (i = 0; i < C64_SECTOR_LENGTH; i++)
{
uint8_t val = br.read_8();
write_bits(bits, cursor, encode_data(val));
}
write_bits(bits, cursor, encode_data(dataChecksum));
write_bits(bits, cursor, encode_data(C64_PADDING));
write_bits(bits, cursor, encode_data(C64_PADDING));
//6. Write inter-sector gap 9 - 12 bytes nor gcr
for (int i=0; i<9; i++)
write_bits(bits, cursor, C64_INTER_SECTOR_GAP, 1*8); /* sync */
}
}
private:
const Commodore64EncoderProto& _config;
uint8_t _formatByte1;
uint8_t _formatByte2;
};
std::unique_ptr<AbstractEncoder> createCommodore64Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "f85.h"
@@ -26,7 +25,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -52,49 +51,63 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType DurangoF85Decoder::advanceToNextRecord()
class DurangoF85Decoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
public:
DurangoF85Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
/* Skip sync bits and ID byte. */
readRawBits(24);
/* Read header. */
const auto& bytes = decode(readRawBits(6*10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
/* Skip sync bits ID byte. */
readRawBits(24);
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
.slice(0, F85_SECTOR_LENGTH+3);
ByteReader br(bytes);
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new DurangoF85Decoder(config));
}
void DurangoF85Decoder::decodeSectorRecord()
{
/* Skip sync bits and ID byte. */
readRawBits(24);
/* Read header. */
const auto& bytes = decode(readRawBits(6*10));
_sector->logicalSector = bytes[2];
_sector->logicalSide = 0;
_sector->logicalTrack = bytes[0];
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void DurangoF85Decoder::decodeDataRecord()
{
/* Skip sync bits ID byte. */
readRawBits(24);
const auto& bytes = decode(readRawBits((F85_SECTOR_LENGTH+3)*10))
.slice(0, F85_SECTOR_LENGTH+3);
ByteReader br(bytes);
_sector->data = br.read(F85_SECTOR_LENGTH);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(CCITT_POLY, 0xbf84, _sector->data);
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -5,17 +5,6 @@
#define F85_DATA_RECORD 0xffffcb /* 1111 1111 1111 1111 1100 1101 */
#define F85_SECTOR_LENGTH 512
class Sector;
class Fluxmap;
class DurangoF85Decoder : public AbstractDecoder
{
public:
virtual ~DurangoF85Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createDurangoF85Decoder(const DecoderProto& config);
#endif

4
arch/f85/f85.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message F85DecoderProto {}

View File

@@ -2,14 +2,12 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "fb100.h"
#include "crc.h"
#include "bytes.h"
#include "decoders/rawbits.h"
#include "track.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
@@ -99,37 +97,52 @@ static uint16_t checksum(const Bytes& bytes)
return (crchi << 8) | crclo;
}
AbstractDecoder::RecordType Fb100Decoder::advanceToNextRecord()
class Fb100Decoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
if (matcher == &SECTOR_ID_PATTERN)
return RecordType::SECTOR_RECORD;
return RecordType::UNKNOWN_RECORD;
public:
Fb100Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_ID_PATTERN, matcher);
if (matcher == &SECTOR_ID_PATTERN)
return RecordType::SECTOR_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
uint16_t wantIdCrc = br.read_be16();
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc)
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Fb100Decoder(config));
}
void Fb100Decoder::decodeSectorRecord()
{
auto rawbits = readRawBits(FB100_RECORD_SIZE*16);
const Bytes bytes = decodeFmMfm(rawbits).slice(0, FB100_RECORD_SIZE);
ByteReader br(bytes);
br.seek(1);
const Bytes id = br.read(FB100_ID_SIZE);
uint16_t wantIdCrc = br.read_be16();
uint16_t gotIdCrc = checksum(id);
const Bytes payload = br.read(FB100_PAYLOAD_SIZE);
uint16_t wantPayloadCrc = br.read_be16();
uint16_t gotPayloadCrc = checksum(payload);
if (wantIdCrc != gotIdCrc)
return;
uint8_t abssector = id[2];
_sector->logicalTrack = abssector >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = abssector & 1;
_sector->data.writer().append(id.slice(5, 12)).append(payload);
_sector->status = (wantPayloadCrc == gotPayloadCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -5,18 +5,7 @@
#define FB100_ID_SIZE 17
#define FB100_PAYLOAD_SIZE 0x500
class Sector;
class Fluxmap;
class Track;
class Fb100Decoder : public AbstractDecoder
{
public:
virtual ~Fb100Decoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createFb100Decoder(const DecoderProto& config);
#endif

4
arch/fb100/fb100.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message Fb100DecoderProto {}

View File

@@ -5,7 +5,8 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "arch/ibm/ibm.pb.h"
#include "proto.h"
#include <string.h>
static_assert(std::is_trivially_copyable<IbmIdam>::value,
@@ -57,11 +58,11 @@ const FluxPattern FM_TRS80DAM1_PATTERN(16, 0xf56b);
/*
* TRS80DAM2 record:
* flux: XXXX-X-X-XX-XXX- = 0xf56c
* flux: XXXX-X-X-XX-XXX- = 0xf56e
* clock: X X - - - X X X = 0xc7
* data: X X X X X - X - = 0xfa
*/
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56c);
const FluxPattern FM_TRS80DAM2_PATTERN(16, 0xf56e);
/* MFM record separator:
* 0xA1 is:
@@ -89,71 +90,139 @@ const FluxMatchers ANY_RECORD_PATTERN(
}
);
AbstractDecoder::RecordType IbmDecoder::advanceToNextRecord()
class IbmDecoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
public:
IbmDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.ibm())
{}
/* If this is the MFM prefix byte, the the decoder is going to expect three
* extra bytes on the front of the header. */
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
RecordType advanceToNextRecord() override
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
Fluxmap::Position here = tell();
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
const Bytes idbytes = decodeFmMfm(idbits);
uint8_t id = idbytes.slice(0, 1)[0];
seek(here);
switch (id)
{
case IBM_IDAM:
return RecordType::SECTOR_RECORD;
/* If this is the MFM prefix byte, the the decoder is going to expect three
* extra bytes on the front of the header. */
_currentHeaderLength = (matcher == &MFM_PATTERN) ? 3 : 0;
case IBM_DAM1:
case IBM_DAM2:
case IBM_TRS80DAM1:
case IBM_TRS80DAM2:
return RecordType::DATA_RECORD;
}
return RecordType::UNKNOWN_RECORD;
Fluxmap::Position here = tell();
resetFluxDecoder();
if (_currentHeaderLength > 0)
readRawBits(_currentHeaderLength*16);
auto idbits = readRawBits(16);
const Bytes idbytes = decodeFmMfm(idbits);
uint8_t id = idbytes.slice(0, 1)[0];
if (eof())
return RecordType::UNKNOWN_RECORD;
seek(here);
switch (id)
{
case IBM_IDAM:
return RecordType::SECTOR_RECORD;
case IBM_DAM1:
case IBM_DAM2:
case IBM_TRS80DAM1:
case IBM_TRS80DAM2:
return RecordType::DATA_RECORD;
}
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord() override
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8();
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
if (trackdata.swap_sides())
_sector->logicalSide ^= 1;
if (trackdata.ignore_side_byte())
_sector->logicalSide = _sector->physicalHead;
if (trackdata.ignore_track_byte())
_sector->logicalTrack = _sector->physicalCylinder;
}
void decodeDataRecord() override
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->data = br.read(_currentSectorSize);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
{
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, cylinder, head);
std::set<unsigned> s;
if (trackdata.has_sectors())
{
for (int sectorId : trackdata.sectors().sector())
s.insert(sectorId);
}
else if (trackdata.has_sector_range())
{
int sectorId = trackdata.sector_range().min_sector();
while (sectorId <= trackdata.sector_range().max_sector())
{
s.insert(sectorId);
sectorId++;
}
}
return s;
}
private:
void getTrackFormat(IbmDecoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head) const
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
}
private:
const IbmDecoderProto& _config;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new IbmDecoder(config));
}
void IbmDecoder::decodeSectorRecord()
{
unsigned recordSize = _currentHeaderLength + IBM_IDAM_LEN;
auto bits = readRawBits(recordSize*16);
auto bytes = decodeFmMfm(bits).slice(0, recordSize);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8() - _sectorBase;
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t wantCrc = br.read_be16();
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()
{
unsigned recordLength = _currentHeaderLength + _currentSectorSize + 3;
auto bits = readRawBits(recordLength*16);
auto bytes = decodeFmMfm(bits).slice(0, recordLength);
ByteReader br(bytes);
br.seek(_currentHeaderLength);
br.read_8(); /* skip ID byte */
_sector->data = br.read(_currentSectorSize);
uint16_t wantCrc = br.read_be16();
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, recordLength-2));
_sector->status = (wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,11 +1,12 @@
#include "globals.h"
#include "record.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "ibm.h"
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "image.h"
#include "arch/ibm/ibm.pb.h"
#include "lib/encoders/encoders.pb.h"
#include "fmt/format.h"
#include <ctype.h>
@@ -56,41 +57,6 @@
* mfm: 01 01 01 01 01 00 01 01 = 0x5545
*/
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
void IbmEncoder::writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void IbmEncoder::writeBytes(const Bytes& bytes)
{
if (_parameters.useFm)
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void IbmEncoder::writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
@@ -99,137 +65,249 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
class IbmEncoder : public AbstractEncoder
{
double clockRateUs = 1e3 / _parameters.clockRateKhz;
if (!_parameters.useFm)
clockRateUs /= 2.0;
int bitsPerRevolution = (_parameters.trackLengthMs * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
public:
IbmEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.ibm())
{}
uint8_t idamUnencoded = decodeUint16(_parameters.idamByte);
uint8_t damUnencoded = decodeUint16(_parameters.damByte);
uint8_t sectorSize = 0;
private:
void writeRawBits(uint32_t data, int width)
{
int s = _parameters.sectorSize >> 7;
while (s > 1)
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
s >>= 1;
sectorSize += 1;
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
uint8_t gapFill = _parameters.useFm ? 0x00 : 0x4e;
writeBytes(_parameters.gap0, gapFill);
if (_parameters.emitIam)
void getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_IAM_SEPARATOR, 16);
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
writeRawBits(_parameters.useFm ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeBytes(_parameters.gap1, gapFill);
}
bool first = true;
for (char sectorChar : _parameters.sectorSkew)
private:
static std::set<unsigned> getSectorIds(const IbmEncoderProto::TrackdataProto& trackdata)
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_parameters.gap3, gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
std::set<unsigned> s;
if (trackdata.has_sectors())
{
Bytes header;
ByteWriter bw(header);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
for (int sectorId : trackdata.sectors().sector())
s.insert(sectorId);
}
else if (trackdata.has_sector_range())
{
int sectorId = trackdata.sector_range().min_sector();
while (sectorId <= trackdata.sector_range().max_sector())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
s.insert(sectorId);
sectorId++;
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector + _parameters.startSectorId);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
}
return s;
}
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
int logicalSide = physicalSide ^ trackdata.swap_sides();
for (int sectorId : getSectorIds(trackdata))
{
const auto& sector = image.get(physicalTrack, logicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
auto writeBytes = [&](const Bytes& bytes)
{
if (trackdata.use_fm())
encodeFm(_bits, _cursor, bytes);
else
encodeMfm(_bits, _cursor, bytes, _lastBit);
};
auto writeFillerRawBytes = [&](int count, uint16_t byte)
{
for (int i=0; i<count; i++)
writeRawBits(byte, 16);
};
auto writeFillerBytes = [&](int count, uint8_t byte)
{
Bytes b { byte };
for (int i=0; i<count; i++)
writeBytes(b);
};
double clockRateUs = 1e3 / trackdata.clock_rate_khz();
if (!trackdata.use_fm())
clockRateUs /= 2.0;
int bitsPerRevolution = (trackdata.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t idamUnencoded = decodeUint16(trackdata.idam_byte());
uint8_t damUnencoded = decodeUint16(trackdata.dam_byte());
uint8_t sectorSize = 0;
{
int s = trackdata.sector_size() >> 7;
while (s > 1)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
s >>= 1;
sectorSize += 1;
}
writeRawBits(_parameters.idamByte, 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeBytes(_parameters.gap2, gapFill);
uint16_t gapFill = trackdata.gap_fill_byte();
writeFillerRawBytes(trackdata.gap0(), gapFill);
if (trackdata.emit_iam())
{
Bytes data;
ByteWriter bw(data);
writeBytes(_parameters.useFm ? 6 : 12, 0x00);
if (!_parameters.useFm)
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
writeRawBits(MFM_IAM_SEPARATOR, 16);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, _parameters.sectorSize);
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!_parameters.useFm)
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(_parameters.damByte, 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
writeRawBits(trackdata.use_fm() ? FM_IAM_RECORD : MFM_IAM_RECORD, 16);
writeFillerRawBytes(trackdata.gap1(), gapFill);
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, gapFill);
int logicalSide = physicalSide ^ trackdata.swap_sides();
bool first = true;
for (int sectorId : trackdata.sectors().sector())
{
if (!first)
writeFillerRawBytes(trackdata.gap3(), gapFill);
first = false;
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
const auto& sectorData = image.get(physicalTrack, logicalSide, sectorId);
if (!sectorData)
continue;
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(idamUnencoded);
bw.write_8(sectorData->logicalTrack);
bw.write_8(sectorData->logicalSide);
bw.write_8(sectorData->logicalSector);
bw.write_8(sectorSize);
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.idam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(header.slice(conventionalHeaderStart));
}
writeFillerRawBytes(trackdata.gap2(), gapFill);
{
Bytes data;
ByteWriter bw(data);
writeFillerBytes(trackdata.use_fm() ? 6 : 12, 0x00);
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
}
bw.write_8(damUnencoded);
Bytes truncatedData = sectorData->data.slice(0, trackdata.sector_size());
bw += truncatedData;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
int conventionalHeaderStart = 0;
if (!trackdata.use_fm())
{
for (int i=0; i<3; i++)
writeRawBits(MFM_RECORD_SEPARATOR, 16);
conventionalHeaderStart += 3;
}
writeRawBits(trackdata.dam_byte(), 16);
conventionalHeaderStart += 1;
writeBytes(data.slice(conventionalHeaderStart));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeFillerRawBytes(1, gapFill);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
private:
const IbmEncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config));
}

View File

@@ -1,9 +1,6 @@
#ifndef IBM_H
#define IBM_H
#include "decoders/decoders.h"
#include "encoders/encoders.h"
/* IBM format (i.e. ordinary PC floppies). */
#define IBM_MFM_SYNC 0xA1 /* sync byte for MFM */
@@ -29,71 +26,12 @@ struct IbmIdam
uint8_t crc[2];
};
class IbmDecoder : public AbstractDecoder
{
public:
IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false,
const std::set<unsigned> requiredSectors=std::set<unsigned>()):
_sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte),
_requiredSectors(requiredSectors)
{}
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const
{ return _requiredSectors; }
private:
unsigned _sectorBase;
bool _ignoreSideByte;
std::set<unsigned> _requiredSectors;
unsigned _currentSectorSize;
unsigned _currentHeaderLength;
};
struct IbmParameters
{
int trackLengthMs;
int sectorSize;
bool emitIam;
int startSectorId;
int clockRateKhz;
bool useFm;
uint16_t idamByte;
uint16_t damByte;
int gap0;
int gap1;
int gap2;
int gap3;
std::string sectorSkew;
};
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const IbmParameters& parameters):
_parameters(parameters)
{}
virtual ~IbmEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
void writeRawBits(uint32_t data, int width);
void writeBytes(const Bytes& bytes);
void writeBytes(int count, uint8_t value);
void writeSync();
private:
IbmParameters _parameters;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
extern std::unique_ptr<AbstractDecoder> createIbmDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createIbmEncoder(const EncoderProto& config);
#endif

68
arch/ibm/ibm.proto Normal file
View File

@@ -0,0 +1,68 @@
syntax = "proto2";
import "lib/common.proto";
message IbmDecoderProto {
// Next: 10
message TrackdataProto {
message SectorsProto {
repeated int32 sector = 1 [(help) = "require these sectors to exist for a good read"];
}
message SectorRangeProto {
optional int32 min_sector = 1 [(help) = "require these sectors to exist for a good read"];
optional int32 max_sector = 2 [(help) = "require these sectors to exist for a good read"];
}
optional int32 cylinder = 7 [(help) = "if set, the format applies only to this track"];
optional int32 head = 8 [(help) = "if set, the format applies only to this head"];
optional bool ignore_side_byte = 2 [default = false, (help) = "ignore side byte in sector header"];
optional bool ignore_track_byte = 6 [default = false, (help) = "ignore track byte in sector header"];
optional bool swap_sides = 4 [default = false, (help) = "put logical side 1 on physical side 0"];
oneof required_sectors {
SectorsProto sectors = 5 [(help) = "require these sectors to exist for a good read"];
SectorRangeProto sector_range = 9 [(help) = "require these sectors to exist for a good read"];
}
}
repeated TrackdataProto trackdata = 1;
}
message IbmEncoderProto {
// Next: 20
message TrackdataProto {
message SectorsProto {
repeated int32 sector = 1 [(help) = "write these sectors (in order) on each track"];
}
message SectorRangeProto {
optional int32 min_sector = 1 [(help) = "write these sectors (in order) on each track"];
optional int32 max_sector = 2 [(help) = "write these sectors (in order) on each track"];
}
optional int32 cylinder = 15 [(help) = "if set, the format applies only to this track"];
optional int32 head = 16 [(help) = "if set, the format applies only to this head"];
optional double track_length_ms = 1 [(help) = "length of track"];
optional int32 sector_size = 2 [default=512, (help) = "number of bytes per sector"];
optional bool emit_iam = 3 [default=true, (help) = "whether to emit an IAM record"];
optional double clock_rate_khz = 5 [(help) = "data clock rate"];
optional bool use_fm = 6 [default=false, (help) = "whether to use FM encoding rather than MFM"];
optional int32 idam_byte = 7 [default=0x5554, (help) = "16-bit raw bit pattern of IDAM byte"];
optional int32 dam_byte = 8 [default=0x5545, (help) = "16-bit raw bit pattern of DAM byte"];
optional int32 gap0 = 9 [default=80, (help) = "size of gap 1 (the post-index gap)"];
optional int32 gap1 = 10 [default=50, (help) = "size of gap 2 (the post-ID gap)"];
optional int32 gap2 = 11 [default=22, (help) = "size of gap 3 (the pre-data gap)"];
optional int32 gap3 = 12 [default=80, (help) = "size of gap 4 (the post-data or format gap)"];
optional bool swap_sides = 14 [default=false, (help) = "swap side bytes when writing"];
optional int32 gap_fill_byte = 18 [default=0x9254, (help) = "16-bit raw bit pattern of gap fill byte"];
oneof required_sectors {
SectorsProto sectors = 17 [(help) = "require these sectors to exist for a good read"];
SectorRangeProto sector_range = 19 [(help) = "require these sectors to exist for a good read"];
}
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -2,10 +2,8 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "track.h"
#include "macintosh.h"
#include "bytes.h"
#include "fmt/format.h"
@@ -26,7 +24,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
@@ -118,92 +116,103 @@ static Bytes decode_crazy_data(const Bytes& input, Sector::Status& status)
uint8_t decode_side(uint8_t side)
{
/* Mac disks, being weird, use the side byte to encode both the side (in
* bit 5) and also whether we're above track 0x3f (in bit 6).
* bit 5) and also whether we're above track 0x3f (in bit 0).
*/
return !!(side & 0x40);
return !!(side & 0x20);
}
AbstractDecoder::RecordType MacintoshDecoder::advanceToNextRecord()
class MacintoshDecoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
public:
MacintoshDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
void MacintoshDecoder::decodeSectorRecord()
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_sector->physicalCylinder & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
uint8_t formatByte = decode_data_gcr(header[3]);
uint8_t wantedsum = decode_data_gcr(header[4]);
if (encodedSector > 11)
return;
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalSide = decode_side(encodedSide);
_sector->logicalSector = encodedSector;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const
{
int count;
if (cylinder < 16)
count = 12;
else if (cylinder < 32)
count = 11;
else if (cylinder < 48)
count = 10;
else if (cylinder < 64)
count = 9;
else
count = 8;
std::set<unsigned> sectors;
while (count--)
sectors.insert(count);
return sectors;
}
};
std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config)
{
/* Skip ID (as we know it's a MAC_SECTOR_RECORD). */
readRawBits(24);
/* Read header. */
auto header = toBytes(readRawBits(7*8)).slice(0, 7);
uint8_t encodedTrack = decode_data_gcr(header[0]);
if (encodedTrack != (_track->physicalTrack & 0x3f))
return;
uint8_t encodedSector = decode_data_gcr(header[1]);
uint8_t encodedSide = decode_data_gcr(header[2]);
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;
uint8_t gotsum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
if (wantedsum == gotsum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config));
}
void MacintoshDecoder::decodeDataRecord()
{
auto id = toBytes(readRawBits(24)).reader().read_be24();
if (id != MAC_DATA_RECORD)
return;
/* Read data. */
readRawBits(8); /* skip spare byte */
auto inputbuffer = toBytes(readRawBits(MAC_ENCODED_SECTOR_LENGTH*8))
.slice(0, MAC_ENCODED_SECTOR_LENGTH);
for (unsigned i=0; i<inputbuffer.size(); i++)
inputbuffer[i] = decode_data_gcr(inputbuffer[i]);
_sector->status = Sector::BAD_CHECKSUM;
Bytes userData = decode_crazy_data(inputbuffer, _sector->status);
_sector->data.clear();
_sector->data.writer().append(userData.slice(12, 512)).append(userData.slice(0, 12));
}
std::set<unsigned> MacintoshDecoder::requiredSectors(Track& track) const
{
int count;
if (track.physicalTrack < 16)
count = 12;
else if (track.physicalTrack < 32)
count = 11;
else if (track.physicalTrack < 48)
count = 10;
else if (track.physicalTrack < 64)
count = 9;
else
count = 8;
std::set<unsigned> sectors;
do
sectors.insert(count);
while (count--);
return sectors;
}

263
arch/macintosh/encoder.cc Normal file
View File

@@ -0,0 +1,263 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "macintosh.h"
#include "crc.h"
#include "writer.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include "arch/macintosh/macintosh.pb.h"
#include <ctype.h>
static bool lastBit;
static double clockRateUsForTrack(unsigned track)
{
if (track < 16)
return 2.623;
if (track < 32)
return 2.861;
if (track < 48)
return 3.148;
if (track < 64)
return 3.497;
return 3.934;
}
static unsigned sectorsForTrack(unsigned track)
{
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
}
static int encode_data_gcr(uint8_t gcr)
{
switch (gcr)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
/* This is extremely inspired by the MESS implementation, written by Nathan Woods
* and R. Belmont: https://github.com/mamedev/mame/blob/4263a71e64377db11392c458b580c5ae83556bc7/src/lib/formats/ap_dsk35.cpp
*/
static Bytes encode_crazy_data(const Bytes& input)
{
Bytes output;
ByteWriter bw(output);
ByteReader br(input);
uint8_t w1, w2, w3, w4;
static const int LOOKUP_LEN = MAC_SECTOR_LENGTH / 3;
uint8_t b1[LOOKUP_LEN + 1];
uint8_t b2[LOOKUP_LEN + 1];
uint8_t b3[LOOKUP_LEN + 1];
uint32_t c1 = 0;
uint32_t c2 = 0;
uint32_t c3 = 0;
for (int j=0;; j++)
{
c1 = (c1 & 0xff) << 1;
if (c1 & 0x0100)
c1++;
uint8_t val = br.read_8();
c3 += val;
if (c1 & 0x0100)
{
c3++;
c1 &= 0xff;
}
b1[j] = (val ^ c1) & 0xff;
val = br.read_8();
c2 += val;
if (c3 > 0xff)
{
c2++;
c3 &= 0xff;
}
b2[j] = (val ^ c3) & 0xff;
if (br.pos == 524)
break;
val = br.read_8();
c1 += val;
if (c2 > 0xff)
{
c1++;
c2 &= 0xff;
}
b3[j] = (val ^ c2) & 0xff;
}
uint32_t c4 = ((c1 & 0xc0) >> 6) | ((c2 & 0xc0) >> 4) | ((c3 & 0xc0) >> 2);
b3[LOOKUP_LEN] = 0;
for (int i = 0; i <= LOOKUP_LEN; i++)
{
w1 = b1[i] & 0x3f;
w2 = b2[i] & 0x3f;
w3 = b3[i] & 0x3f;
w4 = ((b1[i] & 0xc0) >> 2);
w4 |= ((b2[i] & 0xc0) >> 4);
w4 |= ((b3[i] & 0xc0) >> 6);
bw.write_8(w4);
bw.write_8(w1);
bw.write_8(w2);
if (i != LOOKUP_LEN)
bw.write_8(w3);
}
bw.write_8(c4 & 0x3f);
bw.write_8(c3 & 0x3f);
bw.write_8(c2 & 0x3f);
bw.write_8(c1 & 0x3f);
return output;
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_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 uint8_t encode_side(uint8_t track, uint8_t side)
{
/* Mac disks, being weird, use the side byte to encode both the side (in
* bit 5) and also whether we're above track 0x3f (in bit 0).
*/
return (side ? 0x20 : 0x00) | ((track>0x3f) ? 0x01 : 0x00);
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
{
if ((sector->data.size() != 512) && (sector->data.size() != 524))
Error() << "unsupported sector size --- you must pick 512 or 524";
write_bits(bits, cursor, 0xff, 1*8); /* pad byte */
for (int i=0; i<7; i++)
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3*8);
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
uint8_t encodedSector = sector->logicalSector;
uint8_t encodedSide = encode_side(sector->logicalTrack, sector->logicalSide);
uint8_t formatByte = MAC_FORMAT_BYTE;
uint8_t headerChecksum = (encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
write_bits(bits, cursor, encode_data_gcr(encodedTrack), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSector), 1*8);
write_bits(bits, cursor, encode_data_gcr(encodedSide), 1*8);
write_bits(bits, cursor, encode_data_gcr(formatByte), 1*8);
write_bits(bits, cursor, encode_data_gcr(headerChecksum), 1*8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6*8); /* sync */
write_bits(bits, cursor, MAC_DATA_RECORD, 3*8);
write_bits(bits, cursor, encode_data_gcr(sector->logicalSector), 1*8);
Bytes wireData;
wireData.writer().append(sector->data.slice(512, 12)).append(sector->data.slice(0, 512));
for (uint8_t b : encode_crazy_data(wireData))
write_bits(bits, cursor, encode_data_gcr(b), 1*8);
write_bits(bits, cursor, 0xdeaaff, 3*8);
}
class MacintoshEncoder : public AbstractEncoder
{
public:
MacintoshEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.macintosh())
{}
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < MAC_TRACKS_PER_DISK))
{
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, sector);
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
private:
const MacintoshEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config));
}

View File

@@ -6,21 +6,17 @@
#define MAC_SECTOR_LENGTH 524 /* yes, really */
#define MAC_ENCODED_SECTOR_LENGTH 703
#define MAC_FORMAT_BYTE 0x22
class Sector;
class Fluxmap;
#define MAC_TRACKS_PER_DISK 80
class MacintoshDecoder : public AbstractDecoder
{
public:
virtual ~MacintoshDecoder() {}
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
extern std::unique_ptr<AbstractDecoder> createMacintoshDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createMacintoshEncoder(const EncoderProto& config);
#endif

View File

@@ -0,0 +1,14 @@
syntax = "proto2";
import "lib/common.proto";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {
optional double post_index_gap_us = 1 [default = 0.0,
(help) = "post-index gap before first sector header (microseconds)."];
optional double clock_compensation_factor = 2 [default = 1.0,
(help) = "scale the output clock by this much."];
}

View File

@@ -0,0 +1,75 @@
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "micropolis.h"
#include "bytes.h"
#include "fmt/format.h"
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern. */
static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555);
/* Adds all bytes, with carry. */
uint8_t micropolisChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint16_t sum = 0;
while (!br.eof()) {
if (sum > 0xFF) {
sum -= 0x100 - 1;
}
sum += br.read_8();
}
/* The last carry is ignored */
return sum & 0xFF;
}
class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
_fmr->seekToIndexMark();
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(SECTOR_SYNC_PATTERN, matcher);
if (matcher == &SECTOR_SYNC_PATTERN) {
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
readRawBits(16);
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
br.read_8(); /* sync */
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 77)
return;
br.read(10); /* OS data or padding */
_sector->data = br.read(256);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = micropolisChecksum(bytes.slice(1, 2+266));
br.read(5); /* 4 byte ECC and ECC-present flag */
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MicropolisDecoder(config));
}

113
arch/micropolis/encoder.cc Normal file
View File

@@ -0,0 +1,113 @@
#include "globals.h"
#include "micropolis.h"
#include "sector.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "image.h"
#include "lib/encoders/encoders.pb.h"
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
{
if ((sector->data.size() != 256) && (sector->data.size() != MICROPOLIS_ENCODED_SECTOR_SIZE))
Error() << "unsupported sector size --- you must pick 256 or 275";
int fullSectorSize = 40 + MICROPOLIS_ENCODED_SECTOR_SIZE + 40 + 35;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector preamble */
for (int i=0; i<40; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
sectorData = sector->data;
else
{
ByteWriter writer(sectorData);
writer.write_8(0xff); /* Sync */
writer.write_8(sector->logicalTrack);
writer.write_8(sector->logicalSector);
for (int i=0; i<10; i++)
writer.write_8(0); /* Padding */
writer += sector->data;
writer.write_8(micropolisChecksum(sectorData.slice(1)));
for (int i=0; i<5; i++)
writer.write_8(0); /* 4 byte ECC and ECC not present flag */
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
/* sector postamble */
for (int i=0; i<40; i++)
fullSector->push_back(0);
/* filler */
for (int i=0; i<35; i++)
fullSector->push_back(0);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length";
bool lastBit = false;
encodeMfm(bits, cursor, fullSector, lastBit);
/* filler */
for (int i=0; i<5; i++)
{
bits[cursor++] = 1;
bits[cursor++] = 0;
}
}
class MicropolisEncoder : public AbstractEncoder
{
public:
MicropolisEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.micropolis())
{}
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 77))
{
for (int sectorId = 0; sectorId < 16; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 2.00;
if ((physicalTrack < 0) || (physicalTrack >= 77) || sectors.empty())
return std::unique_ptr<Fluxmap>();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor != bits.size())
Error() << "track data mismatched length";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const MicropolisEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new MicropolisEncoder(config));
}

View File

@@ -0,0 +1,16 @@
#ifndef MICROPOLIS_H
#define MICROPOLIS_H
#define MICROPOLIS_ENCODED_SECTOR_SIZE (1+2+266+6)
class AbstractDecoder;
class AbstractEncoder;
class EncoderProto;
class DecoderProto;
extern std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createMicropolisEncoder(const EncoderProto& config);
extern uint8_t micropolisChecksum(const Bytes& bytes);
#endif

View File

@@ -0,0 +1,5 @@
syntax = "proto2";
message MicropolisDecoderProto {}
message MicropolisEncoderProto {}

View File

@@ -5,8 +5,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include "record.h"
#include "track.h"
#include <string.h>
const int SECTOR_SIZE = 256;
@@ -23,53 +21,73 @@ const int SECTOR_SIZE = 256;
*/
const FluxPattern ID_PATTERN(32, 0xaaaaffaf);
void MxDecoder::beginTrack()
class MxDecoder : public AbstractDecoder
{
_currentSector = -1;
_clock = 0;
public:
MxDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
void beginTrack()
{
_currentSector = -1;
_clock = 0;
}
RecordType advanceToNextRecord()
{
if (_currentSector == -1)
{
/* First sector in the track: look for the sync marker. */
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
_logicalTrack = decodeFmMfm(readRawBits(32)).slice(0, 32).reader().read_be16();
}
else if (_currentSector == 10)
{
/* That was the last sector on the disk. */
return UNKNOWN_RECORD;
}
else
{
/* Otherwise we assume the clock from the first sector is still valid.
* The decoder framwork will automatically stop when we hit the end of
* the track. */
_sector->clock = _clock;
}
_currentSector++;
return SECTOR_RECORD;
}
void decodeSectorRecord()
{
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
_sector->logicalTrack = _logicalTrack;
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MxDecoder(config));
}
AbstractDecoder::RecordType MxDecoder::advanceToNextRecord()
{
if (_currentSector == -1)
{
/* First sector in the track: look for the sync marker. */
const FluxMatcher* matcher = nullptr;
_sector->clock = _clock = _fmr->seekToPattern(ID_PATTERN, matcher);
readRawBits(32); /* skip the ID mark */
_logicalTrack = decodeFmMfm(readRawBits(32)).reader().read_be16();
}
else if (_currentSector == 10)
{
/* That was the last sector on the disk. */
return UNKNOWN_RECORD;
}
else
{
/* Otherwise we assume the clock from the first sector is still valid.
* The decoder framwork will automatically stop when we hit the end of
* the track. */
_sector->clock = _clock;
}
_currentSector++;
return SECTOR_RECORD;
}
void MxDecoder::decodeSectorRecord()
{
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2).swab();
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_le16();
uint16_t wantChecksum = br.read_le16();
_sector->logicalTrack = _logicalTrack;
_sector->logicalSide = _track->physicalSide;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE);
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -3,19 +3,6 @@
#include "decoders/decoders.h"
class MxDecoder : public AbstractDecoder
{
public:
virtual ~MxDecoder() {}
void beginTrack();
RecordType advanceToNextRecord();
void decodeSectorRecord();
private:
nanoseconds_t _clock;
int _currentSector;
int _logicalTrack;
};
extern std::unique_ptr<AbstractDecoder> createMxDecoder(const DecoderProto& config);
#endif

4
arch/mx/mx.proto Normal file
View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message MxDecoderProto {}

196
arch/northstar/decoder.cc Normal file
View File

@@ -0,0 +1,196 @@
/* Decoder for North Star 10-sector hard-sectored disks.
*
* Supports both single- and double-density. For the sector format and
* checksum algorithm, see pp. 33 of the North Star Double Density Controller
* manual:
*
* http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf
*
* North Star disks do not contain any track/head/sector information
* encoded in the sector record. For this reason, we have to be absolutely
* sure that the hardSectorId is correct.
*/
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "northstar.h"
#include "bytes.h"
#include "lib/decoders/decoders.pb.h"
#include "fmt/format.h"
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
*
* This is true for most disks; however, I found a few disks, including an
* original North Star DOS/BASIC v2.2.1 DQ disk) that uses 0xFBnn, where
* nn is an incrementing pattern.
*
* 00 00 00 F B
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
* A A A A A A 5 5 4 5
*/
static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL);
/* FM sectors have 16 bytes of 00's followed by 0xFB.
* 00 FB
* 0000 0000 1111 1111 1110 1111
* A A F F E F
*/
static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL);
const FluxMatchers ANY_SECTOR_PATTERN(
{
&MFM_PATTERN,
&FM_PATTERN,
}
);
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.northstar())
{}
/* Search for FM or MFM sector record */
RecordType advanceToNextRecord() override
{
nanoseconds_t now = _fmr->tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
_fmr->seekToIndexMark();
now = _fmr->tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (_fmr->getDuration() - 21e6)) {
_fmr->seekToIndexMark();
return(UNKNOWN_RECORD);
}
int msSinceIndex = std::round(now / 1e6);
const FluxMatcher* matcher = nullptr;
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher);
int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10) {
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else {
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
if (matcher == &MFM_PATTERN) {
_sectorType = SECTOR_TYPE_MFM;
return SECTOR_RECORD;
}
if (matcher == &FM_PATTERN) {
_sectorType = SECTOR_TYPE_FM;
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
void decodeSectorRecord() override
{
unsigned recordSize, payloadSize, headerSize;
if (_sectorType == SECTOR_TYPE_MFM) {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
readRawBits(48);
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
uint8_t sync_char;
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalCylinder;
sync_char = br.read_8(); /* Sync char: 0xFB */
if (_sectorType == SECTOR_TYPE_MFM) {
sync_char = br.read_8();/* MFM second Sync char, usually 0xFB */
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> requiredSectors(unsigned cylinder, unsigned head) const override
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return sectors;
}
private:
const NorthstarDecoderProto& _config;
uint8_t _sectorType = SECTOR_TYPE_MFM;
uint8_t _hardSectorId;
};
std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new NorthstarDecoder(config));
}

166
arch/northstar/encoder.cc Normal file
View File

@@ -0,0 +1,166 @@
#include "globals.h"
#include "northstar.h"
#include "sector.h"
#include "bytes.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "image.h"
#include "lib/encoders/encoders.pb.h"
#define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
#define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<Sector>& sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
switch (sector->data.size()) {
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9) {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
} else {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
bool lastBit = false;
if (doubleDensity == true) {
encodeMfm(bits, cursor, fullSector, lastBit);
}
else {
encodeFm(bits, cursor, fullSector);
}
}
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.northstar())
{}
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 35))
{
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
return std::unique_ptr<Fluxmap>();
const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sectorData : sectors)
write_sector(bits, cursor, sectorData);
if (cursor > bits.size())
Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}
private:
const NorthstarEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
}

View File

@@ -0,0 +1,38 @@
#ifndef NORTHSTAR_H
#define NORTHSTAR_H
/* Northstar floppies are 10-hard sectored disks with a sector format as follows:
*
* |----------------------------------|
* | SYNC Byte | Payload | Checksum |
* |------------+----------+----------|
* | 1 (0xFB) | 256 (SD) | 1 |
* | 2 (0xFBFB) | 512 (DD) | |
* |----------------------------------|
*
*/
#define NORTHSTAR_PREAMBLE_SIZE_SD (16)
#define NORTHSTAR_PREAMBLE_SIZE_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1)
#define NORTHSTAR_HEADER_SIZE_DD (2)
#define NORTHSTAR_PAYLOAD_SIZE_SD (256)
#define NORTHSTAR_PAYLOAD_SIZE_DD (512)
#define NORTHSTAR_CHECKSUM_SIZE (1)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_SD (NORTHSTAR_HEADER_SIZE_SD + NORTHSTAR_PAYLOAD_SIZE_SD + NORTHSTAR_CHECKSUM_SIZE)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_DD (NORTHSTAR_HEADER_SIZE_DD + NORTHSTAR_PAYLOAD_SIZE_DD + NORTHSTAR_CHECKSUM_SIZE)
#define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1)
class AbstractDecoder;
class AbstractEncoder;
class EncoderProto;
class DecoderProto;
extern uint8_t northstarChecksum(const Bytes& bytes);
extern std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config);
#endif /* NORTHSTAR */

View File

@@ -0,0 +1,5 @@
syntax = "proto2";
message NorthstarDecoderProto {}
message NorthstarEncoderProto {}

98
arch/tids990/decoder.cc Normal file
View File

@@ -0,0 +1,98 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "tids990/tids990.h"
#include "crc.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "sector.h"
#include <string.h>
#include <fmt/format.h>
/* The Texas Instruments DS990 uses MFM with a scheme similar to a simplified
* version of the IBM record scheme (it's actually easier to parse than IBM).
* There are 26 sectors per track, each holding a rather weird 288 bytes.
*/
/*
* Sector record:
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 0 = 0x550a
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 00 = 0x11112a44
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 00 = 0x11112244
* ^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*/
const FluxPattern SECTOR_RECORD_PATTERN(32, 0x11112244);
/*
* Data record:
* data: 0 1 0 1 0 1 0 1 .0 0 0 0 1 0 1 1 = 0x550c
* mfm: 00 01 00 01 00 01 00 01.00 10 10 10 01 00 01 01 = 0x11112a45
* special: 00 01 00 01 00 01 00 01.00 10 00 10 01 00 01 01 = 0x11112245
* ^^
* When shifted out of phase, the special 0xa1 byte becomes an illegal
* encoding (you can't do 10 00). So this can't be spoofed by user data.
*/
const FluxPattern DATA_RECORD_PATTERN(32, 0x11112245);
const FluxMatchers ANY_RECORD_PATTERN({ &SECTOR_RECORD_PATTERN, &DATA_RECORD_PATTERN });
class Tids990Decoder : public AbstractDecoder
{
public:
Tids990Decoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return RecordType::SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return RecordType::DATA_RECORD;
return RecordType::UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
ByteReader br(bytes);
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
br.seek(2);
_sector->logicalSide = br.read_8() >> 3;
_sector->logicalTrack = br.read_8();
br.read_8(); /* number of sectors per track */
_sector->logicalSector = br.read_8();
br.read_be16(); /* sector size */
uint16_t wantChecksum = br.read_be16();
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
}
void decodeDataRecord()
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
ByteReader br(bytes);
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
br.seek(2);
_sector->data = br.read(TIDS990_PAYLOAD_SIZE);
uint16_t wantChecksum = br.read_be16();
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Tids990Decoder(config));
}

168
arch/tids990/encoder.cc Normal file
View File

@@ -0,0 +1,168 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "tids990.h"
#include "crc.h"
#include "writer.h"
#include "image.h"
#include "arch/tids990/tids990.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <fmt/format.h>
static int charToInt(char c)
{
if (isdigit(c))
return c - '0';
return 10 + tolower(c) - 'a';
}
static uint8_t decodeUint16(uint16_t raw)
{
Bytes b;
ByteWriter bw(b);
bw.write_be16(raw);
return decodeFmMfm(b.toBits())[0];
}
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.tids990())
{}
private:
void writeRawBits(uint32_t data, int width)
{
_cursor += width;
_lastBit = data & 1;
for (int i=0; i<width; i++)
{
unsigned pos = _cursor - i - 1;
if (pos < _bits.size())
_bits[pos] = data & 1;
data >>= 1;
}
}
void writeBytes(const Bytes& bytes)
{
encodeMfm(_bits, _cursor, bytes, _lastBit);
}
void writeBytes(int count, uint8_t byte)
{
Bytes bytes = { byte };
for (int i=0; i<count; i++)
writeBytes(bytes);
}
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
_bits.resize(bitsPerRevolution);
_cursor = 0;
uint8_t am1Unencoded = decodeUint16(_config.am1_byte());
uint8_t am2Unencoded = decodeUint16(_config.am2_byte());
writeBytes(_config.gap1_bytes(), 0x55);
bool first = true;
for (char sectorChar : _config.sector_skew())
{
int sectorId = charToInt(sectorChar);
if (!first)
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = image.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and
* include the malformed marker bytes. Our encoder doesn't know
* about this, of course, with the result that we have to construct
* the unencoded header, calculate the checksum, and then use the
* same logic to emit the bytes which require special encoding
* before encoding the rest of the header normally. */
{
Bytes header;
ByteWriter bw(header);
writeBytes(12, 0x55);
bw.write_8(am1Unencoded);
bw.write_8(sectorData->logicalSide << 3);
bw.write_8(sectorData->logicalTrack);
bw.write_8(_config.sector_count());
bw.write_8(sectorData->logicalSector);
bw.write_be16(sectorData->data.size());
uint16_t crc = crc16(CCITT_POLY, header);
bw.write_be16(crc);
writeRawBits(_config.am1_byte(), 16);
writeBytes(header.slice(1));
}
writeBytes(_config.gap2_bytes(), 0x55);
{
Bytes data;
ByteWriter bw(data);
writeBytes(12, 0x55);
bw.write_8(am2Unencoded);
bw += sectorData->data;
uint16_t crc = crc16(CCITT_POLY, data);
bw.write_be16(crc);
writeRawBits(_config.am2_byte(), 16);
writeBytes(data.slice(1));
}
}
if (_cursor >= _bits.size())
Error() << "track data overrun";
while (_cursor < _bits.size())
writeBytes(1, 0x55);
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(_bits, clockRateUs*1e3);
return fluxmap;
}
private:
const Tids990EncoderProto& _config;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;
};
std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Tids990Encoder(config));
}

18
arch/tids990/tids990.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef TIDS990_H
#define TIDS990_H
#define TIDS990_PAYLOAD_SIZE 288 /* bytes */
#define TIDS990_SECTOR_RECORD_SIZE 10 /* bytes */
#define TIDS990_DATA_RECORD_SIZE (TIDS990_PAYLOAD_SIZE + 4) /* bytes */
class AbstractEncoder;
class AbstractDecoder;
class DecoderProto;
class EncoderProto;
extern std::unique_ptr<AbstractDecoder> createTids990Decoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createTids990Encoder(const EncoderProto& config);
#endif

View File

@@ -0,0 +1,26 @@
syntax = "proto2";
import "lib/common.proto";
message Tids990DecoderProto {}
message Tids990EncoderProto {
optional double track_length_ms = 1 [ default = 166,
(help) = "length of a track" ];
optional int32 sector_count = 2 [ default = 26,
(help) = "number of sectors per track" ];
optional double clock_rate_khz = 3 [ default = 500,
(help) = "clock rate of data to write" ];
optional int32 am1_byte = 4 [ default = 0x2244,
(help) = "16-bit RAW bit pattern to use for the AM1 ID byte" ];
optional int32 am2_byte = 5 [ default = 0x2245,
(help) = "16-bit RAW bit pattern to use for the AM2 ID byte" ];
optional int32 gap1_bytes = 6 [ default = 80,
(help) = "size of gap 1 (the post-index gap)" ];
optional int32 gap2_bytes = 7 [ default = 21,
(help) = "size of gap 2 (the post-ID gap)" ];
optional int32 gap3_bytes = 8 [ default = 51,
(help) = "size of gap 3 (the post-data or format gap)" ];
optional string sector_skew = 9 [ default = "1mhc72nid83oje94pkfa50lgb6",
(help) = "order to emit sectors" ];
}

View File

@@ -2,13 +2,11 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "victor9k.h"
#include "crc.h"
#include "bytes.h"
#include "track.h"
#include "fmt/format.h"
#include <string.h>
#include <algorithm>
@@ -27,7 +25,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
static Bytes decode(const std::vector<bool>& bits)
{
@@ -54,58 +52,73 @@ static Bytes decode(const std::vector<bool>& bits)
return output;
}
AbstractDecoder::RecordType Victor9kDecoder::advanceToNextRecord()
class Victor9kDecoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
public:
Victor9kDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_sector->clock = _fmr->seekToPattern(ANY_RECORD_PATTERN, matcher);
if (matcher == &SECTOR_RECORD_PATTERN)
return SECTOR_RECORD;
if (matcher == &DATA_RECORD_PATTERN)
return DATA_RECORD;
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
/* Skip the sync marker bit. */
readRawBits(22);
/* Read header. */
auto bytes = decode(readRawBits(4*10)).slice(0, 4);
uint8_t rawTrack = bytes[1];
_sector->logicalSector = bytes[2];
uint8_t gotChecksum = bytes[3];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[1] + bytes[2];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord()
{
/* Skip the sync marker bit. */
readRawBits(22);
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10))
.slice(0, VICTOR9K_SECTOR_LENGTH+5);
ByteReader br(bytes);
/* Check that this is actually a data record. */
if (br.read_8() != 8)
return;
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new Victor9kDecoder(config));
}
void Victor9kDecoder::decodeSectorRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
/* Read header. */
auto bytes = decode(readRawBits(4*10)).slice(0, 4);
uint8_t rawTrack = bytes[1];
_sector->logicalSector = bytes[2];
uint8_t gotChecksum = bytes[3];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[1] + bytes[2];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void Victor9kDecoder::decodeDataRecord()
{
/* Skip the sync marker bit. */
readRawBits(23);
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+5)*10))
.slice(0, VICTOR9K_SECTOR_LENGTH+5);
ByteReader br(bytes);
/* Check that this is actually a data record. */
if (br.read_8() != 8)
return;
_sector->data = br.read(VICTOR9K_SECTOR_LENGTH);
uint16_t gotChecksum = sumBytes(_sector->data);
uint16_t wantChecksum = br.read_le16();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

200
arch/victor9k/encoder.cc Normal file
View File

@@ -0,0 +1,200 @@
#include "globals.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "victor9k.h"
#include "crc.h"
#include "sector.h"
#include "writer.h"
#include "image.h"
#include "fmt/format.h"
#include "arch/victor9k/victor9k.pb.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
static bool lastBit;
static void write_zero_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 0;
}
}
static void write_one_bits(std::vector<bool>& bits, unsigned& cursor, unsigned count)
{
while (count--)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = 1;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, const std::vector<bool>& src)
{
for (bool bit : src)
{
if (cursor < bits.size())
lastBit = bits[cursor++] = bit;
}
}
static void write_bits(std::vector<bool>& bits, unsigned& cursor, uint64_t data, int width)
{
cursor += width;
lastBit = data & 1;
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_bits(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
ByteReader br(bytes);
BitReader bitr(br);
while (!bitr.eof())
{
if (cursor < bits.size())
bits[cursor++] = bitr.get();
}
}
static int encode_data_gcr(uint8_t data)
{
switch (data & 0x0f)
{
#define GCR_ENTRY(gcr, data) \
case data: return gcr;
#include "data_gcr.h"
#undef GCR_ENTRY
}
return -1;
}
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
for (uint8_t b : bytes)
{
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
}
}
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
const Victor9kEncoderProto::TrackdataProto& trackdata,
const Sector& sector)
{
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide<<7);
uint8_t encodedSector = sector.logicalSector;
write_bytes(bits, cursor, Bytes {
encodedTrack,
encodedSector,
(uint8_t)(encodedTrack + encodedSector),
});
write_zero_bits(bits, cursor, trackdata.post_header_gap_bits());
write_one_bits(bits, cursor, trackdata.pre_data_sync_bits());
write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10);
write_bytes(bits, cursor, sector.data);
Bytes checksum(2);
checksum.writer().write_le16(sumBytes(sector.data));
write_bytes(bits, cursor, checksum);
write_zero_bits(bits, cursor, trackdata.post_data_gap_bits());
}
class Victor9kEncoder : public AbstractEncoder
{
public:
Victor9kEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.victor9k())
{}
private:
void getTrackFormat(Victor9kEncoderProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const auto& f : _config.trackdata())
{
if (f.has_min_cylinder() && (cylinder < f.min_cylinder()))
continue;
if (f.has_max_cylinder() && (cylinder > f.max_cylinder()))
continue;
if (f.has_head() && (head != f.head()))
continue;
trackdata.MergeFrom(f);
}
}
public:
std::vector<std::shared_ptr<Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<Sector>> sectors;
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
for (int i = 0; i < trackdata.sector_range().sector_count(); i++)
{
int sectorId = trackdata.sector_range().start_sector() + i;
const auto& sector = image.get(physicalTrack, physicalSide, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<Sector>>& sectors, const Image& image) override
{
Victor9kEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
unsigned bitsPerRevolution = trackdata.original_data_rate_khz() * trackdata.original_period_ms();
std::vector<bool> bits(bitsPerRevolution);
double clockRateUs = 166666.0 / bitsPerRevolution;
unsigned cursor = 0;
fillBitmapTo(bits, cursor, trackdata.post_index_gap_us() / clockRateUs, { true, false });
lastBit = false;
for (const auto& sector : sectors)
write_sector(bits, cursor, trackdata, *sector);
if (cursor >= bits.size())
Error() << fmt::format("track data overrun by {} bits", cursor - bits.size());
fillBitmapTo(bits, cursor, bits.size(), { true, false });
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs*1e3);
return fluxmap;
}
private:
const Victor9kEncoderProto& _config;
};
std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Victor9kEncoder(config));
}
// vim: sw=4 ts=4 et

View File

@@ -1,22 +1,22 @@
#ifndef VICTOR9K_H
#define VICTOR9K_H
#define VICTOR9K_SECTOR_RECORD 0xfffffeab
#define VICTOR9K_DATA_RECORD 0xfffffea4
class AbstractEncoder;
class AbstractDecoder;
class EncoderProto;
class DecoderProto;
/* ... 1101 0101 0111
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
/* ... 1101 0100 1001
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_DATA_RECORD 0xfffffd49
#define VICTOR9K_SECTOR_LENGTH 512
class Sector;
class Fluxmap;
class Victor9kDecoder : public AbstractDecoder
{
public:
virtual ~Victor9kDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
void decodeDataRecord();
};
extern std::unique_ptr<AbstractDecoder> createVictor9kDecoder(const DecoderProto& config);
extern std::unique_ptr<AbstractEncoder> createVictor9kEncoder(const EncoderProto& config);
#endif

View File

@@ -0,0 +1,32 @@
syntax = "proto2";
import "lib/common.proto";
message Victor9kDecoderProto {}
// NEXT: 12
message Victor9kEncoderProto {
message TrackdataProto {
message SectorRangeProto {
optional int32 start_sector = 1 [(help) = "first sector ID on track"];
optional int32 sector_count = 2 [(help) = "number of sectors on track"];
}
optional int32 min_cylinder = 1 [(help) = "minimum cylinder this format applies to"];
optional int32 max_cylinder = 2 [(help) = "maximum cylinder this format applies to"];
optional int32 head = 3 [(help) = "which head this format applies to"];
optional double original_period_ms = 4 [(help) = "original rotational period of this cylinder"];
optional double original_data_rate_khz = 5 [(help) = "original data rate of this cylinder"];
optional double post_index_gap_us = 6 [(help) = "size of post-index gap"];
optional int32 pre_header_sync_bits = 10 [(help) = "number of sync bits before the sector header"];
optional int32 pre_data_sync_bits = 8 [(help) = "number of sync bits before the sector data"];
optional int32 post_data_gap_bits = 9 [(help) = "size of gap between data and the next header"];
optional int32 post_header_gap_bits = 11 [(help) = "size of gap between header and the data"];
optional SectorRangeProto sector_range = 7 [(help) = "write these sectors on each track"];
}
repeated TrackdataProto trackdata = 1;
}

View File

@@ -2,7 +2,6 @@
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "protocol.h"
#include "record.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "zilogmcz.h"
@@ -14,35 +13,49 @@
static const FluxPattern SECTOR_START_PATTERN(16, 0xaaab);
AbstractDecoder::RecordType ZilogMczDecoder::advanceToNextRecord()
class ZilogMczDecoder : public AbstractDecoder
{
const FluxMatcher* matcher = nullptr;
_fmr->seekToIndexMark();
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
if (matcher == &SECTOR_START_PATTERN)
return SECTOR_RECORD;
return UNKNOWN_RECORD;
public:
ZilogMczDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
RecordType advanceToNextRecord()
{
const FluxMatcher* matcher = nullptr;
_fmr->seekToIndexMark();
_sector->clock = _fmr->seekToPattern(SECTOR_START_PATTERN, matcher);
if (matcher == &SECTOR_START_PATTERN)
return SECTOR_RECORD;
return UNKNOWN_RECORD;
}
void decodeSectorRecord()
{
readRawBits(14);
auto rawbits = readRawBits(140*16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new ZilogMczDecoder(config));
}
void ZilogMczDecoder::decodeSectorRecord()
{
readRawBits(14);
auto rawbits = readRawBits(140*16);
auto bytes = decodeFmMfm(rawbits).slice(0, 140);
ByteReader br(bytes);
_sector->logicalSector = br.read_8() & 0x1f;
_sector->logicalSide = 0;
_sector->logicalTrack = br.read_8() & 0x7f;
if (_sector->logicalSector > 31)
return;
if (_sector->logicalTrack > 80)
return;
_sector->data = br.read(132);
uint16_t wantChecksum = br.read_be16();
uint16_t gotChecksum = crc16(MODBUS_POLY, 0x0000, bytes.slice(0, 134));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}

View File

@@ -1,17 +1,7 @@
#ifndef ZILOGMCZ_H
#define ZILOGMCZ_H
class Sector;
class Fluxmap;
class ZilogMczDecoder : public AbstractDecoder
{
public:
virtual ~ZilogMczDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
};
extern std::unique_ptr<AbstractDecoder> createZilogMczDecoder(const DecoderProto& config);
#endif

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message ZilogMczDecoderProto {}

2
dep/agg/AUTHORS Normal file
View File

@@ -0,0 +1,2 @@
Anti-Grain Geometry - Version 2.4
Copyright (C) 2002-2005 Maxim Shemanarev (McSeem)

63
dep/agg/README Normal file
View File

@@ -0,0 +1,63 @@
The Anti-Grain Geometry Project
A high quality rendering engine for C++
http://antigrain.com
Anti-Grain Geometry - Version 2.4
Copyright (C) 2002-2005 Maxim Shemanarev (McSeem)
Permission to copy, use, modify, sell and distribute this software
is granted provided this copyright notice appears in all copies.
This software is provided "as is" without express or implied
warranty, and with no claim as to its suitability for any purpose.
---------------------------------
Use automake to build the library.
If automake is not available you still can use the old make.
There is a very simple Makefile that can be used. Note that
if you use automake it will overwrite Makefile.
---------------------------------
If building on AmigaOS 4.0 or higher type the following for
instructions on what targets are available.
make -f Makefile.AmigaOS
To just build and install AGG into the standard AmigaOS SDK
ready for use type:
make -f Makefile.AmigaOS install
If you just want to build one demo (e.g. lion) use:
make -f Makefile.AmigaOS bin/lion
If you have any questions about the AmigaOS port please
contact Steven Solie (ssolie@telus.net) for help.
---------------------------------
To build all examples using SDL (Mac or Linux) just type:
cd /examples/sdl
make
Individual examples can be built with
make aa_test
In the same way the native Carbon examples can be built with
cd /examples/macosx_carbon
make
In both cases the static library will be built (if it was not already)
from the existing global Makefile in /src/.
The Makefiles for both SDL and Carbon will also attempt to download the
required .bmp files if they are not found in the system for a given
example. If the files could not be fetched (wget) the user will receive
a message explaining where to download the samples from (sphere.bmp,
etc.) Since all programs reside in the same directory there is no need
to duplicate the .bmp files for each program that needs to use them.
---------------------------------

7
dep/agg/README.md Normal file
View File

@@ -0,0 +1,7 @@
This is a vary stripped down copy of the Anti-Grain Antialiasing graphics
rendering library --- I've removed all the platform-specific and control stuff
so that it can be used to generate memory images only.
The original AGG site is dead, so this version is cloned from
https://github.com/NNemec/antigrain.

7
dep/agg/UPSTREAM.md Normal file
View File

@@ -0,0 +1,7 @@
This is a vary stripped down copy of the Anti-Grain Antialiasing graphics
rendering library --- I've removed all the platform-specific and control stuff
so that it can be used to generate memory images only.
The original AGG site is dead, so this version is cloned from
https://github.com/NNemec/antigrain.

View File

@@ -0,0 +1,49 @@
SUBDIRS = ctrl util platform
aggincludedir = $(includedir)/agg2
agginclude_HEADERS = \
agg_alpha_mask_u8.h agg_glyph_raster_bin.h agg_span_allocator.h \
agg_arc.h agg_gsv_text.h agg_span_converter.h \
agg_array.h agg_image_accessors.h agg_span_gouraud.h \
agg_arrowhead.h agg_image_filters.h agg_span_gouraud_gray.h \
agg_basics.h agg_line_aa_basics.h agg_span_gouraud_rgba.h \
agg_bezier_arc.h agg_math.h agg_span_gradient.h \
agg_bitset_iterator.h agg_blur.h agg_math_stroke.h \
agg_span_gradient_alpha.h agg_gradient_lut.h \
agg_bounding_rect.h agg_path_length.h agg_span_image_filter.h \
agg_bspline.h agg_path_storage.h agg_span_image_filter_gray.h \
agg_clip_liang_barsky.h agg_path_storage_integer.h agg_span_image_filter_rgb.h \
agg_color_gray.h agg_pattern_filters_rgba.h agg_span_image_filter_rgba.h \
agg_color_rgba.h agg_pixfmt_amask_adaptor.h agg_span_interpolator_adaptor.h \
agg_config.h agg_pixfmt_gray.h agg_span_interpolator_linear.h \
agg_conv_adaptor_vcgen.h agg_pixfmt_rgb.h agg_span_interpolator_persp.h \
agg_conv_adaptor_vpgen.h agg_pixfmt_rgb_packed.h agg_span_interpolator_trans.h \
agg_conv_bspline.h agg_pixfmt_rgba.h agg_pixfmt_transposer.h \
agg_span_pattern_gray.h \
agg_conv_clip_polygon.h agg_rasterizer_cells_aa.h agg_span_pattern_rgb.h \
agg_conv_clip_polyline.h agg_rasterizer_compound_aa.h agg_span_pattern_rgba.h \
agg_conv_close_polygon.h agg_rasterizer_outline.h agg_span_solid.h \
agg_conv_concat.h agg_rasterizer_outline_aa.h agg_span_subdiv_adaptor.h \
agg_conv_contour.h agg_rasterizer_scanline_aa.h agg_trans_affine.h \
agg_conv_curve.h agg_rasterizer_sl_clip.h agg_trans_bilinear.h \
agg_conv_dash.h agg_renderer_base.h agg_trans_double_path.h \
agg_conv_gpc.h agg_renderer_markers.h \
agg_conv_marker.h agg_renderer_mclip.h agg_trans_perspective.h \
agg_conv_marker_adaptor.h agg_renderer_outline_aa.h agg_trans_single_path.h \
agg_conv_segmentator.h agg_renderer_outline_image.h agg_trans_viewport.h \
agg_conv_shorten_path.h agg_renderer_primitives.h agg_trans_warp_magnifier.h \
agg_conv_smooth_poly1.h agg_renderer_raster_text.h agg_vcgen_bspline.h \
agg_conv_stroke.h agg_renderer_scanline.h agg_vcgen_contour.h \
agg_conv_transform.h agg_rendering_buffer.h agg_vcgen_dash.h \
agg_conv_unclose_polygon.h agg_rendering_buffer_dynarow.h agg_vcgen_markers_term.h \
agg_curves.h agg_rounded_rect.h agg_vcgen_smooth_poly1.h \
agg_scanline_bin.h agg_vcgen_stroke.h \
agg_dda_line.h agg_scanline_boolean_algebra.h agg_vcgen_vertex_sequence.h \
agg_ellipse.h agg_scanline_p.h agg_vertex_sequence.h \
agg_ellipse_bresenham.h agg_scanline_storage_aa.h agg_vpgen_clip_polygon.h \
agg_embedded_raster_fonts.h agg_scanline_storage_bin.h agg_vpgen_clip_polyline.h \
agg_font_cache_manager.h agg_scanline_u.h agg_vpgen_segmentator.h \
agg_gamma_functions.h agg_shorten_path.h \
agg_gamma_lut.h agg_simul_eq.h \
agg_font_cache_manager2.h agg_pixfmt_base.h agg_rasterizer_scanline_aa_nogamma.h \
agg_span_gradient_contour.h agg_span_gradient_image.h

568
dep/agg/include/agg2d.h Normal file
View File

@@ -0,0 +1,568 @@
//----------------------------------------------------------------------------
// Agg2D - Version 1.0
// Based on Anti-Grain Geometry
// Copyright (C) 2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//
// 25 Jan 2007 - Ported to AGG 2.4 Jerry Evans (jerry@novadsp.com)
//
//----------------------------------------------------------------------------
#ifndef AGG2D_INCLUDED
#define AGG2D_INCLUDED
// With this define uncommented you can use floating-point pixel format
//#define AGG2D_USE_FLOAT_FORMAT
#include "agg_basics.h"
#include "agg_trans_affine.h"
#include "agg_trans_viewport.h"
#include "agg_path_storage.h"
#include "agg_conv_stroke.h"
#include "agg_conv_transform.h"
#include "agg_conv_curve.h"
#include "agg_rendering_buffer.h"
#include "agg_renderer_base.h"
#include "agg_renderer_scanline.h"
#include "agg_span_gradient.h"
#include "agg_span_image_filter_rgba.h"
#include "agg_span_allocator.h"
#include "agg_span_converter.h"
#include "agg_span_interpolator_linear.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_gamma_functions.h"
#include "agg_scanline_u.h"
#include "agg_bezier_arc.h"
#include "agg_rounded_rect.h"
#include "agg_font_cache_manager.h"
#include "agg_pixfmt_rgba.h"
#include "agg_image_accessors.h"
#include <string>
class Agg2D
{
#ifdef AGG2D_USE_FLOAT_FORMAT
typedef agg::rgba32 ColorType;
#else
typedef agg::rgba8 ColorType;
#endif
typedef agg::order_bgra ComponentOrder; // Platform dependent!
typedef agg::blender_rgba<ColorType, ComponentOrder> Blender;
typedef agg::comp_op_adaptor_rgba<ColorType, ComponentOrder> BlenderComp;
typedef agg::blender_rgba_pre<ColorType, ComponentOrder> BlenderPre;
typedef agg::comp_op_adaptor_rgba_pre<ColorType, ComponentOrder> BlenderCompPre;
typedef agg::pixfmt_alpha_blend_rgba<Blender, agg::rendering_buffer> PixFormat;
typedef agg::pixfmt_custom_blend_rgba<BlenderComp, agg::rendering_buffer> PixFormatComp;
typedef agg::pixfmt_alpha_blend_rgba<BlenderPre, agg::rendering_buffer> PixFormatPre;
typedef agg::pixfmt_custom_blend_rgba<BlenderCompPre, agg::rendering_buffer> PixFormatCompPre;
typedef agg::renderer_base<PixFormat> RendererBase;
typedef agg::renderer_base<PixFormatComp> RendererBaseComp;
typedef agg::renderer_base<PixFormatPre> RendererBasePre;
typedef agg::renderer_base<PixFormatCompPre> RendererBaseCompPre;
typedef agg::renderer_scanline_aa_solid<RendererBase> RendererSolid;
typedef agg::renderer_scanline_aa_solid<RendererBaseComp> RendererSolidComp;
typedef agg::span_allocator<ColorType> SpanAllocator;
typedef agg::pod_auto_array<ColorType, 256> GradientArray;
typedef agg::span_gradient<ColorType, agg::span_interpolator_linear<>, agg::gradient_x, GradientArray> LinearGradientSpan;
typedef agg::span_gradient<ColorType, agg::span_interpolator_linear<>, agg::gradient_circle, GradientArray> RadialGradientSpan;
typedef agg::conv_curve<agg::path_storage> ConvCurve;
typedef agg::conv_stroke<ConvCurve> ConvStroke;
typedef agg::conv_transform<ConvCurve> PathTransform;
typedef agg::conv_transform<ConvStroke> StrokeTransform;
enum Gradient
{
Solid,
Linear,
Radial
};
public:
friend class Agg2DRenderer;
// Use srgba8 as the "user" color type, even though the underlying color type
// might be something else, such as rgba32. This allows code based on
// 8-bit sRGB values to carry on working as before.
typedef agg::srgba8 Color;
typedef agg::rect_i Rect;
typedef agg::rect_d RectD;
typedef agg::trans_affine Affine;
enum LineJoin
{
JoinMiter = agg::miter_join,
JoinRound = agg::round_join,
JoinBevel = agg::bevel_join
};
enum LineCap
{
CapButt = agg::butt_cap,
CapSquare = agg::square_cap,
CapRound = agg::round_cap
};
enum TextAlignment
{
AlignLeft,
AlignRight,
AlignCenter,
};
enum DrawPathFlag
{
FillOnly,
StrokeOnly,
FillAndStroke,
FillWithLineColor
};
enum ViewportOption
{
Anisotropic,
XMinYMin,
XMidYMin,
XMaxYMin,
XMinYMid,
XMidYMid,
XMaxYMid,
XMinYMax,
XMidYMax,
XMaxYMax
};
struct Transformations
{
double affineMatrix[6];
};
struct Image
{
agg::rendering_buffer renBuf;
Image() {}
Image(unsigned char* buf, unsigned width, unsigned height, int stride) :
renBuf(buf, width, height, stride) {}
void attach(unsigned char* buf, unsigned width, unsigned height, int stride)
{
renBuf.attach(buf, width, height, stride);
}
int width() const { return renBuf.width(); }
int height() const { return renBuf.height(); }
void premultiply();
void demultiply();
};
enum ImageFilter
{
NoFilter,
Bilinear,
Hanning,
Hermite,
Quadric,
Bicubic,
Catrom,
Spline16,
Spline36,
Blackman144
};
enum ImageResample
{
NoResample,
ResampleAlways,
ResampleOnZoomOut
};
enum FontCacheType
{
RasterFontCache,
VectorFontCache
};
enum BlendMode
{
BlendAlpha = agg::end_of_comp_op_e,
BlendClear = agg::comp_op_clear,
BlendSrc = agg::comp_op_src,
BlendDst = agg::comp_op_dst,
BlendSrcOver = agg::comp_op_src_over,
BlendDstOver = agg::comp_op_dst_over,
BlendSrcIn = agg::comp_op_src_in,
BlendDstIn = agg::comp_op_dst_in,
BlendSrcOut = agg::comp_op_src_out,
BlendDstOut = agg::comp_op_dst_out,
BlendSrcAtop = agg::comp_op_src_atop,
BlendDstAtop = agg::comp_op_dst_atop,
BlendXor = agg::comp_op_xor,
BlendAdd = agg::comp_op_plus,
BlendMultiply = agg::comp_op_multiply,
BlendScreen = agg::comp_op_screen,
BlendOverlay = agg::comp_op_overlay,
BlendDarken = agg::comp_op_darken,
BlendLighten = agg::comp_op_lighten,
BlendColorDodge = agg::comp_op_color_dodge,
BlendColorBurn = agg::comp_op_color_burn,
BlendHardLight = agg::comp_op_hard_light,
BlendSoftLight = agg::comp_op_soft_light,
BlendDifference = agg::comp_op_difference,
BlendExclusion = agg::comp_op_exclusion,
};
enum Direction
{
CW, CCW
};
~Agg2D();
Agg2D();
// Setup
//-----------------------
void attach(unsigned char* buf, unsigned width, unsigned height, int stride);
void attach(Image& img);
void clipBox(double x1, double y1, double x2, double y2);
RectD clipBox() const;
void clearAll(Color c);
void clearAll(unsigned r, unsigned g, unsigned b, unsigned a = 255);
void clearClipBox(Color c);
void clearClipBox(unsigned r, unsigned g, unsigned b, unsigned a = 255);
// Conversions
//-----------------------
void worldToScreen(double& x, double& y) const;
void screenToWorld(double& x, double& y) const;
double worldToScreen(double scalar) const;
double screenToWorld(double scalar) const;
void alignPoint(double& x, double& y) const;
bool inBox(double worldX, double worldY) const;
// General Attributes
//-----------------------
void blendMode(BlendMode m);
BlendMode blendMode() const;
void imageBlendMode(BlendMode m);
BlendMode imageBlendMode() const;
void imageBlendColor(Color c);
void imageBlendColor(unsigned r, unsigned g, unsigned b, unsigned a = 255);
Color imageBlendColor() const;
void masterAlpha(double a);
double masterAlpha() const;
void antiAliasGamma(double g);
double antiAliasGamma() const;
void fillColor(Color c);
void fillColor(unsigned r, unsigned g, unsigned b, unsigned a = 255);
void noFill();
void lineColor(Color c);
void lineColor(unsigned r, unsigned g, unsigned b, unsigned a = 255);
void noLine();
Color fillColor() const;
Color lineColor() const;
void fillLinearGradient(double x1, double y1, double x2, double y2, Color c1, Color c2, double profile=1.0);
void lineLinearGradient(double x1, double y1, double x2, double y2, Color c1, Color c2, double profile=1.0);
void fillRadialGradient(double x, double y, double r, Color c1, Color c2, double profile=1.0);
void lineRadialGradient(double x, double y, double r, Color c1, Color c2, double profile=1.0);
void fillRadialGradient(double x, double y, double r, Color c1, Color c2, Color c3);
void lineRadialGradient(double x, double y, double r, Color c1, Color c2, Color c3);
void fillRadialGradient(double x, double y, double r);
void lineRadialGradient(double x, double y, double r);
void lineWidth(double w);
double lineWidth(double w) const;
void lineCap(LineCap cap);
LineCap lineCap() const;
void lineJoin(LineJoin join);
LineJoin lineJoin() const;
void fillEvenOdd(bool evenOddFlag);
bool fillEvenOdd() const;
void textAlignment(TextAlignment alignment);
void textSize(double sizeX, double sizeY);
inline void textSize(double size) { textSize(size, size); }
// Transformations
//-----------------------
Transformations transformations() const;
void transformations(const Transformations& tr);
void resetTransformations();
void affine(const Affine& tr);
void affine(const Transformations& tr);
void rotate(double angle);
void scale(double sx, double sy);
void skew(double sx, double sy);
void translate(double x, double y);
void parallelogram(double x1, double y1, double x2, double y2, const double* para);
void viewport(double worldX1, double worldY1, double worldX2, double worldY2,
double screenX1, double screenY1, double screenX2, double screenY2,
ViewportOption opt=XMidYMid);
// Basic Shapes
//-----------------------
void line(double x1, double y1, double x2, double y2);
void triangle(double x1, double y1, double x2, double y2, double x3, double y3);
void rectangle(double x1, double y1, double x2, double y2);
void roundedRect(double x1, double y1, double x2, double y2, double r);
void roundedRect(double x1, double y1, double x2, double y2, double rx, double ry);
void roundedRect(double x1, double y1, double x2, double y2,
double rxBottom, double ryBottom,
double rxTop, double ryTop);
void ellipse(double cx, double cy, double rx, double ry);
void arc(double cx, double cy, double rx, double ry, double start, double sweep);
void star(double cx, double cy, double r1, double r2, double startAngle, int numRays);
void curve(double x1, double y1, double x2, double y2, double x3, double y3);
void curve(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4);
void polygon(double* xy, int numPoints);
void polyline(double* xy, int numPoints);
// Path commands
//-----------------------
void resetPath();
void moveTo(double x, double y);
void moveRel(double dx, double dy);
void lineTo(double x, double y);
void lineRel(double dx, double dy);
void horLineTo(double x);
void horLineRel(double dx);
void verLineTo(double y);
void verLineRel(double dy);
void arcTo(double rx, double ry,
double angle,
bool largeArcFlag,
bool sweepFlag,
double x, double y);
void arcRel(double rx, double ry,
double angle,
bool largeArcFlag,
bool sweepFlag,
double dx, double dy);
void quadricCurveTo(double xCtrl, double yCtrl,
double xTo, double yTo);
void quadricCurveRel(double dxCtrl, double dyCtrl,
double dxTo, double dyTo);
void quadricCurveTo(double xTo, double yTo);
void quadricCurveRel(double dxTo, double dyTo);
void cubicCurveTo(double xCtrl1, double yCtrl1,
double xCtrl2, double yCtrl2,
double xTo, double yTo);
void cubicCurveRel(double dxCtrl1, double dyCtrl1,
double dxCtrl2, double dyCtrl2,
double dxTo, double dyTo);
void cubicCurveTo(double xCtrl2, double yCtrl2,
double xTo, double yTo);
void cubicCurveRel(double xCtrl2, double yCtrl2,
double xTo, double yTo);
void addEllipse(double cx, double cy, double rx, double ry, Direction dir);
void text(double x, double y, const std::string& text);
void closePolygon();
void drawPath(DrawPathFlag flag = FillAndStroke);
void drawPathNoTransform(DrawPathFlag flag = FillAndStroke);
// Image Transformations
//-----------------------
void imageFilter(ImageFilter f);
ImageFilter imageFilter() const;
void imageResample(ImageResample f);
ImageResample imageResample() const;
void transformImage(const Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
double dstX1, double dstY1, double dstX2, double dstY2);
void transformImage(const Image& img,
double dstX1, double dstY1, double dstX2, double dstY2);
void transformImage(const Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
const double* parallelogram);
void transformImage(const Image& img, const double* parallelogram);
void transformImagePath(const Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
double dstX1, double dstY1, double dstX2, double dstY2);
void transformImagePath(const Image& img,
double dstX1, double dstY1, double dstX2, double dstY2);
void transformImagePath(const Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
const double* parallelogram);
void transformImagePath(const Image& img, const double* parallelogram);
// Image Blending (no transformations available)
void blendImage(Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
double dstX, double dstY, unsigned alpha=255);
void blendImage(Image& img, double dstX, double dstY, unsigned alpha=255);
// Copy image directly, together with alpha-channel
void copyImage(Image& img,
int imgX1, int imgY1, int imgX2, int imgY2,
double dstX, double dstY);
void copyImage(Image& img, double dstX, double dstY);
// Auxiliary
//-----------------------
static double pi() { return agg::pi; }
static double deg2Rad(double v) { return v * agg::pi / 180.0; }
static double rad2Deg(double v) { return v * 180.0 / agg::pi; }
private:
void render(bool fillColor);
#ifdef AGG_USE_FONTS
void render(FontRasterizer& ras, FontScanline& sl);
#endif // AGG_USE_FONTS
void addLine(double x1, double y1, double x2, double y2);
void updateRasterizerGamma();
void renderImage(const Image& img, int x1, int y1, int x2, int y2, const double* parl);
agg::rendering_buffer m_rbuf;
PixFormat m_pixFormat;
PixFormatComp m_pixFormatComp;
PixFormatPre m_pixFormatPre;
PixFormatCompPre m_pixFormatCompPre;
RendererBase m_renBase;
RendererBaseComp m_renBaseComp;
RendererBasePre m_renBasePre;
RendererBaseCompPre m_renBaseCompPre;
RendererSolid m_renSolid;
RendererSolidComp m_renSolidComp;
SpanAllocator m_allocator;
RectD m_clipBox;
BlendMode m_blendMode;
BlendMode m_imageBlendMode;
Color m_imageBlendColor;
agg::scanline_u8 m_scanline;
agg::rasterizer_scanline_aa<> m_rasterizer;
double m_masterAlpha;
double m_antiAliasGamma;
Color m_fillColor;
Color m_lineColor;
GradientArray m_fillGradient;
GradientArray m_lineGradient;
LineCap m_lineCap;
LineJoin m_lineJoin;
Gradient m_fillGradientFlag;
Gradient m_lineGradientFlag;
agg::trans_affine m_fillGradientMatrix;
agg::trans_affine m_lineGradientMatrix;
double m_fillGradientD1;
double m_lineGradientD1;
double m_fillGradientD2;
double m_lineGradientD2;
TextAlignment m_textAlignment;
double m_textSizeX;
double m_textSizeY;
ImageFilter m_imageFilter;
ImageResample m_imageResample;
agg::image_filter_lut m_imageFilterLut;
agg::span_interpolator_linear<> m_fillGradientInterpolator;
agg::span_interpolator_linear<> m_lineGradientInterpolator;
agg::gradient_x m_linearGradientFunction;
agg::gradient_circle m_radialGradientFunction;
double m_lineWidth;
bool m_evenOddFlag;
agg::path_storage m_path;
agg::trans_affine m_transform;
ConvCurve m_convCurve;
ConvStroke m_convStroke;
PathTransform m_pathTransform;
StrokeTransform m_strokeTransform;
#ifdef AGG_USE_FONTS
#ifndef AGG2D_USE_FREETYPE
HDC m_fontDC;
#endif
FontEngine m_fontEngine;
FontCacheManager m_fontCacheManager;
#endif
};
inline bool operator == (const Agg2D::Color& c1, const Agg2D::Color& c2)
{
return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b && c1.a == c2.a;
}
inline bool operator != (const Agg2D::Color& c1, const Agg2D::Color& c2)
{
return !(c1 == c2);
}
#endif

View File

@@ -0,0 +1,499 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// scanline_u8 class
//
//----------------------------------------------------------------------------
#ifndef AGG_ALPHA_MASK_U8_INCLUDED
#define AGG_ALPHA_MASK_U8_INCLUDED
#include <cstring>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
namespace agg
{
//===================================================one_component_mask_u8
struct one_component_mask_u8
{
static unsigned calculate(const int8u* p) { return *p; }
};
//=====================================================rgb_to_gray_mask_u8
template<unsigned R, unsigned G, unsigned B>
struct rgb_to_gray_mask_u8
{
static unsigned calculate(const int8u* p)
{
return (p[R]*77 + p[G]*150 + p[B]*29) >> 8;
}
};
//==========================================================alpha_mask_u8
template<unsigned Step=1, unsigned Offset=0, class MaskF=one_component_mask_u8>
class alpha_mask_u8
{
public:
typedef int8u cover_type;
typedef alpha_mask_u8<Step, Offset, MaskF> self_type;
enum cover_scale_e
{
cover_shift = 8,
cover_none = 0,
cover_full = 255
};
alpha_mask_u8() : m_rbuf(0) {}
explicit alpha_mask_u8(rendering_buffer& rbuf) : m_rbuf(&rbuf) {}
void attach(rendering_buffer& rbuf) { m_rbuf = &rbuf; }
MaskF& mask_function() { return m_mask_function; }
const MaskF& mask_function() const { return m_mask_function; }
//--------------------------------------------------------------------
cover_type pixel(int x, int y) const
{
if(x >= 0 && y >= 0 &&
x < (int)m_rbuf->width() &&
y < (int)m_rbuf->height())
{
return (cover_type)m_mask_function.calculate(
m_rbuf->row_ptr(y) + x * Step + Offset);
}
return 0;
}
//--------------------------------------------------------------------
cover_type combine_pixel(int x, int y, cover_type val) const
{
if(x >= 0 && y >= 0 &&
x < (int)m_rbuf->width() &&
y < (int)m_rbuf->height())
{
return (cover_type)((cover_full + val *
m_mask_function.calculate(
m_rbuf->row_ptr(y) + x * Step + Offset)) >>
cover_shift);
}
return 0;
}
//--------------------------------------------------------------------
void fill_hspan(int x, int y, cover_type* dst, int num_pix) const
{
int xmax = m_rbuf->width() - 1;
int ymax = m_rbuf->height() - 1;
int count = num_pix;
cover_type* covers = dst;
if(y < 0 || y > ymax)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
if(x < 0)
{
count += x;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers, 0, -x * sizeof(cover_type));
covers -= x;
x = 0;
}
if(x + count > xmax)
{
int rest = x + count - xmax - 1;
count -= rest;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers + count, 0, rest * sizeof(cover_type));
}
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*covers++ = (cover_type)m_mask_function.calculate(mask);
mask += Step;
}
while(--count);
}
//--------------------------------------------------------------------
void combine_hspan(int x, int y, cover_type* dst, int num_pix) const
{
int xmax = m_rbuf->width() - 1;
int ymax = m_rbuf->height() - 1;
int count = num_pix;
cover_type* covers = dst;
if(y < 0 || y > ymax)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
if(x < 0)
{
count += x;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers, 0, -x * sizeof(cover_type));
covers -= x;
x = 0;
}
if(x + count > xmax)
{
int rest = x + count - xmax - 1;
count -= rest;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers + count, 0, rest * sizeof(cover_type));
}
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*covers = (cover_type)((cover_full + (*covers) *
m_mask_function.calculate(mask)) >>
cover_shift);
++covers;
mask += Step;
}
while(--count);
}
//--------------------------------------------------------------------
void fill_vspan(int x, int y, cover_type* dst, int num_pix) const
{
int xmax = m_rbuf->width() - 1;
int ymax = m_rbuf->height() - 1;
int count = num_pix;
cover_type* covers = dst;
if(x < 0 || x > xmax)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
if(y < 0)
{
count += y;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers, 0, -y * sizeof(cover_type));
covers -= y;
y = 0;
}
if(y + count > ymax)
{
int rest = y + count - ymax - 1;
count -= rest;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers + count, 0, rest * sizeof(cover_type));
}
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*covers++ = (cover_type)m_mask_function.calculate(mask);
mask += m_rbuf->stride();
}
while(--count);
}
//--------------------------------------------------------------------
void combine_vspan(int x, int y, cover_type* dst, int num_pix) const
{
int xmax = m_rbuf->width() - 1;
int ymax = m_rbuf->height() - 1;
int count = num_pix;
cover_type* covers = dst;
if(x < 0 || x > xmax)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
if(y < 0)
{
count += y;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers, 0, -y * sizeof(cover_type));
covers -= y;
y = 0;
}
if(y + count > ymax)
{
int rest = y + count - ymax - 1;
count -= rest;
if(count <= 0)
{
std::memset(dst, 0, num_pix * sizeof(cover_type));
return;
}
std::memset(covers + count, 0, rest * sizeof(cover_type));
}
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*covers = (cover_type)((cover_full + (*covers) *
m_mask_function.calculate(mask)) >>
cover_shift);
++covers;
mask += m_rbuf->stride();
}
while(--count);
}
private:
alpha_mask_u8(const self_type&);
const self_type& operator = (const self_type&);
rendering_buffer* m_rbuf;
MaskF m_mask_function;
};
typedef alpha_mask_u8<1, 0> alpha_mask_gray8; //----alpha_mask_gray8
typedef alpha_mask_u8<3, 0> alpha_mask_rgb24r; //----alpha_mask_rgb24r
typedef alpha_mask_u8<3, 1> alpha_mask_rgb24g; //----alpha_mask_rgb24g
typedef alpha_mask_u8<3, 2> alpha_mask_rgb24b; //----alpha_mask_rgb24b
typedef alpha_mask_u8<3, 2> alpha_mask_bgr24r; //----alpha_mask_bgr24r
typedef alpha_mask_u8<3, 1> alpha_mask_bgr24g; //----alpha_mask_bgr24g
typedef alpha_mask_u8<3, 0> alpha_mask_bgr24b; //----alpha_mask_bgr24b
typedef alpha_mask_u8<4, 0> alpha_mask_rgba32r; //----alpha_mask_rgba32r
typedef alpha_mask_u8<4, 1> alpha_mask_rgba32g; //----alpha_mask_rgba32g
typedef alpha_mask_u8<4, 2> alpha_mask_rgba32b; //----alpha_mask_rgba32b
typedef alpha_mask_u8<4, 3> alpha_mask_rgba32a; //----alpha_mask_rgba32a
typedef alpha_mask_u8<4, 1> alpha_mask_argb32r; //----alpha_mask_argb32r
typedef alpha_mask_u8<4, 2> alpha_mask_argb32g; //----alpha_mask_argb32g
typedef alpha_mask_u8<4, 3> alpha_mask_argb32b; //----alpha_mask_argb32b
typedef alpha_mask_u8<4, 0> alpha_mask_argb32a; //----alpha_mask_argb32a
typedef alpha_mask_u8<4, 2> alpha_mask_bgra32r; //----alpha_mask_bgra32r
typedef alpha_mask_u8<4, 1> alpha_mask_bgra32g; //----alpha_mask_bgra32g
typedef alpha_mask_u8<4, 0> alpha_mask_bgra32b; //----alpha_mask_bgra32b
typedef alpha_mask_u8<4, 3> alpha_mask_bgra32a; //----alpha_mask_bgra32a
typedef alpha_mask_u8<4, 3> alpha_mask_abgr32r; //----alpha_mask_abgr32r
typedef alpha_mask_u8<4, 2> alpha_mask_abgr32g; //----alpha_mask_abgr32g
typedef alpha_mask_u8<4, 1> alpha_mask_abgr32b; //----alpha_mask_abgr32b
typedef alpha_mask_u8<4, 0> alpha_mask_abgr32a; //----alpha_mask_abgr32a
typedef alpha_mask_u8<3, 0, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_rgb24gray; //----alpha_mask_rgb24gray
typedef alpha_mask_u8<3, 0, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_bgr24gray; //----alpha_mask_bgr24gray
typedef alpha_mask_u8<4, 0, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_rgba32gray; //----alpha_mask_rgba32gray
typedef alpha_mask_u8<4, 1, rgb_to_gray_mask_u8<0, 1, 2> > alpha_mask_argb32gray; //----alpha_mask_argb32gray
typedef alpha_mask_u8<4, 0, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_bgra32gray; //----alpha_mask_bgra32gray
typedef alpha_mask_u8<4, 1, rgb_to_gray_mask_u8<2, 1, 0> > alpha_mask_abgr32gray; //----alpha_mask_abgr32gray
//==========================================================amask_no_clip_u8
template<unsigned Step=1, unsigned Offset=0, class MaskF=one_component_mask_u8>
class amask_no_clip_u8
{
public:
typedef int8u cover_type;
typedef amask_no_clip_u8<Step, Offset, MaskF> self_type;
enum cover_scale_e
{
cover_shift = 8,
cover_none = 0,
cover_full = 255
};
amask_no_clip_u8() : m_rbuf(0) {}
explicit amask_no_clip_u8(rendering_buffer& rbuf) : m_rbuf(&rbuf) {}
void attach(rendering_buffer& rbuf) { m_rbuf = &rbuf; }
MaskF& mask_function() { return m_mask_function; }
const MaskF& mask_function() const { return m_mask_function; }
//--------------------------------------------------------------------
cover_type pixel(int x, int y) const
{
return (cover_type)m_mask_function.calculate(
m_rbuf->row_ptr(y) + x * Step + Offset);
}
//--------------------------------------------------------------------
cover_type combine_pixel(int x, int y, cover_type val) const
{
return (cover_type)((cover_full + val *
m_mask_function.calculate(
m_rbuf->row_ptr(y) + x * Step + Offset)) >>
cover_shift);
}
//--------------------------------------------------------------------
void fill_hspan(int x, int y, cover_type* dst, int num_pix) const
{
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*dst++ = (cover_type)m_mask_function.calculate(mask);
mask += Step;
}
while(--num_pix);
}
//--------------------------------------------------------------------
void combine_hspan(int x, int y, cover_type* dst, int num_pix) const
{
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*dst = (cover_type)((cover_full + (*dst) *
m_mask_function.calculate(mask)) >>
cover_shift);
++dst;
mask += Step;
}
while(--num_pix);
}
//--------------------------------------------------------------------
void fill_vspan(int x, int y, cover_type* dst, int num_pix) const
{
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*dst++ = (cover_type)m_mask_function.calculate(mask);
mask += m_rbuf->stride();
}
while(--num_pix);
}
//--------------------------------------------------------------------
void combine_vspan(int x, int y, cover_type* dst, int num_pix) const
{
const int8u* mask = m_rbuf->row_ptr(y) + x * Step + Offset;
do
{
*dst = (cover_type)((cover_full + (*dst) *
m_mask_function.calculate(mask)) >>
cover_shift);
++dst;
mask += m_rbuf->stride();
}
while(--num_pix);
}
private:
amask_no_clip_u8(const self_type&);
const self_type& operator = (const self_type&);
rendering_buffer* m_rbuf;
MaskF m_mask_function;
};
typedef amask_no_clip_u8<1, 0> amask_no_clip_gray8; //----amask_no_clip_gray8
typedef amask_no_clip_u8<3, 0> amask_no_clip_rgb24r; //----amask_no_clip_rgb24r
typedef amask_no_clip_u8<3, 1> amask_no_clip_rgb24g; //----amask_no_clip_rgb24g
typedef amask_no_clip_u8<3, 2> amask_no_clip_rgb24b; //----amask_no_clip_rgb24b
typedef amask_no_clip_u8<3, 2> amask_no_clip_bgr24r; //----amask_no_clip_bgr24r
typedef amask_no_clip_u8<3, 1> amask_no_clip_bgr24g; //----amask_no_clip_bgr24g
typedef amask_no_clip_u8<3, 0> amask_no_clip_bgr24b; //----amask_no_clip_bgr24b
typedef amask_no_clip_u8<4, 0> amask_no_clip_rgba32r; //----amask_no_clip_rgba32r
typedef amask_no_clip_u8<4, 1> amask_no_clip_rgba32g; //----amask_no_clip_rgba32g
typedef amask_no_clip_u8<4, 2> amask_no_clip_rgba32b; //----amask_no_clip_rgba32b
typedef amask_no_clip_u8<4, 3> amask_no_clip_rgba32a; //----amask_no_clip_rgba32a
typedef amask_no_clip_u8<4, 1> amask_no_clip_argb32r; //----amask_no_clip_argb32r
typedef amask_no_clip_u8<4, 2> amask_no_clip_argb32g; //----amask_no_clip_argb32g
typedef amask_no_clip_u8<4, 3> amask_no_clip_argb32b; //----amask_no_clip_argb32b
typedef amask_no_clip_u8<4, 0> amask_no_clip_argb32a; //----amask_no_clip_argb32a
typedef amask_no_clip_u8<4, 2> amask_no_clip_bgra32r; //----amask_no_clip_bgra32r
typedef amask_no_clip_u8<4, 1> amask_no_clip_bgra32g; //----amask_no_clip_bgra32g
typedef amask_no_clip_u8<4, 0> amask_no_clip_bgra32b; //----amask_no_clip_bgra32b
typedef amask_no_clip_u8<4, 3> amask_no_clip_bgra32a; //----amask_no_clip_bgra32a
typedef amask_no_clip_u8<4, 3> amask_no_clip_abgr32r; //----amask_no_clip_abgr32r
typedef amask_no_clip_u8<4, 2> amask_no_clip_abgr32g; //----amask_no_clip_abgr32g
typedef amask_no_clip_u8<4, 1> amask_no_clip_abgr32b; //----amask_no_clip_abgr32b
typedef amask_no_clip_u8<4, 0> amask_no_clip_abgr32a; //----amask_no_clip_abgr32a
typedef amask_no_clip_u8<3, 0, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_rgb24gray; //----amask_no_clip_rgb24gray
typedef amask_no_clip_u8<3, 0, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_bgr24gray; //----amask_no_clip_bgr24gray
typedef amask_no_clip_u8<4, 0, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_rgba32gray; //----amask_no_clip_rgba32gray
typedef amask_no_clip_u8<4, 1, rgb_to_gray_mask_u8<0, 1, 2> > amask_no_clip_argb32gray; //----amask_no_clip_argb32gray
typedef amask_no_clip_u8<4, 0, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_bgra32gray; //----amask_no_clip_bgra32gray
typedef amask_no_clip_u8<4, 1, rgb_to_gray_mask_u8<2, 1, 0> > amask_no_clip_abgr32gray; //----amask_no_clip_abgr32gray
}
#endif

73
dep/agg/include/agg_arc.h Normal file
View File

@@ -0,0 +1,73 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Arc vertex generator
//
//----------------------------------------------------------------------------
#ifndef AGG_ARC_INCLUDED
#define AGG_ARC_INCLUDED
#include "agg_basics.h"
namespace agg
{
//=====================================================================arc
//
// See Implementation agg_arc.cpp
//
class arc
{
public:
arc() : m_scale(1.0), m_initialized(false) {}
arc(double x, double y,
double rx, double ry,
double a1, double a2,
bool ccw=true);
void init(double x, double y,
double rx, double ry,
double a1, double a2,
bool ccw=true);
void approximation_scale(double s);
double approximation_scale() const { return m_scale; }
void rewind(unsigned);
unsigned vertex(double* x, double* y);
private:
void normalize(double a1, double a2, bool ccw);
double m_x;
double m_y;
double m_rx;
double m_ry;
double m_angle;
double m_start;
double m_end;
double m_scale;
double m_da;
bool m_ccw;
bool m_initialized;
unsigned m_path_cmd;
};
}
#endif

1119
dep/agg/include/agg_array.h Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Simple arrowhead/arrowtail generator
//
//----------------------------------------------------------------------------
#ifndef AGG_ARROWHEAD_INCLUDED
#define AGG_ARROWHEAD_INCLUDED
#include "agg_basics.h"
namespace agg
{
//===============================================================arrowhead
//
// See implementation agg_arrowhead.cpp
//
class arrowhead
{
public:
arrowhead();
void head(double d1, double d2, double d3, double d4)
{
m_head_d1 = d1;
m_head_d2 = d2;
m_head_d3 = d3;
m_head_d4 = d4;
m_head_flag = true;
}
void head() { m_head_flag = true; }
void no_head() { m_head_flag = false; }
void tail(double d1, double d2, double d3, double d4)
{
m_tail_d1 = d1;
m_tail_d2 = d2;
m_tail_d3 = d3;
m_tail_d4 = d4;
m_tail_flag = true;
}
void tail() { m_tail_flag = true; }
void no_tail() { m_tail_flag = false; }
void rewind(unsigned path_id);
unsigned vertex(double* x, double* y);
private:
double m_head_d1;
double m_head_d2;
double m_head_d3;
double m_head_d4;
double m_tail_d1;
double m_tail_d2;
double m_tail_d3;
double m_tail_d4;
bool m_head_flag;
bool m_tail_flag;
double m_coord[16];
unsigned m_cmd[8];
unsigned m_curr_id;
unsigned m_curr_coord;
};
}
#endif

View File

@@ -0,0 +1,574 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_BASICS_INCLUDED
#define AGG_BASICS_INCLUDED
#include <cmath>
#include "agg_config.h"
//---------------------------------------------------------AGG_CUSTOM_ALLOCATOR
#ifdef AGG_CUSTOM_ALLOCATOR
#include "agg_allocator.h"
#else
namespace agg
{
// The policy of all AGG containers and memory allocation strategy
// in general is that no allocated data requires explicit construction.
// It means that the allocator can be really simple; you can even
// replace new/delete to malloc/free. The constructors and destructors
// won't be called in this case, however everything will remain working.
// The second argument of deallocate() is the size of the allocated
// block. You can use this information if you wish.
//------------------------------------------------------------pod_allocator
template<class T> struct pod_allocator
{
static T* allocate(unsigned num) { return new T [num]; }
static void deallocate(T* ptr, unsigned) { delete [] ptr; }
};
// Single object allocator. It's also can be replaced with your custom
// allocator. The difference is that it can only allocate a single
// object and the constructor and destructor must be called.
// In AGG there is no need to allocate an array of objects with
// calling their constructors (only single ones). So that, if you
// replace these new/delete to malloc/free make sure that the in-place
// new is called and take care of calling the destructor too.
//------------------------------------------------------------obj_allocator
template<class T> struct obj_allocator
{
static T* allocate() { return new T; }
static void deallocate(T* ptr) { delete ptr; }
};
}
#endif
//-------------------------------------------------------- Default basic types
//
// If the compiler has different capacity of the basic types you can redefine
// them via the compiler command line or by generating agg_config.h that is
// empty by default.
//
#ifndef AGG_INT8
#define AGG_INT8 signed char
#endif
#ifndef AGG_INT8U
#define AGG_INT8U unsigned char
#endif
#ifndef AGG_INT16
#define AGG_INT16 short
#endif
#ifndef AGG_INT16U
#define AGG_INT16U unsigned short
#endif
#ifndef AGG_INT32
#define AGG_INT32 int
#endif
#ifndef AGG_INT32U
#define AGG_INT32U unsigned
#endif
#ifndef AGG_INT64
#if defined(_MSC_VER) || defined(__BORLANDC__)
#define AGG_INT64 signed __int64
#else
#define AGG_INT64 signed long long
#endif
#endif
#ifndef AGG_INT64U
#if defined(_MSC_VER) || defined(__BORLANDC__)
#define AGG_INT64U unsigned __int64
#else
#define AGG_INT64U unsigned long long
#endif
#endif
//------------------------------------------------ Some fixes for MS Visual C++
#if defined(_MSC_VER)
#pragma warning(disable:4786) // Identifier was truncated...
#endif
#if defined(_MSC_VER)
#define AGG_INLINE __forceinline
#else
#define AGG_INLINE inline
#endif
namespace agg
{
//-------------------------------------------------------------------------
typedef AGG_INT8 int8; //----int8
typedef AGG_INT8U int8u; //----int8u
typedef AGG_INT16 int16; //----int16
typedef AGG_INT16U int16u; //----int16u
typedef AGG_INT32 int32; //----int32
typedef AGG_INT32U int32u; //----int32u
typedef AGG_INT64 int64; //----int64
typedef AGG_INT64U int64u; //----int64u
#if defined(AGG_FISTP)
#pragma warning(push)
#pragma warning(disable : 4035) //Disable warning "no return value"
AGG_INLINE int iround(double v) //-------iround
{
int t;
__asm fld qword ptr [v]
__asm fistp dword ptr [t]
__asm mov eax, dword ptr [t]
}
AGG_INLINE unsigned uround(double v) //-------uround
{
unsigned t;
__asm fld qword ptr [v]
__asm fistp dword ptr [t]
__asm mov eax, dword ptr [t]
}
#pragma warning(pop)
AGG_INLINE int ifloor(double v)
{
return int(floor(v));
}
AGG_INLINE unsigned ufloor(double v) //-------ufloor
{
return unsigned(floor(v));
}
AGG_INLINE int iceil(double v)
{
return int(ceil(v));
}
AGG_INLINE unsigned uceil(double v) //--------uceil
{
return unsigned(ceil(v));
}
#elif defined(AGG_QIFIST)
AGG_INLINE int iround(double v)
{
return int(v);
}
AGG_INLINE int uround(double v)
{
return unsigned(v);
}
AGG_INLINE int ifloor(double v)
{
return int(std::floor(v));
}
AGG_INLINE unsigned ufloor(double v)
{
return unsigned(std::floor(v));
}
AGG_INLINE int iceil(double v)
{
return int(std::ceil(v));
}
AGG_INLINE unsigned uceil(double v)
{
return unsigned(std::ceil(v));
}
#else
AGG_INLINE int iround(double v)
{
return int((v < 0.0) ? v - 0.5 : v + 0.5);
}
AGG_INLINE int uround(double v)
{
return unsigned(v + 0.5);
}
AGG_INLINE int ifloor(double v)
{
int i = int(v);
return i - (i > v);
}
AGG_INLINE unsigned ufloor(double v)
{
return unsigned(v);
}
AGG_INLINE int iceil(double v)
{
return int(std::ceil(v));
}
AGG_INLINE unsigned uceil(double v)
{
return unsigned(std::ceil(v));
}
#endif
//---------------------------------------------------------------saturation
template<int Limit> struct saturation
{
AGG_INLINE static int iround(double v)
{
if(v < double(-Limit)) return -Limit;
if(v > double( Limit)) return Limit;
return agg::iround(v);
}
};
//------------------------------------------------------------------mul_one
template<unsigned Shift> struct mul_one
{
AGG_INLINE static unsigned mul(unsigned a, unsigned b)
{
unsigned q = a * b + (1 << (Shift-1));
return (q + (q >> Shift)) >> Shift;
}
};
//-------------------------------------------------------------------------
typedef unsigned char cover_type; //----cover_type
enum cover_scale_e
{
cover_shift = 8, //----cover_shift
cover_size = 1 << cover_shift, //----cover_size
cover_mask = cover_size - 1, //----cover_mask
cover_none = 0, //----cover_none
cover_full = cover_mask //----cover_full
};
//----------------------------------------------------poly_subpixel_scale_e
// These constants determine the subpixel accuracy, to be more precise,
// the number of bits of the fractional part of the coordinates.
// The possible coordinate capacity in bits can be calculated by formula:
// sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and
// 8-bits fractional part the capacity is 24 bits.
enum poly_subpixel_scale_e
{
poly_subpixel_shift = 8, //----poly_subpixel_shift
poly_subpixel_scale = 1<<poly_subpixel_shift, //----poly_subpixel_scale
poly_subpixel_mask = poly_subpixel_scale-1 //----poly_subpixel_mask
};
//----------------------------------------------------------filling_rule_e
enum filling_rule_e
{
fill_non_zero,
fill_even_odd
};
//-----------------------------------------------------------------------pi
const double pi = 3.14159265358979323846;
//------------------------------------------------------------------deg2rad
inline double deg2rad(double deg)
{
return deg * pi / 180.0;
}
//------------------------------------------------------------------rad2deg
inline double rad2deg(double rad)
{
return rad * 180.0 / pi;
}
//----------------------------------------------------------------rect_base
template<class T> struct rect_base
{
typedef T value_type;
typedef rect_base<T> self_type;
T x1, y1, x2, y2;
rect_base() {}
rect_base(T x1_, T y1_, T x2_, T y2_) :
x1(x1_), y1(y1_), x2(x2_), y2(y2_) {}
void init(T x1_, T y1_, T x2_, T y2_)
{
x1 = x1_; y1 = y1_; x2 = x2_; y2 = y2_;
}
const self_type& normalize()
{
T t;
if(x1 > x2) { t = x1; x1 = x2; x2 = t; }
if(y1 > y2) { t = y1; y1 = y2; y2 = t; }
return *this;
}
bool clip(const self_type& r)
{
if(x2 > r.x2) x2 = r.x2;
if(y2 > r.y2) y2 = r.y2;
if(x1 < r.x1) x1 = r.x1;
if(y1 < r.y1) y1 = r.y1;
return x1 <= x2 && y1 <= y2;
}
bool is_valid() const
{
return x1 <= x2 && y1 <= y2;
}
bool hit_test(T x, T y) const
{
return (x >= x1 && x <= x2 && y >= y1 && y <= y2);
}
bool overlaps(const self_type& r) const
{
return !(r.x1 > x2 || r.x2 < x1
|| r.y1 > y2 || r.y2 < y1);
}
};
//-----------------------------------------------------intersect_rectangles
template<class Rect>
inline Rect intersect_rectangles(const Rect& r1, const Rect& r2)
{
Rect r = r1;
// First process x2,y2 because the other order
// results in Internal Compiler Error under
// Microsoft Visual C++ .NET 2003 69462-335-0000007-18038 in
// case of "Maximize Speed" optimization option.
//-----------------
if(r.x2 > r2.x2) r.x2 = r2.x2;
if(r.y2 > r2.y2) r.y2 = r2.y2;
if(r.x1 < r2.x1) r.x1 = r2.x1;
if(r.y1 < r2.y1) r.y1 = r2.y1;
return r;
}
//---------------------------------------------------------unite_rectangles
template<class Rect>
inline Rect unite_rectangles(const Rect& r1, const Rect& r2)
{
Rect r = r1;
if(r.x2 < r2.x2) r.x2 = r2.x2;
if(r.y2 < r2.y2) r.y2 = r2.y2;
if(r.x1 > r2.x1) r.x1 = r2.x1;
if(r.y1 > r2.y1) r.y1 = r2.y1;
return r;
}
typedef rect_base<int> rect_i; //----rect_i
typedef rect_base<float> rect_f; //----rect_f
typedef rect_base<double> rect_d; //----rect_d
//---------------------------------------------------------path_commands_e
enum path_commands_e
{
path_cmd_stop = 0, //----path_cmd_stop
path_cmd_move_to = 1, //----path_cmd_move_to
path_cmd_line_to = 2, //----path_cmd_line_to
path_cmd_curve3 = 3, //----path_cmd_curve3
path_cmd_curve4 = 4, //----path_cmd_curve4
path_cmd_curveN = 5, //----path_cmd_curveN
path_cmd_catrom = 6, //----path_cmd_catrom
path_cmd_ubspline = 7, //----path_cmd_ubspline
path_cmd_end_poly = 0x0F, //----path_cmd_end_poly
path_cmd_mask = 0x0F //----path_cmd_mask
};
//------------------------------------------------------------path_flags_e
enum path_flags_e
{
path_flags_none = 0, //----path_flags_none
path_flags_ccw = 0x10, //----path_flags_ccw
path_flags_cw = 0x20, //----path_flags_cw
path_flags_close = 0x40, //----path_flags_close
path_flags_mask = 0xF0 //----path_flags_mask
};
//---------------------------------------------------------------is_vertex
inline bool is_vertex(unsigned c)
{
return c >= path_cmd_move_to && c < path_cmd_end_poly;
}
//--------------------------------------------------------------is_drawing
inline bool is_drawing(unsigned c)
{
return c >= path_cmd_line_to && c < path_cmd_end_poly;
}
//-----------------------------------------------------------------is_stop
inline bool is_stop(unsigned c)
{
return c == path_cmd_stop;
}
//--------------------------------------------------------------is_move_to
inline bool is_move_to(unsigned c)
{
return c == path_cmd_move_to;
}
//--------------------------------------------------------------is_line_to
inline bool is_line_to(unsigned c)
{
return c == path_cmd_line_to;
}
//----------------------------------------------------------------is_curve
inline bool is_curve(unsigned c)
{
return c == path_cmd_curve3 || c == path_cmd_curve4;
}
//---------------------------------------------------------------is_curve3
inline bool is_curve3(unsigned c)
{
return c == path_cmd_curve3;
}
//---------------------------------------------------------------is_curve4
inline bool is_curve4(unsigned c)
{
return c == path_cmd_curve4;
}
//-------------------------------------------------------------is_end_poly
inline bool is_end_poly(unsigned c)
{
return (c & path_cmd_mask) == path_cmd_end_poly;
}
//----------------------------------------------------------------is_close
inline bool is_close(unsigned c)
{
return (c & ~(path_flags_cw | path_flags_ccw)) ==
(path_cmd_end_poly | path_flags_close);
}
//------------------------------------------------------------is_next_poly
inline bool is_next_poly(unsigned c)
{
return is_stop(c) || is_move_to(c) || is_end_poly(c);
}
//-------------------------------------------------------------------is_cw
inline bool is_cw(unsigned c)
{
return (c & path_flags_cw) != 0;
}
//------------------------------------------------------------------is_ccw
inline bool is_ccw(unsigned c)
{
return (c & path_flags_ccw) != 0;
}
//-------------------------------------------------------------is_oriented
inline bool is_oriented(unsigned c)
{
return (c & (path_flags_cw | path_flags_ccw)) != 0;
}
//---------------------------------------------------------------is_closed
inline bool is_closed(unsigned c)
{
return (c & path_flags_close) != 0;
}
//----------------------------------------------------------get_close_flag
inline unsigned get_close_flag(unsigned c)
{
return c & path_flags_close;
}
//-------------------------------------------------------clear_orientation
inline unsigned clear_orientation(unsigned c)
{
return c & ~(path_flags_cw | path_flags_ccw);
}
//---------------------------------------------------------get_orientation
inline unsigned get_orientation(unsigned c)
{
return c & (path_flags_cw | path_flags_ccw);
}
//---------------------------------------------------------set_orientation
inline unsigned set_orientation(unsigned c, unsigned o)
{
return clear_orientation(c) | o;
}
//--------------------------------------------------------------point_base
template<class T> struct point_base
{
typedef T value_type;
T x,y;
point_base() {}
point_base(T x_, T y_) : x(x_), y(y_) {}
};
typedef point_base<int> point_i; //-----point_i
typedef point_base<float> point_f; //-----point_f
typedef point_base<double> point_d; //-----point_d
//-------------------------------------------------------------vertex_base
template<class T> struct vertex_base
{
typedef T value_type;
T x,y;
unsigned cmd;
vertex_base() {}
vertex_base(T x_, T y_, unsigned cmd_) : x(x_), y(y_), cmd(cmd_) {}
};
typedef vertex_base<int> vertex_i; //-----vertex_i
typedef vertex_base<float> vertex_f; //-----vertex_f
typedef vertex_base<double> vertex_d; //-----vertex_d
//----------------------------------------------------------------row_info
template<class T> struct row_info
{
int x1, x2;
T* ptr;
row_info() {}
row_info(int x1_, int x2_, T* ptr_) : x1(x1_), x2(x2_), ptr(ptr_) {}
};
//----------------------------------------------------------const_row_info
template<class T> struct const_row_info
{
int x1, x2;
const T* ptr;
const_row_info() {}
const_row_info(int x1_, int x2_, const T* ptr_) :
x1(x1_), x2(x2_), ptr(ptr_) {}
};
//------------------------------------------------------------is_equal_eps
template<class T> inline bool is_equal_eps(T v1, T v2, T epsilon)
{
bool neg1 = v1 < 0.0;
bool neg2 = v2 < 0.0;
if (neg1 != neg2)
return std::fabs(v1) < epsilon && std::fabs(v2) < epsilon;
int int1, int2;
std::frexp(v1, &int1);
std::frexp(v2, &int2);
int min12 = int1 < int2 ? int1 : int2;
v1 = std::ldexp(v1, -min12);
v2 = std::ldexp(v2, -min12);
return std::fabs(v1 - v2) < epsilon;
}
}
#endif

View File

@@ -0,0 +1,159 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e.,
// 4, 7, 10, or 13 vertices.
//
//----------------------------------------------------------------------------
#ifndef AGG_BEZIER_ARC_INCLUDED
#define AGG_BEZIER_ARC_INCLUDED
#include "agg_conv_transform.h"
namespace agg
{
//-----------------------------------------------------------------------
void arc_to_bezier(double cx, double cy, double rx, double ry,
double start_angle, double sweep_angle,
double* curve);
//==============================================================bezier_arc
//
// See implemantaion agg_bezier_arc.cpp
//
class bezier_arc
{
public:
//--------------------------------------------------------------------
bezier_arc() : m_vertex(26), m_num_vertices(0), m_cmd(path_cmd_line_to) {}
bezier_arc(double x, double y,
double rx, double ry,
double start_angle,
double sweep_angle)
{
init(x, y, rx, ry, start_angle, sweep_angle);
}
//--------------------------------------------------------------------
void init(double x, double y,
double rx, double ry,
double start_angle,
double sweep_angle);
//--------------------------------------------------------------------
void rewind(unsigned)
{
m_vertex = 0;
}
//--------------------------------------------------------------------
unsigned vertex(double* x, double* y)
{
if(m_vertex >= m_num_vertices) return path_cmd_stop;
*x = m_vertices[m_vertex];
*y = m_vertices[m_vertex + 1];
m_vertex += 2;
return (m_vertex == 2) ? unsigned(path_cmd_move_to) : m_cmd;
}
// Supplemantary functions. num_vertices() actually returns doubled
// number of vertices. That is, for 1 vertex it returns 2.
//--------------------------------------------------------------------
unsigned num_vertices() const { return m_num_vertices; }
const double* vertices() const { return m_vertices; }
double* vertices() { return m_vertices; }
private:
unsigned m_vertex;
unsigned m_num_vertices;
double m_vertices[26];
unsigned m_cmd;
};
//==========================================================bezier_arc_svg
// Compute an SVG-style bezier arc.
//
// Computes an elliptical arc from (x1, y1) to (x2, y2). The size and
// orientation of the ellipse are defined by two radii (rx, ry)
// and an x-axis-rotation, which indicates how the ellipse as a whole
// is rotated relative to the current coordinate system. The center
// (cx, cy) of the ellipse is calculated automatically to satisfy the
// constraints imposed by the other parameters.
// large-arc-flag and sweep-flag contribute to the automatic calculations
// and help determine how the arc is drawn.
class bezier_arc_svg
{
public:
//--------------------------------------------------------------------
bezier_arc_svg() : m_arc(), m_radii_ok(false) {}
bezier_arc_svg(double x1, double y1,
double rx, double ry,
double angle,
bool large_arc_flag,
bool sweep_flag,
double x2, double y2) :
m_arc(), m_radii_ok(false)
{
init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2);
}
//--------------------------------------------------------------------
void init(double x1, double y1,
double rx, double ry,
double angle,
bool large_arc_flag,
bool sweep_flag,
double x2, double y2);
//--------------------------------------------------------------------
bool radii_ok() const { return m_radii_ok; }
//--------------------------------------------------------------------
void rewind(unsigned)
{
m_arc.rewind(0);
}
//--------------------------------------------------------------------
unsigned vertex(double* x, double* y)
{
return m_arc.vertex(x, y);
}
// Supplemantary functions. num_vertices() actually returns doubled
// number of vertices. That is, for 1 vertex it returns 2.
//--------------------------------------------------------------------
unsigned num_vertices() const { return m_arc.num_vertices(); }
const double* vertices() const { return m_arc.vertices(); }
double* vertices() { return m_arc.vertices(); }
private:
bezier_arc m_arc;
bool m_radii_ok;
};
}
#endif

View File

@@ -0,0 +1,54 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_BITSET_ITERATOR_INCLUDED
#define AGG_BITSET_ITERATOR_INCLUDED
#include "agg_basics.h"
namespace agg
{
class bitset_iterator
{
public:
bitset_iterator(const int8u* bits, unsigned offset = 0) :
m_bits(bits + (offset >> 3)),
m_mask(0x80 >> (offset & 7))
{}
void operator ++ ()
{
m_mask >>= 1;
if(m_mask == 0)
{
++m_bits;
m_mask = 0x80;
}
}
unsigned bit() const
{
return (*m_bits) & m_mask;
}
private:
const int8u* m_bits;
int8u m_mask;
};
}
#endif

1505
dep/agg/include/agg_blur.h Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// bounding_rect function template
//
//----------------------------------------------------------------------------
#ifndef AGG_BOUNDING_RECT_INCLUDED
#define AGG_BOUNDING_RECT_INCLUDED
#include "agg_basics.h"
namespace agg
{
//-----------------------------------------------------------bounding_rect
template<class VertexSource, class GetId, class CoordT>
bool bounding_rect(VertexSource& vs, GetId& gi,
unsigned start, unsigned num,
CoordT* x1, CoordT* y1, CoordT* x2, CoordT* y2)
{
unsigned i;
double x;
double y;
bool first = true;
*x1 = CoordT(1);
*y1 = CoordT(1);
*x2 = CoordT(0);
*y2 = CoordT(0);
for(i = 0; i < num; i++)
{
vs.rewind(gi[start + i]);
unsigned cmd;
while(!is_stop(cmd = vs.vertex(&x, &y)))
{
if(is_vertex(cmd))
{
if(first)
{
*x1 = CoordT(x);
*y1 = CoordT(y);
*x2 = CoordT(x);
*y2 = CoordT(y);
first = false;
}
else
{
if(CoordT(x) < *x1) *x1 = CoordT(x);
if(CoordT(y) < *y1) *y1 = CoordT(y);
if(CoordT(x) > *x2) *x2 = CoordT(x);
if(CoordT(y) > *y2) *y2 = CoordT(y);
}
}
}
}
return *x1 <= *x2 && *y1 <= *y2;
}
//-----------------------------------------------------bounding_rect_single
template<class VertexSource, class CoordT>
bool bounding_rect_single(VertexSource& vs, unsigned path_id,
CoordT* x1, CoordT* y1, CoordT* x2, CoordT* y2)
{
double x;
double y;
bool first = true;
*x1 = CoordT(1);
*y1 = CoordT(1);
*x2 = CoordT(0);
*y2 = CoordT(0);
vs.rewind(path_id);
unsigned cmd;
while(!is_stop(cmd = vs.vertex(&x, &y)))
{
if(is_vertex(cmd))
{
if(first)
{
*x1 = CoordT(x);
*y1 = CoordT(y);
*x2 = CoordT(x);
*y2 = CoordT(y);
first = false;
}
else
{
if(CoordT(x) < *x1) *x1 = CoordT(x);
if(CoordT(y) < *y1) *y1 = CoordT(y);
if(CoordT(x) > *x2) *x2 = CoordT(x);
if(CoordT(y) > *y2) *y2 = CoordT(y);
}
}
}
return *x1 <= *x2 && *y1 <= *y2;
}
}
#endif

View File

@@ -0,0 +1,76 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class bspline
//
//----------------------------------------------------------------------------
#ifndef AGG_BSPLINE_INCLUDED
#define AGG_BSPLINE_INCLUDED
#include "agg_array.h"
namespace agg
{
//----------------------------------------------------------------bspline
// A very simple class of Bi-cubic Spline interpolation.
// First call init(num, x[], y[]) where num - number of source points,
// x, y - arrays of X and Y values respectively. Here Y must be a function
// of X. It means that all the X-coordinates must be arranged in the ascending
// order.
// Then call get(x) that calculates a value Y for the respective X.
// The class supports extrapolation, i.e. you can call get(x) where x is
// outside the given with init() X-range. Extrapolation is a simple linear
// function.
//
// See Implementation agg_bspline.cpp
//------------------------------------------------------------------------
class bspline
{
public:
bspline();
bspline(int num);
bspline(int num, const double* x, const double* y);
void init(int num);
void add_point(double x, double y);
void prepare();
void init(int num, const double* x, const double* y);
double get(double x) const;
double get_stateful(double x) const;
private:
bspline(const bspline&);
const bspline& operator = (const bspline&);
static void bsearch(int n, const double *x, double x0, int *i);
double extrapolation_left(double x) const;
double extrapolation_right(double x) const;
double interpolation(double x, int i) const;
int m_max;
int m_num;
double* m_x;
double* m_y;
pod_array<double> m_am;
mutable int m_last_idx;
};
}
#endif

View File

@@ -0,0 +1,333 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Liang-Barsky clipping
//
//----------------------------------------------------------------------------
#ifndef AGG_CLIP_LIANG_BARSKY_INCLUDED
#define AGG_CLIP_LIANG_BARSKY_INCLUDED
#include "agg_basics.h"
namespace agg
{
//------------------------------------------------------------------------
enum clipping_flags_e
{
clipping_flags_x1_clipped = 4,
clipping_flags_x2_clipped = 1,
clipping_flags_y1_clipped = 8,
clipping_flags_y2_clipped = 2,
clipping_flags_x_clipped = clipping_flags_x1_clipped | clipping_flags_x2_clipped,
clipping_flags_y_clipped = clipping_flags_y1_clipped | clipping_flags_y2_clipped
};
//----------------------------------------------------------clipping_flags
// Determine the clipping code of the vertex according to the
// Cyrus-Beck line clipping algorithm
//
// | |
// 0110 | 0010 | 0011
// | |
// -------+--------+-------- clip_box.y2
// | |
// 0100 | 0000 | 0001
// | |
// -------+--------+-------- clip_box.y1
// | |
// 1100 | 1000 | 1001
// | |
// clip_box.x1 clip_box.x2
//
//
template<class T>
inline unsigned clipping_flags(T x, T y, const rect_base<T>& clip_box)
{
return (x > clip_box.x2) |
((y > clip_box.y2) << 1) |
((x < clip_box.x1) << 2) |
((y < clip_box.y1) << 3);
}
//--------------------------------------------------------clipping_flags_x
template<class T>
inline unsigned clipping_flags_x(T x, const rect_base<T>& clip_box)
{
return (x > clip_box.x2) | ((x < clip_box.x1) << 2);
}
//--------------------------------------------------------clipping_flags_y
template<class T>
inline unsigned clipping_flags_y(T y, const rect_base<T>& clip_box)
{
return ((y > clip_box.y2) << 1) | ((y < clip_box.y1) << 3);
}
//-------------------------------------------------------clip_liang_barsky
template<class T>
inline unsigned clip_liang_barsky(T x1, T y1, T x2, T y2,
const rect_base<T>& clip_box,
T* x, T* y)
{
const double nearzero = 1e-30;
double deltax = x2 - x1;
double deltay = y2 - y1;
double xin;
double xout;
double yin;
double yout;
double tinx;
double tiny;
double toutx;
double touty;
double tin1;
double tin2;
double tout1;
unsigned np = 0;
if(deltax == 0.0)
{
// bump off of the vertical
deltax = (x1 > clip_box.x1) ? -nearzero : nearzero;
}
if(deltay == 0.0)
{
// bump off of the horizontal
deltay = (y1 > clip_box.y1) ? -nearzero : nearzero;
}
if(deltax > 0.0)
{
// points to right
xin = clip_box.x1;
xout = clip_box.x2;
}
else
{
xin = clip_box.x2;
xout = clip_box.x1;
}
if(deltay > 0.0)
{
// points up
yin = clip_box.y1;
yout = clip_box.y2;
}
else
{
yin = clip_box.y2;
yout = clip_box.y1;
}
tinx = (xin - x1) / deltax;
tiny = (yin - y1) / deltay;
if (tinx < tiny)
{
// hits x first
tin1 = tinx;
tin2 = tiny;
}
else
{
// hits y first
tin1 = tiny;
tin2 = tinx;
}
if(tin1 <= 1.0)
{
if(0.0 < tin1)
{
*x++ = (T)xin;
*y++ = (T)yin;
++np;
}
if(tin2 <= 1.0)
{
toutx = (xout - x1) / deltax;
touty = (yout - y1) / deltay;
tout1 = (toutx < touty) ? toutx : touty;
if(tin2 > 0.0 || tout1 > 0.0)
{
if(tin2 <= tout1)
{
if(tin2 > 0.0)
{
if(tinx > tiny)
{
*x++ = (T)xin;
*y++ = (T)(y1 + tinx * deltay);
}
else
{
*x++ = (T)(x1 + tiny * deltax);
*y++ = (T)yin;
}
++np;
}
if(tout1 < 1.0)
{
if(toutx < touty)
{
*x++ = (T)xout;
*y++ = (T)(y1 + toutx * deltay);
}
else
{
*x++ = (T)(x1 + touty * deltax);
*y++ = (T)yout;
}
}
else
{
*x++ = x2;
*y++ = y2;
}
++np;
}
else
{
if(tinx > tiny)
{
*x++ = (T)xin;
*y++ = (T)yout;
}
else
{
*x++ = (T)xout;
*y++ = (T)yin;
}
++np;
}
}
}
}
return np;
}
//----------------------------------------------------------------------------
template<class T>
bool clip_move_point(T x1, T y1, T x2, T y2,
const rect_base<T>& clip_box,
T* x, T* y, unsigned flags)
{
T bound;
if(flags & clipping_flags_x_clipped)
{
if(x1 == x2)
{
return false;
}
bound = (flags & clipping_flags_x1_clipped) ? clip_box.x1 : clip_box.x2;
*y = (T)(double(bound - x1) * (y2 - y1) / (x2 - x1) + y1);
*x = bound;
}
flags = clipping_flags_y(*y, clip_box);
if(flags & clipping_flags_y_clipped)
{
if(y1 == y2)
{
return false;
}
bound = (flags & clipping_flags_y1_clipped) ? clip_box.y1 : clip_box.y2;
*x = (T)(double(bound - y1) * (x2 - x1) / (y2 - y1) + x1);
*y = bound;
}
return true;
}
//-------------------------------------------------------clip_line_segment
// Returns: ret >= 4 - Fully clipped
// (ret & 1) != 0 - First point has been moved
// (ret & 2) != 0 - Second point has been moved
//
template<class T>
unsigned clip_line_segment(T* x1, T* y1, T* x2, T* y2,
const rect_base<T>& clip_box)
{
unsigned f1 = clipping_flags(*x1, *y1, clip_box);
unsigned f2 = clipping_flags(*x2, *y2, clip_box);
unsigned ret = 0;
if((f2 | f1) == 0)
{
// Fully visible
return 0;
}
if((f1 & clipping_flags_x_clipped) != 0 &&
(f1 & clipping_flags_x_clipped) == (f2 & clipping_flags_x_clipped))
{
// Fully clipped
return 4;
}
if((f1 & clipping_flags_y_clipped) != 0 &&
(f1 & clipping_flags_y_clipped) == (f2 & clipping_flags_y_clipped))
{
// Fully clipped
return 4;
}
T tx1 = *x1;
T ty1 = *y1;
T tx2 = *x2;
T ty2 = *y2;
if(f1)
{
if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x1, y1, f1))
{
return 4;
}
if(*x1 == *x2 && *y1 == *y2)
{
return 4;
}
ret |= 1;
}
if(f2)
{
if(!clip_move_point(tx1, ty1, tx2, ty2, clip_box, x2, y2, f2))
{
return 4;
}
if(*x1 == *x2 && *y1 == *y2)
{
return 4;
}
ret |= 2;
}
return ret;
}
}
#endif

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,44 @@
#ifndef AGG_CONFIG_INCLUDED
#define AGG_CONFIG_INCLUDED
// This file can be used to redefine certain data types.
//---------------------------------------
// 1. Default basic types such as:
//
// AGG_INT8
// AGG_INT8U
// AGG_INT16
// AGG_INT16U
// AGG_INT32
// AGG_INT32U
// AGG_INT64
// AGG_INT64U
//
// Just replace this file with new defines if necessary.
// For example, if your compiler doesn't have a 64 bit integer type
// you can still use AGG if you define the follows:
//
// #define AGG_INT64 int
// #define AGG_INT64U unsigned
//
// It will result in overflow in 16 bit-per-component image/pattern resampling
// but it won't result any crash and the rest of the library will remain
// fully functional.
//---------------------------------------
// 2. Default rendering_buffer type. Can be:
//
// Provides faster access for massive pixel operations,
// such as blur, image filtering:
// #define AGG_RENDERING_BUFFER row_ptr_cache<int8u>
//
// Provides cheaper creation and destruction (no mem allocs):
// #define AGG_RENDERING_BUFFER row_accessor<int8u>
//
// You can still use both of them simultaneously in your applications
// This #define is used only for default rendering_buffer type,
// in short hand typedefs like pixfmt_rgba32.
#endif

View File

@@ -0,0 +1,157 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_CONV_ADAPTOR_VCGEN_INCLUDED
#define AGG_CONV_ADAPTOR_VCGEN_INCLUDED
#include "agg_basics.h"
namespace agg
{
//------------------------------------------------------------null_markers
struct null_markers
{
void remove_all() {}
void add_vertex(double, double, unsigned) {}
void prepare_src() {}
void rewind(unsigned) {}
unsigned vertex(double*, double*) { return path_cmd_stop; }
};
//------------------------------------------------------conv_adaptor_vcgen
template<class VertexSource,
class Generator,
class Markers=null_markers> class conv_adaptor_vcgen
{
enum status
{
initial,
accumulate,
generate
};
public:
explicit conv_adaptor_vcgen(VertexSource& source) :
m_source(&source),
m_status(initial)
{}
void attach(VertexSource& source) { m_source = &source; }
Generator& generator() { return m_generator; }
const Generator& generator() const { return m_generator; }
Markers& markers() { return m_markers; }
const Markers& markers() const { return m_markers; }
void rewind(unsigned path_id)
{
m_source->rewind(path_id);
m_status = initial;
}
unsigned vertex(double* x, double* y);
private:
// Prohibit copying
conv_adaptor_vcgen(const conv_adaptor_vcgen<VertexSource, Generator, Markers>&);
const conv_adaptor_vcgen<VertexSource, Generator, Markers>&
operator = (const conv_adaptor_vcgen<VertexSource, Generator, Markers>&);
VertexSource* m_source;
Generator m_generator;
Markers m_markers;
status m_status;
unsigned m_last_cmd;
double m_start_x;
double m_start_y;
};
//------------------------------------------------------------------------
template<class VertexSource, class Generator, class Markers>
unsigned conv_adaptor_vcgen<VertexSource, Generator, Markers>::vertex(double* x, double* y)
{
unsigned cmd = path_cmd_stop;
bool done = false;
while(!done)
{
switch(m_status)
{
case initial:
m_markers.remove_all();
m_last_cmd = m_source->vertex(&m_start_x, &m_start_y);
m_status = accumulate;
case accumulate:
if(is_stop(m_last_cmd)) return path_cmd_stop;
m_generator.remove_all();
m_generator.add_vertex(m_start_x, m_start_y, path_cmd_move_to);
m_markers.add_vertex(m_start_x, m_start_y, path_cmd_move_to);
for(;;)
{
cmd = m_source->vertex(x, y);
if(is_vertex(cmd))
{
m_last_cmd = cmd;
if(is_move_to(cmd))
{
m_start_x = *x;
m_start_y = *y;
break;
}
m_generator.add_vertex(*x, *y, cmd);
m_markers.add_vertex(*x, *y, path_cmd_line_to);
}
else
{
if(is_stop(cmd))
{
m_last_cmd = path_cmd_stop;
break;
}
if(is_end_poly(cmd))
{
m_generator.add_vertex(*x, *y, cmd);
break;
}
}
}
m_generator.rewind(0);
m_status = generate;
case generate:
cmd = m_generator.vertex(x, y);
if(is_stop(cmd))
{
m_status = accumulate;
break;
}
done = true;
break;
}
}
return cmd;
}
}
#endif

View File

@@ -0,0 +1,159 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_CONV_ADAPTOR_VPGEN_INCLUDED
#define AGG_CONV_ADAPTOR_VPGEN_INCLUDED
#include "agg_basics.h"
namespace agg
{
//======================================================conv_adaptor_vpgen
template<class VertexSource, class VPGen> class conv_adaptor_vpgen
{
public:
explicit conv_adaptor_vpgen(VertexSource& source) : m_source(&source) {}
void attach(VertexSource& source) { m_source = &source; }
VPGen& vpgen() { return m_vpgen; }
const VPGen& vpgen() const { return m_vpgen; }
void rewind(unsigned path_id);
unsigned vertex(double* x, double* y);
private:
conv_adaptor_vpgen(const conv_adaptor_vpgen<VertexSource, VPGen>&);
const conv_adaptor_vpgen<VertexSource, VPGen>&
operator = (const conv_adaptor_vpgen<VertexSource, VPGen>&);
VertexSource* m_source;
VPGen m_vpgen;
double m_start_x;
double m_start_y;
unsigned m_poly_flags;
int m_vertices;
};
//------------------------------------------------------------------------
template<class VertexSource, class VPGen>
void conv_adaptor_vpgen<VertexSource, VPGen>::rewind(unsigned path_id)
{
m_source->rewind(path_id);
m_vpgen.reset();
m_start_x = 0;
m_start_y = 0;
m_poly_flags = 0;
m_vertices = 0;
}
//------------------------------------------------------------------------
template<class VertexSource, class VPGen>
unsigned conv_adaptor_vpgen<VertexSource, VPGen>::vertex(double* x, double* y)
{
unsigned cmd = path_cmd_stop;
for(;;)
{
cmd = m_vpgen.vertex(x, y);
if(!is_stop(cmd)) break;
if(m_poly_flags && !m_vpgen.auto_unclose())
{
*x = 0.0;
*y = 0.0;
cmd = m_poly_flags;
m_poly_flags = 0;
break;
}
if(m_vertices < 0)
{
if(m_vertices < -1)
{
m_vertices = 0;
return path_cmd_stop;
}
m_vpgen.move_to(m_start_x, m_start_y);
m_vertices = 1;
continue;
}
double tx, ty;
cmd = m_source->vertex(&tx, &ty);
if(is_vertex(cmd))
{
if(is_move_to(cmd))
{
if(m_vpgen.auto_close() && m_vertices > 2)
{
m_vpgen.line_to(m_start_x, m_start_y);
m_poly_flags = path_cmd_end_poly | path_flags_close;
m_start_x = tx;
m_start_y = ty;
m_vertices = -1;
continue;
}
m_vpgen.move_to(tx, ty);
m_start_x = tx;
m_start_y = ty;
m_vertices = 1;
}
else
{
m_vpgen.line_to(tx, ty);
++m_vertices;
}
}
else
{
if(is_end_poly(cmd))
{
m_poly_flags = cmd;
if(is_closed(cmd) || m_vpgen.auto_close())
{
if(m_vpgen.auto_close()) m_poly_flags |= path_flags_close;
if(m_vertices > 2)
{
m_vpgen.line_to(m_start_x, m_start_y);
}
m_vertices = 0;
}
}
else
{
// path_cmd_stop
if(m_vpgen.auto_close() && m_vertices > 2)
{
m_vpgen.line_to(m_start_x, m_start_y);
m_poly_flags = path_cmd_end_poly | path_flags_close;
m_vertices = -2;
continue;
}
break;
}
}
}
return cmd;
}
}
#endif

View File

@@ -0,0 +1,48 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_CONV_BSPLINE_INCLUDED
#define AGG_CONV_BSPLINE_INCLUDED
#include "agg_basics.h"
#include "agg_vcgen_bspline.h"
#include "agg_conv_adaptor_vcgen.h"
namespace agg
{
//---------------------------------------------------------conv_bspline
template<class VertexSource>
struct conv_bspline : public conv_adaptor_vcgen<VertexSource, vcgen_bspline>
{
typedef conv_adaptor_vcgen<VertexSource, vcgen_bspline> base_type;
conv_bspline(VertexSource& vs) :
conv_adaptor_vcgen<VertexSource, vcgen_bspline>(vs) {}
void interpolation_step(double v) { base_type::generator().interpolation_step(v); }
double interpolation_step() const { return base_type::generator().interpolation_step(); }
private:
conv_bspline(const conv_bspline<VertexSource>&);
const conv_bspline<VertexSource>&
operator = (const conv_bspline<VertexSource>&);
};
}
#endif

View File

@@ -0,0 +1,63 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// Polygon clipping converter
// There an optimized Liang-Basky algorithm is used.
// The algorithm doesn't optimize the degenerate edges, i.e. it will never
// break a closed polygon into two or more ones, instead, there will be
// degenerate edges coinciding with the respective clipping boundaries.
// This is a sub-optimal solution, because that optimization would require
// extra, rather expensive math while the rasterizer tolerates it quite well,
// without any considerable overhead.
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CLIP_POLYGON_INCLUDED
#define AGG_CONV_CLIP_POLYGON_INCLUDED
#include "agg_basics.h"
#include "agg_conv_adaptor_vpgen.h"
#include "agg_vpgen_clip_polygon.h"
namespace agg
{
//=======================================================conv_clip_polygon
template<class VertexSource>
struct conv_clip_polygon : public conv_adaptor_vpgen<VertexSource, vpgen_clip_polygon>
{
typedef conv_adaptor_vpgen<VertexSource, vpgen_clip_polygon> base_type;
conv_clip_polygon(VertexSource& vs) :
conv_adaptor_vpgen<VertexSource, vpgen_clip_polygon>(vs) {}
void clip_box(double x1, double y1, double x2, double y2)
{
base_type::vpgen().clip_box(x1, y1, x2, y2);
}
double x1() const { return base_type::vpgen().x1(); }
double y1() const { return base_type::vpgen().y1(); }
double x2() const { return base_type::vpgen().x2(); }
double y2() const { return base_type::vpgen().y2(); }
private:
conv_clip_polygon(const conv_clip_polygon<VertexSource>&);
const conv_clip_polygon<VertexSource>&
operator = (const conv_clip_polygon<VertexSource>&);
};
}
#endif

View File

@@ -0,0 +1,63 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// polyline clipping converter
// There an optimized Liang-Basky algorithm is used.
// The algorithm doesn't optimize the degenerate edges, i.e. it will never
// break a closed polyline into two or more ones, instead, there will be
// degenerate edges coinciding with the respective clipping boundaries.
// This is a sub-optimal solution, because that optimization would require
// extra, rather expensive math while the rasterizer tolerates it quite well,
// without any considerable overhead.
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CLIP_polyline_INCLUDED
#define AGG_CONV_CLIP_polyline_INCLUDED
#include "agg_basics.h"
#include "agg_conv_adaptor_vpgen.h"
#include "agg_vpgen_clip_polyline.h"
namespace agg
{
//=======================================================conv_clip_polyline
template<class VertexSource>
struct conv_clip_polyline : public conv_adaptor_vpgen<VertexSource, vpgen_clip_polyline>
{
typedef conv_adaptor_vpgen<VertexSource, vpgen_clip_polyline> base_type;
conv_clip_polyline(VertexSource& vs) :
conv_adaptor_vpgen<VertexSource, vpgen_clip_polyline>(vs) {}
void clip_box(double x1, double y1, double x2, double y2)
{
base_type::vpgen().clip_box(x1, y1, x2, y2);
}
double x1() const { return base_type::vpgen().x1(); }
double y1() const { return base_type::vpgen().y1(); }
double x2() const { return base_type::vpgen().x2(); }
double y2() const { return base_type::vpgen().y2(); }
private:
conv_clip_polyline(const conv_clip_polyline<VertexSource>&);
const conv_clip_polyline<VertexSource>&
operator = (const conv_clip_polyline<VertexSource>&);
};
}
#endif

View File

@@ -0,0 +1,125 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CLOSE_POLYGON_INCLUDED
#define AGG_CONV_CLOSE_POLYGON_INCLUDED
#include "agg_basics.h"
namespace agg
{
//======================================================conv_close_polygon
template<class VertexSource> class conv_close_polygon
{
public:
explicit conv_close_polygon(VertexSource& vs) : m_source(&vs) {}
void attach(VertexSource& source) { m_source = &source; }
void rewind(unsigned path_id);
unsigned vertex(double* x, double* y);
private:
conv_close_polygon(const conv_close_polygon<VertexSource>&);
const conv_close_polygon<VertexSource>&
operator = (const conv_close_polygon<VertexSource>&);
VertexSource* m_source;
unsigned m_cmd[2];
double m_x[2];
double m_y[2];
unsigned m_vertex;
bool m_line_to;
};
//------------------------------------------------------------------------
template<class VertexSource>
void conv_close_polygon<VertexSource>::rewind(unsigned path_id)
{
m_source->rewind(path_id);
m_vertex = 2;
m_line_to = false;
}
//------------------------------------------------------------------------
template<class VertexSource>
unsigned conv_close_polygon<VertexSource>::vertex(double* x, double* y)
{
unsigned cmd = path_cmd_stop;
for(;;)
{
if(m_vertex < 2)
{
*x = m_x[m_vertex];
*y = m_y[m_vertex];
cmd = m_cmd[m_vertex];
++m_vertex;
break;
}
cmd = m_source->vertex(x, y);
if(is_end_poly(cmd))
{
cmd |= path_flags_close;
break;
}
if(is_stop(cmd))
{
if(m_line_to)
{
m_cmd[0] = path_cmd_end_poly | path_flags_close;
m_cmd[1] = path_cmd_stop;
m_vertex = 0;
m_line_to = false;
continue;
}
break;
}
if(is_move_to(cmd))
{
if(m_line_to)
{
m_x[0] = 0.0;
m_y[0] = 0.0;
m_cmd[0] = path_cmd_end_poly | path_flags_close;
m_x[1] = *x;
m_y[1] = *y;
m_cmd[1] = cmd;
m_vertex = 0;
m_line_to = false;
continue;
}
break;
}
if(is_vertex(cmd))
{
m_line_to = true;
break;
}
}
return cmd;
}
}
#endif

View File

@@ -0,0 +1,73 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CONCAT_INCLUDED
#define AGG_CONV_CONCAT_INCLUDED
#include "agg_basics.h"
namespace agg
{
//=============================================================conv_concat
// Concatenation of two paths. Usually used to combine lines or curves
// with markers such as arrowheads
template<class VS1, class VS2> class conv_concat
{
public:
conv_concat(VS1& source1, VS2& source2) :
m_source1(&source1), m_source2(&source2), m_status(2) {}
void attach1(VS1& source) { m_source1 = &source; }
void attach2(VS2& source) { m_source2 = &source; }
void rewind(unsigned path_id)
{
m_source1->rewind(path_id);
m_source2->rewind(0);
m_status = 0;
}
unsigned vertex(double* x, double* y)
{
unsigned cmd;
if(m_status == 0)
{
cmd = m_source1->vertex(x, y);
if(!is_stop(cmd)) return cmd;
m_status = 1;
}
if(m_status == 1)
{
cmd = m_source2->vertex(x, y);
if(!is_stop(cmd)) return cmd;
m_status = 2;
}
return path_cmd_stop;
}
private:
conv_concat(const conv_concat<VS1, VS2>&);
const conv_concat<VS1, VS2>&
operator = (const conv_concat<VS1, VS2>&);
VS1* m_source1;
VS2* m_source2;
int m_status;
};
}
#endif

View File

@@ -0,0 +1,65 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// conv_stroke
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CONTOUR_INCLUDED
#define AGG_CONV_CONTOUR_INCLUDED
#include "agg_basics.h"
#include "agg_vcgen_contour.h"
#include "agg_conv_adaptor_vcgen.h"
namespace agg
{
//-----------------------------------------------------------conv_contour
template<class VertexSource>
struct conv_contour : public conv_adaptor_vcgen<VertexSource, vcgen_contour>
{
typedef conv_adaptor_vcgen<VertexSource, vcgen_contour> base_type;
conv_contour(VertexSource& vs) :
conv_adaptor_vcgen<VertexSource, vcgen_contour>(vs)
{
}
void line_join(line_join_e lj) { base_type::generator().line_join(lj); }
void inner_join(inner_join_e ij) { base_type::generator().inner_join(ij); }
void width(double w) { base_type::generator().width(w); }
void miter_limit(double ml) { base_type::generator().miter_limit(ml); }
void miter_limit_theta(double t) { base_type::generator().miter_limit_theta(t); }
void inner_miter_limit(double ml) { base_type::generator().inner_miter_limit(ml); }
void approximation_scale(double as) { base_type::generator().approximation_scale(as); }
void auto_detect_orientation(bool v) { base_type::generator().auto_detect_orientation(v); }
line_join_e line_join() const { return base_type::generator().line_join(); }
inner_join_e inner_join() const { return base_type::generator().inner_join(); }
double width() const { return base_type::generator().width(); }
double miter_limit() const { return base_type::generator().miter_limit(); }
double inner_miter_limit() const { return base_type::generator().inner_miter_limit(); }
double approximation_scale() const { return base_type::generator().approximation_scale(); }
bool auto_detect_orientation() const { return base_type::generator().auto_detect_orientation(); }
private:
conv_contour(const conv_contour<VertexSource>&);
const conv_contour<VertexSource>&
operator = (const conv_contour<VertexSource>&);
};
}
#endif

View File

@@ -0,0 +1,201 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// classes conv_curve
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_CURVE_INCLUDED
#define AGG_CONV_CURVE_INCLUDED
#include "agg_basics.h"
#include "agg_curves.h"
namespace agg
{
//---------------------------------------------------------------conv_curve
// Curve converter class. Any path storage can have Bezier curves defined
// by their control points. There're two types of curves supported: curve3
// and curve4. Curve3 is a conic Bezier curve with 2 endpoints and 1 control
// point. Curve4 has 2 control points (4 points in total) and can be used
// to interpolate more complicated curves. Curve4, unlike curve3 can be used
// to approximate arcs, both circular and elliptical. Curves are approximated
// with straight lines and one of the approaches is just to store the whole
// sequence of vertices that approximate our curve. It takes additional
// memory, and at the same time the consecutive vertices can be calculated
// on demand.
//
// Initially, path storages are not suppose to keep all the vertices of the
// curves (although, nothing prevents us from doing so). Instead, path_storage
// keeps only vertices, needed to calculate a curve on demand. Those vertices
// are marked with special commands. So, if the path_storage contains curves
// (which are not real curves yet), and we render this storage directly,
// all we will see is only 2 or 3 straight line segments (for curve3 and
// curve4 respectively). If we need to see real curves drawn we need to
// include this class into the conversion pipeline.
//
// Class conv_curve recognizes commands path_cmd_curve3 and path_cmd_curve4
// and converts these vertices into a move_to/line_to sequence.
//-----------------------------------------------------------------------
template<class VertexSource,
class Curve3=curve3,
class Curve4=curve4> class conv_curve
{
public:
typedef Curve3 curve3_type;
typedef Curve4 curve4_type;
typedef conv_curve<VertexSource, Curve3, Curve4> self_type;
explicit conv_curve(VertexSource& source) :
m_source(&source), m_last_x(0.0), m_last_y(0.0) {}
void attach(VertexSource& source) { m_source = &source; }
void approximation_method(curve_approximation_method_e v)
{
m_curve3.approximation_method(v);
m_curve4.approximation_method(v);
}
curve_approximation_method_e approximation_method() const
{
return m_curve4.approximation_method();
}
void approximation_scale(double s)
{
m_curve3.approximation_scale(s);
m_curve4.approximation_scale(s);
}
double approximation_scale() const
{
return m_curve4.approximation_scale();
}
void angle_tolerance(double v)
{
m_curve3.angle_tolerance(v);
m_curve4.angle_tolerance(v);
}
double angle_tolerance() const
{
return m_curve4.angle_tolerance();
}
void cusp_limit(double v)
{
m_curve3.cusp_limit(v);
m_curve4.cusp_limit(v);
}
double cusp_limit() const
{
return m_curve4.cusp_limit();
}
void rewind(unsigned path_id);
unsigned vertex(double* x, double* y);
private:
conv_curve(const self_type&);
const self_type& operator = (const self_type&);
VertexSource* m_source;
double m_last_x;
double m_last_y;
curve3_type m_curve3;
curve4_type m_curve4;
};
//------------------------------------------------------------------------
template<class VertexSource, class Curve3, class Curve4>
void conv_curve<VertexSource, Curve3, Curve4>::rewind(unsigned path_id)
{
m_source->rewind(path_id);
m_last_x = 0.0;
m_last_y = 0.0;
m_curve3.reset();
m_curve4.reset();
}
//------------------------------------------------------------------------
template<class VertexSource, class Curve3, class Curve4>
unsigned conv_curve<VertexSource, Curve3, Curve4>::vertex(double* x, double* y)
{
if(!is_stop(m_curve3.vertex(x, y)))
{
m_last_x = *x;
m_last_y = *y;
return path_cmd_line_to;
}
if(!is_stop(m_curve4.vertex(x, y)))
{
m_last_x = *x;
m_last_y = *y;
return path_cmd_line_to;
}
double ct2_x = 0;
double ct2_y = 0;
double end_x = 0;
double end_y = 0;
unsigned cmd = m_source->vertex(x, y);
switch(cmd)
{
case path_cmd_curve3:
m_source->vertex(&end_x, &end_y);
m_curve3.init(m_last_x, m_last_y,
*x, *y,
end_x, end_y);
m_curve3.vertex(x, y); // First call returns path_cmd_move_to
m_curve3.vertex(x, y); // This is the first vertex of the curve
cmd = path_cmd_line_to;
break;
case path_cmd_curve4:
m_source->vertex(&ct2_x, &ct2_y);
m_source->vertex(&end_x, &end_y);
m_curve4.init(m_last_x, m_last_y,
*x, *y,
ct2_x, ct2_y,
end_x, end_y);
m_curve4.vertex(x, y); // First call returns path_cmd_move_to
m_curve4.vertex(x, y); // This is the first vertex of the curve
cmd = path_cmd_line_to;
break;
}
m_last_x = *x;
m_last_y = *y;
return cmd;
}
}
#endif

View File

@@ -0,0 +1,68 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// conv_dash
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_DASH_INCLUDED
#define AGG_CONV_DASH_INCLUDED
#include "agg_basics.h"
#include "agg_vcgen_dash.h"
#include "agg_conv_adaptor_vcgen.h"
namespace agg
{
//---------------------------------------------------------------conv_dash
template<class VertexSource, class Markers=null_markers>
struct conv_dash : public conv_adaptor_vcgen<VertexSource, vcgen_dash, Markers>
{
typedef Markers marker_type;
typedef conv_adaptor_vcgen<VertexSource, vcgen_dash, Markers> base_type;
conv_dash(VertexSource& vs) :
conv_adaptor_vcgen<VertexSource, vcgen_dash, Markers>(vs)
{
}
void remove_all_dashes()
{
base_type::generator().remove_all_dashes();
}
void add_dash(double dash_len, double gap_len)
{
base_type::generator().add_dash(dash_len, gap_len);
}
void dash_start(double ds)
{
base_type::generator().dash_start(ds);
}
void shorten(double s) { base_type::generator().shorten(s); }
double shorten() const { return base_type::generator().shorten(); }
private:
conv_dash(const conv_dash<VertexSource, Markers>&);
const conv_dash<VertexSource, Markers>&
operator = (const conv_dash<VertexSource, Markers>&);
};
}
#endif

View File

@@ -0,0 +1,432 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// General Polygon Clipper based on the GPC library by Alan Murta
// Union, Intersection, XOR, A-B, B-A
// Contact the author if you intend to use it in commercial applications!
// http://www.cs.man.ac.uk/aig/staff/alan/software/
// Alan Murta (email: gpc@cs.man.ac.uk)
//
//----------------------------------------------------------------------------
#ifndef AGG_CONV_GPC_INCLUDED
#define AGG_CONV_GPC_INCLUDED
#include <cstring>
#include "agg_basics.h"
#include "agg_array.h"
extern "C"
{
#include "gpc.h"
}
namespace agg
{
enum gpc_op_e
{
gpc_or,
gpc_and,
gpc_xor,
gpc_a_minus_b,
gpc_b_minus_a
};
//================================================================conv_gpc
template<class VSA, class VSB> class conv_gpc
{
enum status
{
status_move_to,
status_line_to,
status_stop
};
struct contour_header_type
{
int num_vertices;
int hole_flag;
gpc_vertex* vertices;
};
typedef pod_bvector<gpc_vertex, 8> vertex_array_type;
typedef pod_bvector<contour_header_type, 6> contour_header_array_type;
public:
typedef VSA source_a_type;
typedef VSB source_b_type;
typedef conv_gpc<source_a_type, source_b_type> self_type;
~conv_gpc()
{
free_gpc_data();
}
conv_gpc(source_a_type& a, source_b_type& b, gpc_op_e op = gpc_or) :
m_src_a(&a),
m_src_b(&b),
m_status(status_move_to),
m_vertex(-1),
m_contour(-1),
m_operation(op)
{
std::memset(&m_poly_a, 0, sizeof(m_poly_a));
std::memset(&m_poly_b, 0, sizeof(m_poly_b));
std::memset(&m_result, 0, sizeof(m_result));
}
void attach1(VSA& source) { m_src_a = &source; }
void attach2(VSB& source) { m_src_b = &source; }
void operation(gpc_op_e v) { m_operation = v; }
// Vertex Source Interface
void rewind(unsigned path_id);
unsigned vertex(double* x, double* y);
private:
conv_gpc(const conv_gpc<VSA, VSB>&);
const conv_gpc<VSA, VSB>& operator = (const conv_gpc<VSA, VSB>&);
//--------------------------------------------------------------------
void free_polygon(gpc_polygon& p);
void free_result();
void free_gpc_data();
void start_contour();
void add_vertex(double x, double y);
void end_contour(unsigned orientation);
void make_polygon(gpc_polygon& p);
void start_extracting();
bool next_contour();
bool next_vertex(double* x, double* y);
//--------------------------------------------------------------------
template<class VS> void add(VS& src, gpc_polygon& p)
{
unsigned cmd;
double x, y;
double start_x = 0.0;
double start_y = 0.0;
bool line_to = false;
unsigned orientation = 0;
m_contour_accumulator.remove_all();
while(!is_stop(cmd = src.vertex(&x, &y)))
{
if(is_vertex(cmd))
{
if(is_move_to(cmd))
{
if(line_to)
{
end_contour(orientation);
orientation = 0;
}
start_contour();
start_x = x;
start_y = y;
}
add_vertex(x, y);
line_to = true;
}
else
{
if(is_end_poly(cmd))
{
orientation = get_orientation(cmd);
if(line_to && is_closed(cmd))
{
add_vertex(start_x, start_y);
}
}
}
}
if(line_to)
{
end_contour(orientation);
}
make_polygon(p);
}
private:
//--------------------------------------------------------------------
source_a_type* m_src_a;
source_b_type* m_src_b;
status m_status;
int m_vertex;
int m_contour;
gpc_op_e m_operation;
vertex_array_type m_vertex_accumulator;
contour_header_array_type m_contour_accumulator;
gpc_polygon m_poly_a;
gpc_polygon m_poly_b;
gpc_polygon m_result;
};
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::free_polygon(gpc_polygon& p)
{
int i;
for(i = 0; i < p.num_contours; i++)
{
pod_allocator<gpc_vertex>::deallocate(p.contour[i].vertex,
p.contour[i].num_vertices);
}
pod_allocator<gpc_vertex_list>::deallocate(p.contour, p.num_contours);
std::memset(&p, 0, sizeof(gpc_polygon));
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::free_result()
{
if(m_result.contour)
{
gpc_free_polygon(&m_result);
}
std::memset(&m_result, 0, sizeof(m_result));
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::free_gpc_data()
{
free_polygon(m_poly_a);
free_polygon(m_poly_b);
free_result();
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::start_contour()
{
contour_header_type h;
std::memset(&h, 0, sizeof(h));
m_contour_accumulator.add(h);
m_vertex_accumulator.remove_all();
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
inline void conv_gpc<VSA, VSB>::add_vertex(double x, double y)
{
gpc_vertex v;
v.x = x;
v.y = y;
m_vertex_accumulator.add(v);
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::end_contour(unsigned /*orientation*/)
{
if(m_contour_accumulator.size())
{
if(m_vertex_accumulator.size() > 2)
{
contour_header_type& h =
m_contour_accumulator[m_contour_accumulator.size() - 1];
h.num_vertices = m_vertex_accumulator.size();
h.hole_flag = 0;
// TO DO: Clarify the "holes"
//if(is_cw(orientation)) h.hole_flag = 1;
h.vertices = pod_allocator<gpc_vertex>::allocate(h.num_vertices);
gpc_vertex* d = h.vertices;
int i;
for(i = 0; i < h.num_vertices; i++)
{
const gpc_vertex& s = m_vertex_accumulator[i];
d->x = s.x;
d->y = s.y;
++d;
}
}
else
{
m_vertex_accumulator.remove_last();
}
}
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::make_polygon(gpc_polygon& p)
{
free_polygon(p);
if(m_contour_accumulator.size())
{
p.num_contours = m_contour_accumulator.size();
p.hole = 0;
p.contour = pod_allocator<gpc_vertex_list>::allocate(p.num_contours);
int i;
gpc_vertex_list* pv = p.contour;
for(i = 0; i < p.num_contours; i++)
{
const contour_header_type& h = m_contour_accumulator[i];
pv->num_vertices = h.num_vertices;
pv->vertex = h.vertices;
++pv;
}
}
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::start_extracting()
{
m_status = status_move_to;
m_contour = -1;
m_vertex = -1;
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
bool conv_gpc<VSA, VSB>::next_contour()
{
if(++m_contour < m_result.num_contours)
{
m_vertex = -1;
return true;
}
return false;
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
inline bool conv_gpc<VSA, VSB>::next_vertex(double* x, double* y)
{
const gpc_vertex_list& vlist = m_result.contour[m_contour];
if(++m_vertex < vlist.num_vertices)
{
const gpc_vertex& v = vlist.vertex[m_vertex];
*x = v.x;
*y = v.y;
return true;
}
return false;
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
void conv_gpc<VSA, VSB>::rewind(unsigned path_id)
{
free_result();
m_src_a->rewind(path_id);
m_src_b->rewind(path_id);
add(*m_src_a, m_poly_a);
add(*m_src_b, m_poly_b);
switch(m_operation)
{
case gpc_or:
gpc_polygon_clip(GPC_UNION,
&m_poly_a,
&m_poly_b,
&m_result);
break;
case gpc_and:
gpc_polygon_clip(GPC_INT,
&m_poly_a,
&m_poly_b,
&m_result);
break;
case gpc_xor:
gpc_polygon_clip(GPC_XOR,
&m_poly_a,
&m_poly_b,
&m_result);
break;
case gpc_a_minus_b:
gpc_polygon_clip(GPC_DIFF,
&m_poly_a,
&m_poly_b,
&m_result);
break;
case gpc_b_minus_a:
gpc_polygon_clip(GPC_DIFF,
&m_poly_b,
&m_poly_a,
&m_result);
break;
}
start_extracting();
}
//------------------------------------------------------------------------
template<class VSA, class VSB>
unsigned conv_gpc<VSA, VSB>::vertex(double* x, double* y)
{
if(m_status == status_move_to)
{
if(next_contour())
{
if(next_vertex(x, y))
{
m_status = status_line_to;
return path_cmd_move_to;
}
m_status = status_stop;
return path_cmd_end_poly | path_flags_close;
}
}
else
{
if(next_vertex(x, y))
{
return path_cmd_line_to;
}
else
{
m_status = status_move_to;
}
return path_cmd_end_poly | path_flags_close;
}
return path_cmd_stop;
}
}
#endif

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