mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 98a28225d6 | ||
|  | d041b538bb | 
| @@ -1,9 +1,19 @@ | ||||
| #ifndef AESLANIER_H | ||||
| #define AESLANIER_H | ||||
|  | ||||
| /* MFM: | ||||
|  *  | ||||
|  * Raw bits: | ||||
|  *    5    5    5    5    5    1    2    2 | ||||
|  * 0101 0101 0101 0101 0101 0001 0010 0010 | ||||
|  * 0 0  0 0  0 0  0 0  0 0  0 0  0 1  0 1 | ||||
|  *        0         0         0         5 | ||||
|  * Decoded bits. | ||||
|  */ | ||||
|  | ||||
| #define AESLANIER_RECORD_SEPARATOR 0x55555122 | ||||
| #define AESLANIER_SECTOR_LENGTH 256 | ||||
| #define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5) | ||||
| #define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 4) | ||||
|  | ||||
| extern std::unique_ptr<Decoder> createAesLanierDecoder( | ||||
|     const DecoderProto& config); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message AesLanierDecoderProto {} | ||||
| message AesLanier5DecoderProto {} | ||||
|  | ||||
|   | ||||
							
								
								
									
										21
									
								
								arch/aeslanier5/aeslanier5.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								arch/aeslanier5/aeslanier5.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #ifndef AESLANIER5_H | ||||
| #define AESLANIER5_H | ||||
|  | ||||
| /* Format is FM: | ||||
|  * | ||||
|  *    a    a    a    a    f    b    e    f | ||||
|  * 1010 1010 1010 1010 1111 1011 1110 1111 | ||||
|  *  0 0  0 0  0 0  0 0  1 1  0 1  1 0  1 1 | ||||
|  *         0         0         d         b | ||||
|  *  | ||||
|  * However, note that this pattern is _not_ reversed... | ||||
|  */ | ||||
|  | ||||
| #define AESLANIER5_RECORD_SEPARATOR 0xaaaaaaaaaaaafbefLL | ||||
| #define AESLANIER5_SECTOR_LENGTH 151 | ||||
| #define AESLANIER5_RECORD_SIZE (AESLANIER5_SECTOR_LENGTH + 3) | ||||
|  | ||||
| extern std::unique_ptr<Decoder> createAesLanier5Decoder( | ||||
|     const DecoderProto& config); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										4
									
								
								arch/aeslanier5/aeslanier5.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								arch/aeslanier5/aeslanier5.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message AesLanierDecoderProto {} | ||||
|  | ||||
							
								
								
									
										78
									
								
								arch/aeslanier5/decoder.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								arch/aeslanier5/decoder.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "aeslanier5.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/fluxmapreader.h" | ||||
| #include "lib/data/fluxpattern.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include <string.h> | ||||
|  | ||||
| static const FluxPattern SECTOR_PATTERN(32, AESLANIER5_RECORD_SEPARATOR); | ||||
|  | ||||
| /* This is actually FM, rather than MFM, but it our MFM/FM decoder copes fine | ||||
|  * with it. */ | ||||
|  | ||||
| class AesLanier5Decoder : public Decoder | ||||
| { | ||||
| public: | ||||
|     AesLanier5Decoder(const DecoderProto& config): Decoder(config) {} | ||||
|  | ||||
|     nanoseconds_t advanceToNextRecord() override | ||||
|     { | ||||
|         return seekToPattern(SECTOR_PATTERN); | ||||
|     } | ||||
|  | ||||
|     void decodeSectorRecord() override | ||||
|     { | ||||
|         /* Skip ID mark (we know it's a AESLANIER5_RECORD_SEPARATOR). */ | ||||
|  | ||||
|         readRawBits(SECTOR_PATTERN.length()); | ||||
|  | ||||
|         const auto& rawbits = readRawBits(AESLANIER5_RECORD_SIZE * 16); | ||||
|         const auto& bytes = | ||||
|             decodeFmMfm(rawbits).slice(0, AESLANIER5_RECORD_SIZE); | ||||
|         const auto& reversed = bytes.reverseBits(); | ||||
|  | ||||
|         uint8_t encodedTrack = reversed[0]; | ||||
|         uint8_t encodedSector = reversed[1]; | ||||
|  | ||||
|         _sector->logicalTrack = encodedTrack >> 1; | ||||
|         _sector->logicalSide = 0; | ||||
|         _sector->logicalSector = encodedSector; | ||||
|  | ||||
|         /* Check header 'checksum' (which seems far too simple to mean much). */ | ||||
|  | ||||
|         { | ||||
|             uint8_t wanted = reversed[2]; | ||||
|             uint8_t got = reversed[0] + reversed[1]; | ||||
|             if (wanted != got) | ||||
|                 return; | ||||
|         } | ||||
|  | ||||
|         /* Check data checksum, which also includes the header and is | ||||
|          * significantly better. */ | ||||
|  | ||||
|         _sector->data = reversed.slice(3, AESLANIER5_SECTOR_LENGTH); | ||||
|         uint8_t wanted, got; | ||||
|         ByteReader br(_sector->data); | ||||
|         if ((encodedSector == 0) || (encodedSector == 8)) | ||||
|         { | ||||
|             wanted = br.seek(17).read_8() + br.seek(150).read_8(); | ||||
|             got = sumBytes(_sector->data.slice(0, 17)) + sumBytes(_sector->data.slice(18, 132)); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             wanted = br.seek(150).read_8(); | ||||
|             got = sumBytes(_sector->data.slice(0, AESLANIER5_SECTOR_LENGTH-1)); | ||||
|         } | ||||
|         _sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<Decoder> createAesLanier5Decoder(const DecoderProto& config) | ||||
| { | ||||
|     return std::unique_ptr<Decoder>(new AesLanier5Decoder(config)); | ||||
| } | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "lib/config/config.h" | ||||
| #include "arch/agat/agat.h" | ||||
| #include "arch/aeslanier/aeslanier.h" | ||||
| #include "arch/aeslanier5/aeslanier5.h" | ||||
| #include "arch/amiga/amiga.h" | ||||
| #include "arch/apple2/apple2.h" | ||||
| #include "arch/brother/brother.h" | ||||
| @@ -70,6 +71,7 @@ std::unique_ptr<Decoder> Arch::createDecoder(const DecoderProto& config) | ||||
|         decoders = { | ||||
|             {DecoderProto::kAgat,       createAgatDecoder       }, | ||||
|             {DecoderProto::kAeslanier,  createAesLanierDecoder  }, | ||||
|             {DecoderProto::kAeslanier5, createAesLanier5Decoder  }, | ||||
|             {DecoderProto::kAmiga,      createAmigaDecoder      }, | ||||
|             {DecoderProto::kApple2,     createApple2Decoder     }, | ||||
|             {DecoderProto::kBrother,    createBrotherDecoder    }, | ||||
|   | ||||
| @@ -5,6 +5,7 @@ proto( | ||||
|     name="proto", | ||||
|     srcs=[ | ||||
|         "./aeslanier/aeslanier.proto", | ||||
|         "./aeslanier5/aeslanier5.proto", | ||||
|         "./agat/agat.proto", | ||||
|         "./amiga/amiga.proto", | ||||
|         "./apple2/apple2.proto", | ||||
| @@ -36,6 +37,7 @@ cxxlibrary( | ||||
|     srcs=[ | ||||
|         "./arch.cc", | ||||
|         "./aeslanier/decoder.cc", | ||||
|         "./aeslanier5/decoder.cc", | ||||
|         "./agat/agat.cc", | ||||
|         "./agat/decoder.cc", | ||||
|         "./agat/encoder.cc", | ||||
| @@ -83,6 +85,7 @@ cxxlibrary( | ||||
|         "arch/f85/f85.h": "./f85/f85.h", | ||||
|         "arch/mx/mx.h": "./mx/mx.h", | ||||
|         "arch/aeslanier/aeslanier.h": "./aeslanier/aeslanier.h", | ||||
|         "arch/aeslanier5/aeslanier5.h": "./aeslanier5/aeslanier5.h", | ||||
|         "arch/northstar/northstar.h": "./northstar/northstar.h", | ||||
|         "arch/brother/data_gcr.h": "./brother/data_gcr.h", | ||||
|         "arch/brother/brother.h": "./brother/brother.h", | ||||
|   | ||||
| @@ -31,13 +31,16 @@ based on what looks right. If anyone knows _anything_ about these disks, | ||||
|  | ||||
| ## Options | ||||
|  | ||||
| (no options) | ||||
|   - Format variants: | ||||
|       - `8`: use the format found on 8" disks | ||||
|       - `5`: use the format found on 5.25" disks | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| To read: | ||||
|  | ||||
|   - `fluxengine read aeslanier -s drive:0 -o aeslanier.img` | ||||
|   - `fluxengine read aeslanier --8 -s drive:0 -o aeslanier.img` | ||||
|   - `fluxengine read aeslanier --5 -s drive:0 -o aeslanier.img` | ||||
|  | ||||
| ## References | ||||
|  | ||||
|   | ||||
| @@ -33,6 +33,11 @@ public: | ||||
|  | ||||
|     bool matches(const unsigned* intervals, FluxMatch& match) const override; | ||||
|  | ||||
|     unsigned length() const | ||||
|     { | ||||
|         return _bits; | ||||
|     } | ||||
|  | ||||
|     unsigned intervals() const override | ||||
|     { | ||||
|         return _intervals.size(); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ syntax = "proto2"; | ||||
|  | ||||
| import "arch/agat/agat.proto"; | ||||
| import "arch/aeslanier/aeslanier.proto"; | ||||
| import "arch/aeslanier5/aeslanier5.proto"; | ||||
| import "arch/amiga/amiga.proto"; | ||||
| import "arch/apple2/apple2.proto"; | ||||
| import "arch/brother/brother.proto"; | ||||
| @@ -22,7 +23,7 @@ import "arch/zilogmcz/zilogmcz.proto"; | ||||
| import "lib/fluxsink/fluxsink.proto"; | ||||
| import "lib/config/common.proto"; | ||||
|  | ||||
| //NEXT: 33 | ||||
| //NEXT: 34 | ||||
| message DecoderProto { | ||||
| 	optional double pulse_debounce_threshold = 1 [default = 0.30, | ||||
| 		(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"]; | ||||
| @@ -37,6 +38,7 @@ message DecoderProto { | ||||
|  | ||||
| 	oneof format { | ||||
| 		AesLanierDecoderProto aeslanier = 7; | ||||
| 		AesLanier5DecoderProto aeslanier5 = 33; | ||||
| 		AgatDecoderProto agat = 28; | ||||
| 		AmigaDecoderProto amiga = 8; | ||||
| 		Apple2DecoderProto apple2 = 13; | ||||
|   | ||||
| @@ -14,6 +14,8 @@ of nearly £50,000 in 2018!). | ||||
| processing software off twin 5.25" drive units, but apparently other software | ||||
| was available. | ||||
|  | ||||
| **Note:** the following is wrong and needs to be updated. | ||||
|  | ||||
| The disk format is exceptionally weird. They used 77 track, 32 sector, single-sided | ||||
| _hard_ sectored disks, where there were multiple index holes, | ||||
| indicating to the hardware where the sectors start. The encoding scheme | ||||
| @@ -46,21 +48,56 @@ image_writer { | ||||
| 	type: IMAGETYPE_IMG | ||||
| } | ||||
|  | ||||
| decoder { | ||||
| 	aeslanier {} | ||||
| } | ||||
| option_group { | ||||
| 	comment: "$formats" | ||||
|  | ||||
| layout { | ||||
| 	format_type: FORMATTYPE_80TRACK | ||||
| 	tracks: 77 | ||||
| 	sides: 1 | ||||
| 	layoutdata { | ||||
| 		sector_size: 256 | ||||
| 		physical { | ||||
| 			start_sector: 0 | ||||
| 			count: 32 | ||||
| 	option { | ||||
| 		name: '8' | ||||
| 		comment: 'use the format found on 8" disks' | ||||
| 		set_by_default: true | ||||
|  | ||||
| 		config { | ||||
| 			decoder { | ||||
| 				aeslanier {} | ||||
| 			} | ||||
|  | ||||
| 			layout { | ||||
| 				format_type: FORMATTYPE_80TRACK | ||||
| 				tracks: 77 | ||||
| 				sides: 1 | ||||
| 				layoutdata { | ||||
| 					sector_size: 256 | ||||
| 					physical { | ||||
| 						start_sector: 0 | ||||
| 						count: 32 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	option { | ||||
| 		name: '83' | ||||
| 		comment: '83kB 5.25" 35-track 16-sector SSSD' | ||||
|  | ||||
| 		config { | ||||
| 			decoder { | ||||
| 				aeslanier5 {} | ||||
| 			} | ||||
|  | ||||
| 			layout { | ||||
| 				format_type: FORMATTYPE_40TRACK | ||||
| 				tracks: 35 | ||||
| 				sides: 1 | ||||
| 				layoutdata { | ||||
| 					sector_size: 150 | ||||
| 					physical { | ||||
| 						start_sector: 0 | ||||
| 						count: 16 | ||||
| 						skew: 3 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -246,12 +246,13 @@ void FluxViewerControl::OnPaint(wxPaintEvent&) | ||||
|             dc.SetTextForeground(*wxBLACK); | ||||
|             for (auto& sector : trackdata->sectors) | ||||
|             { | ||||
|                 nanoseconds_t sr = sector->dataEndTime; | ||||
|                 if (!sr) | ||||
|                     sr = sector->headerEndTime; | ||||
|                 nanoseconds_t tl = sector->headerStartTime; | ||||
|                 nanoseconds_t tr = sector->dataEndTime; | ||||
|                 if (!tl && !sector->headerEndTime) | ||||
|                     tl = sector->dataStartTime; | ||||
|  | ||||
|                 int sp = sector->headerStartTime / _nanosecondsPerPixel; | ||||
|                 int sw = (sr - sector->headerStartTime) / _nanosecondsPerPixel; | ||||
|                 int sp = tl / _nanosecondsPerPixel; | ||||
|                 int sw = (tr - tl) / _nanosecondsPerPixel; | ||||
|  | ||||
|                 wxRect rect = {x + sp, t1y - ch2, sw, ch}; | ||||
|                 bool hovered = rect.Contains(_mouseX, _mouseY); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user