various speed improvements

This commit is contained in:
4am 2019-02-02 08:33:39 -05:00
parent 47b9b27a4c
commit dd8761c7c5
7 changed files with 72 additions and 57 deletions

View File

@ -2,6 +2,7 @@ from passport.loggers import *
from passport.rwts import * from passport.rwts import *
from passport.patchers import * from passport.patchers import *
from passport.strings import * from passport.strings import *
from passport.constants import *
from passport.util import * from passport.util import *
from passport import wozardry from passport import wozardry
import bitarray import bitarray
@ -165,32 +166,16 @@ class BasePassportProcessor: # base class
def IDDiversi(self, t00s00): def IDDiversi(self, t00s00):
"""returns True if T00S00 is Diversi-DOS bootloader, or False otherwise""" """returns True if T00S00 is Diversi-DOS bootloader, or False otherwise"""
return find.at(0xF1, t00s00, return find.at(0xF1, t00s00, kIDDiversiDOSBootloader)
b'\xB3\xA3\xA0\xD2\xCF\xD2\xD2\xC5'
b'\x8D\x87\x8D')
def IDProDOS(self, t00s00): def IDProDOS(self, t00s00):
"""returns True if T00S00 is ProDOS bootloader, or False otherwise""" """returns True if T00S00 is ProDOS bootloader, or False otherwise"""
return find.at(0x00, t00s00, return find.at(0x00, t00s00, kIDProDOSBootloader)
b'\x01'
b'\x38'
b'\xB0\x03'
b'\x4C')
def IDPascal(self, t00s00): def IDPascal(self, t00s00):
"""returns True if T00S00 is Pascal bootloader, or False otherwise""" """returns True if T00S00 is Pascal bootloader, or False otherwise"""
if find.wild_at(0x00, t00s00, return find.wild_at(0x00, t00s00, kIDPascalBootloader1) or \
b'\x01' find.at(0x00, t00s00, kIDPascalBootloader2)
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')
def IDDavidDOS(self, t00s00): def IDDavidDOS(self, t00s00):
"""returns True if T00S00 is David-DOS II bootloader, or False otherwise""" """returns True if T00S00 is David-DOS II bootloader, or False otherwise"""
@ -683,30 +668,23 @@ class BasePassportProcessor: # base class
# .woz files correctly # .woz files correctly
self.tracks[physical_track_num] = self.g.disk_image.seek(physical_track_num) 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)) self.g.logger.debug("Seeking to track %s" % hex(self.g.track))
try_again = True
tried_reseek = False tried_reseek = False
while try_again: while True:
try_again = False
physical_sectors = self.rwts.decode_track(self.tracks[physical_track_num], logical_track_num, self.burn) 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: if self.rwts.enough(logical_track_num, physical_sectors):
# TODO this is bad, we should just ask the RWTS object if we decoded enough sectors, break
# so that SunburstRWTS can override the logic on track 0x11
continue
if supports_reseek and not tried_reseek: if supports_reseek and not tried_reseek:
self.tracks[physical_track_num] = self.g.disk_image.reseek(physical_track_num) 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)) self.g.logger.debug("Reseeking to track %s" % hex(self.g.track))
tried_reseek = True tried_reseek = True
try_again = True
continue continue
self.g.logger.debug("found %d sectors" % len(physical_sectors)) 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]): if (0x0F not in physical_sectors) and self.SkipTrack(logical_track_num, self.tracks[physical_track_num]):
physical_sectors = None physical_sectors = None
continue break
if self.g.tried_univ: if self.g.tried_univ:
if logical_track_num == 0x22 and (0x0F not in physical_sectors): if logical_track_num == 0x22 and (0x0F not in physical_sectors):
self.g.logger.PrintByID("fail")
self.g.logger.PrintByID("fatal220f") self.g.logger.PrintByID("fatal220f")
return False return False
else: else:
@ -716,11 +694,9 @@ class BasePassportProcessor: # base class
self.g.logger.PrintByID("switch", {"sector":0x0F}) # TODO find exact sector self.g.logger.PrintByID("switch", {"sector":0x0F}) # TODO find exact sector
self.rwts = UniversalRWTS(self.g) self.rwts = UniversalRWTS(self.g)
self.g.tried_univ = True self.g.tried_univ = True
try_again = True
continue continue
if logical_track_num == 0 and type(self.rwts) != UniversalRWTSIgnoreEpilogues: if logical_track_num == 0 and type(self.rwts) != UniversalRWTSIgnoreEpilogues:
self.rwts = UniversalRWTSIgnoreEpilogues(self.g) self.rwts = UniversalRWTSIgnoreEpilogues(self.g)
try_again = True
continue continue
self.g.logger.PrintByID("fail") self.g.logger.PrintByID("fail")
return False return False

View File

@ -10,30 +10,29 @@ class A2RImage:
self.filename = filename self.filename = filename
self.tracks = collections.OrderedDict() self.tracks = collections.OrderedDict()
self.a2r_image = a2rchery.A2RReader(filename, stream) self.a2r_image = a2rchery.A2RReader(filename, stream)
self.speed = 0
def to_bits(self, flux_record): def to_bits(self, flux_record):
"""|flux_record| is a dictionary of 'capture_type', 'data_length', 'tick_count', and 'data'""" """|flux_record| is a dictionary of 'capture_type', 'data_length', 'tick_count', and 'data'"""
bits = bitarray.bitarray() bits = bitarray.bitarray()
estimated_track_length = 0
if not flux_record or flux_record["capture_type"] != a2rchery.kCaptureTiming: if not flux_record or flux_record["capture_type"] != a2rchery.kCaptureTiming:
return bits, estimated_track_length, 0 return bits
ticks = 0 fluxxen = flux_record["data"][1:]
flux_total = 0 if not self.speed:
fluxxen = flux_record["data"] speeds = [(len([1 for i in fluxxen[:8192] if i%t==0]), t) for t in range(0x1e,0x23)]
speeds = [(len([1 for i in fluxxen if i%t==0]), t) for t in range(0x1c,0x25)] speeds.sort()
speeds.sort() self.speed = speeds[-1][1]
speed = speeds[-1][1] speed = self.speed
for flux_value in fluxxen[1:]: flux_total = flux_start = -speed//2
ticks += flux_value for flux_value in fluxxen:
if not estimated_track_length and ticks > flux_record["tick_count"]:
estimated_track_length = len(bits)
flux_total += flux_value flux_total += flux_value
if flux_value == 0xFF: if flux_value == 0xFF:
continue continue
bits.extend([0] * ((flux_total - speed//2) // speed)) if flux_total >= speed:
bits.append(1) bits.extend("0" * (flux_total // speed))
flux_total = 0 bits.extend("1")
return bits, estimated_track_length, speed flux_total = flux_start
return bits
def seek(self, track_num): def seek(self, track_num):
if type(track_num) != float: if type(track_num) != float:
@ -48,16 +47,20 @@ class A2RImage:
# (if the caller determines that they're not good, it will call reseek() # (if the caller determines that they're not good, it will call reseek()
# which is smarter but takes longer) # which is smarter but takes longer)
bits = bitarray.bitarray() bits = bitarray.bitarray()
for flux_record in self.a2r_image.flux.get(location, [{}])[:1]: if location in self.a2r_image.flux:
bits, track_length, speed = self.to_bits(flux_record) bits = self.to_bits(self.a2r_image.flux[location][0])
self.tracks[location] = Track(bits, len(bits)) self.tracks[location] = Track(bits, len(bits))
return self.tracks[location] return self.tracks[location]
def reseek(self, track_num): def reseek(self, track_num):
location = int(track_num * 4) location = int(track_num * 4)
all_bits = bitarray.bitarray() # reset cached speed so we'll recalculate it
for flux_record in self.a2r_image.flux.get(location, [{}]): self.speed = 0
bits, track_length, speed = self.to_bits(flux_record) # read the rest of the flux records and concatenate them on the end
all_bits.extend(bits) # 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)) self.tracks[location] = Track(all_bits, len(all_bits))
return self.tracks[location] return self.tracks[location]

22
passport/constants.py Normal file
View File

@ -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

View File

@ -204,6 +204,9 @@ class RWTS:
break break
return sectors return sectors
def enough(self, logical_track_num, physical_sectors):
return len(physical_sectors) == self.sectors_per_track
from .universal import * from .universal import *
from .dos33 import * from .dos33 import *
from .sunburst import * from .sunburst import *

View File

@ -16,7 +16,17 @@ class SunburstRWTS(DOS33RWTS):
self.data_prologue[1], self.data_prologue[1],
self.data_prologue_third_nibble_by_track[logical_track_num]) self.data_prologue_third_nibble_by_track[logical_track_num])
DOS33RWTS.seek(self, 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: if logical_track_num >= 0x11:
return logical_track_num + 0.5 return logical_track_num + 0.5
else: else:
return float(logical_track_num) 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)

View File

@ -1,4 +1,4 @@
__date__ = "2019-02-01" __date__ = "2019-02-02"
STRINGS = { STRINGS = {
"header": "Passport.py by 4am (" + __date__ + ")\n", # max 32 characters "header": "Passport.py by 4am (" + __date__ + ")\n", # max 32 characters

View File

@ -1,6 +1,7 @@
import re import re
WILDCARD = b'\x97' WILDCARD = b'\x97'
WILDSTR = "97"
def wild(source_bytes, search_bytes): 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.""" """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."""