diff --git a/xbas99.py b/xbas99.py index bdf7dff..6566904 100755 --- a/xbas99.py +++ b/xbas99.py @@ -22,6 +22,7 @@ import sys import re import os.path +import traceback VERSION = "1.5.0" @@ -169,15 +170,16 @@ class BasicProgram: # maximum number of bytes/tokens per BASIC line maxTokensPerLine = 254 - def __init__(self, data=None, source=None, long_=False): + def __init__(self, data=None, source=None, long_=False, tifiles_=False): self.lines = {} self.textlits = [] self.warnings = [] if data: try: - self.load(data, long_) + self.load(data, long_, tifiles_) except IndexError: self.warn("Program file is corrupted") +# print traceback.format_exc() elif source: self.parse(source) @@ -188,16 +190,23 @@ def warn(self, text): # program -> source - def load(self, data, long_): + def load(self, data, long_, tifiles_): """load tokenized BASIC program""" if long_ or data[1:3] == "\xab\xcd": # convert long format INT/VAR 254 to PROGRAM - program, p = "", 11 + if tifiles_: + p = 128 + else: + p = 11 + program = "" while p < len(data): l = ord(data[p]) + 1 program += data[p + 1:p + l] - p += l + p += l + 1 data = "XX" + data[5:7] + data[3:5] + "XX" + program +# f = open("data", "wb") +# f.write(data) +# f.close() # extract line number table and token table ptrTokens = ordw(data[2:4]) + 1 ptrLineNumbers = ordw(data[4:6]) @@ -257,6 +266,9 @@ def getSource(self): softspace = True else: lit, typ, n = Tokens.literal(tokens[p:]) + if not lit: + sys.stderr.write("Illegal token\n") + sys.exit(1) istext = (lit[0] in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + Tokens.STMTSEP) and typ is None if (((istext or lit == "#" or @@ -412,8 +424,8 @@ def getImage(self, long_=False, protected=False): chrw(checksum) + chrw(lastAddr - 1)) chunks = [(linoTable + tokenTable)[i:i + 254] for i in xrange(0, len(linoTable + tokenTable), 254)] - return (chr(len(header)) + header + - "".join([chr(len(c)) + c for c in chunks])) + return (chr(len(header)) + header + (chr(0x00)*(255-len(header))) + + "".join([chr(len(c)) + c + chr(0xff) for c in chunks])) else: header = (chrw(checksum) + chrw(tokenTabAddr - 1) + chrw(linoTabAddr) + chrw(lastAddr - 1)) @@ -468,6 +480,7 @@ def join(lines, minLinoDelta=1, maxLinoDelta=3): def main(): import argparse + tifiles = False args = argparse.ArgumentParser( version=VERSION, @@ -493,6 +506,8 @@ def main(): help="join split source lines (for -e)") args.add_argument("-o", "--output", dest="output", metavar="", help="set output filename") + args.add_argument("-t", "--tifiles", action="store_true", dest="astifiles", + help="assume TIFILES format program to list or decode") opts = args.parse_args() #setup @@ -507,11 +522,24 @@ def main(): else: with open(opts.source, "rb") as fin: image = fin.read() + if image[1:8] == "TIFILES": + opts.astifiles = True + if opts.astifiles: + flags = ord(image[0x0a]) + reclen = ord(image[0x0d]) + if (flags & 0x01) or ( (flags & 0x82) and reclen == 0xfe): + image = image[128:] + tifiles = True + else: + print "File is not in TIFILES PROGRAM or IV254 format." + sys.exit(1) + if image[1:3] == "\xab\xcd": + opts.long_ = True if opts.merge: program = BasicProgram() program.merge(image) else: - program = BasicProgram(data=image, long_=opts.long_) + program = BasicProgram(data=image, long_=opts.long_, tifiles_=tifiles) data = program.getSource() output = "-" if opts.list_ else opts.output or barename + ".b99" elif opts.dump: @@ -535,6 +563,20 @@ def main(): raise BasicError("Invalid line delta for join") program = BasicProgram(source=lines) data = program.getImage(long_=opts.long_, protected=opts.protect) + if opts.astifiles: + tifiles_header = chr(0x07) + "TIFILES" + chr(0x00) + print hex(len(data)), len(data), (len(data) // 256) + data += chr(0x00)*( (((len(data) // 256) + 1) * 256) - len(data)) + tifiles_header += chr( (len(data) / 256) + 1) + if opts.long_: + tifiles_header += chr(0x82) + chr(0x01) + tifiles_header += chr(0xba) # wrong, placeholder + else: + tifiles_header += chr(0x01) + chr(0x00) + tifiles_header += chr(0xba) # wrong, placeholder + tifiles_header += chr(0x00)*(128-len(tifiles_header)) + + data = tifiles_header + data output = opts.output or barename + ".prg" if program and program.warnings: diff --git a/xdm99.py b/xdm99.py index 8726ae5..fc939d2 100755 --- a/xdm99.py +++ b/xdm99.py @@ -99,8 +99,9 @@ class Disk: bytesPerSector = 256 defaultSectorsPerTrack = 9 defaultTracks = 40 - maxSectors = 1600 + maxSectors = 2880 blankByte = "\xe5" + clusterSize = 1 def __init__(self, image): if len(image) < 2 * Disk.bytesPerSector: @@ -125,10 +126,13 @@ def __init__(self, image): if len(self.image) < self.totalSectors * Disk.bytesPerSector: self.warn("Disk image truncated", "image") self.checkGeometry() + if self.totalSectors == 2880: + Disk.clusterSize = 2 self.usedSectors = 0 try: - for i in xrange(used(self.totalSectors, 8)): - self.usedSectors += bin(ord(self.allocBitmap[i])).count("1") + for i in xrange(used(self.totalSectors / self.clusterSize, 8)): + self.usedSectors += bin(ord(self.allocBitmap[i])).count("1") * self.clusterSize + except IndexError: self.warn("Allocation map corrupted", "alloc") self.catalog = {} @@ -163,7 +167,11 @@ def initCatalog(self): if error: fd.error = True self.catalog[fd.name] = File(fd=fd, data=data) - scount += fd.totalSectors + 1 + # Myarc DSDD80T always uses multiple of 512 bytes, including dir + # entries + if ((fd.totalSectors % 2) != 0) and Disk.clusterSize == 2: + fd.totalSectors += 1 + scount += fd.totalSectors + Disk.clusterSize # consistency check if scount != self.usedSectors - 2: self.warn( @@ -187,7 +195,13 @@ def readFile(self, name, sectors, clusters): self.warn("%s: File contents corrupted" % name) error = True continue - if len(data) != sectors * Disk.bytesPerSector: + if Disk.clusterSize is 2 and ((Disk.bytesPerSector * sectors) % 512): + # pad to multiple of 2 sectors for DSDD80T + expectedLength = Disk.bytesPerSector * (sectors+1) + else: + expectedLength = Disk.bytesPerSector * sectors + + if len(data) != expectedLength: self.warn("%s: File size mismatch: found %d bytes, expected %d" % ( name, len(data), sectors * Disk.bytesPerSector)) error = True @@ -321,10 +335,11 @@ def checkAllocation(self): """check sector allocation for consistency""" reads = {n: [] for n in xrange(self.totalSectors)} allocated = [] - for i in xrange(used(self.totalSectors, 8)): + for i in xrange(used(self.totalSectors / self.clusterSize, 8)): byte = ord(self.allocBitmap[i]) - for j in xrange(8): - allocated.append(byte & 1 << j != 0) + for j in xrange(8): + for k in xrange(self.clusterSize): + allocated.append(byte & 1 << j != 0) # unallocated sectors for n, context in self.readSectors: reads[n].append(context)