28
28
29
29
from serial import Serial
30
30
from bitstring import BitArray
31
- import sys , re , time , platform , hashlib , os , binascii
31
+ import sys , re , time , platform , hashlib , os , binascii , ast
32
32
from optparse import OptionParser
33
33
34
34
def main ():
@@ -72,7 +72,7 @@ def __init__(self):
72
72
)
73
73
parser .add_option ("-s" , "--serialdevice" ,
74
74
dest = "serialDeviceName" ,
75
- help = "device name of the serial device, for example /dev/ttyUSB0" ,
75
+ help = "device name of the serial device, for example /dev/ttyUSB0 (use value 'simulated' to test functionality) " ,
76
76
default = self .serialDeviceAddresses [ platform .system () ]
77
77
)
78
78
parser .add_option ("-r" , "--retries" , dest = "retries" ,
@@ -82,13 +82,14 @@ def __init__(self):
82
82
)
83
83
(options , args ) = parser .parse_args ()
84
84
85
- if platform .system () != "Windows" and not os .path .exists (options .serialDeviceName ):
85
+ if options . serialDeviceName != "simulated" and platform .system () != "Windows" and not os .path .exists (options .serialDeviceName ):
86
86
raise Exception ( "Serial device does not exist: " + options .serialDeviceName )
87
87
88
88
if not options .disktype in self .diskFormatTypes .keys ():
89
89
raise Exception ("Error: disk format " + options .disktype + " is unknown" )
90
90
diskFormat = self .diskFormatTypes [ options .disktype ]()
91
- IBMDoubleDensityFloppyDiskImager ( diskFormat , options .outputImage , options .retries , options .serialDeviceName )
91
+ options .storeBitstream = False #tmp debug
92
+ IBMDoubleDensityFloppyDiskImager ( diskFormat , options .outputImage , options .retries , options .serialDeviceName , options .storeBitstream )
92
93
93
94
def getDocDiskType (self ):
94
95
dft = ''
@@ -160,24 +161,28 @@ class IBMDoubleDensityFloppyDiskImager:
160
161
and collects all the sector data of all tracks
161
162
to store it into an image file
162
163
'''
163
- def __init__ ( self , diskFormat , imagename , retries , serialDevice ):
164
+ def __init__ ( self , diskFormat , imagename , retries , serialDevice , storeBitstream = False ):
164
165
165
166
print ("Selected disk format is " + diskFormat .name + ", we expect " + str (diskFormat .expectedSectorsPerTrack ) + " sectors per track" )
166
167
print ("Target image file is: " + imagename )
167
168
print ("Serial device is: " + serialDevice )
168
169
169
170
image = b''
170
171
trackData = {}
172
+ rawTracks = {}
171
173
trackLength = diskFormat .expectedSectorsPerTrack * diskFormat .sectorSize
172
- vldtr = SingleTrackSectorListValidator ( retries , diskFormat , serialDevice )
174
+ vldtr = SingleTrackSectorListValidator ( retries , diskFormat , serialDevice , storeBitstream )
173
175
for trackno in diskFormat .trackRange :
174
176
trackData [ trackno ] = {}
177
+ rawTracks [ trackno ] = {}
175
178
for headno in diskFormat .headRange :
176
179
trackDataTmp = vldtr .processTrack ( trackno , headno )
177
180
if not len (trackDataTmp ) == trackLength :
178
181
print ("ERROR track should have " + str (trackLength ) + " bytes but has " + str (len (trackData )))
179
182
trackData [ trackno ][ headno ] = trackDataTmp
180
183
image += trackData [ trackno ][ headno ]
184
+ if storeBitstream is True :
185
+ rawTracks [trackno ][headno ] = vldtr .getDecompressedBitstream ()
181
186
print ("Writing image to file " + imagename )
182
187
with open (imagename , 'wb' ) as f :
183
188
f .write ( image )
@@ -187,6 +192,10 @@ def __init__( self, diskFormat, imagename, retries, serialDevice ):
187
192
print ("SHA1 : " + result .hexdigest ())
188
193
result = hashlib .sha256 (image )
189
194
print ("SHA256: " + result .hexdigest ())
195
+
196
+ if storeBitstream is True :
197
+ with open ('raw_debug_image_d81.py' , "w" ) as f :
198
+ f .write (repr (rawTracks ))
190
199
vldtr .printSerialStats ()
191
200
192
201
class SingleTrackSectorListValidator :
@@ -195,14 +204,19 @@ class SingleTrackSectorListValidator:
195
204
structured data of all found sectors of one track. validates crc values and
196
205
manages optional read retries.
197
206
'''
198
- def __init__ (self , retries , diskFormat , serialDevice ):
207
+ def __init__ (self , retries , diskFormat , serialDevice , storeBitstream = False ):
199
208
self .maxRetries = retries
200
209
self .diskFormat = diskFormat
201
210
self .minSectorNumber = 1
202
211
self .validSectorData = {}
203
- self .ArduinoFloppyControlInterface = ArduinoFloppyControlInterface (serialDevice , diskFormat )
204
- self .trackParser = SingleIBMTrackSectorParser (self .diskFormat , self .ArduinoFloppyControlInterface )
205
- self .ArduinoFloppyControlInterface .openSerialConnection ()
212
+ self .storeBitstream = storeBitstream
213
+ self .decompressedBitstream = ""
214
+ if serialDevice == "simulated" :
215
+ self .arduino = ArduinoSimulator (diskFormat )
216
+ else :
217
+ self .arduino = ArduinoFloppyControlInterface (serialDevice , diskFormat )
218
+ self .trackParser = SingleIBMTrackSectorParser (self .diskFormat , self .arduino )
219
+ self .arduino .openSerialConnection ()
206
220
207
221
def printSerialStats (self ):
208
222
self .trackParser .printSerialStats ()
@@ -215,6 +229,8 @@ def processTrack(self, trackno, headno):
215
229
if self .retries < self .maxRetries :
216
230
print (" Repeat track read - attempt " + str ( self .maxRetries - self .retries + 1 ) + " of " + str (self .maxRetries ) )
217
231
self .addValidSectors ( self .trackParser .detectSectors (trackno , headno ), trackno , headno )
232
+ if self .storeBitstream is True :
233
+ self .decompressedBitstream = self .trackParser .getDecompressedBitstream ()
218
234
vsc = len (self .validSectorData )
219
235
print (f"Reading track: { trackno :2d} , head: { headno } . Number of valid sectors found: { vsc } /{ self .diskFormat .expectedSectorsPerTrack } " )
220
236
if vsc == self .diskFormat .expectedSectorsPerTrack :
@@ -235,6 +251,9 @@ def processTrack(self, trackno, headno):
235
251
print (" Not enough sectors found." );
236
252
return trackData
237
253
254
+ def getDecompressedBitstream (self ):
255
+ return self .decompressedBitstream
256
+
238
257
def getCRC (self , data ):
239
258
'''
240
259
to calculate crc, we can either use binascii.crc_hqx(data, value) or crcmod
@@ -286,10 +305,31 @@ class SingleIBMTrackSectorParser:
286
305
'''
287
306
def __init__ (self , diskFormat , arduinoFloppyControlInterface ):
288
307
self .diskFormat = diskFormat
289
- self .ArduinoFloppyControlInterface = arduinoFloppyControlInterface
308
+ self .arduino = arduinoFloppyControlInterface
290
309
self .sectorDataBitSize = self .diskFormat .sectorSize * 16
310
+ self .decompressedBitstream = ""
291
311
292
- def mfmDecode ( self , stream ):
312
+ def detectSectors (self , trackno , headno ):
313
+ cnt = 0
314
+ sectors = []
315
+ if self .diskFormat .swapsides is False :
316
+ headno = 1 if headno == 0 else 0
317
+ self .decompressedBitstream = self .arduino .getDecompressedBitstream (trackno , headno )
318
+ (sectorMarkers , dataMarkers ) = self .getMarkers ()
319
+ for sectorStart in sectorMarkers :
320
+ if len (dataMarkers ) <= cnt :
321
+ pass
322
+ elif dataMarkers [cnt ] <= sectorStart :
323
+ print ("Datamarker is being ignored. " + str ( dataMarkers [cnt ] ) + " " + str (sectorStart ))
324
+ else :
325
+ sectors .append (self .parseSingleSector (sectorStart , dataMarkers [cnt ]))
326
+ cnt += 1
327
+ return sectors
328
+
329
+ def getDecompressedBitstream (self ):
330
+ return self .decompressedBitstream
331
+
332
+ def mfmDecode (self , stream ):
293
333
result = ""
294
334
keep = False ;
295
335
for char in stream :
@@ -315,16 +355,16 @@ def getMarkers(self):
315
355
dataMarkers = []
316
356
dataMarkersTmp = []
317
357
rawSectors = re .split ( self .diskFormat .sectorStartMarker , self .decompressedBitstream )
318
- del rawSectors [- 1 ]
358
+ del rawSectors [- 1 ] #delete last entry
319
359
previousBits = 0
320
360
for rawSector in rawSectors :
321
361
previousBits += len ( rawSector ) + self .diskFormat .sectorStartMarkerLength
322
362
sectorMarkers .append ( previousBits )
323
363
dataMarkerMatchesIterator = re .finditer ( self .diskFormat .sectorDataStartMarker , self .decompressedBitstream )
324
364
for dataMarker in dataMarkerMatchesIterator :
325
- (from1 , to1 ) = (dataMarker .span () )
326
- if to1 >= sectorMarkers [0 ] + self .diskFormat .legalOffsetRangeLowerBorder :
327
- dataMarkersTmp .append (to1 )
365
+ (startPosDataMarker , endPosDataMarker ) = (dataMarker .span () )
366
+ if endPosDataMarker >= sectorMarkers [0 ] + self .diskFormat .legalOffsetRangeLowerBorder :
367
+ dataMarkersTmp .append (endPosDataMarker )
328
368
else :
329
369
print ("Ignoring datamarker - is in front of first sector marker" )
330
370
cnt = 0
@@ -334,7 +374,7 @@ def getMarkers(self):
334
374
print ("getMarkers / Unusual offset found: " + str (offset ))
335
375
#now we check if the sector's data might be cut off at the end
336
376
#of the chunk of the track we have, the added 32 represents
337
- #the CRC checksum of the sector data
377
+ #the length of the CRC checksum of the sector data
338
378
overshoot = dataMarker + self .sectorDataBitSize + 32
339
379
if overshoot <= len ( self .decompressedBitstream ):
340
380
dataMarkers .append ( dataMarker )
@@ -344,23 +384,6 @@ def getMarkers(self):
344
384
sectorMarkers .remove (sectorMarkers [cnt ])
345
385
return (sectorMarkers , dataMarkers )
346
386
347
- def detectSectors (self , trackno , headno ):
348
- cnt = 0
349
- sectors = []
350
- if self .diskFormat .swapsides is False :
351
- headno = 1 if headno == 0 else 0
352
- self .decompressedBitstream = self .ArduinoFloppyControlInterface .getDecompressedBitstream (trackno , headno )
353
- (sectorMarkers , dataMarkers ) = self .getMarkers ()
354
- for sectorStart in sectorMarkers :
355
- if len (dataMarkers ) <= cnt :
356
- pass
357
- elif dataMarkers [cnt ] <= sectorStart :
358
- print ("Datamarker is being ignored. " + str ( dataMarkers [cnt ] ) + " " + str (sectorStart ))
359
- else :
360
- sectors .append (self .parseSingleSector (sectorStart , dataMarkers [cnt ]))
361
- cnt += 1
362
- return sectors
363
-
364
387
def parseSingleSector (self , sectorStart , dataMarker ):
365
388
dataMarker = dataMarker - sectorStart
366
389
self .currentSectorBitstream = self .decompressedBitstream [sectorStart : sectorStart + self .sectorDataBitSize + 32 + dataMarker ]
@@ -376,7 +399,7 @@ def parseSingleSector(self, sectorStart, dataMarker):
376
399
}
377
400
378
401
def printSerialStats (self ):
379
- (tdtr ,tdtc ) = self .ArduinoFloppyControlInterface .getStats ()
402
+ (tdtr ,tdtc ) = self .arduino .getStats ()
380
403
print ( "Total duration track read : " + tdtr + " seconds" )
381
404
print ( "Total duration serial commands: " + tdtc + " seconds" )
382
405
@@ -540,5 +563,25 @@ def getStats(self):
540
563
tdtc = str (int (self .total_duration_cmds * 100 )/ 100 )
541
564
return (tdtr , tdtc )
542
565
566
+ class ArduinoSimulator (ArduinoFloppyControlInterface ):
567
+
568
+ def __init__ (self , diskFormat ):
569
+ super ().__init__ ("bla" , diskFormat )
570
+ with open ('raw_debug_image_d81.py' , 'r' ) as f : self .rawTrackData = ast .literal_eval (f .read ())
571
+
572
+ def __del__ (self ):
573
+ pass
574
+
575
+ def openSerialConnection (self ):
576
+ pass
577
+
578
+ def connectionIsUsable (self , cmd ):
579
+ return True
580
+
581
+ def getDecompressedBitstream (self , track , head ):
582
+ if head == 0 :
583
+ time .sleep (1 )
584
+ return self .rawTrackData [track ][head ]
585
+
543
586
if __name__ == '__main__' :
544
587
main ()
0 commit comments