From dd8761c7c54a372a8b987e5e81aa1bee3ecab7a5 Mon Sep 17 00:00:00 2001 From: 4am Date: Sat, 2 Feb 2019 08:33:39 -0500 Subject: [PATCH] various speed improvements --- passport/__init__.py | 44 +++++++++--------------------------- passport/a2rimage.py | 47 +++++++++++++++++++++------------------ passport/constants.py | 22 ++++++++++++++++++ passport/rwts/__init__.py | 3 +++ passport/rwts/sunburst.py | 10 +++++++++ passport/strings.py | 2 +- passport/util/find.py | 1 + 7 files changed, 72 insertions(+), 57 deletions(-) create mode 100644 passport/constants.py diff --git a/passport/__init__.py b/passport/__init__.py index 32c8209..e6b9e12 100755 --- a/passport/__init__.py +++ b/passport/__init__.py @@ -2,6 +2,7 @@ from passport.loggers import * from passport.rwts import * from passport.patchers import * from passport.strings import * +from passport.constants import * from passport.util import * from passport import wozardry import bitarray @@ -165,32 +166,16 @@ class BasePassportProcessor: # base class def IDDiversi(self, t00s00): """returns True if T00S00 is Diversi-DOS bootloader, or False otherwise""" - return find.at(0xF1, t00s00, - b'\xB3\xA3\xA0\xD2\xCF\xD2\xD2\xC5' - b'\x8D\x87\x8D') + return find.at(0xF1, t00s00, kIDDiversiDOSBootloader) def IDProDOS(self, t00s00): """returns True if T00S00 is ProDOS bootloader, or False otherwise""" - return find.at(0x00, t00s00, - b'\x01' - b'\x38' - b'\xB0\x03' - b'\x4C') + return find.at(0x00, t00s00, kIDProDOSBootloader) def IDPascal(self, t00s00): """returns True if T00S00 is Pascal bootloader, or False otherwise""" - if find.wild_at(0x00, t00s00, - b'\x01' - b'\xE0\x60' - b'\xF0\x03' - b'\x4C' + find.WILDCARD + b'\x08'): - return True - return find.at(0x00, t00s00, - b'\x01' - b'\xE0\x70' - b'\xB0\x04' - b'\xE0\x40' - b'\xB0') + return find.wild_at(0x00, t00s00, kIDPascalBootloader1) or \ + find.at(0x00, t00s00, kIDPascalBootloader2) def IDDavidDOS(self, t00s00): """returns True if T00S00 is David-DOS II bootloader, or False otherwise""" @@ -683,30 +668,23 @@ class BasePassportProcessor: # base class # .woz files correctly self.tracks[physical_track_num] = self.g.disk_image.seek(physical_track_num) self.g.logger.debug("Seeking to track %s" % hex(self.g.track)) - try_again = True tried_reseek = False - while try_again: - try_again = False + while True: physical_sectors = self.rwts.decode_track(self.tracks[physical_track_num], logical_track_num, self.burn) - if len(physical_sectors) == self.rwts.sectors_per_track: - # TODO this is bad, we should just ask the RWTS object if we decoded enough sectors, - # so that SunburstRWTS can override the logic on track 0x11 - continue + if self.rwts.enough(logical_track_num, physical_sectors): + break if supports_reseek and not tried_reseek: self.tracks[physical_track_num] = self.g.disk_image.reseek(physical_track_num) self.g.logger.debug("Reseeking to track %s" % hex(self.g.track)) tried_reseek = True - try_again = True continue self.g.logger.debug("found %d sectors" % len(physical_sectors)) - if self.rwts.__class__ is SunburstRWTS and logical_track_num == 0x11: - # TODO this is bad, see above - continue if (0x0F not in physical_sectors) and self.SkipTrack(logical_track_num, self.tracks[physical_track_num]): physical_sectors = None - continue + break if self.g.tried_univ: if logical_track_num == 0x22 and (0x0F not in physical_sectors): + self.g.logger.PrintByID("fail") self.g.logger.PrintByID("fatal220f") return False else: @@ -716,11 +694,9 @@ class BasePassportProcessor: # base class self.g.logger.PrintByID("switch", {"sector":0x0F}) # TODO find exact sector self.rwts = UniversalRWTS(self.g) self.g.tried_univ = True - try_again = True continue if logical_track_num == 0 and type(self.rwts) != UniversalRWTSIgnoreEpilogues: self.rwts = UniversalRWTSIgnoreEpilogues(self.g) - try_again = True continue self.g.logger.PrintByID("fail") return False diff --git a/passport/a2rimage.py b/passport/a2rimage.py index b1e8f3a..6c95fed 100755 --- a/passport/a2rimage.py +++ b/passport/a2rimage.py @@ -10,30 +10,29 @@ class A2RImage: self.filename = filename self.tracks = collections.OrderedDict() self.a2r_image = a2rchery.A2RReader(filename, stream) + self.speed = 0 def to_bits(self, flux_record): """|flux_record| is a dictionary of 'capture_type', 'data_length', 'tick_count', and 'data'""" bits = bitarray.bitarray() - estimated_track_length = 0 if not flux_record or flux_record["capture_type"] != a2rchery.kCaptureTiming: - return bits, estimated_track_length, 0 - ticks = 0 - flux_total = 0 - fluxxen = flux_record["data"] - speeds = [(len([1 for i in fluxxen if i%t==0]), t) for t in range(0x1c,0x25)] - speeds.sort() - speed = speeds[-1][1] - for flux_value in fluxxen[1:]: - ticks += flux_value - if not estimated_track_length and ticks > flux_record["tick_count"]: - estimated_track_length = len(bits) + return bits + fluxxen = flux_record["data"][1:] + if not self.speed: + speeds = [(len([1 for i in fluxxen[:8192] if i%t==0]), t) for t in range(0x1e,0x23)] + speeds.sort() + self.speed = speeds[-1][1] + speed = self.speed + flux_total = flux_start = -speed//2 + for flux_value in fluxxen: flux_total += flux_value if flux_value == 0xFF: continue - bits.extend([0] * ((flux_total - speed//2) // speed)) - bits.append(1) - flux_total = 0 - return bits, estimated_track_length, speed + if flux_total >= speed: + bits.extend("0" * (flux_total // speed)) + bits.extend("1") + flux_total = flux_start + return bits def seek(self, track_num): if type(track_num) != float: @@ -48,16 +47,20 @@ class A2RImage: # (if the caller determines that they're not good, it will call reseek() # which is smarter but takes longer) bits = bitarray.bitarray() - for flux_record in self.a2r_image.flux.get(location, [{}])[:1]: - bits, track_length, speed = self.to_bits(flux_record) + if location in self.a2r_image.flux: + bits = self.to_bits(self.a2r_image.flux[location][0]) self.tracks[location] = Track(bits, len(bits)) return self.tracks[location] def reseek(self, track_num): location = int(track_num * 4) - all_bits = bitarray.bitarray() - for flux_record in self.a2r_image.flux.get(location, [{}]): - bits, track_length, speed = self.to_bits(flux_record) - all_bits.extend(bits) + # reset cached speed so we'll recalculate it + self.speed = 0 + # read the rest of the flux records and concatenate them on the end + # of the existing bitstream (this assumes you've called seek() before + # on this track) + all_bits = self.tracks[location].bits + for flux_record in self.a2r_image.flux[location][1:]: + all_bits.extend(self.to_bits(flux_record)) self.tracks[location] = Track(all_bits, len(all_bits)) return self.tracks[location] diff --git a/passport/constants.py b/passport/constants.py new file mode 100644 index 0000000..e03f7f6 --- /dev/null +++ b/passport/constants.py @@ -0,0 +1,22 @@ +from passport.util import * + +kIDDiversiDOSBootloader = bytes.fromhex("B3 A3 A0 D2 CF D2 D2 C5 8D 87 8D") + +kIDProDOSBootloader = bytes.fromhex( + "01" + "38" # SEC + "B0 03" # BCS +3 + "4C") # JMP + +kIDPascalBootloader1 = bytes.fromhex( + "01" + "E0 60" # CPX #$60 + "F0 03" # BEQ +3 + "4C" + find.WILDSTR + "08") # JMP $08** + +kIDPascalBootloader2 = bytes.fromhex( + "01" + "E0 70" # CPX #$70 + "B0 04" # BCS +4 + "E0 40" # CPX #$40 + "B0") # BCS diff --git a/passport/rwts/__init__.py b/passport/rwts/__init__.py index 3ec0e9d..979d8c7 100644 --- a/passport/rwts/__init__.py +++ b/passport/rwts/__init__.py @@ -204,6 +204,9 @@ class RWTS: break return sectors + def enough(self, logical_track_num, physical_sectors): + return len(physical_sectors) == self.sectors_per_track + from .universal import * from .dos33 import * from .sunburst import * diff --git a/passport/rwts/sunburst.py b/passport/rwts/sunburst.py index 0014851..e763507 100644 --- a/passport/rwts/sunburst.py +++ b/passport/rwts/sunburst.py @@ -16,7 +16,17 @@ class SunburstRWTS(DOS33RWTS): self.data_prologue[1], self.data_prologue_third_nibble_by_track[logical_track_num]) DOS33RWTS.seek(self, logical_track_num) + if logical_track_num == 0x11: + self.sector_order = (0x00, 0x07, 0x08, 0x06, 0x0D, 0x05, 0x0C, 0x04, 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F) + else: + self.sector_order = self.kDefaultSectorOrder16 + if logical_track_num >= 0x11: return logical_track_num + 0.5 else: return float(logical_track_num) + + def enough(self, logical_track_num, physical_sectors): + if logical_track_num == 0x11: + return len(physical_sectors) >= 14 + return DOS33RWTS.enough(self, logical_track_num, physical_sectors) diff --git a/passport/strings.py b/passport/strings.py index 5d5d379..9c0e8ea 100644 --- a/passport/strings.py +++ b/passport/strings.py @@ -1,4 +1,4 @@ -__date__ = "2019-02-01" +__date__ = "2019-02-02" STRINGS = { "header": "Passport.py by 4am (" + __date__ + ")\n", # max 32 characters diff --git a/passport/util/find.py b/passport/util/find.py index 1f8ba8c..b3d0ab1 100644 --- a/passport/util/find.py +++ b/passport/util/find.py @@ -1,6 +1,7 @@ import re WILDCARD = b'\x97' +WILDSTR = "97" def wild(source_bytes, search_bytes): """Search source_bytes (bytes object) for the first instance of search_bytes (bytes_object). search_bytes may contain WILDCARD, which matches any single byte (like "." in a regular expression). Returns index of first match, or -1 if no matches."""