Compare commits

...

832 Commits

Author SHA1 Message Date
David Given
f634988356 Add support for emitting index sync bytes to the Northstar encoder. This tells
the FluxEngine to wait for the next index pulse before continuing the write.
This also allows FluxEngine to decode its own encoded bytestreams, allowing
encodedecodetest to work.
2022-03-14 22:11:08 +01:00
David Given
07b48b2e0d Merge pull request #493 from davidgiven/drive
Move drive configuration settings out of flux_source/flux_sink into a common config area.
2022-03-14 19:39:40 +00:00
David Given
294672d946 Update documentation. 2022-03-13 21:15:18 +01:00
David Given
ba3f806616 Move a lot of drive parameters into a toplevel drive{} config rather than
leaving them in the source/sink configs (which means they have to be set
twice).
2022-03-13 21:13:56 +01:00
David Given
9e4d99faca Merge pull request #489 from jepler/further-apple-fixes
Further apple fixes - have now booted a DOS 3.3 written with FE + GW
2022-03-08 16:01:15 +00:00
Jeff Epler
6cfe69634c Merge remote-tracking branch 'origin/master' into further-apple-fixes 2022-03-08 07:45:20 -06:00
Jeff Epler
61be3714a5 apple2: encoder: Fix writing of "FF40"
I was writing "FF40" as "FF48": 1111 1111 0000 instead of 1111 1111 00.
This sequence of four zeros will not allow the real hardware to synchronize
to the bitstream.

With this change, I was able to boot a DOS 3.3 disk I rearranged into
"physical" order; some 'there's a disk error' raspberries occur (probably
indicating a data error within a sector) during the boot process, so
something is still obviously marginal, but this is a huge step forward.
2022-03-08 07:44:03 -06:00
Jeff Epler
0aed615ee5 Fix crash when reading disks written on real apple2
`readRaw8` throws an exception if you run out of data. This did not
turn up during my testing, because I was reading back a disk written by
fluxengine, so everything was aligned to the index pulse and no sector
ever straddled a revolution. On a "real" apple2-written disk, this was
not the case.

Incidentally, the "extra 0" problem exists on a real apple2-written
floppy as well.
2022-03-07 22:40:33 -06:00
David Given
6797037bdb Merge pull request #488 from davidgiven/multiread
Refactor to deprecate F_DESYNC.
2022-03-07 23:14:07 +00:00
David Given
39f8b25fd8 Tidy. 2022-03-07 00:28:19 +01:00
David Given
96214bf3fd Refactor for better multi-read support. Each read is now held separately, with
F_DESYNC being deprecated, and FluxSource returns an iterator which can be used
to retry reads.
2022-03-07 00:07:42 +01:00
David Given
400cd87802 Update documentation with new dependencies.
Fixes: #487
2022-03-06 12:07:55 +01:00
David Given
00c458db1e Merge pull request #486 from davidgiven/brother
Add support for creating bootable Brother 120kB disks.
2022-03-06 01:00:45 +00:00
David Given
1454e200db Create bootable Brother disks. 2022-03-06 01:32:19 +01:00
David Given
752875061c Format. 2022-03-06 00:47:11 +01:00
David Given
78186d8a45 Archive. 2022-03-06 00:46:53 +01:00
David Given
a4ef434f11 Merge pull request #485 from davidgiven/brother
Add support for writing 120kB Brother disk images.
2022-03-04 23:49:09 +00:00
David Given
9842c9945d Typo fix. 2022-03-05 00:33:10 +01:00
David Given
6dcd97dedf Create executable files (although possibly not correctly). 2022-03-05 00:15:11 +01:00
David Given
549a984eab Add support for writing 120kB Brother disk images. Also add some utils tests,
because they needed it.
2022-03-04 23:42:29 +01:00
David Given
aa805f81e0 Simplify. 2022-03-04 20:28:09 +01:00
David Given
93a67dadf6 Merge from master. 2022-03-04 20:19:42 +01:00
David Given
a31fcdb753 Merge pull request #484 from davidgiven/brother
Fix the Brother 120kB format.
2022-03-02 23:36:02 +00:00
David Given
0edca836f0 Let the image reader do the physical/logical track mapping. 2022-03-03 00:18:49 +01:00
David Given
8537f291b7 Update README. 2022-03-02 23:57:22 +01:00
David Given
46611ec720 Fix writing to 120kB Brother disks. 2022-03-02 23:56:02 +01:00
David Given
cbc3db8100 Display erased tracks a bit more gracefully. 2022-03-02 23:55:53 +01:00
David Given
5bd633d5bb Merge pull request #481 from jepler/apple2decoder
apple2: Comment decoder, add fix/workaround for stray 0 at start of sector data
2022-03-02 22:26:00 +00:00
Jeff Epler
79eec41bcd Revamp how extra zeros are handled
It's not OK to call seek() here. Instead, add a function which can read
an apple 8-bit flux value in terms of readRaw8 and readRawBits. Apply
this function to all the data bytes, rather than just the first one.
2022-03-02 07:45:24 -06:00
Jeff Epler
386d22a45e Makefile: give a clear error at build time if wx is not available 2022-03-02 07:44:09 -06:00
Jeff Epler
d90fcbf7ad Handle extra 0-bits at the start of a sector 2022-03-02 07:43:45 -06:00
Jeff Epler
c4e4520058 apple2 decoder: explain the progression of sector status values 2022-03-02 07:43:43 -06:00
David Given
dd49f01499 Merge pull request #480 from jepler/apple2encoder
Add apple2 encoder
2022-03-02 10:24:05 +00:00
Jeff Epler
2c698cee71 Mark apple2 image writing as dinosaur, add note about writing, not ro anymore 2022-03-01 19:05:53 -06:00
Jeff Epler
87cb4b6d18 Add apple2 encoder
This is tested with encodedecodetest.sh but is not tested on HW yet.
It's likely that the sector order (interleave) doesn't match real systems.
2022-02-28 16:22:53 -06:00
David Given
2b7c747209 Merge pull request #471 from jepler/rx50-pdp11-format
Add support for PDP-11 RX-50 disks
2022-02-27 18:59:10 +00:00
Jeff Epler
707308b490 Fix alphetization of format list 2022-02-27 10:47:33 -06:00
Jeff Epler
ed1012bf07 Fix rx50 format description & test it
gap3 follows f10f52ded8/src/image/img.c (L135)
2022-02-27 10:47:22 -06:00
David Given
cc5b2bc27b Merge pull request #479 from davidgiven/wxw
Add a GUI.
2022-02-27 12:24:38 +00:00
David Given
a1a7cfa735 We don't need the rich text wxwidgets addon. 2022-02-27 13:12:29 +01:00
David Given
4624ff92df Remember to set the high density bit. 2022-02-27 13:11:11 +01:00
David Given
47dde98728 Include the GUI exe in release artifacts. 2022-02-27 13:05:39 +01:00
David Given
6f1031e95b Writing flux now works. 2022-02-27 12:54:57 +01:00
David Given
d5245e3784 Don't capture parameters in static blocks. That never ends well... 2022-02-27 12:51:51 +01:00
David Given
d97e72edb6 The image writers now log to the logger. 2022-02-27 00:27:44 +01:00
David Given
23b9e9ef5f The image readers now log to the logger. 2022-02-27 00:20:20 +01:00
David Given
02c7b86f85 You can write images now. 2022-02-27 00:07:59 +01:00
Paul Devine
f796b6b40d works in physical victor 9000 machine 2022-02-26 14:59:09 -08:00
David Given
528454c361 You can now load images into memory. 2022-02-26 23:32:24 +01:00
Jeff Epler
9ae3f7e61d Correct description of media type 2022-02-26 15:41:14 -06:00
David Given
dd33922810 Errors are now caught. 2022-02-26 19:52:07 +01:00
David Given
b52fdb3155 Additional settings pane now works. 2022-02-26 19:33:20 +01:00
Jeff Epler
edf9f9e714 Implement review suggestions 2022-02-26 12:06:08 -06:00
David Given
38eda6ed3c Add log and settings panels. The log panel is populated during a read (or
write).
2022-02-26 18:36:21 +01:00
David Given
32f0c5dc09 Update buttons as the application state changes. 2022-02-26 18:10:00 +01:00
David Given
b45b45b1b3 Enable double buffering for the visualiser in Windows. 2022-02-26 13:36:28 +00:00
David Given
f1c60b7177 Adjust header order so that things build on Windows. 2022-02-26 13:33:00 +00:00
David Given
2d8ff826f3 Merge pull request #475 from hharte/improve-hard-sector-decode
Improve hard sector decode
2022-02-26 12:37:48 +00:00
Howard M. Harte
d028db1ba3 northstar: Improve decode a little more.
seekToPattern() can skip the index hole if it doesn't find the
SYNC pattern.  If this happens too close to the end of the flux
stream, it can result in a conflicted sector.  In this case,
discard the sector.
2022-02-25 22:08:57 -08:00
Howard M. Harte
2d4e2b87bc micropolis: Improve decode a little more.
seekToPattern() can skip the index hole if it doesn't find the
SYNC pattern.  If this happens too close to the end of the flux
stream, it can result in a conflicted sector.  In this case,
discard the sector.
2022-02-25 21:59:18 -08:00
David Given
1d7a75c7b3 Okay, the visualiser looks pretty much done. 2022-02-26 00:23:17 +01:00
David Given
65584a953d Merge pull request #473 from hharte/fix-fluxmap-split
fluxmap: Don't push_back() if Fluxmap begins with F_DESYNC.
2022-02-25 11:15:31 +00:00
Howard M. Harte
4cbd19d2e5 fluxmap: Don't push_back() if Fluxmap begins with F_DESYNC.
Fluxmap::split() creates a 0-length Fluxmap if the Fluxmap
begins with F_DESYNC.  Fix this by not doing push_back()
if a F_DESYNC is encountered at the start of the Fluxmap.
2022-02-24 19:07:09 -08:00
David Given
a59b59fea4 First sector visualisation! Doesn't look bad. 2022-02-25 01:31:10 +01:00
David Given
5c063a9de3 Replumb the visualiser data flow... again. I think I might have it right this
time.
2022-02-25 00:36:12 +01:00
David Given
3666a7d7bd Merge. 2022-02-24 23:52:18 +01:00
David Given
8250112c7f Merge pull request #468 from JohnVeness/JohnVeness-fix-docs
Fix various doc typos
2022-02-24 22:16:48 +00:00
David Given
30957f4105 Merge pull request #472 from davidgiven/proto
Make the internal disk data structures all const.
2022-02-24 22:15:58 +00:00
David Given
eade2e279e Make the internal disk data structures all const, to allow us to pass them to
the GUI UI thread safely.
2022-02-24 22:46:10 +01:00
Jeff Epler
b4489b9402 Add support for PDP-11 RX-50 disks
According to my source [https://www.cbmstuff.com/forum/showthread.php?tid=634] the format of RX-50 is

 * single sided
 * 80 tracks
 * 10 sectors per track
 * 96 tpi (tracks per inch)
 * 300 rpm (revolutions per minute)
 * 250 KHz data rate

I have a disk labeled
```
BL-T540E-MC
CZUFDE0 MICRO PDP-11
USER TESTS
© 1983, 1894
DIGITAL EQUIPMENT CORP.
```
and stamped 14131.

The image read all sectors and the content looks plausible.  `strings` on it says things like
```
$ strings rx50.img  | grep -i pdp
; This file implements the USER FRIENDLY Diagnostic for the MICRO PDP11 & 11/73
```
however, I don't have an emulator or other system to use the disk image with so
I can't 100% vouch for the image being complete and correct.
2022-02-23 17:24:05 -06:00
David Given
a67b7c80c1 Rework the visualiser to look better, maybe? Also fiddle with the logger
somewhat. There's a pending problem where it's not safe to send mutable objects
through the logger to the visualiser, which will need work.
2022-02-23 23:50:29 +01:00
David Given
dfc0cdd0fa Merge from master. 2022-02-23 20:43:19 +01:00
John Veness
d6faf5b074 Change to consistent "-sided" 2022-02-23 11:05:57 +00:00
John Veness
44ffa6a3b0 Change to consistent "-sided" 2022-02-23 10:58:13 +00:00
John Veness
168b78d9d7 Change to consistent "-sided" 2022-02-23 10:57:17 +00:00
John Veness
bd8f313ccb Change to consistent "-sided" 2022-02-23 10:56:36 +00:00
David Given
4a0fc3d566 Merge pull request #469 from hharte/fix-warnings
Fix override warnings in decoders.
2022-02-23 10:53:15 +00:00
Howard M. Harte
8d04d17e39 Fix override warnings in decoders. 2022-02-22 22:45:39 -08:00
John Veness
9f44b1e783 Add space between sentences 2022-02-23 00:08:15 +00:00
John Veness
1b15271fe2 Fix "seperated" typo 2022-02-22 23:45:51 +00:00
John Veness
f451d3203c Made case consistent on "Read only" 2022-02-22 23:44:19 +00:00
John Veness
c713d38c19 Fix representing typo 2022-02-22 23:39:34 +00:00
John Veness
4d51f9d097 Fix GreaseWeazel internal link and bracket 2022-02-22 23:36:25 +00:00
John Veness
8750341862 Fix grammar in building.md 2022-02-22 23:31:56 +00:00
John Veness
3a4fe086ea Fix resale/rescale typo 2022-02-22 23:06:55 +00:00
John Veness
212e457c4c Fixed grammar in README 2022-02-22 23:06:06 +00:00
John Veness
790e2b534f Fixed end bracket in README 2022-02-22 22:54:16 +00:00
David Given
48414f0ce9 Merge pull request #467 from davidgiven/logging
Miscellaneous minor fixes
2022-02-22 22:53:19 +00:00
David Given
b5c3e75f10 Fix the new logger output. 2022-02-22 23:39:08 +01:00
David Given
548e07ce17 Fix off-by-one error in the MX decoder. 2022-02-22 22:51:14 +01:00
David Given
0aa0c6866c Do a very basic read/write visualisation. It looks like suck. 2022-02-22 22:35:13 +01:00
David Given
3d4cf7df26 Merge from master. 2022-02-22 18:12:33 +01:00
David Given
c4ba180a0c Merge pull request #462 from davidgiven/logging
Overhaul the logging system
2022-02-22 13:11:28 +00:00
David Given
bd392b91b7 Make the C++ old enough for Ubuntu. 2022-02-22 13:56:50 +01:00
David Given
042f7b0502 Log messages are now forwarded to the UI thread. 2022-02-22 00:50:58 +01:00
David Given
0fc5f0ee7d Merge pull request #465 from hharte/fix-northstar-decoder
northstar: Fix after decoder change.
2022-02-21 21:42:13 +00:00
Howard M. Harte
8cbef669b1 northstar: Fix after decoder change.
Recent changes to the decoders broke North Star.
2022-02-21 13:34:34 -08:00
David Given
f9004fb14c Merge. 2022-02-21 22:22:10 +01:00
David Given
40a42c65c1 The rotational speed message is now done via the logger. 2022-02-21 22:21:46 +01:00
David Given
e14030e369 Actually start using bits of the decoder framework. 2022-02-21 22:02:28 +01:00
David Given
c6cef191a7 Start work on setting the controls in the main window. 2022-02-21 00:57:39 +01:00
David Given
6ca9f83bfe Add the main threading stuff. 2022-02-21 00:14:27 +01:00
David Given
b3fcdc5f40 Come up with a UI design I'm happy with. Start work on the disk visualiser. 2022-02-20 22:41:47 +01:00
David Given
c6591cc11a Start using wxformbuilder for GUI design. 2022-02-20 00:07:47 +01:00
David Given
e31ba479b2 Merge from master. 2022-02-19 23:38:51 +01:00
David Given
659b668012 Merge pull request #463 from hharte/improve_micropolis_decoder
Improve micropolis decoder
2022-02-19 22:34:14 +00:00
Howard M. Harte
d69944dd8c Micropolis: Add support for MZOS checksum.
Vector MZOS uses a different sector checksum than the standard
Micropolis checksum used by MDOS, CP/M and OASIS.  Automatically
detect the checksum on the disk by default, but allow the user
to force the use of a specific checksum using the
--decoder.micropolis.checksum_type parameter.
2022-02-19 14:10:56 -08:00
Howard M. Harte
a25111e411 Micropolis: Improve decode.
* Discard a partial sector at the end of the track.
    * Do not seek to the index mark for the first sector.
    * Use a 64-bit pattern to match the SYNC.
    * If SYNC is found too early, search for a subsequent SYNC.
    * While decoding the sector record, enforce the SYNC pattern
      and track ID.
2022-02-19 14:10:44 -08:00
Howard M. Harte
6866d5d3fa Require 16 sectors for Micropolis disks. 2022-02-19 14:10:35 -08:00
David Given
21b3d1c521 Remove stuff which is too modern for Ubuntu. 2022-02-19 22:56:13 +01:00
David Given
f2bdd1cc49 More work on the logger overhaul: the reader should be done now. 2022-02-19 22:48:44 +01:00
David Given
9f09794ae6 Merge pull request #461 from hharte/fix_upgrade_windows
upgrade-flux-file: Fix for Windows
2022-02-19 21:29:54 +00:00
Howard M. Harte
6a1fb3d829 upgrade-flux-file: Fix for Windows
Close the database and remove the input file prior to renaming.
2022-02-19 13:14:25 -08:00
David Given
4cb9d0b50a Merge pull request #458 from davidgiven/atarist
Fix the Atari ST profiles
2022-02-16 22:24:48 +01:00
David Given
73f37ef289 Update Atari ST documentation. 2022-02-16 22:06:26 +01:00
David Given
dbda19a209 Make sure that Atari ST disks ignore any spurious sector 66, because of
FastCopy.
2022-02-16 22:03:15 +01:00
David Given
0d7de7bbc0 Add support for ignoring certain sectors (needed for FastCopy disks on the
Atari ST).
2022-02-16 22:02:50 +01:00
David Given
649b78611c Work-in-progress logging overhaul. 2022-02-16 21:24:17 +01:00
David Given
eeb5ba40fb Merge pull request #455 from jepler/serial-behavior-improvements
Some quality of life improvements for Adafruit generic GW-compatibles
2022-02-13 12:43:18 +01:00
Jeff Epler
33bd912476 Some quality of life improvements for Adafruit generic GW-compatibles
The addition of the new tcsetattr call fixes a problem where interrupting
fluxengine during a flux read will leave data in the device and/or Linux's
serial buffers, so that the next invocation of fluxengine will fail similar
to
```
Error: command returned garbage (0x27 != 0x0 with status 0x31)
```
(the specific value differs because it's actually flux data)

Merely changing the existing tcsetattr call to specify TCSAFLUSH was not
enough; moving it after the 200ms pause seems to be enough.

Note that it doesn't seem feasible in our USB stack to make DTR reset
the device, since that would take down the USB stack and require a fresh
USB connection.

The addition of the special case for `rlen == 0` in read is for when the
GW-compatible board is reset or crashes and usb-disconnects during a
reading operation. Without this change, fluxengine spins forever at 100%
of a CPU, repeating a read().  After the change, this will cause
the host computer to print messages like:
```
  2.1: 200 ms in 68928 bytes
  3.0: Error: serial read returned no data (device removed?)
```
and exit.

I only tested these changes on Linux (Debian with kernel 5.10) and in
particular I don't know if/how it will work on a Mac.
2022-02-12 22:04:18 -06:00
David Given
33f1084f0a Merge from master. 2022-02-13 00:20:12 +01:00
David Given
e8a9b7cae3 Merge pull request #454 from davidgiven/mx
Fix a regression in the MX decoder.
2022-02-13 00:05:24 +01:00
David Given
f6b1d9c493 Fix a regression in the MX decoder. 2022-02-12 23:37:20 +01:00
David Given
624c34b378 Merge pull request #453 from davidgiven/mx
Fix regressions caused by the decoder change.
2022-02-12 22:56:57 +01:00
David Given
bc6753e5bf Correctly record sector positions. 2022-02-12 22:42:40 +01:00
David Given
c539debc84 Fix the Amiga decoder, which got broken with the decoder change. Also fix the
encoder which was always broken (but Amigas apparently didn't care).
2022-02-12 22:42:15 +01:00
David Given
830f4cec0f Merge pull request #452 from davidgiven/mx
Prevent bad reads on gapless formats.
2022-02-12 15:32:36 +01:00
David Given
03dd9e6e83 Add back support for capturing raw records. 2022-02-12 15:16:25 +01:00
David Given
e8d1c90182 Fix, hopefully, the rest of the decoders. 2022-02-12 15:02:42 +01:00
David Given
0933dc1afa Partially complete rework of all the decoders to avoid seeking inside the
fluxmap. This requires resetting the FluxDecoder, which loses any pending
state, resulting in bad reads for (some) formats which don't have gaps between
sectors --- the DVK MX is the main victim.
2022-02-12 00:55:09 +01:00
David Given
610b7fe95c Merge pull request #451 from jepler/scp-84track
Support 84-track SCP files
2022-02-11 16:25:35 +01:00
Jeff Epler
a6bf6f901f Error gracefully when requested to write a too-large scp image
This prevents a crash when more tracks were recorded than fit in an
scp image. When I first encountered a problem doing an 84-track operation,
this led to a segfault later on since memory was written past the end
of _fileheader, and (I think) stomped on a pointer.
2022-02-11 09:10:20 -06:00
Jeff Epler
ca2e37e852 Include 168 track data headers
According to version 2.2 of the SCP image specification
<https://www.cbmstuff.com/downloads/scp/scp_image_specs.txt>
the number of Track Data Entries for a floppy can be up to 168,
so that up to 84 tracks can be recorded (e.g., fluxengine -c 0-83).

> BYTES 0x10-0x2AF (for floppy image files) are a table of longwords with each entry being
> an offset to a Track Data Header (TDH) for each track that is stored in the image.  The table
> is always sequential.  There is an entry for every track, with up to 168 entries supported for
> floppy disks.  This means that floppy disk images of up to 84 tracks with sides 0/1 are possible.
2022-02-11 09:08:59 -06:00
David Given
d1ffaaa327 Merge pull request #449 from davidgiven/macdsk
Change the mac profiles to produce simple sector images rather the DiskCopy files.
2022-02-08 22:04:48 +01:00
David Given
8b7f551505 Change the mac profiles to produce simple sector images rather the DiskCopy
files (as that seems to be what most people typically use).
2022-02-08 21:21:22 +01:00
David Given
759b3f8fcd Merge pull request #447 from adafruit/master
fix 2 byte pack for revolution count
2022-02-08 20:04:57 +01:00
lady ada
9ec56f1dfa fix 2 byte pack for revolution count 2022-02-08 11:43:20 -05:00
David Given
84e997949f Merge. 2022-02-06 22:14:11 +01:00
David Given
f82bd45fec Pull out extension profiles into their own section of the documentation list. 2022-02-06 22:13:26 +01:00
David Given
3863724007 Merge pull request #443 from davidgiven/upgrade
Rework flux file upgrading and remove the Sqlite dependency.
2022-02-04 22:16:31 +01:00
David Given
7e0ed9fe93 Try to fix the build on Windows and OSX. 2022-02-04 22:00:44 +01:00
David Given
945e3f3c5f Typo fix. 2022-02-04 21:29:56 +01:00
David Given
6f100219f7 Include the upgrade-flux-file binaries in the releases. 2022-02-04 21:29:04 +01:00
David Given
89688394f8 Replace the upgradefluxfile builtin with a seperate upgrade-flux-file tool.
This allows us to remove all the SQL stuff from the main program, and restores
the ability to upgrade from version 2 SQL files.
2022-02-04 21:27:24 +01:00
David Given
091ef6d972 Merge pull request #439 from tdaede/d88_doublestep
D88: Add 2D (40 track double density) support.
2022-02-03 00:27:55 +01:00
Eric Anderson
e501c44985 Merge branch 'master' into micropolis-275 2022-01-29 10:09:35 -07:00
Eric Anderson
bc9b761903 Prefer IMG for Micropolis, and use a vgi profile to swap format 2022-01-29 10:05:54 -07:00
Thomas Daede
dfb461b05c D88: Add 2D (40 track double density) support.
This assumes a 360rpm drive, and double stepping.

If fluxengine supports the concept of 40 vs 80 track drives in the
future, it might make sense for this to assume a 300rpm drive
instead.
2022-01-28 10:50:36 -08:00
David Given
86f6a2f722 Merge pull request #437 from davidgiven/verify
Fix --no-verify.
2022-01-27 22:05:23 +01:00
David Given
77d6d0d5be Fix --no-verify. 2022-01-27 20:49:38 +01:00
David Given
4946909c6d Merge pull request #436 from davidgiven/dim
Fix some autoconfigure issues.
2022-01-27 00:07:23 +01:00
David Given
2439736cb4 When autoconfiguring, set up the decoder to allow disk verification. 2022-01-26 23:53:44 +01:00
David Given
8fb2ad1986 Automatically set the number of heads and cylinders where appropriate. 2022-01-26 23:53:16 +01:00
David Given
1d7bbcec66 Merge pull request #435 from davidgiven/brother
Don't use signed values for track/sector/side values
2022-01-26 23:29:51 +01:00
David Given
6ac9d34aac Don't rely on the geometry autodetector for Brother formats. 2022-01-26 23:13:40 +01:00
David Given
3369029e9a Don't use signed ints for the track/side/sector values. 2022-01-26 23:13:22 +01:00
David Given
11f411b745 Merge pull request #433 from davidgiven/rawread
Don't crash when trying to read flux from a fl2 file where there isn't any.
2022-01-26 23:09:48 +01:00
David Given
9bb6e15900 Don't crash when trying to read flux from a fl2 file where there isn't any
flux.
2022-01-26 22:09:50 +01:00
David Given
d2a6b37f5f Merge pull request #430 from tdaede/greaseweazle_time_read
Support time-based reads for Greaseweazle V24+.
2022-01-26 21:17:44 +01:00
David Given
0c1f6163f7 Merge pull request #432 from tdaede/no_verify
Add flag to skip verification during write.
2022-01-26 21:16:21 +01:00
Thomas Daede
6860317a58 Add flag to skip verification during write.
Fixes #425
2022-01-26 11:45:28 -08:00
Thomas Daede
77257b0989 Support time-based reads for Greaseweazle V24+.
This substantially speeds up read and verify operations, allowing
only 1.2 revs rather than 2 revs to be read.

Fixes #426
2022-01-26 08:02:30 -08:00
David Given
8f35da5163 Merge pull request #428 from tdaede/doc_pc98
Add documentation for PC-98, X68000, etc.
2022-01-26 17:00:40 +01:00
Thomas Daede
82d8c40b5f Add documentation for PC-98, X68000, etc. 2022-01-25 09:53:56 -08:00
David Given
dbfc0f98c3 Merge pull request #422 from tdaede/nfdr0
Add NFD r0 image reader.
2022-01-22 23:28:55 +01:00
David Given
ca15bf4a92 Merge pull request #417 from tdaede/fluxsink_ratio
Add option to rescale flux source pulses.
2022-01-22 23:27:53 +01:00
David Given
3e4d4cc002 Hotfix for Windows build. 2022-01-22 22:29:34 +01:00
David Given
4257544402 Merge. 2022-01-22 21:20:48 +01:00
David Given
83d44bd03e Update documentation. 2022-01-22 20:46:40 +01:00
David Given
745e0685a4 Allow specifying the GreaseWeazle serial port directly (for devices which don't
have the GreaseWeazle VID/PID).
2022-01-22 20:41:27 +01:00
David Given
d6db2128df Toggle DTR on Unixes as well as on Windows. 2022-01-22 20:39:20 +01:00
David Given
6e83ed5654 Update clang-format file. 2022-01-22 20:37:58 +01:00
Thomas Daede
a4f44933ec Add NFD r0 image reader.
This format is very similar to D88, but used mostly for PC-98
rather than PC-88.
2022-01-22 08:33:43 -08:00
David Given
fe080e1d90 Merge from master. 2022-01-21 19:36:18 +01:00
David Given
dd462873d2 Merge pull request #410 from pauldevine/victor9k
Victor9k Double Sided Support
2022-01-20 23:51:24 +01:00
Paul Devine
628fec2c92 Merge branch 'victor9k' of github.com:pauldevine/fluxengine into victor9k 2022-01-20 13:57:25 -08:00
Paul Devine
c4700f3cb8 updated documentation for double-sided, made note about documentaiton bug for track format 2022-01-20 13:56:35 -08:00
Paul Devine
fac606c475 adding Victor9k double sided src format 2022-01-20 13:56:35 -08:00
Paul Devine
5a0aadabc1 adding support for double-sided Victor9k disks 2022-01-20 13:56:35 -08:00
Paul Devine
283b9871cb correct media size in README 2022-01-20 13:56:35 -08:00
David Given
f9d88cc2a0 Merge pull request #420 from davidgiven/contains
Don't use string::contains as it's not sufficiently widespread yet.
2022-01-20 20:40:19 +01:00
David Given
c751810b84 Don't use string::contains. 2022-01-20 20:24:10 +01:00
David Given
2dbc9c39b1 Merge pull request #419 from davidgiven/clang
Add initial clang-format file.
2022-01-20 19:54:07 +01:00
Paul Devine
a9933c7764 updated documentation for double-sided, made note about documentaiton bug for track format 2022-01-19 16:21:40 -08:00
David Given
ffcac3a12d Add initial clang-format file. 2022-01-20 00:08:26 +01:00
David Given
a4130e5f78 Merge pull request #418 from tdaede/auto_fdi
FDI: Add support for automatically setting format.
2022-01-19 23:12:41 +01:00
Thomas Daede
de8f8b7d91 FDI: Add support for automatically setting format. 2022-01-17 17:02:26 -08:00
Thomas Daede
b8c58b12fd Add option to rescale flux source pulses.
This actually scales by the reciprotal of the flux sink, to
allow the same value to be set for both options.
2022-01-17 16:53:50 -08:00
David Given
4af355e1f9 Merge pull request #416 from jepler/patch-1
Fix a little markup error
2022-01-17 15:36:43 +01:00
Jeff Epler
0e7e9d5643 Line columns up again 2022-01-16 20:08:01 -06:00
Jeff Epler
fafadc3333 Fix a markup typo 2022-01-16 20:07:08 -06:00
Limor "Ladyada" Fried
752c92bb21 Merge pull request #1 from adafruit/floppzy
PR check!
2022-01-16 13:38:23 -05:00
lady ada
45d7b284e3 1) update CI to add zip on commit for quick testing
2) fix gw readflux command (should be 8 bytes long but was packed as 10)
3) add support for non-gw VID/PID (generic serial port that is gw compatible)
4) manually set/clear DTR on serial port devices - this seems to be essential for tinyusb/arduino CDC

not tested with a gw but ought not to have broken anything!
2022-01-16 12:43:47 -05:00
Paul Devine
085fca7e31 adding Victor9k double sided src format 2022-01-10 15:34:37 -08:00
Paul Devine
8744114790 adding support for double-sided Victor9k disks 2022-01-10 15:33:29 -08:00
Paul Devine
c658a764d0 correct media size in README 2022-01-10 15:06:29 -08:00
David Given
6a07ba850c Merge pull request #409 from davidgiven/hcs
Support HCS as well as CHS format in the image reader/writer.
2022-01-11 00:03:04 +01:00
David Given
c4e29e74b2 Support HCS as well as CHS format in the image reader/writer. 2022-01-10 23:49:25 +01:00
David Given
40bca8b38c Merge pull request #370 from tdaede/rescale 2022-01-10 23:02:18 +01:00
David Given
18af881fe5 Attach the underlying raw records to Sector structures; add a .raw exporter for
getting the MFM/FM/GCR stream.
2022-01-04 23:49:39 +01:00
David Given
dfd97d9fc5 Merge pull request #359 from tdaede/compdb
Add Make target to build compdb.
2022-01-03 00:15:18 +01:00
David Given
ebd2c329ff Merge pull request #397 from hharte/master
reader: Fix conflicting sector error message.
2022-01-02 17:38:11 +01:00
David Given
265b169d43 Merge from master. 2022-01-02 15:07:38 +01:00
David Given
d154b1464f Merge pull request #404 from davidgiven/shugart
Add support for selecting the Shugart or IBMPC GreaseWeazle bus types.
2022-01-01 23:05:26 +01:00
David Given
a32ea6e5f8 Add support for selecting the Shugart or IBMPC GreaseWeazle bus types. 2022-01-01 22:45:27 +01:00
David Given
d7b21bf07e Merge pull request #400 from davidgiven/update
Update firmware to version 16.
2021-12-31 21:41:05 +01:00
David Given
d120790da7 Update firmware to version 16. 2021-12-31 20:24:51 +00:00
Eric Anderson
bd854d29a4 Merge branch 'master' into micropolis-275 2021-12-29 12:39:07 -06:00
Eric Anderson
d06e3db90b Support .vgi for image writing
vgi was already added to imagereader, but was missing from imagewriter.
2021-12-29 12:23:04 -06:00
Howard M. Harte
5ada533b06 reader: Fix conflicting sector error message.
The conflicting sector error message incorrectly displays the track instead
of the sector in conflict.
2021-12-25 09:10:53 -08:00
David Given
6f5e648751 Merge pull request #394 from hharte/fixserial
Windows: Support serial ports > COM9
2021-12-17 23:06:41 +00:00
Howard M. Harte
9ee5b3eaf3 Windows: Support serial ports > COM9
Windows requires the use of the DOS UNC path name for COM ports
greater than COM9.  Prefix the port name with \\.\
2021-12-17 14:21:19 -08:00
David Given
0560da246b Add wxwidgets to the build dependencies. 2021-12-17 20:24:50 +00:00
David Given
8b6b3ee3b8 Fix wxwidgets building on Windows. We now have it working on all three
platforms.
2021-12-17 20:10:11 +00:00
David Given
45fe83951a Merge pull request #392 from davidgiven/warnings
Fix some minor build glitches and warnings.
2021-12-16 13:54:26 +00:00
David Given
57e5d8911d Fix missing build dependency causing sporadic build failures. 2021-12-16 14:27:45 +01:00
David Given
5768a766b8 Silence stray warnings. 2021-12-16 14:27:18 +01:00
David Given
051e9e38f3 Add a boilerplate wxwidgets example. 2021-12-15 22:55:57 +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
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
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
3c3e520594 Merge pull request #378 from tdaede/ibm_n88basic
Add ibm_n88basic format.
2021-12-08 19:56:35 +00:00
Thomas Daede
ce1daf6a2b Add option to rescale flux sink pulses.
Note that this does not affect the decoder used for verify,
but at least for tame rescaling the decoders are still able to
lock.

This is a precursor to automatic scaling via the disk speed
measurement.
2021-12-07 14:45:32 -08: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
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
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
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
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
Thomas Daede
08c1671f21 Add Make target to build compdb. 2021-12-03 02:13:01 -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
Eric Anderson
3b95e56418 Support Micropolis variations and prefer VGI extension 2021-11-07 21:54:00 -08: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
ce5fcaf172 Micropolis raw 275 byte sectors
These are used by formats like VGI since the ECC varied per machine and
the 10 extra bytes of user data may contain useful information.
2021-10-02 22:59:14 -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
689 changed files with 120497 additions and 12595 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

42
.clang-format Normal file
View File

@@ -0,0 +1,42 @@
---
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignArrayOfStructures: Left
AlignEscapedNewlines: Left
AllowAllArgumentsOnNextLine: 'true'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'true'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'false'
BinPackParameters: 'false'
BreakConstructorInitializers: 'AfterColon'
BreakBeforeBraces: Allman
BreakInheritanceList: AfterColon
BreakStringLiterals: 'true'
IndentCaseLabels: 'true'
IndentWidth: '4'
ColumnLimit: '80'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
IncludeBlocks: Preserve
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'true'
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'false'
SortUsingDeclarations: 'true'
SpaceAfterTemplateKeyword: 'true'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeCtorInitializerColon: 'false'
SpaceBeforeInheritanceColon: 'true'
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: 'false'
...

View File

@@ -10,32 +10,56 @@ jobs:
with:
fetch-depth: 1
- name: apt
run: sudo apt update && 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 libwxgtk3.0-gtk3-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 wxwidgets
- 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
mingw-w64-i686-wxWidgets
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: build
run: |
make
- name: zip
run: |
zip -9 fluxengine.zip fluxengine.exe fluxengine-debug.exe fluxengine-gui.exe fluxengine-gui-debug.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.repository.name }}.${{ github.sha }}
path: fluxengine.zip

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

@@ -0,0 +1,65 @@
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
mingw-w64-i686-wxWidgets
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: build
run: |
make
- name: zip
run: |
zip -9 fluxengine.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.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 }}

7
.gitignore vendored
View File

@@ -1,2 +1,9 @@
.obj
.project
/.ninja*
/brother120tool
/brother240tool
/fluxengine
/brother120tool-*
/brother240tool-*
/fluxengine-*

View File

@@ -1,249 +1,249 @@
:400000000080002011000000A10F0000A10F0000064A08B5136843F020031360044B1A6803F53F5302331A6000F0D8FFE8460040FA46004010B5054C237833B9044B13B1EF
:400040000448AFF300800123237010BD6881FF1F0000000020380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF00000000E9
:400080006C81FF1F20380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F64
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080024B012200205A7293
:4001000002F012B98881FF1F10B5C4B2204601F027F90128FAD110BD08B572B60F4B0F49DA680132DA601A690132C82A08BF00221A615A6918690132A72A08BF00224A613A
:400140005B69002B0CBF02230023002814BF184643F0010002F050FE62B608BD8881FF1F38B50446C5B2284602F080F8062002F09DFA44F00200C0B202F078F8062002F055
:4001800095FA284602F072F8BDE83840062002F077BA10B5642402F063F830B90120FFF7DFFF013CF7D1204610BD012010BD70B5C4B2054620460E4601F0D2F8012805D089
:4001C000204601F0EBF92846FFF79EFF204601F0CFF8314605460246204601F08BF9204601F0BEF80028FAD1284670BD38B5044D0024285D013402F009FA402CF9D138BDE9
:40020000A081FF1F08B502F023FC002002F02CFC02F03EFC02F048FC80B208BD10B50446012002F03BF8642002F02AFAFFF7EAFF2080002002F032F8642002F021FAFFF7ED
:40024000E1FF608010BD08B502F02EFD002002F037FD02F049FD02F053FD80B208BD10B50446FFF796FF322002F00AFAFFF7EBFF20800020FFF774FF322002F001FAFFF740
:40028000E2FF608010BD0FB400B593B014AB53F8042B402102A8019302F0AAFE02A802F04AF802F054F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF72B
:4002C00023FF62782146BDE81040042001F0A0B83438000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8583043B10020E3
:4003000001F0A8FF002002F03BFD002384F8583010BD00BF8881FF1F38B5104D837895F85B2004469A4204D0FFF7E4FF002385F85E302368C5F859302279094B1A71A3784D
:40034000002B14BF0220012002F01AFDE07802F011FD2079BDE8384002F048BD8881FF1FE181FF1F38B50D4C94F8585065B904F15900FFF7D1FF012001F06CFF4FF47A7031
:4003800002F07EF984F85E50E3682366012384F85830BDE8384002F0B1B900BF8881FF1FF8B51E4C0646FFF7DDFF94F85E3003B15EB91B48FFF767FFFFF7EBFE0120002353
:4003C00084F85E00636602F071F93246616E1548FFF759FF114D0027636E9E4216D001F03FFF00B16F66636E9E4205DD0020FFF7B7FE6B6E013305E005DA0120FFF7B0FE47
:400400006B6E013B6B6602F079F9E5E7322002F037F9BDE8F8400448FFF735BF8881FF1F4138000048380000653800002DE9F04F99B062B602F0CCF99949042002F0F0F914
:40044000984801F019FF984802F0BCFC974801F04DFF02F09DFB02F06FFA002002F090FC01F068FF0221002000F030FF904C012001F0A8F8002384F85B30FFF773FFFFF79A
:4004800088FE84F86800FFF735FF012384F85B30FFF768FFFFF77DFE84F86900FFF72AFF844B94F86800844994F869202546002A14BF0A461A46002808BF19467F48FFF7E3
:4004C000E2FE0321084602F0F9F8264602F016F994F8583043B12A6EEB689B1A41F28832934201D9FFF706FF00F028FF18B97448FFF7C9FE04E000F027FF0028F7D10BE0E5
:4005000000F01CFF10B902F0F9F8F9E76D48FFF7BAFE032001F042F8032000F021FF0128D4D16948FFF7F8FE68490320FFF73FFE94F86A106648FFF7A6FE94F86A30023B1D
:40054000142B00F2AB83DFE813F01500A9031E00A9032400A9034600A9036C00A903CF00A903AA01A903E202A9030103A9030803A903220303238DF820308DF821300E23FD
:400580008DF82230FFE294F86C00FFF709FF514BF6E2FFF7E7FE00236372E068627A02F0FF0132B9EB681B1AB3F57A7FF6DD0B4608E03BB100227272F168627A12B9EB68CB
:4005C0005B1AFAE707228DF8202004228DF82120ADF82230D7E20220FFF796FD4FF000080DF1200A02F08AF84FF480790027C9EB0803DA1907F80A200137402FF9D10220BC
:40060000FFF782FD3A465146022000F001FFB9F10109EBD108F10108B8F1400FE2D12E4B38E04FF0010A4FF000080DF1200B02F065F84FF0000959460120FFF7B8FD08EBBF
:40064000090300270493049B1BF807203B44DBB2934209D08DE80C0041463B464A461F48FFF711FE4FF0000A0137402FEBD109F10109B9F5807FDED108F10108B8F1400F92
:40068000D5D151461648FFF7FEFDBAF1000F00F00E81144B1B8807A8ADF81C3074E200BF19010000F900000091000000C50000008881FF1F77380000733800007A38000022
:4006C00092380000A5380000E181FF1FF281FF1FAF3800002438000026380000BE380000DA3800002838000094F86C0001F0D6FD606EFFF755FE02F0E7FBB24BDFF8E0825C
:400700001A78002702F0FB021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0D5FB0220FFF7EEFC012141F6FF734FF48042084602F028FB84F8AA00FD
:4007400001F04AFF08F807000137402FF8D1DFF894A200270AF199091FFA89F80137402F14BF3A4600221AF8010F22440623127E402101F065FF424646F24A519AF800005D
:4007800001F070FF08F14008402F1FFA88F8E5D196F86D3033B100237372637A002BFCD00023737200234FF0FF32236062602372236894F8AA002344197E01F0C5FE94F87B
:4007C000AA0001F083FE012194F8AA0001F056FE2368002BFCD0002398467360D6F80CA0012701F08BFFE368B4F86E20CAEB030393420DD367B1042195F8AA0001F0B0FEC5
:4008000094F8AA0001F0BCFE0028F9D107463072237AFBB96A682B689A4202D1002FE0D118E00220FFF770FC6968402209EB8111022000F0EDFD6A68634B01321340002B4F
:40084000BEBF03F1FF3363F03F03013308F101086360C6E70220277AFFF756FC00221146022000F0D5FDFFB20220FFF74DFCFFF7BDFC37B15548FFF706FD0220FFF72AFDB4
:4008800006E0534B08A81B88ADF82030FFF710FD627A4146237A4F48FFF7F5FC01E24E48FFF7F1FCD4F86E7017F03F0701D00320F5E1012001F0F8FC95F86C0001F0EEFCF4
:4008C00002F002FB454BDFF818811A7842F004021A701A7842F001021A701A7802F0FE021A701A7802F0FE021A7002F0F1FA686EFFF756FD01214FF4804341F6FF7208462E
:4009000001F0D8FC85F8AA0001F066FE08F807000137402FF8D1DFF8CC90002709F199031FFA83F804930137402F14BF3A46002219F8010F22440523127E402101F080FEB9
:40094000414646F24C4299F8000001F08BFE08F14008402F1FFA88F8E5D100274FF0FF33376098467360BB463B46D6F86E9037725FEA99190CBF4FF0010A4FF0000A216867
:40098000114A01310A40002ABCBF02F1FF3262F03F026068B8BF013282426BD02BB1227A002A76D16A7A002A73D12068049A059302EB8010BAF1000F16D040223F2102F0C9
:4009C000E3FA1CE09A6500403F000080E43800002A380000FE380000113900009C640040A081FF1F9F81FF1F014601370120FFF7DEFBC7EB0903D3F1000A4AEB030A21689F
:400A0000AB4A01310A40002ABEBF02F1FF3262F03F02013222606268059B01322AD12A683F2A27D14FF00008C5F8048001F070FC85F808806B6895F8AA002B44197E01F0BC
:400A400083FD95F8AA0001F041FD012195F8AA0001F014FD85F80980637A002BFCD04FF00008012086F8098001F05EFC404601F01BFC00E023B1237A5BB96B7A4BB90123A1
:400A8000626842453FF47BAF0BF1010BD5F8048075E701F043FC012001F006FC002001F043FC042194F8AA0001F05AFD94F8AA0001F066FD0028F9D196F8AA0001F0F4FC07
:400AC000737A327A0293012303920193CDF800A05B463A4649467748FFF7D5FBBAF1000F08D0FFF783FB237A63B17348FFF7CBFB0220D4E0B945F4D070490120FFF757FBF9
:400B00000137F7E76E48FFF7BEFB6E4B38E094F86C0001F0C3FB606EFFF742FC6A48FFF7B2FB00236372637A002BFCD0012001F0FBFB00237372637A002BFCD0002001F0AA
:400B4000F3FB6248FFF79FFB614B19E0002084F85E00FFF725FC5F4B12E094F8683023B195F869200AB985F86C2094F869201AB113B9012385F86C305748FFF7CDFB574B39
:400B80001B88ADF8203008A8FFF792FB89E0FFF7B1FB02F07BF8002002F01EF82A2701F049FF002001F0ECFE3A46002108A802F0EBF917238DF820308DF8217001F09EFD75
:400BC000002001F047FB002002F0DAF8C82001F057FD0DF12200FFF721FB0DF13600FFF73EFB01F08BFD012002F0CAF8322001F047FD0DF12600FFF711FB0DF13A00FFF7D3
:400C00002EFB012001F026FB4FF4967001F038FD01F074FD0DF12E00FFF700FB0DF14200FFF71DFB002001F015FB4FF4967001F027FD01F063FD022002F0A2F8322001F05B
:400C40001FFD0DEB0700FFF7E9FA0DF13E00FFF706FB012001F0FEFA4FF4967001F010FD01F04CFD0DF13200FFF7D8FA0DF14600FFF7F5FA002001F0EDFA4FF4967001F06F
:400C8000FFFC01F03BFD002002F07AF8002384F85E3001F07DFF01F04FFE74E70120FFF719FB032000F07AFC0D48FFF7ECFA0DE43F0000801B3900004B3900003892FF1F04
:400CC000553900002C3800005D3900006B3900002E38000030380000F281FF1F32380000783900002DE9F04172B6884B61221A70A3F5F06301221A801924854A9C7092E8C5
:400D000003008033062283F8002283E80300522203F580731A707F4B7F4A1B787F4EDBB2137040F618027E4B00251A8041F2512223F8022C33784FF4F07003F0010343EAA5
:400D4000450502F0B9F8013C05F003052ED0032DF0D1744B4FF480721A8007221A70724A002548211570917002221D705D7103F8032C0422DA716D4A6D4C13786D4E43F049
:400D80000103137012F8013C062743F0030302F8013C2378012243F0800323705B4B1A70654A137843F02003137000E0FEE707FB056300219A881868013502F0E5F8072D53
:400DC000F5D15E485E4E002550F8041F05F1105303F1440221F0FF074533C9B20B4452005B0002329A4206D012F802EC12F801CC0EF807C0F5E7B0420D44E5D1514A00239D
:400E000013609360136193614F4B504F1A68504BDFF888811A604F4B1A684F4B1A604F4A137843F002031370137C43F0020313742378A2F5863243F040032370413A1378DE
:400E400043F010031370464A464B07CA03C31A80454A2833106843F8250C127903F8212C424A07CA03C31A80414AE83B07CA03C31A80404A083307CA03C31A803E4A3F4B12
:400E8000A2F5616203CBC2F8100EC2F8141E1378042043F008031370394B02F5AA521B783D78DBB298F80060EDB203F007010C321B091170F6B2537045F003033B7046F096
:400EC000030388F800302F4B48221A702E4A402313702E49937013729372082382F81F3220220A7048710A72294A0A20137001F0DDFB284B88F8006044223D70264D1A7039
:400F000094E80F0007C52B80BDE8F08100480040B00900480F010049A146004025420040224200400440004006400040A2430040A04300407D390000E8460040FCFFFF478E
:400F40008400004800760040B8090048F846004020760040BC090048287600400350014070090048C05100407C09004884090048900900489C09004832510040A8090048D5
:400F8000CF0100491D51004001590040235B0040585B004076580040B0430040F946004008B501F0C5FF03680C2B00D1FEE7FEE7084908B50B68084A1844821A802A01DC5E
:400FC000086005E001F0B4FF0C2303604FF0FF33184608BDCC80FF1F8893FF1F80B51148114B0025C0B1A3F1100192C922460439161BB74204D051F8046F42F8046BF7E7D1
:40100000114653F8046C8C1AA64202D041F8045BF9E701381033E5E701F090FFFFF706FAFEE700BF010000004C3B0000124A134B10B51A60124A134C1368134843F40073A8
:4010400013600023032B98BF54F823204FEA830188BF0E4A0133302B4250F3D10C4B1A780C4B1A700C4B084A1A60FFF73BFEBDE8104001F0EDB900BF0004FA050CED00E042
:4010800014ED00E0000000000080FF1FA10F0000BC760040C080FF1F08ED00E0F8B501F013FF4B4A01271378022643F001031370137C484C43F001031374474B02F5E352E3
:4010C0001F700B3203F8946C1378054603F07F031370002001F0EAFA2378404A03F0F90323701378384603F0DF03137023783B43237001F0DBFA282001F0D8FA384B3046E8
:401100001A7802F07F021A701A7802F0BF021A7023783343237001F0C9FA2378314A43F0040323700023137053702F4AFF2199540133092BFBD1284601F0CAFE072117206D
:4011400001F0FCFA2949172001F0EAFA0721182001F0F4FA2649182001F0E2FA0721152001F0ECFA2349152001F0DAFA0721052001F0E4FA2049052001F0D2FA0721062008
:4011800001F0DCFA1D49062001F0CAFA0721084601F0D4FA1A49072001F0C2FA0721082001F0CCFA1749082001F0BAFA0021162001F0C4FA1449162001F0B2FA07210C20FD
:4011C00001F0BCFABDE8F84010490C2001F0A8BAA5430040944300409D60004012600040F851004084600040AD92FF1F6B1A0000A5180000691A00009D190000C9190000FE
:40120000F9190000311A0000711A0000E51A0000214B224A10B5187000231370204A40201370204A0F2413701F4A13701F4A13701F4A13701F4A13701F4B4FF400021A60B6
:401240004FF080721A604FF400121A6020221A601860802018604FF480701860174804704FF480001860164B1A70933B19B91A7802F0FE0202E01A7842F001021A70114B51
:4012800003221A70802203F8202C012001F014FE0D4B04221A7010BDC892FF1FCE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F00E100E09E60004062
:4012C0009C600040286000401260004070B5074C054623780E461BB9FFF7E0FE0123237031462846BDE87040FFF792BF7892FF1F0A4A002313700A4A13700A4A13700A4A82
:4013000013700A4A13700A4A13700A4A13700A4B03221A70802203F8202C7047CE92FF1FCC92FF1FCD92FF1FC992FF1FB892FF1FCB92FF1F4093FF1F28600040014B187899
:40134000704700BFCD92FF1F044B1A7802F0FF001AB118780022C0B21A707047CC92FF1F024A0C2303FB002040787047D492FF1F431E072B0CD8074A064B00010344805C33
:401380005B7800F00F0043EA0020023880B2704700207047FC5F00401A4A38B50C2303FB00231B79090C13F0800F00F1FF35044619BF8AB24FF480438BB24FF48042032DA2
:4013C00018D8DFE805F002070C110021084601F01BF80DE00021084600F0FAFF08E00021084600F0D9FF03E00021084600F0B8FF054B1855EDB2072D03D801F0EDF8034BBC
:40140000185538BDD492FF1FA492FF1FAD92FF1F431E072B2DE9F0470446894615465CD82F4F0C2202FB0072D388DFF8B8A09BB2C3F500739D424FF00C0303FB007388BF8B
:40144000D588DB7884BFC5F50075ADB2254A43EA15230601B354B244EBB28AF80130224B1A5C9846FF2A01D1FFF796FF0C2303FB047200215170B9F1000F28D03DB31B4FEC
:40148000385D01F011F811232946FE2218F8040001F0D6F806F5C04278321FFA89F118F8040001F0DFF8124D18F80410385D01F04BF80121385D00F0E1FF735D43F0020316
:4014C0007355735D03F0FD037355BDE8F08703FB04746379DBB28AF80230BDE8F08700BFD492FF1FFC5F0040AD92FF1FA492FF1F706000402DE9F047044615468846002946
:4015000040D0431E072B3FD8FFF732FFA84203D22046FFF72DFF05461D4E335DFF2B03D141462046FFF738FFDFF868A027011AF8040000F0B9FF1223FE222946305D01F01E
:401540007FF807F5C0411FFA88F27831305D01F089F8DFF84490315D1AF8040000F0F4FF01211AF8040000F089FF17F8093043F0020307F8093017F8093003F0FD0307F8E8
:40158000093002E00D4600E000252846BDE8F087AD92FF1FA492FF1F70600040431E072B0AD8064A0C2303FB002300225A705A79034BD2B200011A54704700BFD492FF1F5E
:4015C000FE5F0040431E072B9FBF024B000108221A547047FE5F004030B51A4A1A491B4D0878138803449BB21380194A00231488D8B2A4B27CB1082B0CD050680078C0B2ED
:40160000E85450680133013050601088013880B21080ECE718460B780E4C082B0E4A00D040B10E4D2B7883F080032B700F232370022301E0022323701370094B18700870CB
:4016400030BD00BF4493FF1F4093FF1F00600040BC92FF1FB992FF1FCE92FF1FCA92FF1F4193FF1F074B02221A70074B80221A70064B0F221A70064A00231370054A012089
:4016800013707047CE92FF1FCA92FF1FB992FF1F4093FF1F4193FF1F30B5164B16491B780A8803F00F03023BDBB21A4492B20A80124C134A0020118889B279B173B155682D
:4016C000215C013BC9B229705168DBB20131516011880130013989B21180ECE7094A1370094A137883F080031370084B0B221A7030BD00BF296000404493FF1F0060004010
:40170000BC92FF1F4193FF1FCA92FF1FB992FF1F064A06231370064A01201370054B80221A70054B00221A70704700BFCE92FF1FB992FF1FCA92FF1F4193FF1F054B9A68E5
:401740003AB19A68044910709A680988518000229A607047BC92FF1F4493FF1F08B5124B1A78D2B21A701B78DBB21A0602D50F4A137008BD0220FFF7E1FF0D4B1B7803F0CF
:401780006003202B05D0402B06D043B900F012FC04E001F0A1FB01E000F046FD10B9034B03221A7008BD00BF28600040B992FF1F0060004008B5084A084B0120197813881C
:4017C0000B449BB21380064B00221A70FFF7B6FF044B03221A7008BD4493FF1F4093FF1FCE92FF1FB992FF1F08B50C4B1B78DBB2042B07D0062B09D0022B0DD1BDE8084046
:40180000FFF7D8BFBDE80840FFF746BF0320FFF795FF034B03221A7008BD00BFCE92FF1FB992FF1F08B5054B002201201A70FFF785FF034B03221A7008BD00BFCE92FF1FCB
:40184000B992FF1F08B50A4B1A7832B11A78094942F080020A7000221A70074B002201201A70FFF76BFF054B03221A7008BD00BFB892FF1F08600040CE92FF1FB992FF1FC1
:40188000074B1B78DBB2042B05D0062B05D0022B05D1FFF7A1BEFFF7C5BFFFF7D3BF7047CE92FF1F38B51D4C2378DBB2DD0634D518060AD503F00F03012B2ED1FFF74EFF43
:4018C000174B1B78190609D538BD5A0602D5FFF7D7FF03E09D0620D5FFF786FF23781B061BD4104B1A78104B1B7813430F4A13701278934211D10A4A0849154613782078EC
:40190000DBB2000605D41378DBB20B700B7803F00F0328788342F1D138BD38BD28600040B992FF1FCA92FF1F4193FF1F29600040054A00231380054A916819B191680B701E
:4019400092685380704700BF4493FF1FBC92FF1F0E4808B503889BB213B9FFF783FE13E00B4B02221A700B4B00221A70FFF7E0FF094AD1799379028843EA012392B293422A
:4019800038BF0380FFF728FE012008BDBC92FF1FCE92FF1FCA92FF1F00600040084B01221A700F3B9B7C074B1A7B02F00302012A1EBFDA7B82F08002DA7301225A73704723
:4019C0000B600040D492FF1F094B02221A700F3B93F82230074B1A7E02F00302012A1EBFDA7E82F08002DA7601225A76704700BF0B600040D492FF1F0B4B04221A700F3B22
:401A000093F83230094B93F8242002F00302012A1EBF93F8272082F0800283F82720012283F82520704700BF0B600040D492FF1F0B4B08221A700F3B93F84230094B93F857
:401A4000302002F00302012A1EBF93F8332082F0800283F83320012283F83120704700BF0B600040D492FF1F7047FFF741BC0000F0B5184B184E19780C27C9B201234FF029
:401A8000000C31B3CA0720D5144A4FEA031E7244947850782040C5070DD507FB03652C79240608D5147804F0FE0414706D790C4CEDB204F80E50840706D507FB0364257960
:401AC0002D0658BF84F801C090700133DBB24908D7E7F0BD9F600040D492FF1F70600040FE5F004000F0ACBC70B50446184B88B003AA03F11006154618685968083303C5BA
:401B0000B3422A46F7D11B782B70FCB12223237001AD03232846637000F08AFE002220461146AB5C08AC04EB131414F8144C03F00F03847008AC234413F8143C0132082A48
:401B4000C1700371417100F10400EAD108B070BDA73900002DE9F0431C4D01222E460C201F274FF0800E4FF0080C194B00FB02581401234418705F70164998F80590214449
:401B8000B9F1000F07D098F8044024064CBF887081F802C001E081F802E000FB0261CC880132E4B29C71CC88092AC4F30724DC71CC88E4B21C71C988C1F307215971D4D1CC
:401BC000054BFF221A70BDE8F08300BFD492FF1F70600040FC5F00400A600040064B074A1B7802EBC30253681A7C824286BF03EBC003586900207047C892FF1F083A0000F6
:401C00002DE9F84F424B1A78002A7ED01878414D0138C0B2FFF7E2FFA8463F4AC3681478007ADFF800C1E4B203EBC0000C2600274FF0010E834268D01A78A24263D11CF82A
:401C40000420597891425ED19A7893F8039002F07F0206FB02FA05EB0A01CF7093F802B009F0030981F804B093F803B005F80AB0B3F804A0A1F808A093F902A0BAF1000FB7
:401C80000BDAB9F1010F0CBF4FF007094FF00D0981F8059081F801E009E0B9F1010F0CBF4FF005094FF0090981F805904F704FEA02191A4906FB0282494481F802E0B2F807
:401CC00008A0CAF3072A81F800A0B2F808A05FFA8AFA81F801A0B2F806A011495FFA8AFA494481F806A0B2F80690C9F3072981F80790B2F806905FFA89F981F80490D288FB
:401D0000C2F307224A71083394E7BDE8F88F00BFCD92FF1FD492FF1FC992FF1FFC5F004070600040BA92FF1F08B5064B18780138C0B2FFF753FF20B143681B7900EBC3008B
:401D4000406908BDCD92FF1F00212DE9F84F0B464E4E0C2707FB01F401313219092933554FF000059370494CD3701381937253705371EFD118B1464B1D70464B1D70464B17
:401D80001A78002A7FD0187801250138C0B2FFF725FFA8464368DFF8F8E0DB790C2713F0400F3E4B4FF0000C1A7814BF42F0010202F0FE021A70027AD20007FB0541C368D1
:401DC00003EB02094B4531D093F802A00AF07F06AE4229D10E89B3F804B0B6B25E4538BFA1F808B01E7893F801B01EF80660B3451AD181F804A0DE780E7093F902A0DE78D4
:401E0000BAF1000F06F0030607DA012E0CBF07260D264E7181F8018006E0012E0CBF052609264E7181F801C00833CBE70135092DC3D1C1680A328B1C0A440C20083393423F
:401E400009D013F8081C13F80A5C01F07F0100FB01418D72F2E7FFF767FF114B0121186000230C2000FB0142D3801289013113449BB203F00102134409299BB2F2D1BDE88C
:401E8000F84FFFF767BEBDE8F88F00BFD492FF1FBA92FF1F4293FF1FCD92FF1FCB92FF1FD092FF1F114B1B7903F07F035A1E072A19D80F490C2202FB031291781B0141F08F
:401EC000010191700021D170517841F002015170127912F0800F074A1A4414BF8D2389239370FFF715BC0020704700BF00600040D492FF1FFC5F004030B4194B1A7902F0D9
:401F00007F02531E072B27D8164B0C2404FB02339978154D01F0FE0199700021D97029461201505D114400F07F0050555A7802F0FD025A701A795B78120605D5012B01D168
:401F40008C7006E00D2303E0012B0CBF082309238B7030BCFFF7DCBB002030BC704700BF00600040D492FF1FFC5F004010B50D4B0D4C21791878C9B20138C0B2FFF72EFE81
:401F800043681B798B4201D2012909D8074A0848535CDBB24354A3780120DBB2535410BD002010BDCD92FF1F00600040BA92FF1F4293FF1F38B58A4A8A4C13780021DBB24F
:401FC00021801806517840F18D800A2900F20581DFE811F05D00030103010301030103010B0003017E0003018200D3787C49012B09D17D4B1A787D4B03EBC2035B685B68B3
:402000006360122310E0CB78022B12D18878FFF7E5FD002800F0E180436863606368DA7863689B7843EA02232380BDE83840FFF78FBCCB78032B26D16D4B00228878D5B28F
:40204000854209D3664A91786A4AEE2908BF1346634A917881B106E0187801320028F1D018780344EAE764499278097C914203D16248FFF739FD614B1A78002A00F0AD80B9
:402080001A78228018E0BDE8384000F025BF13F0030313D0022B40F0A0802380504B0C211B7903F07F02564B01FB02339A78554BD2B21A7000225A706360B6E70222228083
:4020C000514A11784F4AC9B2117053706260ACE7012323804D4BEFE70123238013794C4A1344E9E701390A2977D8DFE801F037764F76067676760A7620009378454ADBB2B5
:402100005AE0937803F0FF0153B9404B1A7891425FD01970404B01201870FFF715FE58E0481EC0B2FFF75AFD0028EED155E0FFF71DFF002851D02A4A384913791279DBB209
:40214000D2B20A70364A3249D25CCB5C9A4240D0314B01221A70FFF753FD3AE003F00303012B2BD009D3022B37D11D4B9B78002B33D1BDE83840FFF7BFBE194B9B78012B8E
:402180002BD1214A137803F0FD0315E003F00303012B13D008D3022B1FD1114B9B78E3B9BDE83840FFF77EBE0D4B9B78012B14D1154A137843F0020313700AE0084B1A79FA
:4021C0005AB998781B791749DBB2CA5C22EA0002CA54BDE83840FFF79BBA002038BD00BF00600040BC92FF1FC892FF1F083A00006C3A0000F4390000DF3A00006093FF1FEA
:40220000D492FF1F7992FF1FCB92FF1FCD92FF1FBA92FF1FB892FF1FCC92FF1FC992FF1F4293FF1FCF92FF1F074B1A78120609D55B78012B06D1054B054A5A6012781A8093
:40224000FFF786BB0020704700600040BC92FF1FCC390000014B1870704700BF78640040014B1878704700BF6B650040014B1870704700BF79640040064A0123136002F631
:4022800088321268E0211064034A1170A2F540721360704780E100E000E400E0014B1870704700BF79650040014B1870704700BF7D64004073B515461E460B4C05230022D4
:4022C000019200920A4601461846237000F064F932462946207800F01FF90221207800F009F9207802B070BDD080FF1F064A0423136002F688321268E0219064034A1170F2
:40230000A2F202321360704780E100E002E400E0014B04221A60704700E100E0014B04221A60704780E100E0014B1870704700BF7B650040704738B505460078012428B18D
:4023400000F062FD285D0134E4B2F8E738BD08B50D2000F059FDBDE808400A2000F054BDF7B516461F460B4C00230325019300930A4601462846257000F00EF93A463146E4
:40238000207800F0C9F80221207800F0B3F8207803B0F0BDE080FF1FF7B516461F460B4C00230225019300930A4601462846257000F0F2F83A463146207800F0ADF82946CC
:4023C000207800F097F8207803B0F0BDE180FF1FF7B516461F460B4C00230125019300930A4601462846257000F0D6F83A463146207800F091F80221207800F07BF8207805
:4024000003B0F0BDE280FF1F73B515461E460B4C0023019300930A4601461846237000F0BBF832462946207800F076F80221207800F060F8207802B070BD00BFE380FF1F72
:40244000024B1878C0F38010704700BF8F450040074A7F23802113705170064A013BDBB202F80839002BF9D1034A1370704700BFE480FF1FF87B00400078004017280FD838
:40248000084B0001C25C11B142F0200201E002F0DF02C254C25C42F00102C25400207047012070471070004017280BD8064B0001C25C02F0FE02C254C25C02F0DF02C25451
:4024C00000207047012070471070004017280DD8074900010B4603441A7942F004021A71435C43F00103435400207047012070471070004017280BD8064A0001835C490093
:4025000003F0F10301F00E011943815400207047012070471070004041F6FF73994208BF4FF400519A4208BF4FF4005217289FBFC00000F1804000F5EC4081809ABFC28032
:40254000002001207047000017289FBF034B00011954002088BF0120704700BF1970004017289FBF054B00011A5C01F007019DBF1143195400200120704700BF147000404E
:4025800017289FBF034B0001185C00F0070088BFFF20704714700040172810B51AD8C00001F07F0100F1804441EAC21204F5EC44D2B222709DF8082003F00F0343EA0213C5
:4025C000DBB263709DF80C30002003F00F03A370E07010BD012010BD10B500F075FC0A4A5378182B0AD91478013B5370E30003F1804303F5F0431B78137000E0FF2400F0A3
:4026000067FC204610BD00BFE480FF1F030610B5044611D400F058FC084AE300117803F1804303F5F04319705378147001335370BDE8104000F04CBC10BD00BFE480FF1F18
:4026400030B504060CD411F4704509D1C40004F1804404F5F0442180A270E370284630BD012030BD03065FBFC00000F1804000F5F04081805ABFC2800020012070470000CD
:4026800038B50446084DB4F5004F05D9286800F013FCA4F50044F6E7034B58686043BDE8384000F009BC00BFEC80FF1F024B1B7A584300F001BC00BFEC80FF1F0E4B00F0E7
:4026C00003001A78490102F0FC02104318701A7801F0600142F080021A701A7802F07F021A701A7802F09F020A431A701A7842F010021A70704700BF83430040014B012238
:402700001A70704784430040044B00F00F021B6853F8220043F82210704700BF08ED00E0054A00F01F00126800F1100352F8230042F82310704700BF08ED00E000F01F0049
:4027400000F16040490100F56440C9B2017070470F4B10B50F4900240F205C609C60DC601C615C61FFF7D0FF0B4A136843F0040313600A4B4FF47A72DB68B3FBF2F3084A5C
:402780001360084B4FF400421C60C3F8E82010BD7C92FF1FFD27000010E000E0EC80FF1F14E000E018E000E0024A136843F002031360704710E000E008B5FFF7F5FF034A7F
:4027C000136843F00103136008BD00BF10E000E010B5054CA3691BB9FFF7BAFF0123A361BDE81040FFF7E8BF7C92FF1F024B1868C0F30040704700BF10E000E038B5FFF7EC
:40280000F5FF012808D1054D002455F8243003B198470134052CF8D138BD00BF8092FF1F024B03EB80035868596070477C92FF1F134B144A1B78DBB20360127843EA0223B0
:40284000114A0360127843EA0243104A0360127843EA026303600E4B0E4A1B78DBB24360127843EA02230C4A4360127843EA02430A4A4360127843EA02634360704700BFF1
:402880000301004904010049EC460040020100490101004900010049050100490601004910B500F011FB204A044613780A2043F002031370137C43F00203137412F80A3C08
:4028C00043F0010302F80A3C937943F00103937102F5AB52137843F003031370134B18221A7013F8012C42F0400203F8012C13F8012C02F0FC0203F8012CCE2203F8062C7C
:40290000A3F597530222183B1A70094A137843F008031370FFF7CAFE064B10222046BDE810401A6000F0D4BAAB4300400E5900402F5B004080E200E008B500F0C5FA0F4A3B
:40294000137803F0FE031370A2F5AA521D3A137803F0FD031370137C03F0FD03137412F80A3C03F0FE0302F80A3C937903F0FE039371BDE8084000F0ABBA00BF0859004037
:40298000044A137803F03F0343EA8010C0B21070704700BF08590040082804D00A280CBF8223C22300E0422308380E4AC0B20428137098BF0C4B4FF0000298BF33F9101028
:4029C0000A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470A590040B83900004A93FF1F4C93FF1F5093FF1F08B5102000F0A6F974
:402A000007210420FFF79AFE07490420FFF788FE064A0C20137843F006031370FFF7BCFF034B00221A8008BDF12A0000095900404893FF1F10B5054C23781BB9FFF7DCFF7A
:402A400001232370BDE81040FFF72ABF9892FF1F044B1A7802F0FB021A701A7842F001021A7070470859004010B5084B1C7814F0010403D10028F9D0002404E02046FFF7A2
:402A800015FE024B1B78204610BD00BF09590040034A044B1B881088181A00B2704700BF5093FF1FA25B00400E4A13881BB223B111880A2309B2594301E00B4B19680B4B61
:402AC0001B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270474A93FF1F4C93FF1F4893FF1F7047000010B500F0E7F9214A0446137861
:402B00000A2043F001031370137C43F00103137412F80A3C43F0020302F80A3C937943F00203937102F5AA521832137843F003031370144B18221A7013F8012C42F0400201
:402B400003F8012C13F8012C02F0FC0203F8012CCE2203F8062CA3F597530222123B1A70094A137843F008031370FFF79FFD074B08222046BDE810401A6000F0A9B900BFB0
:402B8000AB43004006590040275B004080E200E008B500F099F90F4A137803F0FE031370A2F5AA52153A137803F0FE031370137C03F0FE03137412F80A3C03F0FD0302F87F
:402BC0000A3C937903F0FD039371BDE8084000F07FB900BF00590040044A137803F03F0343EA8010C0B21070704700BF00590040082804D00A280CBF8223C22300E0422383
:402C000008380E4AC0B20428137098BF0C4B4FF0000298BF33F910100A4B88BF11461A8042F210734B4341F2883103F6C41393FBF1F305490B60054B1A8070470259004054
:402C4000C23900005693FF1F5C93FF1F5493FF1F08B5102000F084F807210320FFF76EFD07490320FFF75CFD064A0C20137843F006031370FFF7BCFF034B00221A8008BD33
:402C8000492D0000015900405893FF1F10B5054C23781BB9FFF7DCFF01232370BDE81040FFF728BF9992FF1F044B1A7802F0FB021A701A7842F001021A70704700590040D8
:402CC00010B5084B1C7814F0010403D10028F9D0002404E02046FFF7E9FC024B1B78204610BD00BF01590040034A044B1B881088181A00B2704700BF5493FF1FA05B004034
:402D00000E4A13881BB223B111880A2309B2594301E00B4B19680B4B1B88C01A42F2107300B203FB00F2022391FBF3F30028D8BF5B42134493FBF1F000B270475693FF1FD5
:402D40005C93FF1F5893FF1F70470000034A00F0F800137803431370704700BF02410040034A00F0F800137803431370704700BF06410040014B1870704700BF7A65004015
:402D8000014B1870704700BF7E64004073B515461E460B4C04230022019200920A46014618462370FFF7F8FB324629462078FFF7B3FB02212078FFF79DFB207802B070BDA7
:402DC000FC80FF1F074A0223136002F688321268E0215064044A11706FF440710A441360704700BF80E100E001E400E0014B1870704700BF7A640040014B1870704700BF9D
:402E00007B64004000000000FEB5494652465B460EB40746244909688A46244A12682448022100F071F8030020480068C018204900F06AF8143883460121C9430C4601254A
:402E4000002600F041F8814651460B7823400B705846013000F030F83800F04028400B78234003430B70584600F026F80136072EF2D9002001300138013001200B78234041
:402E800003430B705846043000F016F8484600F01FF800BF00BF00BF0EBC894692469B46FEBD00BFAFF30080D480FF1FF880FF1F00C20100000000000230800803D000BFAA
:402EC00001380046FCD17047EFF3108072B6704780F31088704700BF094A137803F00303012B0AD0022B09D113790C2103F07F02044B01FB02339B7A00E013790020704714
:402F000000600040D492FF1F002902D0B0FBF1F0704708B14FF0FF3000F008B80029F8D00246B0FBF1F000FB11217047704700BF014B1868704700BF6081FF1F0E4B70B57A
:402F40001E460E4C0025E41AA410A54204D056F8253098470135F8E700F0DEFD084B094C1E46E41AA4100025A54204D056F8253098470135F8E770BD243B0000243B00007F
:402F8000243B00002C3B000003460244934202D003F8011BFAE7704730B5141E05469BB0184604DA8B232B604FF0FF301DE04FF40273ADF80C300CBF234604F1FF33029350
:402FC00005934FF6FF7300910491ADF80E3002461E9B6946284600F073F8431CBCBF8B232B6014B1009B00221A701BB030BD000007B5009313460A46014603480068FFF741
:40300000CBFF03B05DF804FB6081FF1F2DE9F0478E6882469E420C46914698463ED88A8912F4906F3AD02568096902236F1A656905EB450595FBF3F57B1C43449D4238BF75
:403040001D4653050FD5294600F04AFB064698B13A46216900F0D2FAA38923F4906343F08003A38113E02A4600F098FB064670B92169504600F0E8FA0C23CAF80030A38908
:403080004FF0FF3043F04003A381BDE8F08726613E44266046466561ED1BA560464528BF464649463246206800F0B3FAA36800209B1BA36023681E442660BDE8F087000061
:4030C0002DE9F04F9DB003938B8980461C060D4616460DD50B695BB9402100F001FB2860286118B90C23C8F80030CDE040236B610023099320238DF82930DFF89CB1302302
:403100008DF82A3037463C4614F8013B1BB9B7EB060910D003E0252BF9D02746F3E74B46324629464046FFF771FF013000F0A780099B4B4409933B78002B00F0A080002335
:403140004FF0FF3204930793059206938DF853301A930126052221784E4800F041FA671C049B38B14B4A3C46801A06FA00F018430490EFE7D90644BF20228DF853201A0773
:4031800044BF2B228DF8532022782A2A03D0079A00210A200BE0039A111D12680391002A10DA524243F00200079204900BE027463B780134303B092B03D800FB0232012141
:4031C000F5E701B107923B782E2B1ED17B782A2B0AD1039B02371A1D1B680392002BB8BF4FF0FF33059310E0002319460593781C0A2407463A780130303A092A03D804FB46
:4032000001210123F5E703B1059103223978224800F0E6F940B14023CBEB000003FA00F0049B013718430490397806221B487E1C8DF8281000F0D4F988B1194B33B9039BDF
:40324000073323F007030833039314E003AB00932A46144B04A94046AFF3008007E003AB00932A460F4B04A9404600F093F8B0F1FF3F824603D0099B5344099342E7AB897F
:403280005B0601D4099801E04FF0FF301DB0BDE8F08F00BFF33A0000F93A0000FD3A0000000000000D3000002DE9F04791461F460A698B6806469342B8BF1346C9F8003093
:4032C00091F843200C46DDF8208012B10133C9F800302368990642BFD9F800300233C9F80030256815F0060510D104F1190A07E00123524639463046C04701301AD001355B
:40330000E368D9F800209B1A9D42F1DB94F843302268003318BF012392060FD5E118302081F843005A1C94F845102244023382F8431003E04FF0FF30BDE8F08704F1430253
:4033400039463046C0470130F4D02268D9F80050E36802F00602042A08BF5D1B2269A3680CBF25EAE57500259342C4BF9B1AED184FF000091A344D4509D0012322463946F2
:403380003046C0470130D5D009F10109F3E70020BDE8F0872DE9F04317460A7E85B06E2A984606460C460C9B01F1430E00F0AE8011D8632A22D009D8002A00F0BB80582A01
:4033C00040F0CA8081F84520834955E0642A1ED0692A1CD0C0E0732A00F0B08009D86F2A2ED0702A40F0B8800A6842F020020A603EE0752A24D0782A3AD0ADE01A6801F114
:403400004205111D1960136884F84230A8E021681A6811F0800F02D0111D196008E011F0400F02F10401196002D0B2F9003000E01368002B3CDA2D225B4284F8432037E0C5
:4034400021681A6811F0800F02D0111D196007E011F0400F02F10401196001D0138800E01368227E5C496F2A14BF0A2208221BE078225A4984F845202268186812F0800F0E
:4034800000F104051D6003D1550601D5038800E00368D00744BF42F0200222601BB9226822F0200222601022002084F8430001E049490A226568002DA56008DB206820F0CC
:4034C000040020602BB9002D7DD175460CE0002B79D07546B3FBF2F002FB1033CB5C05F8013D03460028F5D1082A0BD12368DA0708D5236962689A42DEBF302305F8013CF7
:4035000005F1FF35C5EB0E0323612EE008681A6810F0800F496903D0101D1860136808E010F0400F02F104001860136801D0198000E0196000232361754616E01A68111DFC
:403540001960156800216268284600F049F808B1401B6060636804E004F1420584F8422001232361002384F84330CDF800803B4603AA21463046FFF797FE013002D14FF056
:40358000FF3026E023692A4639463046C0470130F5D023689B0710D5002504F1190907E001234A4639463046C0470130E7D00135E368039A9B1A9D42F2DBE068039B9842DC
:4035C000B8BF184605E00B7804F1420584F842308AE705B0BDE8F083A7390000043B000010B5C9B202449042034605D01C7801308C42F8D1184610BD002010BD10B5431EF7
:403600000A44914204D011F8014B03F8014FF8E710BD884210B501EB020301D8421E0BE09842FBD28118D21AD34204D013F8014D01F8014DF8E710BD994204D011F8014B40
:4036400002F8014FF8E710BD38B50546002944D051F8043C0C1F002BB8BFE41800F0D4F81E4A1368114613B96360146030E0A3420DD92268A018834201BF18685B68121885
:40368000226063600C6023E0A24203D813465A68002AF9D118681918A1420BD12168014458188242196013D110685268014419605A600DE002D90C232B6009E021686018A3
:4036C000824201BF106852680918216062605C602846BDE8384000F098B838BDA092FF1F70B5CD1C25F0030508350C2D38BF0C25002D064601DBA94202D90C23336046E018
:4037000000F082F8234B1C681A462146A1B10B685B1B0ED40B2B03D90B60CC18CD501EE08C420BBF63684B681360636018BF0C4615E00C464968E9E7174C23681BB9304658
:4037400000F052F820602946304600F04DF8431C18D0C41C24F00304A0420DD12560304600F053F804F10B00231D20F00700C31A0ED05A42E25070BD211A304600F034F863
:403780000130EBD10C233360304600F03EF8002070BD00BFA092FF1F9C92FF1FF8B5074615460E4621B91146BDE8F840FFF798BF1AB9FFF749FF2846F8BD00F027F8854208
:4037C0000ED929463846FFF78BFF044650B131462A46FFF713FF31463846FFF735FF01E03046F8BD2046F8BD38B5064C0023054608462360FDF7DCFB431C02D1236803B13B
:403800002B6038BD8493FF1F7047704751F8040C0028BEBF091851F8043CC0180438704700000000050209020B020D020F021102130215027265706C792030782530327881
:4038400000686F6D696E6700626567696E6E696E67207365656B2066726F6D20256420746F2025640066696E6973686564207365656B00796573006E6F00647269766520AA
:40388000303A20257320647269766520313A2025730057616974696E6720666F72205553422E2E2E0055534220726561647900636F6D6D616E64203078253032780066610F
:4038C000696C2025642B25642B2564203D3D2025642C206E6F74202564007061737365643D256400756E64657272756E206166746572202564207061636B65747300636FE8
:40390000756E743D256420693D256420643D256400636D645F777269746500703D25642063723D25642063773D256420663D256420773D256420696E6465783D2564207526
:403940006E64657272756E3D256400756E64657272756E2100737563636573730073746172742065726173696E670073746F702065726173696E670069646C650000510001
:4039800040100040510040300000000140001000140140000800400140000A004C014000020050014020003031323334353637383941424344454600000100000004000096
:4039C00000100001000000040000001028000000000104000100000000000000000157494E5553420000303030303100000000000000000012034D0053004600540031002A
:403A0000300030000100000001000000103A000001000000DF3A0000000000000000000001000000283A000001000000B13A0000040000004A3A00000000000000000000E9
:403A400000000000483A0000FF00000001024000FF00000082024000FF00000003034000FF00000084034000FF00020304030904160346006C007500780045006E006700CA
:403A800069006E0065002A0343006F0077006C00610072006B00200054006500630068006E006F006C006F0067006900650073000009022E0001010080320904000004FFC9
:403AC00000000107050102400000070582024000000705030340000A0705840340000A12010002FF0001080912006E0100020180014300232D302B2000686C4C00656667E2
:403B0000454647003031323334353637383961626364656600000000F8B500BFF8BC08BC9E467047590000002D100000F8B500BFF8BC08BC9E46704735000000503B0000FD
:403B4000C880FF1FA00000002012000000000000000000008893FF1FFF000000675000400C00000007000000FFFFFFFF7F8000003F0000000000007D00FA000040000000DA
:403B80000090D003FF0000000000000000000000000000000000000000000000000000000000000000000000F13A0000000000000000000000000000000000000000000078
:403BC000000000000000000000000000000000000000000000000000000000000000000000000000000000000081FF1F000000000000000000000000000000000000000026
:403C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084
:403C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044
:403C80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004
:403CC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C4
:403D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
:403D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043
:4000000000800020110000009D1000009D100000064A08B5136843F020031360044B1A6803F53F5302331A6001F056F8E8460040FA46004010B5054C237833B9044B13B17D
:400040000448AFF300800123237010BD6881FF1F00000000E8380000084B10B51BB108490848AFF300800848036803B910BD074B002BFBD0BDE81040184700BF0000000021
:400080006C81FF1FE8380000C880FF1F000000000A4A0B4B116801310B40002BBEBF03F1FF3363F03F030133136011685368994202BF024B01221A72704700BF8881FF1F9C
:4000C0003F0000800A4A0B4B516801310B40002BBEBF03F1FF3363F03F030133536051681368994202BF024B01221A72704700BF8881FF1F3F000080114BDA68196919B9FD
:4001000001221A75597514E09969521A19698A4294BF587D00201875187D094908B1002204E0086982428CBF002201224A75DA689A611B7D13B1002002F056B9704700BF60
:400140008881FF1F10B5C4B2204601F087F90128FAD110BD08B572B60F4B0F49DA680132DA60DA690132C82A08BF0022DA611A6AD8690132A72A08BF00220A621B6A002BE5
:400180000CBF02230023002814BF184643F0010002F092FE62B608BD8881FF1F38B50446C5B2284602F0C2F8062002F0DFFA44F00200C0B202F0BAF8062002F0D7FA2846BD
:4001C00002F0B4F8BDE83840062002F0B9BA10B5642402F0A5F828B9FFF7E0FF013CF8D1204610BD012010BD70B5C4B2054620460E4601F033F9012805D0204601F04CFAA8
:400200002846FFF79FFF204601F030F9314605460246204601F0ECF9204601F01FF90028FAD1284670BD000038B5044D0024285D013402F04BFA402CF9D138BDAC81FF1F30
:4002400008B502F065FC002002F06EFC02F080FC02F08AFC80B208BD10B50446012002F07DF8642002F06CFAFFF7EAFF2080002002F074F8642002F063FAFFF7E1FF60801C
:4002800010BD08B502F070FD002002F079FD02F08BFD02F095FD80B208BD10B50446FFF796FF322002F04CFAFFF7EBFF20800120FFF774FF322002F043FAFFF7E2FF608072
:4002C00010BD0FB400B593B014AB53F8042B402102A8019302F0F0FE02A802F08CF802F096F813B05DF804EB04B0704710B5044601780648FFF7E5FF0420FFF723FF6278E6
:400300002146BDE81040042001F000B9043A000007B50023ADF804308DF80600032301A88DF80530FFF7E2FF03B05DF804FB000010B5074C94F8643043B1002001F0EAFF85
:40034000002002F07DFD002384F8643010BD00BF8881FF1F38B5124D837895F8672004469A4204D0FFF7E4FF002385F86A302368C5F8653022790B4B1A71A378002B14BF31
:400380000220012002F05CFDE078B0FA80F0400902F050FD2079BDE8384002F087BD00BF8881FF1FED81FF1F38B50D4C94F8645065B904F16500FFF7CDFF012001F0AAFFDC
:4003C0004FF47A7002F0BCF984F86A50E368E366012384F86430BDE8384002F0EFB900BF8881FF1FF8B5214C0546FFF7DDFF94F86A3003B15DB91E48FFF763FFFFF7E7FECE
:400400000120002384F86A00236702F0AFF92A46216F1848FFF755FF144E0027236F9D4216D001F07DFF00B13767236F9D4205DD0120FFF7B3FE336F013305E005DA002053
:40044000FFF7ACFE336F013B336702F0B7F9E5E7322002F075F92A2DCCBF0020012002F031FDBDE8F8400448FFF72BBF8881FF1F113A0000183A0000353A00002DE9F04F70
:4004800099B062B602F004FA9E49042002F028FA9D4801F051FF9D4802F0F4FC9C4801F085FF02F0D5FB02F0A7FA002002F0C8FC01F0A0FF0221002000F086FF954C012011
:4004C00001F0FEF8002384F86730FFF76DFFFFF77EFE84F87400FFF72BFF012384F86730FFF762FFFFF773FE84F87500FFF720FF894B94F87400894994F875202546002AA4
:4005000014BF0A461A46002808BF19468448FFF7D8FE0321084602F031F9264602F04EF994F8643043B1EA6EEB689B1A41F28832934201D9FFF7FCFE00F07EFF18B9794820
:40054000FFF7BFFE04E000F07DFF0028F7D10BE000F072FF10B902F031F9F9E77248FFF7B0FE032001F098F8032000F077FF0128D4D16E48FFF7EEFE6D490320FFF734FE85
:4005800094F876106B48FFF79CFE94F87630023B142B00F2D683DFE813F01500D4031E00D4032400D4035000D4037600D403D900D403C101D4030803D4032C03D40333036C
:4005C000D4034D0303238DF820308DF8213010238DF822302AE394F87800FFF703FF564B21E340F2DC57FFF7DFFE00232375E068227D02F0FF012AB9EB681B1ABB42F7DD43
: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
@@ -4098,51 +4098,51 @@
:40FF80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041
:40FFC0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
:0200000480007A
:400000000145004008520040015B0040016400400101014003050140030701404E0801404A0901406C0A0140550B01405A0C0140490D01405A0E0140340F0140041701403E
:40004000471801405A1901404F1A01404C1B01400C4001400A4101400F4201400D430140044401400C450140064601400C4701400F4801400B490140194C0140074D0140FA
:4000800005500140045101407E02080509411080118219026006610A7C4027212A0AE280E640EA05EE10E620EA01EE8C00180220041806E009020A100C010E02100111076F
:4000C00012041407150216F8170118801A181B011C181E4020FF260828012A062E072F0630FF330335043B083E01580459045B045C905F0181408210833085E18680870885
:4001000088018A248B108C7C8E828F1091FC92039302961097039B109D209E109FDCA180A210A330A401A648A9E1AA1CAB04AE10B01FB31FB4F0B7E0BA20D608D80BD90B5D
:40014000DB04DC99DD90DF01012003A20404055208040AA10B010D080EA80F0210201280135015041680175919011A801B301E402180228424202514260429202B802F0167
:400180003180321433023614370138083B513D2058105980600A68026B046D4078018410884091209358942C9540970199729B599C019F04A240A301A58CA628A792A901F2
:4001C000AA10B010C0FFC2FDC4FFCA15CCEFCE2FD60CD80CDE01E622E804EE8C004801400311044805F10708081009100A200BE10C5A0E200F0E10C01201144818481C02F7
:400200001D201E041F112180224023112448288629F12A782B022DF12E042F04303F310F328033F0344038023B023E1440254520480249FF4AFF4BFF4D204EF0511058045A
:40024000590B5A045B045C995D095F01610862406340648066406740800481018220830E8404861088808A048BC08D018E0291EF92029403952F96FC974099019A049B04BE
:400280009C049D809E409F2FA0FFA101A302A404A6F9A710A804A901AA08AB08AC01AE02AF20B4FFB51FB7E0BE10D804D904DB04DC90DF01004401020204042405020606B2
:4002C000084809200A800B280D280E800F021180128813081408174918401A041B051F2023502680270829202B822C802E802F28318034403608375138083B513D903E02AA
:4003000042044620470849014A0268056E507801830489108A808E409120928093209540965099729A829B519C019D019E209F04A090A160A242A308A405A50CA628A7130E
:40034000A840AA01B540C0FFC2FFC4FFCAFDCCF8CEBFD004D208DE01E404E640EE1000800102027E044006100702083E09010A400C400E020F041040111812201320147E22
:400380001703188019021C401D081E04210222012502284029022A082D202F1830FF310833203507371039203E013F05400247EC483049FF4AFF4BFF5004560158045904C1
:4003C0005A045B045C905D095F0162C080368209842D8612880789028B018C3F9209961299019B029C24A104A480A640A840AA80AEC0B040B104B238B301B407B680B7021B
:40040000B882B988BE41BF45D80BD904DB04DC09DF01001A010403820408060A07400A700B410C800D280E80102011501711189019A01C80202021402202253026802760C1
:4004400029012D022E202F11302036A939103C803D283F02472056056A806B016C946D806EA06F18768A780189C08A1091309323942895049620974099129E249F45A0205B
:40048000A201A480A55CA668A79BA820A950AA80B080B640C0FFC2FDC457CAF1CCF4CEF4D020DE0100020140020C04100550072008040A080E010F10107211041280130203
:4004C000140E150816F018101B201C801E20201021602310244025022680270528102C082D072E0430FF310133703506370838023F445608580459045B045C995D905F0174
:40050000810C82078340840185808604870C88018A028B048C408EA08F02900191FF9457950397FC9840990C9A909B109C019D019E069F02A057A302A50CA7F1A840AB0883
:40054000AD0CAE08AF20B030B1FFB40FB6C0BF01D804D904DC09DF010220034904280540074009180A410C800D080E881004118812801604178119021A401D501E021F114A
:400580002112224427802B922C842E602F103102321033043628374139043B413DA83E013F025880630169908A048E048F01C0FFC2FFC4DFCAFBCCF7CEFBD608D808E280A8
:4005C000E404E680EA08EE0B011406410714080109140A120B080CF10E040F201241150A16411714180119041A281C801D0A1E01211422412504264E29042B102D182F0158
:40060000320F3307353836F039083A085608580859045B045C995D905F018210846088018A048C7C8D018E02921C96039A109C409D04A210A401A608AA10AD02AE10B1016E
:40064000B21FB304B440B502B620BF15D80BD904DB04DC09DF01012803810524070108100A120B800C200E060F50102011011280135014101501170418081A161E021F885A
:4006800020202101221024042518262828022D062E802F0430043240330C35103608374039803D283E403F0258405A205D505F086401650867066A806C016F0378017F01D2
:4006C000814083088501912492489358940C96019702984299339A809B409C059D80A104A280A301A460A508A628A782AC02B048C0EFC2FFC43FCAF8CC78CEF8D67CD870F3
:40070000DE11E204E402E680EE0B01FC02900302042005C106C0072808C10A240B900CFC0E020F901120120313C018011A481B901E901F032290239C2690279029012A9012
:400740002B442E9C2F9031E034E0361F371F3A203B02420245E0480249FF4AFF4BFF4F83580B590B5A045B045C995D095F01821084618501860888618A04921096109A0394
:400780009C409E30A07CA202A61CA820AA50AC80B080B101B260B41FB660BE01BF01D808D904DB04DC09DF01012002C0036204080520074008020A050C400E0A0F50105066
:4007C0001350141015401608170519401B301C041D041E491F4022422780284029242A4830103242380A3B503F044110484059015A985F80600661406202632069806D40BA
:4008000078017F018240900A9120921C9356958096019780980199219B059D04A281A302A440AD10B010B580C07FC2FBC4FFCA0FCC0DCE4FD004D61FD80FDE11E004EE085F
:400840001A401F083328360437103A406A406B108B20C630CCF0CE1032043380364037023A013E809E40A604CCF0CE60502055108D2096089EC09F02A020A110A644A7809D
:40088000A910AA01B420D460EAA08A809EC09F02A002A120A202A640A780AA06AE04B002EA80EE3014107310C404DC01530164108B1090109710B010B410D401D801E4017C
:4008C000E808EA041B048301901097029C10C60808080B080E020F408710903097089C10A810AB04AF04C20F240280018A409A209E40A002A120A202AB02AE60AF80C820D3
:40090000E420EA10EED051205202722074029A20A002A120A202D4A0DC80DE2005400B200C800E401F40501056205C106120824084108B40903097209C10AA01AC08AF40A1
:40094000C001C20DC601D406D604D802E60182209E20AD40B120B480EC01EE0201010D010F0111011D0100FF01AB020211050000BF0000A09F001F0000000000000000004A
:40098000100000004000000000000000C0000000FF0000B84700470000010000800000028200820000000000000303000300000027001801270018010004000000050000C9
:4009C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F7
:400A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B6
:400A40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076
:400A80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036
:400AC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F6
:400B000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B5
: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
@@ -4615,12 +4615,12 @@
:0200000490105A
:04000000BC90ACAF55
:0200000490303A
:02000000861365
:0200000090D09E
:0200000490402A
:4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0
:400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080
:400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040
:4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
:0200000490501A
:0C00000000012E16106900002E2F967CC7
:0C00000000012E16106900002E30A139FE
: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" />
@@ -871,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" />
@@ -4161,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,13 +26,6 @@
<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>
@@ -2847,6 +2840,72 @@
</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>
@@ -3235,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>
@@ -3414,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>
@@ -3427,9 +3486,7 @@
<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="150" />

View File

@@ -23,36 +23,49 @@ module Sampler (
reg [5:0] counter;
reg index_q;
reg rdata_q;
reg index_edge;
reg rdata_edge;
reg req_toggle;
reg rdata_toggle;
reg old_rdata_toggle;
reg index_toggle;
reg old_index_toggle;
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
old_rdata_toggle <= 0;
old_index_toggle <= 0;
index_edge <= 0;
rdata_edge <= 0;
index_q <= 0;
rdata_q <= 0;
counter <= 0;
req_toggle <= 0;
end
else
begin
/* Both index and rdata are active high -- positive-going edges
* indicate the start of an index pulse and read pulse, respectively.
*/
/* If data_toggle or index_toggle have changed state, this means that they've
* gone high since the last sampleclock. */
index_edge <= index && !index_q;
index_q <= index;
index_edge <= index_toggle != old_index_toggle;
old_index_toggle <= index_toggle;
rdata_edge <= rdata && !rdata_q;
rdata_q <= rdata;
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 };

View File

@@ -65,7 +65,11 @@ begin
if (dataclocked)
begin
pulsepending <= opcode[7];
countdown <= opcode[5:0] - 1; /* compensate for delay in last tick */
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

View File

Binary file not shown.

View File

@@ -13,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;
@@ -69,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)
@@ -114,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);
}
@@ -249,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");
}
@@ -267,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();
@@ -277,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;
@@ -286,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);
@@ -381,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. */
{
@@ -401,10 +439,12 @@ 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;
}
dma_writing_to_td = 0;
@@ -478,6 +518,7 @@ abort:;
wait_until_writeable(FLUXENGINE_DATA_IN_EP_NUM);
deinit_dma();
STEP_REG_Write(0);
if (saved_dma_underrun)
{
print("underrun after %d packets");
@@ -523,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;
@@ -533,7 +575,6 @@ 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 */
@@ -590,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)
;
@@ -625,6 +666,7 @@ 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 ---
@@ -638,6 +680,7 @@ abort:
deinit_dma();
STEP_REG_Write(0);
if (dma_underrun)
{
print("underrun!");
@@ -653,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)
;
@@ -665,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);
@@ -790,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:
@@ -892,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,41 @@
PACKAGES = zlib sqlite3 libusb-1.0
PACKAGES = zlib sqlite3 protobuf
export CFLAGS = --std=c++14 -ffunction-sections -fdata-sections
export LDFLAGS =
export CFLAGS = \
-ffunction-sections -fdata-sections
export CXXFLAGS = $(CFLAGS) \
-x c++ --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 LDFLAGS +=
export LIBS = -static -lz -lsqlite3 -lusb-1.0
export CFLAGS += -I/mingw32/include
export CXXFLAGS += $(shell wx-config --cxxflags --static=yes)
export GUILDFLAGS += -lmingw32
export LIBS += -L/mingw32/lib -static -lsqlite3 \
$(shell wx-config --libs --static=yes core base) -lz \
-lcomctl32 -loleaut32 -lspoolss -loleacc -lwinspool \
-lsetupapi -lwinusb -lole32 -lprotobuf -luuid
export EXTENSION = .exe
else
@@ -24,22 +44,43 @@ ifneq ($(packages-exist),yes)
$(warning These pkg-config packages are installed: $(shell pkg-config --list-all | sort | awk '{print $$1}'))
$(error You must have these pkg-config packages installed: $(PACKAGES))
endif
export CXX = g++
export AR = ar rcs
export STRIP = strip
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
export LDFLAGS +=
export LIBS = $(shell pkg-config --libs $(PACKAGES))
export EXTENSION =
wx-exist = $(shell wx-config --cflags > /dev/null && echo yes)
ifneq ($(wx-exist),yes)
$(error You must have these wx-config installed)
endif
export PROTOC = protoc
export CC = gcc
export CXX = g++
export AR = ar rc
export RANLIB = ranlib
export STRIP = strip
export CFLAGS += $(shell pkg-config --cflags $(PACKAGES)) $(shell wx-config --cxxflags)
export LDFLAGS +=
export LIBS += $(shell pkg-config --libs $(PACKAGES))
export GUILIBS += $(shell wx-config --libs core base)
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 IOKit -framework CoreFoundation
export CFLAGS += -Wno-deprecated-declarations
endif
endif
export XXD = xxd
CFLAGS += -Ilib -Idep/fmt -Iarch
export OBJDIR = .obj
all: .obj/build.ninja
@ninja -f .obj/build.ninja
@ninja -f .obj/build.ninja -k 0
@if command -v cscope > /dev/null; then cscope -bRq; fi
clean:
@echo CLEAN
@@ -50,3 +91,5 @@ clean:
@mkdir -p $(OBJDIR)
@sh $< > $@
compdb:
@ninja -f .obj/build.ninja -t compdb > compile_commands.json

View File

@@ -2,7 +2,12 @@ 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/)
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,6 +29,14 @@ 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>
**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.
**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
@@ -52,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?
------
@@ -70,20 +90,23 @@ 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) |
| [Atari ST](doc/disk-atarist.md) | 🦄 | 🦄 | technically the same as IBM, almost |
| [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 400kB/800kB](doc/disk-macintosh.md) | 🦄 | 🦄 | |
| [NEC PC-98](doc/disk-ibm.md) | 🦄 | 🦄 | trimode drive not required |
| [Sharp X68000](doc/disk-ibm.md) | 🦄 | 🦄 | |
| [TRS-80](doc/disk-trs80.md) | 🦖 | 🦖* | a minor variation of the IBM scheme |
{: .datatable }
`*`: these formats are variations of the generic IBM format, and since the
@@ -103,8 +126,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) | 🦖 | | 5.25" GCR encoded |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |
{: .datatable }
### Notes
@@ -122,7 +149,7 @@ at least, check the CRC so what data's there is probably good.
There hasn't been a lot of demand for this yet; if you have a pressing
need to write weird disks, [please
ask](https://github.com/davidgiven/fluxengine/issues/new). I haven't
implement write support for PC disks because they're boring and I'm lazy,
implemented write support for PC disks because they're boring and I'm lazy,
and also because they vary so much that figuring out how to specify them
is hard.
@@ -188,6 +215,24 @@ 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.

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,54 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip ID mark (we know it's a AESLANIER_RECORD_SEPARATOR). */
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

@@ -7,33 +7,10 @@
#define AMIGA_TRACKS_PER_DISK 80
#define AMIGA_SECTORS_PER_TRACK 11
#define AMIGA_RECORD_SIZE 0x21f
#define AMIGA_RECORD_SIZE 0x21c
class Sector;
class Fluxmap;
class SectorSet;
class AmigaDecoder : public AbstractDecoder
{
public:
virtual ~AmigaDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
};
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,47 +21,66 @@
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;
}
void AmigaDecoder::decodeSectorRecord()
{
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));
_sector->data.clear();
_sector->data.writer().append(amigaDeinterleave(ptr, 512)).append(recoveryinfo);
_sector->status = (gotdatachecksum == wanteddatachecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> AmigaDecoder::requiredSectors(Track& track) const
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
return sectors;
public:
AmigaDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.amiga())
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw48() != AMIGA_SECTOR_RECORD)
return;
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();
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(0, 40));
if (gotheaderchecksum != wantedheaderchecksum)
return;
uint32_t wanteddatachecksum = amigaDeinterleave(ptr, 4).reader().read_be32();
uint32_t gotdatachecksum = amigaChecksum(rawbytes.slice(56, 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;
nanoseconds_t _clock;
};
std::unique_ptr<AbstractDecoder> createAmigaDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new AmigaDecoder(config));
}

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,45 @@ 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<const 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, 0xaaaa, 2*8);
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 +85,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<const 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<const 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<const 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

@@ -1,25 +1,18 @@
#ifndef APPLE2_H
#define APPLE2_H
#include <memory.h>
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define APPLE2_SECTOR_RECORD 0xd5aa96
#define APPLE2_DATA_RECORD 0xd5aaad
#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);
extern std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config);
#endif

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

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

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,96 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != APPLE2_SECTOR_RECORD)
return;
/* 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 the checksum is correct, upgrade the sector from MISSING
// to DATA_MISSING in anticipation of its data record
if (checksum == (volume ^ _sector->logicalTrack ^ _sector->logicalSector))
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Check ID. */
if (readRaw24() != APPLE2_DATA_RECORD)
return;
// Sometimes there's a 1-bit gap between APPLE2_DATA_RECORD and
// the data itself. This has been seen on real world disks
// such as the Apple II Operating System Kit from Apple2Online.
// However, I haven't seen it described in any of the various
// references.
//
// This extra '0' bit would not affect the real disk interface,
// as it was a '1' reaching the top bit of a shift register
// that triggered a byte to be available, but it affects the
// way the data is read here.
//
// While the floppies tested only seemed to need this applied
// to the first byte of the data record, applying it
// consistently to all of them doesn't seem to hurt, and
// simplifies the code.
/* Read and decode data. */
auto readApple8 = [&]() {
auto result = 0;
while((result & 0x80) == 0) {
auto b = readRawBits(1);
if(b.empty()) break;
result = (result << 1) | b[0];
}
return result;
};
constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH+2;
uint8_t bytes[recordLength];
for(auto &byte : bytes) {
byte = readApple8();
}
// Upgrade the sector from MISSING to BAD_CHECKSUM.
// If decode_crazy_data succeeds, it upgrades the sector to
// OK.
_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);
}

185
arch/apple2/encoder.cc Normal file
View File

@@ -0,0 +1,185 @@
#include "globals.h"
#include "arch/apple2/apple2.h"
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#include "sector.h"
#include "writer.h"
#include "image.h"
#include "fmt/format.h"
#include "lib/encoders/encoders.pb.h"
#include <ctype.h>
#include "bytes.h"
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;
}
class Apple2Encoder : public AbstractEncoder
{
public:
Apple2Encoder(const EncoderProto& config):
AbstractEncoder(config)
{}
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
constexpr auto numSectors = 16;
if (physicalSide == 0)
{
int logicalTrack = physicalTrack / 2;
unsigned numSectors = 16;
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<const Sector>>& sectors, const Image& image) override
{
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
int logicalTrack = physicalTrack / 2;
double clockRateUs = 4.;
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
int bitsPerRevolution = 200000.0 / clockRateUs;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (const auto& sector : sectors) {
if(sector) {
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:
uint8_t volume_id = 254;
/* 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
* as well as Understanding the Apple II (1983) Chapter 9
* https://archive.org/details/Understanding_the_Apple_II_1983_Quality_Software/page/n230/mode/1up?view=theater
*/
void writeSector(std::vector<bool>& bits, unsigned& cursor, const Sector& sector) const
{
if ((sector.status == Sector::OK) or (sector.status == Sector::BAD_CHECKSUM))
{
auto write_bit = [&](bool val) {
if(cursor <= bits.size()) { bits[cursor] = val; }
cursor++;
};
auto write_bits = [&](uint32_t bits, int width) {
for(int i=width; i--;) {
write_bit(bits & (1u << i));
}
};
auto write_gcr44 = [&](uint8_t value) {
write_bits((value << 7) | value | 0xaaaa, 16);
};
auto write_gcr6 = [&](uint8_t value) {
write_bits(encode_data_gcr(value), 8);
};
// The special "FF40" sequence is used to synchronize the receiving
// shift register. It's written as "1111 1111 00"; FF indicates the
// 8 consecutive 1-bits, while "40" indicates the total number of
// microseconds.
auto write_ff40 = [&](int n=1) {
for(;n--;) {
write_bits(0xff << 2, 10);
}
};
// There is data to encode to disk.
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector.data.size());
// Write address syncing leader : A sequence of "FF40"s; 5 of them
// are said to suffice to synchronize the decoder.
// "FF40" indicates that the actual data written is "1111
// 1111 00" i.e., 8 1s and a total of 40 microseconds
//
// In standard formatting, the first logical sector apparently gets
// extra padding.
write_ff40(sector.logicalSector == 0 ? 32 : 8);
// Write address field: APPLE2_SECTOR_RECORD + sector identifier + DE AA EB
write_bits(APPLE2_SECTOR_RECORD, 24);
write_gcr44(volume_id);
write_gcr44(sector.logicalTrack);
write_gcr44(sector.logicalSector);
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
write_bits(0xDEAAEB, 24);
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector data + sum + DE AA EB (+ mystery bits cut off of the scan?)
write_ff40(8);
write_bits(APPLE2_DATA_RECORD, 24);
// Convert the sector data to GCR, append the checksum, and write it out
constexpr auto TWOBIT_COUNT = 0x56; // Size of the 'twobit' area at the start of the GCR data
uint8_t checksum = 0;
for(int i=0; i<APPLE2_ENCODED_SECTOR_LENGTH; i++) {
int value;
if(i >= TWOBIT_COUNT) {
value = sector.data[i - TWOBIT_COUNT] >> 2;
} else {
uint8_t tmp = sector.data[i];
value = ((tmp & 1) << 1) | ((tmp & 2) >> 1);
tmp = sector.data[i + TWOBIT_COUNT];
value |= ((tmp & 1) << 3) | ((tmp & 2) << 1);
if(i + 2*TWOBIT_COUNT < APPLE2_SECTOR_LENGTH) {
tmp = sector.data[i + 2*TWOBIT_COUNT];
value |= ((tmp & 1) << 5) | ((tmp & 2) << 3);
}
}
checksum ^= value;
// assert(checksum & ~0x3f == 0);
write_gcr6(checksum);
checksum = value;
}
if(sector.status == Sector::BAD_CHECKSUM) checksum ^= 0x3f;
write_gcr6(checksum);
write_bits(0xDEAAEB, 24);
}
}
};
std::unique_ptr<AbstractEncoder> createApple2Encoder(const EncoderProto& config)
{
return std::unique_ptr<AbstractEncoder>(new Apple2Encoder(config));
}

View File

@@ -13,36 +13,7 @@
#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:
BrotherEncoder(int format, int bias):
_format(format),
_bias(bias)
{}
virtual ~BrotherEncoder() {}
private:
int _format;
int _bias;
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

@@ -1,10 +1,8 @@
#include "globals.h"
#include "sql.h"
#include "fluxmap.h"
#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 +38,7 @@ static int decode_data_gcr(uint8_t gcr)
#undef GCR_ENTRY
}
return -1;
};
}
static int decode_header_gcr(uint16_t word)
{
@@ -52,58 +50,70 @@ static int decode_header_gcr(uint16_t word)
#undef GCR_ENTRY
}
return -1;
}
class BrotherDecoder : public AbstractDecoder
{
public:
BrotherDecoder(const DecoderProto& config):
AbstractDecoder(config)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw32() != BROTHER_SECTOR_RECORD)
return;
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() override
{
if (readRaw32() != BROTHER_DATA_RECORD)
return;
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,58 +100,107 @@ 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
{
int logicalTrack;
if (physicalSide != 0)
return std::unique_ptr<Fluxmap>();
physicalTrack -= _bias;
switch (_format)
{
case 120:
if ((physicalTrack < 0) || (physicalTrack >= (BROTHER_TRACKS_PER_120KB_DISK*2))
|| (physicalTrack & 1))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack/2;
break;
public:
BrotherEncoder(const EncoderProto& config):
AbstractEncoder(config),
_config(config.brother())
{}
case 240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
logicalTrack = physicalTrack;
break;
public:
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const Sector>> sectors;
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;
break;
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return sectors;
break;
}
for (int sectorId=0; sectorId<BROTHER_SECTORS_PER_TRACK; sectorId++)
{
const auto& sector = image.get(physicalTrack, 0, sectorId);
if (sector)
sectors.push_back(sector);
}
return sectors;
}
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++)
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
const std::vector<std::shared_ptr<const Sector>>& sectors, 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;
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>();
break;
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
case BROTHER240:
if ((physicalTrack < 0) || (physicalTrack >= BROTHER_TRACKS_PER_240KB_DISK))
return std::unique_ptr<Fluxmap>();
break;
}
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);
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(physicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, sectorData->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;
}
if (cursor >= bits.size())
Error() << "track data overrun";
fillBitmapTo(bits, cursor, bits.size(), { true, false });
private:
const BrotherEncoderProto& _config;
// 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;
};
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,52 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw20() != C64_SECTOR_RECORD)
return;
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() override
{
if (readRaw20() != C64_DATA_RECORD)
return;
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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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, std::shared_ptr<const 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,59 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip sync bits and ID byte. */
if (readRaw24() != F85_SECTOR_RECORD)
return;
/* 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() override
{
/* Skip sync bits ID byte. */
if (readRaw24() != F85_DATA_RECORD)
return;
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,48 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_ID_PATTERN);
}
void decodeSectorRecord() override
{
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,
@@ -89,71 +90,165 @@ 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;
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
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;
void decodeSectorRecord() override
{
/* This is really annoying because the IBM record scheme has a
* variable-sized header _and_ the checksum covers this header too. So
* we have to read and decode a byte at a time until we know where the
* record itself starts, saving the bytes for the checksumming later.
*/
case IBM_DAM1:
case IBM_DAM2:
case IBM_TRS80DAM1:
case IBM_TRS80DAM2:
return RecordType::DATA_RECORD;
}
return RecordType::UNKNOWN_RECORD;
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]() {
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if (id != IBM_IDAM)
return;
ByteReader br(bytes);
br.seek(bw.pos);
auto bits = readRawBits(IBM_IDAM_LEN*16);
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
IbmDecoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, _sector->physicalCylinder, _sector->physicalHead);
_sector->logicalTrack = br.read_8();
_sector->logicalSide = br.read_8();
_sector->logicalSector = br.read_8();
_currentSectorSize = 1 << (br.read_8() + 7);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
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;
for (int sector : trackdata.ignore_sector())
if (_sector->logicalSector == sector)
{
_sector->status = Sector::MISSING;
break;
}
}
void decodeDataRecord() override
{
/* This is the same deal as the sector record. */
Bytes bytes;
ByteWriter bw(bytes);
auto readByte = [&]() {
auto bits = readRawBits(16);
auto bytes = decodeFmMfm(bits).slice(0, 1);
uint8_t byte = bytes[0];
bw.write_8(byte);
return byte;
};
uint8_t id = readByte();
if (id == 0xa1)
{
readByte();
readByte();
id = readByte();
}
if ((id != IBM_DAM1) && (id != IBM_DAM2)
&& (id != IBM_TRS80DAM1) && (id != IBM_TRS80DAM2))
return;
ByteReader br(bytes);
br.seek(bw.pos);
auto bits = readRawBits((_currentSectorSize + 2) * 16);
bw += decodeFmMfm(bits).slice(0, _currentSectorSize+2);
_sector->data = br.read(_currentSectorSize);
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, br.pos));
uint16_t wantCrc = br.read_be16();
_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,138 +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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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;
hexdump(std::cout, data.slice(0, 64));
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

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

@@ -0,0 +1,70 @@
syntax = "proto2";
import "lib/common.proto";
message IbmDecoderProto {
// Next: 11
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"];
repeated int32 ignore_sector = 10 [(help) = "sectors with these IDs will not be read"];
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,91 +116,96 @@ 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 & 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
if (readRaw24() != MAC_SECTOR_RECORD)
return;
/* 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() override
{
if (readRaw24() != 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 override
{
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)
{
return std::unique_ptr<AbstractDecoder>(new MacintoshDecoder(config));
}
void MacintoshDecoder::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 != (_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 */
}
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;
while (count--)
sectors.insert(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<const 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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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."];
}

191
arch/micropolis/decoder.cc Normal file
View File

@@ -0,0 +1,191 @@
#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"
#include "lib/decoders/decoders.pb.h"
/* The sector has a preamble of MFM 0x00s and uses 0xFF as a sync pattern.
*
* 00 00 00 F F
* 0000 0000 0000 0000 0000 0000 0101 0101 0101 0101
* A A A A A A 5 5 5 5
*/
static const FluxPattern SECTOR_SYNC_PATTERN(64, 0xAAAAAAAAAAAA5555LL);
/* Pattern to skip past current SYNC. */
static const FluxPattern SECTOR_ADVANCE_PATTERN(64, 0xAAAAAAAAAAAAAAAALL);
/* Standard Micropolis checksum. 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;
}
/* Vector MZOS does not use the standard Micropolis checksum.
* The checksum is initially 0.
* For each data byte in the 256-byte payload, rotate left,
* carrying bit 7 to bit 0. XOR with the current checksum.
*
* Unlike the Micropolis checksum, this does not cover the 12-byte
* header (track, sector, 10 OS-specific bytes.)
*/
uint8_t mzosChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
uint8_t databyte;
while (!br.eof()) {
databyte = br.read_8();
checksum ^= ((databyte << 1) | (databyte >> 7));
}
return checksum;
}
class MicropolisDecoder : public AbstractDecoder
{
public:
MicropolisDecoder(const DecoderProto& config):
AbstractDecoder(config),
_config(config.micropolis())
{
_checksumType = _config.checksum_type();
}
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = 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) {
seekToIndexMark();
now = 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 > (getFluxmapDuration() - 12.5e6)) {
seekToIndexMark();
return 0;
}
nanoseconds_t clock = seekToPattern(SECTOR_SYNC_PATTERN);
auto syncDelta = tell().ns() - now;
/* Due to the weak nature of the Micropolis SYNC patern,
* it's possible to detect a false SYNC during the gap
* between the sector pulse and the write gate. If the SYNC
* is detected less than 100uS after the sector pulse, search
* for another valid SYNC.
*
* Reference: Vector Micropolis Disk Controller Board Technical
* Information Manual, pp. 1-16.
*/
if ((syncDelta > 0) && (syncDelta < 100e3)) {
seekToPattern(SECTOR_ADVANCE_PATTERN);
clock = seekToPattern(SECTOR_SYNC_PATTERN);
}
_sector->headerStartTime = tell().ns();
/* seekToPattern() can skip past the index hole, if this happens
* too close to the end of the Fluxmap, discard the sector.
*/
if (_sector->headerStartTime > (getFluxmapDuration() - 12.5e6)) {
return 0;
}
return clock;
}
void decodeSectorRecord() override
{
readRawBits(48);
auto rawbits = readRawBits(MICROPOLIS_ENCODED_SECTOR_SIZE*16);
auto bytes = decodeFmMfm(rawbits).slice(0, MICROPOLIS_ENCODED_SECTOR_SIZE);
ByteReader br(bytes);
int syncByte = br.read_8(); /* sync */
if (syncByte != 0xFF)
return;
_sector->logicalTrack = br.read_8();
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = br.read_8();
if (_sector->logicalSector > 15)
return;
if (_sector->logicalTrack > 76)
return;
if (_sector->logicalTrack != _sector->physicalCylinder)
return;
br.read(10); /* OS data or padding */
auto data = br.read(MICROPOLIS_PAYLOAD_SIZE);
uint8_t wantChecksum = br.read_8();
/* If not specified, automatically determine the checksum type.
* Once the checksum type is determined, it will be used for the
* entire disk.
*/
if (_checksumType == 0) {
/* Calculate both standard Micropolis (MDOS, CP/M, OASIS) and MZOS checksums */
if (wantChecksum == micropolisChecksum(bytes.slice(1, 2+266))) {
_checksumType = 1;
} else if (wantChecksum == mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE))) {
_checksumType = 2;
std::cout << "Note: MZOS checksum detected." << std::endl;
}
}
uint8_t gotChecksum;
if (_checksumType == 2) {
gotChecksum = mzosChecksum(bytes.slice(MICROPOLIS_HEADER_SIZE, MICROPOLIS_PAYLOAD_SIZE));
} else {
gotChecksum = micropolisChecksum(bytes.slice(1, 2+266));
}
br.read(5); /* 4 byte ECC and ECC-present flag */
if (_config.sector_output_size() == MICROPOLIS_PAYLOAD_SIZE)
_sector->data = data;
else if (_config.sector_output_size() == MICROPOLIS_ENCODED_SECTOR_SIZE)
_sector->data = bytes;
else
Error() << "Sector output size may only be 256 or 275";
_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, 10, 11, 12, 13, 14, 15 };
return sectors;
}
private:
const MicropolisDecoderProto& _config;
int _checksumType; /* -1 = auto, 1 = Micropolis, 2=MZOS */
};
std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new MicropolisDecoder(config));
}

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

@@ -0,0 +1,121 @@
#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<const 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)
{
if (sector->data[0] != 0xFF)
Error() << "275 byte sector doesn't start with sync byte 0xFF. Corrupted sector";
uint8_t wantChecksum = sector->data[1+2+266];
uint8_t gotChecksum = micropolisChecksum(sector->data.slice(1, 2+266));
if (wantChecksum != gotChecksum)
std::cerr << "Warning: checksum incorrect. Sector: " << sector->logicalSector << std::endl;
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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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,18 @@
#ifndef MICROPOLIS_H
#define MICROPOLIS_H
#define MICROPOLIS_PAYLOAD_SIZE (256)
#define MICROPOLIS_HEADER_SIZE (1+2+10)
#define MICROPOLIS_ENCODED_SECTOR_SIZE (MICROPOLIS_HEADER_SIZE + MICROPOLIS_PAYLOAD_SIZE + 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,13 @@
syntax = "proto2";
import "lib/common.proto";
message MicropolisDecoderProto {
optional int32 sector_output_size = 1 [default = 256,
(help) = "How much of the raw sector should be saved. Must be 256 or 275"];
optional int32 checksum_type = 2 [default = 0,
(help) = "Checksum type to use: 0 = automatically detect, 1 = Micropolis, 2 = MZOS"];
}
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;
@@ -18,58 +16,71 @@ const int SECTOR_SIZE = 256;
*/
/* FM beginning of track marker:
* 0 0 f 3 decoded nibbles
* 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1
* 1010 1010 1010 1010 1111 1111 1010 1111
* a a a a f f a f
* a a a a f f a f encoded nibbles
*/
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() override
{
_clock = _sector->clock = seekToPattern(ID_PATTERN);
_currentSector = 0;
}
nanoseconds_t advanceToNextRecord() override
{
if (_currentSector == 11)
{
/* That was the last sector on the disk. */
return 0;
}
else
return _clock;
}
void decodeSectorRecord() override
{
/* Skip the ID pattern and track word, which is only present on the
* first sector. We don't trust the track word because some driver
* don't write it correctly. */
if (_currentSector == 0)
readRawBits(64);
auto bits = readRawBits((SECTOR_SIZE+2)*16);
auto bytes = decodeFmMfm(bits).slice(0, SECTOR_SIZE+2);
uint16_t gotChecksum = 0;
ByteReader br(bytes);
for (int i=0; i<(SECTOR_SIZE/2); i++)
gotChecksum += br.read_be16();
uint16_t wantChecksum = br.read_be16();
_sector->logicalTrack = _sector->physicalCylinder;
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _currentSector;
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
_sector->status = (gotChecksum == wantChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
_currentSector++;
}
private:
nanoseconds_t _clock;
int _currentSector;
};
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)).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 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 {}

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

@@ -0,0 +1,176 @@
/* 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"
#define MFM_ID 0xaaaaaaaaaaaa5545LL
#define FM_ID 0xaaaaaaaaaaaaffefLL
/*
* 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, MFM_ID);
/* 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, FM_ID);
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 */
nanoseconds_t advanceToNextRecord() override
{
nanoseconds_t now = 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) {
seekToIndexMark();
now = tell().ns();
}
int msSinceIndex = std::round(now / 1e6);
/* 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.
*/
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns();
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 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;
return clock;
}
void decodeSectorRecord() override
{
nanoseconds_t before = tell().ns();
uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
nanoseconds_t after = tell().ns();
/* Discard any sectors which span the end of a revolution. This can sometimes
* cause spurious bad sectors which can trigger conflicts. */
if (int(before / 200e9) != int(after / 200e9))
return;
unsigned recordSize, payloadSize, headerSize;
if (id == MFM_ID) {
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;
}
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
_sector->logicalSide = _sector->physicalHead;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalCylinder;
if (headerSize == NORTHSTAR_HEADER_SIZE_DD) {
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 - 1, 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 _hardSectorId;
};
std::unique_ptr<AbstractDecoder> createNorthstarDecoder(const DecoderProto& config)
{
return std::unique_ptr<AbstractDecoder>(new NorthstarDecoder(config));
}

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

@@ -0,0 +1,176 @@
#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<const Sector>& sector)
{
int preambleSize = 0;
int encodedSectorSize = 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;
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;
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 (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size()
<< ") expected: " << fullSector->size() << " got "
<< fullSectorSize;
/* A few bytes of sector postamble just so the blank skip area doesn't start
* immediately after the data. */
for (int i = 0; i < 10; 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<const Sector>> collectSectors(
int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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;
auto fluxmap = std::make_unique<Fluxmap>();
bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
fluxmap->appendIndex();
first = false;
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
write_sector(bits, cursor, sectorData);
fluxmap->appendBits(bits, clockRateUs * 1e3);
}
// if (fluxmap->duration() > 200e6)
// Error() << "track data overrun";
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,35 @@
#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)
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 uint16_t SECTOR_ID = 0x550a;
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 = 0x550b
* 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 uint16_t DATA_ID = 0x550b;
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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
auto bits = readRawBits(TIDS990_SECTOR_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_SECTOR_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != SECTOR_ID)
return;
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE-3));
_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() override
{
auto bits = readRawBits(TIDS990_DATA_RECORD_SIZE*16);
auto bytes = decodeFmMfm(bits).slice(0, TIDS990_DATA_RECORD_SIZE);
ByteReader br(bytes);
if (br.read_be16() != DATA_ID)
return;
uint16_t gotChecksum = crc16(CCITT_POLY, bytes.slice(1, TIDS990_DATA_RECORD_SIZE-3));
_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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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,66 @@ 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)
{}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(ANY_RECORD_PATTERN);
}
void decodeSectorRecord() override
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_SECTOR_RECORD)
return;
/* Read header. */
auto bytes = decode(readRawBits(3*10)).slice(0, 3);
uint8_t rawTrack = bytes[0];
_sector->logicalSector = bytes[1];
uint8_t gotChecksum = bytes[2];
_sector->logicalTrack = rawTrack & 0x7f;
_sector->logicalSide = rawTrack >> 7;
uint8_t wantChecksum = bytes[0] + bytes[1];
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) || (_sector->logicalSide > 1))
return;
if (wantChecksum == gotChecksum)
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
}
void decodeDataRecord() override
{
/* Check the ID. */
if (readRaw32() != VICTOR9K_DATA_RECORD)
return;
/* Read data. */
auto bytes = decode(readRawBits((VICTOR9K_SECTOR_LENGTH+4)*10))
.slice(0, VICTOR9K_SECTOR_LENGTH+4);
ByteReader br(bytes);
_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;
}

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

@@ -0,0 +1,208 @@
#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_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
{
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
write_bits(bits, cursor, encode_data_gcr(b), 5);
}
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
{
for (uint8_t b : bytes)
write_byte(bits, cursor, b);
}
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
{
for (int i = 0; i < length/10; i++)
write_byte(bits, cursor, '0');
}
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_gap(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_gap(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<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override
{
std::vector<std::shared_ptr<const 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<const 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,24 @@
#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
#define VICTOR9K_HEADER_ID 0x7
/* ... 1101 0100 1001
* ^^ ^^^^ ^^^^ ten bit IO byte */
#define VICTOR9K_DATA_RECORD 0xfffffd49
#define VICTOR9K_DATA_ID 0x8
#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,45 @@
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)
{}
nanoseconds_t advanceToNextRecord() override
{
seekToIndexMark();
return seekToPattern(SECTOR_START_PATTERN);
}
void decodeSectorRecord() override
{
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

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