Skip to content

Commit 64c526e

Browse files
author
Henning Pingel
committed
adding arduino simulator class with raw d81 image
1 parent 4e980a5 commit 64c526e

File tree

2 files changed

+78
-35
lines changed

2 files changed

+78
-35
lines changed

disk2image.py

+78-35
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
from serial import Serial
3030
from bitstring import BitArray
31-
import sys, re, time, platform, hashlib, os, binascii
31+
import sys, re, time, platform, hashlib, os, binascii, ast
3232
from optparse import OptionParser
3333

3434
def main():
@@ -72,7 +72,7 @@ def __init__(self):
7272
)
7373
parser.add_option("-s", "--serialdevice",
7474
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)",
7676
default=self.serialDeviceAddresses[ platform.system() ]
7777
)
7878
parser.add_option("-r", "--retries", dest="retries",
@@ -82,13 +82,14 @@ def __init__(self):
8282
)
8383
(options, args) = parser.parse_args()
8484

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):
8686
raise Exception( "Serial device does not exist: " + options.serialDeviceName )
8787

8888
if not options.disktype in self.diskFormatTypes.keys():
8989
raise Exception("Error: disk format " + options.disktype + " is unknown")
9090
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 )
9293

9394
def getDocDiskType(self):
9495
dft = ''
@@ -160,24 +161,28 @@ class IBMDoubleDensityFloppyDiskImager:
160161
and collects all the sector data of all tracks
161162
to store it into an image file
162163
'''
163-
def __init__( self, diskFormat, imagename, retries, serialDevice ):
164+
def __init__( self, diskFormat, imagename, retries, serialDevice, storeBitstream = False ):
164165

165166
print ("Selected disk format is " + diskFormat.name + ", we expect " + str(diskFormat.expectedSectorsPerTrack) + " sectors per track")
166167
print ("Target image file is: " + imagename)
167168
print ("Serial device is: " + serialDevice)
168169

169170
image = b''
170171
trackData = {}
172+
rawTracks = {}
171173
trackLength = diskFormat.expectedSectorsPerTrack * diskFormat.sectorSize
172-
vldtr = SingleTrackSectorListValidator( retries, diskFormat, serialDevice )
174+
vldtr = SingleTrackSectorListValidator( retries, diskFormat, serialDevice, storeBitstream )
173175
for trackno in diskFormat.trackRange:
174176
trackData[ trackno ] = {}
177+
rawTracks[ trackno ] = {}
175178
for headno in diskFormat.headRange:
176179
trackDataTmp = vldtr.processTrack( trackno, headno )
177180
if not len(trackDataTmp) == trackLength:
178181
print ("ERROR track should have " + str(trackLength) + " bytes but has " + str(len(trackData)))
179182
trackData[ trackno ][ headno ] = trackDataTmp
180183
image += trackData[ trackno ][ headno ]
184+
if storeBitstream is True:
185+
rawTracks[trackno][headno] = vldtr.getDecompressedBitstream()
181186
print ("Writing image to file " + imagename)
182187
with open(imagename, 'wb') as f:
183188
f.write( image)
@@ -187,6 +192,10 @@ def __init__( self, diskFormat, imagename, retries, serialDevice ):
187192
print("SHA1 : " + result.hexdigest())
188193
result = hashlib.sha256(image)
189194
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))
190199
vldtr.printSerialStats()
191200

192201
class SingleTrackSectorListValidator:
@@ -195,14 +204,19 @@ class SingleTrackSectorListValidator:
195204
structured data of all found sectors of one track. validates crc values and
196205
manages optional read retries.
197206
'''
198-
def __init__(self, retries, diskFormat, serialDevice):
207+
def __init__(self, retries, diskFormat, serialDevice, storeBitstream = False):
199208
self.maxRetries = retries
200209
self.diskFormat = diskFormat
201210
self.minSectorNumber = 1
202211
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()
206220

207221
def printSerialStats(self):
208222
self.trackParser.printSerialStats()
@@ -215,6 +229,8 @@ def processTrack(self, trackno, headno):
215229
if self.retries < self.maxRetries:
216230
print (" Repeat track read - attempt " + str( self.maxRetries - self.retries +1 ) + " of " + str(self.maxRetries) )
217231
self.addValidSectors( self.trackParser.detectSectors(trackno, headno), trackno, headno )
232+
if self.storeBitstream is True:
233+
self.decompressedBitstream = self.trackParser.getDecompressedBitstream()
218234
vsc = len(self.validSectorData)
219235
print (f"Reading track: {trackno:2d}, head: {headno}. Number of valid sectors found: {vsc}/{self.diskFormat.expectedSectorsPerTrack}")
220236
if vsc == self.diskFormat.expectedSectorsPerTrack:
@@ -235,6 +251,9 @@ def processTrack(self, trackno, headno):
235251
print(" Not enough sectors found.");
236252
return trackData
237253

254+
def getDecompressedBitstream(self):
255+
return self.decompressedBitstream
256+
238257
def getCRC(self, data):
239258
'''
240259
to calculate crc, we can either use binascii.crc_hqx(data, value) or crcmod
@@ -286,10 +305,31 @@ class SingleIBMTrackSectorParser:
286305
'''
287306
def __init__(self, diskFormat, arduinoFloppyControlInterface):
288307
self.diskFormat = diskFormat
289-
self.ArduinoFloppyControlInterface = arduinoFloppyControlInterface
308+
self.arduino = arduinoFloppyControlInterface
290309
self.sectorDataBitSize = self.diskFormat.sectorSize * 16
310+
self.decompressedBitstream = ""
291311

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):
293333
result = ""
294334
keep = False;
295335
for char in stream:
@@ -315,16 +355,16 @@ def getMarkers(self):
315355
dataMarkers = []
316356
dataMarkersTmp = []
317357
rawSectors = re.split( self.diskFormat.sectorStartMarker, self.decompressedBitstream)
318-
del rawSectors[-1]
358+
del rawSectors[-1] #delete last entry
319359
previousBits = 0
320360
for rawSector in rawSectors:
321361
previousBits += len( rawSector ) + self.diskFormat.sectorStartMarkerLength
322362
sectorMarkers.append( previousBits )
323363
dataMarkerMatchesIterator = re.finditer( self.diskFormat.sectorDataStartMarker, self.decompressedBitstream)
324364
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)
328368
else:
329369
print("Ignoring datamarker - is in front of first sector marker")
330370
cnt = 0
@@ -334,7 +374,7 @@ def getMarkers(self):
334374
print ("getMarkers / Unusual offset found: "+str(offset))
335375
#now we check if the sector's data might be cut off at the end
336376
#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
338378
overshoot = dataMarker + self.sectorDataBitSize + 32
339379
if overshoot <= len( self.decompressedBitstream ):
340380
dataMarkers.append( dataMarker )
@@ -344,23 +384,6 @@ def getMarkers(self):
344384
sectorMarkers.remove(sectorMarkers[cnt])
345385
return (sectorMarkers, dataMarkers)
346386

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-
364387
def parseSingleSector(self, sectorStart, dataMarker):
365388
dataMarker = dataMarker - sectorStart
366389
self.currentSectorBitstream = self.decompressedBitstream[sectorStart : sectorStart + self.sectorDataBitSize + 32 + dataMarker]
@@ -376,7 +399,7 @@ def parseSingleSector(self, sectorStart, dataMarker):
376399
}
377400

378401
def printSerialStats(self):
379-
(tdtr,tdtc) = self.ArduinoFloppyControlInterface.getStats()
402+
(tdtr,tdtc) = self.arduino.getStats()
380403
print ( "Total duration track read : " + tdtr + " seconds")
381404
print ( "Total duration serial commands: " + tdtc + " seconds")
382405

@@ -540,5 +563,25 @@ def getStats(self):
540563
tdtc = str(int(self.total_duration_cmds*100)/100)
541564
return (tdtr, tdtc)
542565

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+
543586
if __name__ == '__main__':
544587
main()

raw_debug_image_d81.zip

107 KB
Binary file not shown.

0 commit comments

Comments
 (0)