mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Merge.
This commit is contained in:
		| @@ -6,6 +6,7 @@ | ||||
| #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. */ | ||||
| static const FluxPattern SECTOR_SYNC_PATTERN(32, 0xaaaa5555); | ||||
| @@ -28,7 +29,8 @@ class MicropolisDecoder : public AbstractDecoder | ||||
| { | ||||
| public: | ||||
| 	MicropolisDecoder(const DecoderProto& config): | ||||
| 		AbstractDecoder(config) | ||||
| 		AbstractDecoder(config), | ||||
| 		_config(config.micropolis()) | ||||
| 	{} | ||||
|  | ||||
| 	RecordType advanceToNextRecord() | ||||
| @@ -59,13 +61,21 @@ public: | ||||
| 			return; | ||||
|  | ||||
| 		br.read(10);  /* OS data or padding */ | ||||
| 		_sector->data = br.read(256); | ||||
| 		auto data = br.read(256); | ||||
| 		uint8_t wantChecksum = br.read_8(); | ||||
| 		uint8_t gotChecksum = micropolisChecksum(bytes.slice(1, 2+266)); | ||||
| 		br.read(5);  /* 4 byte ECC and ECC-present flag */ | ||||
|  | ||||
| 		if (_config.sector_output_size() == 256) | ||||
| 			_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; | ||||
| 	} | ||||
| private: | ||||
| 	const MicropolisDecoderProto& _config; | ||||
| }; | ||||
|  | ||||
| std::unique_ptr<AbstractDecoder> createMicropolisDecoder(const DecoderProto& config) | ||||
|   | ||||
| @@ -19,7 +19,15 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::s | ||||
| 		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); | ||||
|   | ||||
| @@ -1,5 +1,11 @@ | ||||
| syntax = "proto2"; | ||||
|  | ||||
| message MicropolisDecoderProto {} | ||||
| 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"]; | ||||
| } | ||||
|  | ||||
| message MicropolisEncoderProto {} | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| Disk: Micropolis | ||||
| ================ | ||||
|  | ||||
| Micropolis MetaFloppy disks use MFM and hard sectors. They were 100 TPI and | ||||
| stored 315k per side. Each of the 16 sectors contains 266 bytes of "user data," | ||||
| allowing 10 bytes of metadata for use by the operating system. Micropolis DOS | ||||
| (MDOS) used the metadata bytes, but CP/M did not. | ||||
| Micropolis MetaFloppy disks use MFM and hard sectors. Mod I was 48 TPI and | ||||
| stored 143k per side. Mod II was 100 TPI and stored 315k per side. Each of the | ||||
| 16 sectors contains 266 bytes of "user data," allowing 10 bytes of metadata for | ||||
| use by the operating system. Micropolis DOS (MDOS) used the metadata bytes, but | ||||
| CP/M did not. | ||||
|  | ||||
| Some later systems were Micropolis-compatible and so were also 100 TPI, like | ||||
| the Vector Graphic Dual-Mode Disk Controller which was paired with a Tandon | ||||
| @@ -19,31 +20,55 @@ pinout as a 96tpi PC 5.25" drive. In use they should be identical. | ||||
| Reading disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| Based on your floppy drive, just do one of: | ||||
|  | ||||
| ``` | ||||
| fluxengine read micropolis | ||||
| fluxengine read micropolis143 # single-sided Mod I | ||||
| fluxengine read micropolis287 # double-sided Mod I | ||||
| fluxengine read micropolis315 # single-sided Mod II | ||||
| fluxengine read micropolis630 # double-sided Mod II | ||||
| ``` | ||||
|  | ||||
| You should end up with a `micropolis.img` which is 630784 bytes long (for a | ||||
| normal DD disk). The image is written in CHS order, but HCS is generally used | ||||
| by CP/M tools so the image needs to be post-processed. For only half-full disks | ||||
| or single-sided disks, you can use `--heads 0` to read only one side of the | ||||
| disk which works around the problem. | ||||
| You should end up with a `micropolis.img` of the corresponding size. The image | ||||
| is written in CHS order, but HCS is generally used by CP/M tools so | ||||
| double-sided disk images may need to be post-processed. Half-full double-sided | ||||
| disks can be read as single-sided disks to work around the problem. | ||||
|  | ||||
| The [CP/M BIOS](https://www.seasip.info/Cpm/bios.html) defined SELDSK, SETTRK, | ||||
| and SETSEC, but no function to select the head/side. Double-sided floppies | ||||
| could be represented as having either twice the number of sectors, for CHS, or | ||||
| twice the number of tracks, HCS; the second side's tracks logically followed | ||||
| the first side (e.g., tracks 77-153). Micropolis disks tended to be the latter. | ||||
| twice the number of tracks, HCS; the second side's tracks in opposite order | ||||
| logically followed the first side (e.g., tracks 77-153). Micropolis disks | ||||
| tended to be the latter. | ||||
|  | ||||
| It's also possible to output to VGI, which retains OS-specific "user data" and | ||||
| machine-specific ECC. Add "vgi" to the command line after the chosen Micropolis | ||||
| profile: | ||||
| ``` | ||||
| fluxengine read micropolis143 vgi # single-sided Mod I | ||||
| fluxengine read micropolis287 vgi # double-sided Mod I | ||||
| fluxengine read micropolis315 vgi # single-sided Mod II | ||||
| fluxengine read micropolis630 vgi # double-sided Mod II | ||||
| ``` | ||||
|  | ||||
| You should end up with a `micropolis.vgi` instead. The format is well-defined | ||||
| for double-sided disks so post-processing is not necessary. | ||||
|  | ||||
| Writing disks | ||||
| ------------- | ||||
|  | ||||
| Just do: | ||||
| Just do one of: | ||||
|  | ||||
| ``` | ||||
| fluxengine write micropolis -i micropolis.img | ||||
| fluxengine write micropolis143 # single-sided Mod I | ||||
| fluxengine write micropolis287 # double-sided Mod I | ||||
| fluxengine write micropolis315 # single-sided Mod II | ||||
| fluxengine write micropolis630 # double-sided Mod II | ||||
|  | ||||
| fluxengine write micropolis143 vgi # single-sided Mod I | ||||
| fluxengine write micropolis287 vgi # double-sided Mod I | ||||
| fluxengine write micropolis315 vgi # single-sided Mod II | ||||
| fluxengine write micropolis630 vgi # double-sided Mod II | ||||
| ``` | ||||
|  | ||||
| Useful references | ||||
|   | ||||
| @@ -9,9 +9,10 @@ import "lib/fluxsink/fluxsink.proto"; | ||||
| import "lib/usb/usb.proto"; | ||||
| import "lib/common.proto"; | ||||
|  | ||||
| // NEXT_TAG: 13 | ||||
| // NEXT_TAG: 14 | ||||
| message ConfigProto { | ||||
| 	optional string comment = 8; | ||||
| 	optional bool is_extension = 13; | ||||
|  | ||||
| 	optional ImageReaderProto image_reader = 12; | ||||
| 	optional ImageWriterProto image_writer = 9; | ||||
|   | ||||
| @@ -71,6 +71,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st | ||||
| 		{".nfd",      [&]() { proto->mutable_nfd(); }}, | ||||
| 		{".nsi",      [&]() { proto->mutable_nsi(); }}, | ||||
| 		{".td0",      [&]() { proto->mutable_td0(); }}, | ||||
| 		{".vgi",      [&]() { proto->mutable_img(); }}, | ||||
| 		{".xdf",      [&]() { proto->mutable_img(); }}, | ||||
| 	}; | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,7 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st | ||||
| 		{".nsi",      [&]() { proto->mutable_nsi(); }}, | ||||
| 		{".raw",      [&]() { proto->mutable_raw(); }}, | ||||
| 		{".st",       [&]() { proto->mutable_img(); }}, | ||||
| 		{".vgi",      [&]() { proto->mutable_img(); }}, | ||||
| 		{".xdf",      [&]() { proto->mutable_img(); }}, | ||||
| 	}; | ||||
|  | ||||
|   | ||||
| @@ -518,13 +518,17 @@ FORMATS="\ | ||||
|     ibm720_525 \ | ||||
|     mac400 \ | ||||
|     mac800 \ | ||||
|     micropolis \ | ||||
|     micropolis143 \ | ||||
|     micropolis287 \ | ||||
|     micropolis315 \ | ||||
|     micropolis630 \ | ||||
|     mx \ | ||||
|     n88basic \ | ||||
|     northstar175 \ | ||||
|     northstar350 \ | ||||
|     northstar87 \ | ||||
|     tids990 \ | ||||
|     vgi \ | ||||
|     victor9k_ss \ | ||||
|     victor9k_ds \ | ||||
|     zilogmcz \ | ||||
|   | ||||
| @@ -101,7 +101,7 @@ static void globalHelp() | ||||
|  | ||||
| void showProfiles(const std::string& command, const std::map<std::string, std::string>& profiles) | ||||
| { | ||||
| 	std::cout << "syntax: fluxengine " << command << " <profile> [<options>...]\n" | ||||
| 	std::cout << "syntax: fluxengine " << command << " <profile> [<extensions...>] [<options>...]\n" | ||||
| 				 "Use --help for option help.\n" | ||||
| 	             "Available profiles include:\n"; | ||||
|  | ||||
| @@ -110,10 +110,22 @@ void showProfiles(const std::string& command, const std::map<std::string, std::s | ||||
| 		ConfigProto config; | ||||
| 		if (!config.ParseFromString(it.second)) | ||||
| 			Error() << "couldn't load config proto"; | ||||
| 		std::cout << fmt::format("  {}: {}\n", it.first, config.comment()); | ||||
| 		if (!config.is_extension()) | ||||
| 			std::cout << fmt::format("  {}: {}\n", it.first, config.comment()); | ||||
| 	} | ||||
|  | ||||
| 	std::cout << "Or use a text file containing your own configuration.\n"; | ||||
| 	std::cout << "Available profile options include:\n"; | ||||
|  | ||||
| 	for (const auto& it : profiles) | ||||
| 	{ | ||||
| 		ConfigProto config; | ||||
| 		if (!config.ParseFromString(it.second)) | ||||
| 			Error() << "couldn't load config proto"; | ||||
| 		if (config.is_extension()) | ||||
| 			std::cout << fmt::format("  {}: {}\n", it.first, config.comment()); | ||||
| 	} | ||||
|  | ||||
| 	std::cout << "Profiles and extensions may also be textpb files .\n"; | ||||
| 	exit(1); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										61
									
								
								src/formats/micropolis143.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/formats/micropolis143.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod I 143kB 5.25" SSDD hard-sectored' | ||||
|  | ||||
| flux_source { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| flux_sink { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_reader { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 35 | ||||
| 		sides: 1 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_writer { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 35 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| encoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| decoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| cylinders { | ||||
| 	start: 0 | ||||
| 	end: 34 | ||||
| } | ||||
|  | ||||
| heads { | ||||
| 	start: 0 | ||||
| 	end: 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										61
									
								
								src/formats/micropolis287.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/formats/micropolis287.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod I 287kB 5.25" DSDD hard-sectored' | ||||
|  | ||||
| flux_source { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| flux_sink { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_reader { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 35 | ||||
| 		sides: 2 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_writer { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 35 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| encoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| decoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| cylinders { | ||||
| 	start: 0 | ||||
| 	end: 34 | ||||
| } | ||||
|  | ||||
| heads { | ||||
| 	start: 0 | ||||
| 	end: 1 | ||||
| } | ||||
|  | ||||
							
								
								
									
										61
									
								
								src/formats/micropolis315.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/formats/micropolis315.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| comment: 'Micropolis MetaFloppy Mod II 315kB 5.25" SSDD hard-sectored' | ||||
|  | ||||
| flux_source { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| flux_sink { | ||||
| 	drive { | ||||
| 		hard_sector_count: 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_reader { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 77 | ||||
| 		sides: 1 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_writer { | ||||
| 	filename: "micropolis.img" | ||||
| 	img { | ||||
| 		tracks: 77 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| encoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| decoder { | ||||
| 	micropolis {} | ||||
| } | ||||
|  | ||||
| cylinders { | ||||
| 	start: 0 | ||||
| 	end: 76 | ||||
| } | ||||
|  | ||||
| heads { | ||||
| 	start: 0 | ||||
| 	end: 0 | ||||
| } | ||||
|  | ||||
| @@ -1,4 +1,4 @@ | ||||
| comment: 'Micropolis MetaFloppy 630kB 5.25" DSDD hard-sectored' | ||||
| comment: 'Micropolis MetaFloppy Mod II 630kB 5.25" DSDD hard-sectored' | ||||
| 
 | ||||
| flux_source { | ||||
| 	drive { | ||||
| @@ -29,7 +29,16 @@ image_reader { | ||||
| 
 | ||||
| image_writer { | ||||
| 	filename: "micropolis.img" | ||||
| 	img {} | ||||
| 	img { | ||||
| 		tracks: 77 | ||||
| 		trackdata { | ||||
| 			sector_size: 256 | ||||
| 			sector_range { | ||||
| 				start_sector: 0 | ||||
| 				sector_count: 16 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| encoder { | ||||
							
								
								
									
										27
									
								
								src/formats/vgi.textpb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/formats/vgi.textpb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| comment: 'Adjust a Micropolis profile to use the VGI image format' | ||||
| is_extension: true | ||||
|  | ||||
| image_reader { | ||||
| 	filename: "micropolis.vgi" | ||||
| 	img { | ||||
| 		trackdata { | ||||
| 			sector_size: 275 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| image_writer { | ||||
| 	filename: "micropolis.vgi" | ||||
| 	img { | ||||
| 		trackdata { | ||||
| 			sector_size: 275 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| decoder { | ||||
| 	micropolis { | ||||
| 		sector_output_size: 275 | ||||
| 	} | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user