mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			FluxEngine
			...
			FluxEngine
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ebcb9c4bb0 | ||
|  | 2520834b18 | ||
|  | a1f3087046 | ||
|  | e9670e205e | ||
|  | 658e2b7295 | ||
|  | 7b4a8d6de2 | ||
|  | e8f7b51aef | ||
|  | 9d6bc57a5f | ||
|  | 73766f92b4 | ||
|  | 80badf3b54 | ||
|  | 116529f85a | ||
|  | 5a2b2bc07a | 
| @@ -4122,8 +4122,8 @@ | ||||
| :400540008B029E80A580AF20C820E280EA4073807F029B80A302AB80DC80DE20EA8005200B800C020D040F08520254205E4061028C028F04924095089780A202AF04C001D7 | ||||
| :40058000C20DD403D604D802E202E602A920AD02B420EC0801010D010F0111011D0100BD012A10050820324039804620082010C42000080280000000FE1001ED00E3001C2E | ||||
| :4005C00000000000000000000000040182EF3D10000000083800C11F07E0000000080220000010004603020000E0000412FFFFFF0020F00120100000000008000404040460 | ||||
| :4006000099990001000800004020800840200008000040A8000000000000000000000000000000003F00003F1F001F0000000000000000001000000040000000000000003B | ||||
| :40064000C0000000FF0020FF4700470000010000A00000BFBF009F00000000000001000000000000000000000000000000000000000000000000000000000000000000004F | ||||
| :4006000099990001000800004020800840200008000040A8000000000000000000000000000000003F0000201F001F0000000000000000001000000040000000000000005A | ||||
| :40064000C0000000FF0000B84700470000010000A000003FBF009F000000000000010000000000000000000000000000000000000000000000000000000000000000000036 | ||||
| :40068000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003A | ||||
| :4006C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FA | ||||
| :4007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B9 | ||||
| @@ -4615,12 +4615,12 @@ | ||||
| :0200000490105A | ||||
| :04000000BC90ACAF55 | ||||
| :0200000490303A | ||||
| :020000004C1A98 | ||||
| :020000004B149F | ||||
| :0200000490402A | ||||
| :4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0 | ||||
| :400040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080 | ||||
| :400080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040 | ||||
| :4000C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 | ||||
| :0200000490501A | ||||
| :0C00000000012E16106900002E2A5C83FF | ||||
| :0C00000000012E16106900002E2A5B7D06 | ||||
| :00000001FF | ||||
										
											Binary file not shown.
										
									
								
							| @@ -129,8 +129,8 @@ reverse engineered it to find out. | ||||
|  | ||||
| Standard Linux mtools will access the filesystem image and allow you to move | ||||
| files in and out. However, you'll need to change the media type bytes at | ||||
| offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. Once | ||||
| done, this will work: | ||||
| offsets 0x015 and 0x100 from 0x58 to 0xf0 before mtools will touch it. The | ||||
| supplied `brother240tool` will do this. Once done, this will work: | ||||
|  | ||||
| ``` | ||||
| mdir -i brother.img | ||||
|   | ||||
| @@ -123,8 +123,12 @@ admittedly expensive.) | ||||
|     sheet](http://www.bitsavers.org/pdf/mitsubishi/floppy/M4851/TJ2-G30211A_M4851_DSHH_48TPI_OEM_Manual_Nov83.pdf): | ||||
|     the equivalent data sheet for a representative 5.25" drive. | ||||
|  | ||||
|   - [The DRG Business Machines YD-174 manual](https://electrickery.hosting.philpem.me.uk/comp/divcomp/doc/YE_Data_YD-174_8inchFloppyDriveTechnicalManual.pdf): | ||||
| 	the equivalent manual (data sheets hadn't been invented then) for a | ||||
| 	representative 8" drive. | ||||
|  | ||||
|   - [KryoFlux stream file | ||||
|     documentation](https://www.kryoflux.com/download/kryoflux_stream_protocol_rev1.1.pdf): | ||||
|     the format of KryoFlux stream files (partially supported by FluxEngine) | ||||
|  | ||||
|    | ||||
|    | ||||
|   | ||||
							
								
								
									
										18
									
								
								doc/using.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								doc/using.md
									
									
									
									
									
								
							| @@ -221,11 +221,19 @@ directory. | ||||
|   file format in a non-backwards-compatible way; this tool will upgrade flux | ||||
|   files to the new format. | ||||
|  | ||||
|   - `fluxengine convert`: converts various formats to various other formats. | ||||
|   You can use this to convert Catweasel or Supercard Pro flux files to | ||||
|   FluxEngine's native format, for flux files to various other formats useful | ||||
|   for debugging (including VCD which can be loaded into | ||||
|   [sigrok](http://sigrok.org)). | ||||
|   - `fluxengine convert`: converts flux files from various formats to various | ||||
|   other formats. You can use this to convert Catweasel flux files to | ||||
|   FluxEngine's native format, FluxEngine flux files to various other formats | ||||
|   useful for debugging (including VCD which can be loaded into | ||||
|   [sigrok](http://sigrok.org)), and bidirectional conversion to and from | ||||
|   Supercard Pro `.scp` format. | ||||
|  | ||||
|   **Important SCP note:** import (`fluxengine convert scptoflux`) should be | ||||
|   fairly robust, but export (`fluxengine convert fluxtoscp`) should only be | ||||
|   done with great caution as FluxEngine files contain features which can't be | ||||
|   represented very well in `.scp` format and they're probably pretty dubious. | ||||
|   As ever, please [get in | ||||
|   touch](https://github.com/davidgiven/fluxengine/issues/new) with any reports. | ||||
|  | ||||
| Commands which normally take `--source` or `--dest` get a sensible default if | ||||
| left unspecified. `fluxengine read ibm` on its own will read drive 0 and | ||||
|   | ||||
							
								
								
									
										88
									
								
								lib/flags.cc
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								lib/flags.cc
									
									
									
									
									
								
							| @@ -29,7 +29,7 @@ void FlagGroup::addFlag(Flag* flag) | ||||
|     _flags.push_back(flag); | ||||
| } | ||||
|  | ||||
| void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
| std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[]) | ||||
| { | ||||
|     if (_initialised) | ||||
|         throw std::runtime_error("called parseFlags() twice"); | ||||
| @@ -66,6 +66,7 @@ void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
|  | ||||
|     /* Now actually parse them. */ | ||||
|  | ||||
|     std::vector<std::string> filenames; | ||||
|     int index = 1; | ||||
|     while (index < argc) | ||||
|     { | ||||
| @@ -76,52 +77,73 @@ void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
|         std::string value; | ||||
|         bool usesthat = false; | ||||
|  | ||||
|         if ((thisarg.size() == 0) || (thisarg[0] != '-')) | ||||
|             Error() << "non-option parameter " << thisarg << " seen (try --help)"; | ||||
|         if ((thisarg.size() > 1) && (thisarg[1] == '-')) | ||||
|         if (thisarg.size() == 0) | ||||
|         { | ||||
|             /* Long option. */ | ||||
|  | ||||
|             auto equals = thisarg.rfind('='); | ||||
|             if (equals != std::string::npos) | ||||
|             { | ||||
|                 key = thisarg.substr(0, equals); | ||||
|                 value = thisarg.substr(equals+1); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 key = thisarg; | ||||
|                 value = thatarg; | ||||
|                 usesthat = true; | ||||
|             } | ||||
|             /* Ignore this argument. */ | ||||
|         } | ||||
|         else if (thisarg[0] != '-') | ||||
|         { | ||||
|             /* This is a filename. */ | ||||
|             filenames.push_back(thisarg); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             /* Short option. */ | ||||
|             /* This is a flag. */ | ||||
|  | ||||
|             if (thisarg.size() > 2) | ||||
|             if ((thisarg.size() > 1) && (thisarg[1] == '-')) | ||||
|             { | ||||
|                 key = thisarg.substr(0, 2); | ||||
|                 value = thisarg.substr(2); | ||||
|                 /* Long option. */ | ||||
|  | ||||
|                 auto equals = thisarg.rfind('='); | ||||
|                 if (equals != std::string::npos) | ||||
|                 { | ||||
|                     key = thisarg.substr(0, equals); | ||||
|                     value = thisarg.substr(equals+1); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     key = thisarg; | ||||
|                     value = thatarg; | ||||
|                     usesthat = true; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 key = thisarg; | ||||
|                 value = thatarg; | ||||
|                 usesthat = true; | ||||
|                 /* Short option. */ | ||||
|  | ||||
|                 if (thisarg.size() > 2) | ||||
|                 { | ||||
|                     key = thisarg.substr(0, 2); | ||||
|                     value = thisarg.substr(2); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     key = thisarg; | ||||
|                     value = thatarg; | ||||
|                     usesthat = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             auto flag = flags_by_name.find(key); | ||||
|             if (flag == flags_by_name.end()) | ||||
|                 Error() << "unknown flag '" << key << "'; try --help"; | ||||
|  | ||||
|             flag->second->set(value); | ||||
|             if (usesthat && flag->second->hasArgument()) | ||||
|                 index++; | ||||
|         } | ||||
|  | ||||
|         auto flag = flags_by_name.find(key); | ||||
|         if (flag == flags_by_name.end()) | ||||
|             Error() << "unknown flag '" << key << "'; try --help"; | ||||
|  | ||||
|         flag->second->set(value); | ||||
|  | ||||
|         index++; | ||||
|         if (usesthat && flag->second->hasArgument()) | ||||
|             index++; | ||||
|     } | ||||
|  | ||||
|     return filenames; | ||||
| } | ||||
|  | ||||
| void FlagGroup::parseFlags(int argc, const char* argv[]) | ||||
| { | ||||
|     auto filenames = parseFlagsWithFilenames(argc, argv); | ||||
|     if (!filenames.empty()) | ||||
|         Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)"; | ||||
| } | ||||
|  | ||||
| void FlagGroup::checkInitialised() const | ||||
|   | ||||
| @@ -14,6 +14,7 @@ public: | ||||
|  | ||||
| public: | ||||
|     void parseFlags(int argc, const char* argv[]); | ||||
|     std::vector<std::string> parseFlagsWithFilenames(int argc, const char* argv[]); | ||||
|     void addFlag(Flag* flag); | ||||
|     void checkInitialised() const; | ||||
|  | ||||
|   | ||||
| @@ -190,6 +190,7 @@ buildlibrary libfrontend.a \ | ||||
|     src/fe-cwftoflux.cc \ | ||||
|     src/fe-erase.cc \ | ||||
|     src/fe-fluxtoau.cc \ | ||||
|     src/fe-fluxtoscp.cc \ | ||||
|     src/fe-fluxtovcd.cc \ | ||||
|     src/fe-inspect.cc \ | ||||
|     src/fe-readadfs.cc \ | ||||
| @@ -232,6 +233,13 @@ buildsimpleprogram brother120tool \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| buildsimpleprogram brother240tool \ | ||||
| 	-Idep/emu \ | ||||
|     tools/brother240tool.cc \ | ||||
|     libbackend.a \ | ||||
|     libemu.a \ | ||||
|     libfmt.a \ | ||||
|  | ||||
| runtest bitaccumulator-test tests/bitaccumulator.cc | ||||
| runtest bytes-test          tests/bytes.cc | ||||
| runtest compression-test    tests/compression.cc | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| #include "fluxmap.h" | ||||
| #include "writer.h" | ||||
|  | ||||
| static FlagGroup flags; | ||||
| static FlagGroup flags { &writerFlags }; | ||||
|  | ||||
| int mainErase(int argc, const char* argv[]) | ||||
| { | ||||
|   | ||||
							
								
								
									
										188
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/fe-fluxtoscp.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| #include "globals.h" | ||||
| #include "flags.h" | ||||
| #include "fluxmap.h" | ||||
| #include "sql.h" | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "dataspec.h" | ||||
| #include "fmt/format.h" | ||||
| #include "decoders/fluxmapreader.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
| #include <algorithm> | ||||
|  | ||||
| static FlagGroup flags { }; | ||||
|  | ||||
| static SettableFlag fortyTrackMode( | ||||
|     { "--48", "-4" }, | ||||
|     "set 48 tpi mode; only every other physical track is emitted" | ||||
| ); | ||||
|  | ||||
| static SettableFlag singleSided( | ||||
|     { "--single-sided", "-s" }, | ||||
|     "only emit side 0" | ||||
| ); | ||||
|  | ||||
| static IntFlag diskType( | ||||
|     { "--disk-type" }, | ||||
|     "sets the SCP disk type byte", | ||||
|     0xff | ||||
| ); | ||||
|  | ||||
| static sqlite3* inputDb; | ||||
|  | ||||
| static void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: fluxengine convert fluxtoscp <fluxfile> <scpfile>\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| static void write_le32(uint8_t dest[4], uint32_t v) | ||||
| { | ||||
|     dest[0] = v; | ||||
|     dest[1] = v >> 8; | ||||
|     dest[2] = v >> 16; | ||||
|     dest[3] = v >> 24; | ||||
| } | ||||
|  | ||||
| static int strackno(int track, int side) | ||||
| { | ||||
|     if (fortyTrackMode) | ||||
|         track /= 2; | ||||
|     if (singleSided) | ||||
|         return track; | ||||
|     else | ||||
|         return (track << 1) | side; | ||||
| } | ||||
|  | ||||
| int mainConvertFluxToScp(int argc, const char* argv[]) | ||||
| { | ||||
|     auto filenames = flags.parseFlagsWithFilenames(argc, argv); | ||||
|     if (filenames.size() != 2) | ||||
|         syntax(); | ||||
|  | ||||
|     inputDb = sqlOpen(filenames[0], SQLITE_OPEN_READONLY); | ||||
|     auto tracks = sqlFindFlux(inputDb); | ||||
|  | ||||
|     int maxTrack = 0; | ||||
|     int maxSide = 0; | ||||
|     for (auto p : tracks) | ||||
|     { | ||||
|         if (singleSided && (p.second == 1)) | ||||
|             continue; | ||||
|         maxTrack = std::max(maxTrack, (int)p.first); | ||||
|         maxSide = std::max(maxSide, (int)p.second); | ||||
|     } | ||||
|     int maxStrack = strackno(maxTrack, maxSide); | ||||
|  | ||||
|     std::cout << fmt::format("Writing {} {} SCP file containing {} SCP tracks\n", | ||||
|         fortyTrackMode ? "48 tpi" : "96 tpi", | ||||
|         singleSided ? "single sided" : "double sided", | ||||
|         maxStrack + 1 | ||||
|     ); | ||||
|  | ||||
|     ScpHeader fileheader = {0}; | ||||
|     fileheader.file_id[0] = 'S'; | ||||
|     fileheader.file_id[1] = 'C'; | ||||
|     fileheader.file_id[2] = 'P'; | ||||
|     fileheader.file_id[3] = 0x18; /* Version 1.8 of the spec */ | ||||
|     fileheader.type = diskType; | ||||
|     fileheader.revolutions = 5; | ||||
|     fileheader.start_track = 0; | ||||
|     fileheader.end_track = maxStrack; | ||||
|     fileheader.flags = SCP_FLAG_INDEXED | (fortyTrackMode ? 0 : SCP_FLAG_96TPI); | ||||
|     fileheader.cell_width = 0; | ||||
|     fileheader.heads = singleSided ? 1 : 0; | ||||
|  | ||||
|     Bytes trackdata; | ||||
|     ByteWriter trackdataWriter(trackdata); | ||||
|  | ||||
|     int trackstep = 1 + fortyTrackMode; | ||||
|     int maxside = singleSided ? 0 : 1; | ||||
|     for (int track = 0; track <= maxTrack; track += trackstep) | ||||
|     { | ||||
|         for (int side = 0; side <= maxside; side++) | ||||
|         { | ||||
|             int strack = strackno(track, side); | ||||
|             std::cout << fmt::format("FE track {}.{}, SCP track {}: ", track, side, strack) << std::flush; | ||||
|  | ||||
|             auto fluxmap = sqlReadFlux(inputDb, track, side); | ||||
|             ScpTrack trackheader = {0}; | ||||
|             trackheader.track_id[0] = 'T'; | ||||
|             trackheader.track_id[1] = 'R'; | ||||
|             trackheader.track_id[2] = 'K'; | ||||
|             trackheader.strack = strack; | ||||
|  | ||||
|             FluxmapReader fmr(*fluxmap); | ||||
|             Bytes fluxdata; | ||||
|             ByteWriter fluxdataWriter(fluxdata); | ||||
|  | ||||
|             int revolution = 0; | ||||
|             unsigned revTicks = 0; | ||||
|             unsigned totalTicks = 0; | ||||
|             unsigned ticksSinceLastPulse = 0; | ||||
|             uint32_t startOffset = 0; | ||||
|             while (revolution < 5) | ||||
|             { | ||||
|                 unsigned ticks; | ||||
|                 int opcode = fmr.readOpcode(ticks); | ||||
|                 if (ticks) | ||||
|                 { | ||||
|                     ticksSinceLastPulse += ticks; | ||||
|                     totalTicks += ticks; | ||||
|                     revTicks += ticks; | ||||
|                 } | ||||
|  | ||||
|                 switch (opcode) | ||||
|                 { | ||||
|                     case -1: /* end of flux, treat like an index marker */ | ||||
|                     case F_OP_INDEX: | ||||
|                     { | ||||
|                         auto* revheader = &trackheader.revolution[revolution]; | ||||
|                         write_le32(revheader->offset, startOffset + sizeof(ScpTrack)); | ||||
|                         write_le32(revheader->length, (fluxdataWriter.pos - startOffset) / 2); | ||||
|                         write_le32(revheader->index, revTicks * NS_PER_TICK / 25); | ||||
|                         revolution++; | ||||
|                         revheader++; | ||||
|                         revTicks = 0; | ||||
|                         startOffset = fluxdataWriter.pos; | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     case F_OP_PULSE: | ||||
|                     { | ||||
|                         unsigned t = ticksSinceLastPulse * NS_PER_TICK / 25; | ||||
|                         while (t >= 0x10000) | ||||
|                         { | ||||
|                             fluxdataWriter.write_be16(0); | ||||
|                             t -= 0x10000; | ||||
|                         } | ||||
|                         fluxdataWriter.write_be16(t); | ||||
|                         ticksSinceLastPulse = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             write_le32(fileheader.track[strack], trackdataWriter.pos + sizeof(ScpHeader)); | ||||
|             trackdataWriter += Bytes((uint8_t*)&trackheader, sizeof(trackheader)); | ||||
|             trackdataWriter += fluxdata; | ||||
|  | ||||
|             std::cout << fmt::format("{} ms in {} bytes\n", | ||||
|                 totalTicks * MS_PER_TICK, | ||||
|                 fluxdata.size()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sqlClose(inputDb); | ||||
|      | ||||
|     std::cout << "Writing output file...\n"; | ||||
|     std::ofstream of(filenames[1], std::ios::out | std::ios::binary); | ||||
|     if (!of.is_open()) | ||||
|         Error() << "cannot open output file"; | ||||
|     of.write((const char*) &fileheader, sizeof(fileheader)); | ||||
|     of.write((const char*) trackdata.begin(), trackdata.size()); | ||||
|     of.close(); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -8,7 +8,7 @@ static FlagGroup flags; | ||||
| static DataSpecFlag source( | ||||
|     { "--source", "-s" }, | ||||
|     "source for data", | ||||
|     ":d=0"); | ||||
|     ":d=0:t=0:s=0"); | ||||
|  | ||||
| int mainRpm(int argc, const char* argv[]) | ||||
| { | ||||
|   | ||||
| @@ -4,37 +4,9 @@ | ||||
| #include "bytes.h" | ||||
| #include "protocol.h" | ||||
| #include "fmt/format.h" | ||||
| #include "scp.h" | ||||
| #include <fstream> | ||||
|  | ||||
| struct ScpHeader | ||||
| { | ||||
|     char file_id[3];       // file ID - 'SCP' | ||||
|     uint8_t version;       // major/minor in nibbles | ||||
|     uint8_t type;          // disk type - subclass/class in nibbles | ||||
|     uint8_t revolutions;   // up to 5 | ||||
|     uint8_t start_track;   // 0..165 | ||||
|     uint8_t end_track;     // 0..165 | ||||
|     uint8_t flags;         // see below | ||||
|     uint8_t cell_width;    // in bits, 0 meaning 16 | ||||
|     uint8_t heads;         // 0 = both, 1 = side 0 only, 2 = side 1 only | ||||
|     uint8_t resolution;    // 25ns * (resolution+1) | ||||
|     uint8_t checksum[4];   // of data after this point | ||||
|     uint8_t track[165][4]; // track offsets, not necessarily 165 | ||||
| }; | ||||
|  | ||||
| struct ScpTrack | ||||
| { | ||||
|     char track_id[3];      // 'TRK' | ||||
|     uint8_t strack;        // SCP track number | ||||
|     struct | ||||
|     { | ||||
|         uint8_t index[4];  // time for one revolution | ||||
|         uint8_t length[4]; // number of bitcells | ||||
|         uint8_t offset[4]; // offset to bitcell data, relative to track header | ||||
|     } | ||||
|     revolution[5]; | ||||
| }; | ||||
|  | ||||
| static std::ifstream inputFile; | ||||
| static sqlite3* outputDb; | ||||
| static ScpHeader header; | ||||
| @@ -109,6 +81,7 @@ static void read_track(int strack) | ||||
|  | ||||
|     Fluxmap fluxmap; | ||||
|     nanoseconds_t pending = 0; | ||||
|     unsigned inputBytes = 0; | ||||
|     for (int revolution = 0; revolution < header.revolutions; revolution++) | ||||
|     { | ||||
|         if (revolution != 0) | ||||
| @@ -133,12 +106,14 @@ static void read_track(int strack) | ||||
|                 pending = 0; | ||||
|             } | ||||
|             else | ||||
|                 pending += interval; | ||||
|                 pending += 0x10000; | ||||
|         } | ||||
|  | ||||
|         inputBytes += datalength*2; | ||||
|     } | ||||
|  | ||||
|     std::cout << fmt::format(" {} ms in {} output bytes\n", | ||||
|         fluxmap.duration() / 1e6, fluxmap.bytes()); | ||||
|     std::cout << fmt::format(" {} ms in {} input bytes and {} output bytes\n", | ||||
|         fluxmap.duration() / 1e6, inputBytes, fluxmap.bytes()); | ||||
|     sqlWriteFlux(outputDb, trackno(strack), headno(strack), fluxmap); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ typedef int command_cb(int agrc, const char* argv[]); | ||||
| extern command_cb mainErase; | ||||
| extern command_cb mainConvertCwfToFlux; | ||||
| extern command_cb mainConvertFluxToAu; | ||||
| extern command_cb mainConvertFluxToScp; | ||||
| extern command_cb mainConvertFluxToVcd; | ||||
| extern command_cb mainConvertScpToFlux; | ||||
| extern command_cb mainInspect; | ||||
| @@ -86,6 +87,7 @@ static std::vector<Command> convertables = | ||||
|     { "cwftoflux",     mainConvertCwfToFlux, "Converts CatWeasel stream files to flux.", }, | ||||
|     { "scptoflux",     mainConvertScpToFlux, "Converts Supercard Pro stream files to flux.", }, | ||||
|     { "fluxtoau",      mainConvertFluxToAu,  "Converts (one track of a) flux file to an .au audio file.", }, | ||||
|     { "fluxtoscp",     mainConvertFluxToScp, "Converrt a flux file to a Supercard Pro file.", }, | ||||
|     { "fluxtovcd",     mainConvertFluxToVcd, "Converts (one track of a) flux file to a VCD file.", }, | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										43
									
								
								src/scp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/scp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| #ifndef SCP_H | ||||
| #define SCP_H | ||||
|  | ||||
| struct ScpHeader | ||||
| { | ||||
|     char file_id[3];       // file ID - 'SCP' | ||||
|     uint8_t version;       // major/minor in nibbles | ||||
|     uint8_t type;          // disk type - subclass/class in nibbles | ||||
|     uint8_t revolutions;   // up to 5 | ||||
|     uint8_t start_track;   // 0..165 | ||||
|     uint8_t end_track;     // 0..165 | ||||
|     uint8_t flags;         // see below | ||||
|     uint8_t cell_width;    // in bits, 0 meaning 16 | ||||
|     uint8_t heads;         // 0 = both, 1 = side 0 only, 2 = side 1 only | ||||
|     uint8_t resolution;    // 25ns * (resolution+1) | ||||
|     uint8_t checksum[4];   // of data after this point | ||||
|     uint8_t track[165][4]; // track offsets, not necessarily 165 | ||||
| }; | ||||
|  | ||||
| enum | ||||
| { | ||||
|     SCP_FLAG_INDEXED    = (1<<0), | ||||
|     SCP_FLAG_96TPI      = (1<<1), | ||||
|     SCP_FLAG_360RPM     = (1<<2), | ||||
|     SCP_FLAG_NORMALIZED = (1<<3), | ||||
|     SCP_FLAG_READWRITE  = (1<<4), | ||||
|     SCP_FLAG_FOOTER     = (1<<5) | ||||
| }; | ||||
|  | ||||
| struct ScpTrack | ||||
| { | ||||
|     char track_id[3];      // 'TRK' | ||||
|     uint8_t strack;        // SCP track number | ||||
|     struct | ||||
|     { | ||||
|         uint8_t index[4];  // time for one revolution | ||||
|         uint8_t length[4]; // number of bitcells | ||||
|         uint8_t offset[4]; // offset to bitcell data, relative to track header | ||||
|     } | ||||
|     revolution[5]; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										56
									
								
								tools/brother240tool.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								tools/brother240tool.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #include "globals.h" | ||||
| #include "fmt/format.h" | ||||
| #include <fstream> | ||||
|  | ||||
| static std::fstream inputFile; | ||||
|  | ||||
| void syntax() | ||||
| { | ||||
|     std::cout << "Syntax: brother240tool <image>\n" | ||||
|                  "The disk image will be flipped from Brother to DOS format and back\n" | ||||
|                  "again.\n"; | ||||
|     exit(0); | ||||
| } | ||||
|  | ||||
| uint8_t getbyte(uint32_t offset) | ||||
| { | ||||
|     inputFile.seekg(offset, std::ifstream::beg); | ||||
|     return inputFile.get(); | ||||
| } | ||||
|  | ||||
| void putbyte(uint32_t offset, uint8_t value) | ||||
| { | ||||
|     inputFile.seekp(offset, std::ifstream::beg); | ||||
|     inputFile.put(value); | ||||
| } | ||||
|  | ||||
| int main(int argc, const char* argv[]) | ||||
| { | ||||
|     if (argc < 2) | ||||
|         syntax(); | ||||
|      | ||||
|     inputFile.open(argv[1], std::ios::in | std::ios::out | std::ios::binary); | ||||
|     if (!inputFile.is_open()) | ||||
| 		Error() << fmt::format("cannot open input file '{}'", argv[1]); | ||||
|  | ||||
|     uint8_t b1 = getbyte(0x015); | ||||
|     uint8_t b2 = getbyte(0x100); | ||||
|     if ((b1 == 0x58) && (b2 == 0x58)) | ||||
|     { | ||||
|         std::cerr << "Flipping from Brother to DOS.\n"; | ||||
|         putbyte(0x015, 0xf0); | ||||
|         putbyte(0x100, 0xf0); | ||||
|     } | ||||
|     else if ((b1 == 0xf0) && (b2 == 0xf0)) | ||||
|     { | ||||
|         std::cerr << "Flipping from DOS to Brother.\n"; | ||||
|         putbyte(0x015, 0x58); | ||||
|         putbyte(0x100, 0x58); | ||||
|     } | ||||
|     else | ||||
|         Error() << "Unknown image format."; | ||||
|  | ||||
|     inputFile.close(); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user