Convert Ophis to Python 3.

Most of the work is handled by 2to3, but there's a few extra tricks
needed to finish the job, mostly about picking the right bits to be
Unicode and the right bits to be bytes.
This commit is contained in:
Michael Martin 2019-01-09 20:45:01 -08:00
parent 971fafd918
commit 41bf01d035
12 changed files with 197 additions and 195 deletions

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
from os.path import realpath, dirname, join from os.path import realpath, dirname, join
from sys import argv, exit, path from sys import argv, exit, path

View File

@ -10,7 +10,8 @@ import Ophis.CmdLine
import Ophis.IR as IR import Ophis.IR as IR
import Ophis.Frontend as FE import Ophis.Frontend as FE
import Ophis.Errors as Err import Ophis.Errors as Err
import math, os.path import math
import os.path
basecharmap = "".join([chr(x) for x in range(256)]) basecharmap = "".join([chr(x) for x in range(256)])
currentcharmap = basecharmap currentcharmap = basecharmap
@ -68,7 +69,7 @@ def pragmaIncbin(ppt, line, result):
line.expect("EOL") line.expect("EOL")
if type(filename) == str: if type(filename) == str:
try: try:
f = file(os.path.join(FE.context_directory, filename), "rb") f = open(os.path.join(FE.context_directory, filename), "rb")
if offset.hardcoded and (size is None or size.hardcoded): if offset.hardcoded and (size is None or size.hardcoded):
# We know how big it will be, we can just use the values. # We know how big it will be, we can just use the values.
# First check to make sure they're sane # First check to make sure they're sane
@ -94,7 +95,7 @@ def pragmaIncbin(ppt, line, result):
size = IR.ConstantExpr(-1) size = IR.ConstantExpr(-1)
f.seek(offset.value()) f.seek(offset.value())
bytes = f.read(size.value()) bytes = f.read(size.value())
bytes = [IR.ConstantExpr(ord(x)) for x in bytes] bytes = [IR.ConstantExpr(x) for x in bytes]
result.append(IR.Node(ppt, "Byte", *bytes)) result.append(IR.Node(ppt, "Byte", *bytes))
else: else:
# offset or length could change based on label placement. # offset or length could change based on label placement.
@ -103,7 +104,7 @@ def pragmaIncbin(ppt, line, result):
# alias. Don't use symbolic aliases when extracting tiny # alias. Don't use symbolic aliases when extracting tiny
# pieces out of humongous files, I guess. # pieces out of humongous files, I guess.
bytes = f.read() bytes = f.read()
bytes = [IR.ConstantExpr(ord(x)) for x in bytes] bytes = [IR.ConstantExpr(x) for x in bytes]
if size is None: if size is None:
size = IR.SequenceExpr([IR.ConstantExpr(len(bytes)), size = IR.SequenceExpr([IR.ConstantExpr(len(bytes)),
"-", "-",
@ -141,7 +142,7 @@ def pragmaCharmapbin(ppt, line, result):
line.expect("EOL") line.expect("EOL")
if type(filename) == str: if type(filename) == str:
try: try:
f = file(os.path.join(FE.context_directory, filename), "rb") f = open(os.path.join(FE.context_directory, filename), "rb")
bytes = f.read() bytes = f.read()
f.close() f.close()
except IOError: except IOError:

View File

@ -18,14 +18,14 @@ def log(err):
the global error count.""" the global error count."""
global count global count
count = count + 1 count = count + 1
print>>sys.stderr, currentpoint + ": " + err print(currentpoint + ": " + err, file=sys.stderr)
def report(): def report():
"Print out the number of errors." "Print out the number of errors."
if count == 0: if count == 0:
print>>sys.stderr, "No errors" print("No errors", file=sys.stderr)
elif count == 1: elif count == 1:
print>>sys.stderr, "1 error" print("1 error", file=sys.stderr)
else: else:
print>>sys.stderr, str(count) + " errors" print(str(count) + " errors", file=sys.stderr)

View File

@ -70,7 +70,7 @@ def lex(point, line):
return return
elif firstchar in bases: elif firstchar in bases:
try: try:
result.append(Lexeme("NUM", long(rest, bases[firstchar][1]))) result.append(Lexeme("NUM", int(rest, bases[firstchar][1])))
return return
except ValueError: except ValueError:
Err.log('Invalid ' + bases[firstchar][0] + ' constant: ' + Err.log('Invalid ' + bases[firstchar][0] + ' constant: ' +
@ -79,7 +79,7 @@ def lex(point, line):
return return
elif firstchar.isdigit(): elif firstchar.isdigit():
try: try:
result.append(Lexeme("NUM", long(token))) result.append(Lexeme("NUM", int(token)))
except ValueError: except ValueError:
Err.log('Identifiers may not begin with a number') Err.log('Identifiers may not begin with a number')
result.append(Lexeme("LABEL", "ERROR")) result.append(Lexeme("LABEL", "ERROR"))
@ -403,19 +403,19 @@ def parse_file(ppt, filename, load_once=False):
filename)) filename))
if load_once and filename in loadedfiles: if load_once and filename in loadedfiles:
if Cmd.print_loaded_files: if Cmd.print_loaded_files:
print>>sys.stderr, "Skipping " + filename print("Skipping " + filename, file=sys.stderr)
return IR.NullNode return IR.NullNode
loadedfiles[filename] = True loadedfiles[filename] = True
if Cmd.print_loaded_files: if Cmd.print_loaded_files:
if filename != '-': if filename != '-':
print>>sys.stderr, "Loading " + filename print("Loading " + filename, file=sys.stderr)
else: else:
print>>sys.stderr, "Loading from standard input" print("Loading from standard input", file=sys.stderr)
try: try:
if filename != '-': if filename != '-':
if context_directory is not None: if context_directory is not None:
filename = os.path.join(context_directory, filename) filename = os.path.join(context_directory, filename)
f = file(filename) f = open(filename, "rt")
linelist = f.readlines() linelist = f.readlines()
f.close() f.close()
context_directory = os.path.abspath(os.path.dirname(filename)) context_directory = os.path.abspath(os.path.dirname(filename))
@ -423,8 +423,8 @@ def parse_file(ppt, filename, load_once=False):
context_directory = os.getcwd() context_directory = os.getcwd()
linelist = sys.stdin.readlines() linelist = sys.stdin.readlines()
pptlist = ["%s:%d" % (filename, i + 1) for i in range(len(linelist))] pptlist = ["%s:%d" % (filename, i + 1) for i in range(len(linelist))]
lexlist = map(lex, pptlist, linelist) lexlist = list(map(lex, pptlist, linelist))
IRlist = map(parse_line, pptlist, lexlist) IRlist = list(map(parse_line, pptlist, lexlist))
IRlist = [node for node in IRlist if node is not IR.NullNode] IRlist = [node for node in IRlist if node is not IR.NullNode]
context_directory = old_context context_directory = old_context
return IR.SequenceNode(ppt, IRlist) return IR.SequenceNode(ppt, IRlist)

View File

@ -179,7 +179,7 @@ class SequenceExpr(Expr):
return True return True
def value(self, env=None): def value(self, env=None):
subs = map((lambda x: x.value(env)), self.operands) subs = list(map((lambda x: x.value(env)), self.operands))
result = subs[0] result = subs[0]
index = 1 index = 1
for op in self.operators: for op in self.operators:

View File

@ -43,7 +43,7 @@ class Listing(object):
out = file(self.filename, "w") out = file(self.filename, "w")
for x in self.listing: for x in self.listing:
if type(x) is str: if type(x) is str:
print>>out, x print(x, file=out)
elif type(x) is tuple: elif type(x) is tuple:
i = 0 i = 0
pc = x[0] pc = x[0]
@ -59,7 +59,7 @@ class Listing(object):
charline += "." charline += "."
else: else:
charline += chr(c) charline += chr(c)
print>>out, "%-54s |%-16s|" % (dataline, charline) print("%-54s |%-16s|" % (dataline, charline), file=out)
i += 16 i += 16
if self.filename != "-": if self.filename != "-":
out.close() out.close()

View File

@ -55,7 +55,7 @@ def expandMacro(ppt, name, arglist):
Err.log("Undefined macro '%s'" % name) Err.log("Undefined macro '%s'" % name)
return IR.NullNode return IR.NullNode
argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg) argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg)
for (i, arg) in zip(xrange(1, sys.maxint), arglist)] for (i, arg) in zip(range(1, sys.maxsize), arglist)]
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i))
for i in range(1, len(arglist) + 1)] for i in range(1, len(arglist) + 1)]
body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data) body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data)
@ -70,7 +70,7 @@ def dump():
global macros global macros
for mac in macros: for mac in macros:
body = macros[mac] body = macros[mac]
print>>sys.stderr, "Macro: " + mac print("Macro: " + mac, file=sys.stderr)
for node in body: for node in body:
print>>sys.stderr, node print(node, file=sys.stderr)
print>>sys.stderr, "" print("", file=sys.stderr)

View File

@ -72,7 +72,7 @@ def run_all():
try: try:
outfile = Ophis.CmdLine.outfile outfile = Ophis.CmdLine.outfile
if outfile == '-': if outfile == '-':
output = sys.stdout output = sys.stdout.buffer
if sys.platform == "win32": if sys.platform == "win32":
# We can't dump our binary in text mode; that would be # We can't dump our binary in text mode; that would be
# disastrous. So, we'll do some platform-specific # disastrous. So, we'll do some platform-specific
@ -80,16 +80,16 @@ def run_all():
import msvcrt import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
elif outfile is None: elif outfile is None:
output = file('ophis.bin', 'wb') output = open('ophis.bin', 'wb')
else: else:
output = file(outfile, 'wb') output = open(outfile, 'wb')
output.write("".join(map(chr, a.output))) output.write(bytes(a.output))
output.flush() output.flush()
if outfile != '-': if outfile != '-':
output.close() output.close()
return 0 return 0
except IOError: except IOError:
print>>sys.stderr, "Could not write to " + outfile print("Could not write to " + outfile, file=sys.stderr)
return 1 return 1
else: else:
Err.report() Err.report()

View File

@ -67,18 +67,18 @@ class Pass(object):
printing debugging information.""" printing debugging information."""
if Err.count == 0: if Err.count == 0:
if Cmd.print_pass: if Cmd.print_pass:
print>>sys.stderr, "Running: " + self.name print("Running: " + self.name, file=sys.stderr)
env.reset() env.reset()
self.prePass() self.prePass()
node.accept(self, env) node.accept(self, env)
self.postPass() self.postPass()
env.reset() env.reset()
if Cmd.print_labels: if Cmd.print_labels:
print>>sys.stderr, "Current labels:" print("Current labels:", file=sys.stderr)
print>>sys.stderr, env print(env, file=sys.stderr)
if Cmd.print_ir: if Cmd.print_ir:
print>>sys.stderr, "Current IR:" print("Current IR:", file=sys.stderr)
print>>sys.stderr, node print(node, file=sys.stderr)
class FixPoint(object): class FixPoint(object):
@ -92,7 +92,7 @@ class FixPoint(object):
def go(self, node, env): def go(self, node, env):
"""Runs this FixPoint's passes, in order, until the fixpoint """Runs this FixPoint's passes, in order, until the fixpoint
is true. Always runs the passes at least once.""" is true. Always runs the passes at least once."""
for i in xrange(100): for i in range(100):
if Err.count != 0: if Err.count != 0:
break break
for p in self.passes: for p in self.passes:
@ -102,7 +102,7 @@ class FixPoint(object):
if self.fixpoint(): if self.fixpoint():
break break
if Cmd.print_pass: if Cmd.print_pass:
print>>sys.stderr, "Fixpoint failed, looping back" print("Fixpoint failed, looping back", file=sys.stderr)
else: else:
Err.log("Can't make %s converge! Maybe there's a recursive " Err.log("Can't make %s converge! Maybe there's a recursive "
"dependency somewhere?" % self.name) "dependency somewhere?" % self.name)
@ -120,7 +120,7 @@ class DefineMacros(Pass):
if self.inDef: if self.inDef:
Err.log("Unmatched .macro") Err.log("Unmatched .macro")
elif Cmd.print_ir: elif Cmd.print_ir:
print>>sys.stderr, "Macro definitions:" print("Macro definitions:", file=sys.stderr)
Macro.dump() Macro.dump()
def visitMacroBegin(self, node, env): def visitMacroBegin(self, node, env):
@ -197,11 +197,11 @@ class InitLabels(Pass):
env[label] = 0 env[label] = 0
self.changed = True self.changed = True
if label in ['a', 'x', 'y'] and self.runcount == 1: if label in ['a', 'x', 'y'] and self.runcount == 1:
print>>sys.stderr, str(node.ppt) + ": WARNING: " \ print(str(node.ppt) + ": WARNING: " \
"using register name as label" "using register name as label", file=sys.stderr)
if label in Ops.opcodes and self.runcount == 1: if label in Ops.opcodes and self.runcount == 1:
print>>sys.stderr, str(node.ppt) + ": WARNING: " \ print(str(node.ppt) + ": WARNING: " \
"using opcode name as label" "using opcode name as label", file=sys.stderr)
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
@ -636,16 +636,16 @@ class ExtendBranches(PCTracker):
if Cmd.enable_4502_exts: if Cmd.enable_4502_exts:
node.nodetype = "RelativeLong" node.nodetype = "RelativeLong"
if Cmd.warn_on_branch_extend: if Cmd.warn_on_branch_extend:
print>>sys.stderr, str(node.ppt) + ": WARNING: " \ print(str(node.ppt) + ": WARNING: " \
"branch out of range, replacing with 16-bit relative branch" "branch out of range, replacing with 16-bit relative branch", file=sys.stderr)
else: else:
if opcode == 'bra': if opcode == 'bra':
# If BRA - BRanch Always - is out of range, it's a JMP. # If BRA - BRanch Always - is out of range, it's a JMP.
node.data = ('jmp', expr, None) node.data = ('jmp', expr, None)
node.nodetype = "Absolute" node.nodetype = "Absolute"
if Cmd.warn_on_branch_extend: if Cmd.warn_on_branch_extend:
print>>sys.stderr, str(node.ppt) + ": WARNING: " \ print(str(node.ppt) + ": WARNING: " \
"bra out of range, replacing with jmp" "bra out of range, replacing with jmp", file=sys.stderr)
else: else:
# Otherwise, we replace it with a 'macro' of sorts by hand: # Otherwise, we replace it with a 'macro' of sorts by hand:
# $branch LOC -> $reversed_branch ^+5; JMP LOC # $branch LOC -> $reversed_branch ^+5; JMP LOC
@ -661,11 +661,11 @@ class ExtendBranches(PCTracker):
node.nodetype = 'SEQUENCE' node.nodetype = 'SEQUENCE'
node.data = expansion node.data = expansion
if Cmd.warn_on_branch_extend: if Cmd.warn_on_branch_extend:
print>>sys.stderr, str(node.ppt) + ": WARNING: " + \ print(str(node.ppt) + ": WARNING: " + \
opcode + " out of range, " \ opcode + " out of range, " \
"replacing with " + \ "replacing with " + \
ExtendBranches.reversed[opcode] + \ ExtendBranches.reversed[opcode] + \
"/jmp combo" "/jmp combo", file=sys.stderr)
self.changed = True self.changed = True
node.accept(self, env) node.accept(self, env)
else: else:
@ -690,11 +690,11 @@ class ExtendBranches(PCTracker):
node.nodetype = 'SEQUENCE' node.nodetype = 'SEQUENCE'
node.data = expansion node.data = expansion
if Cmd.warn_on_branch_extend: if Cmd.warn_on_branch_extend:
print>>sys.stderr, str(node.ppt) + ": WARNING: " + \ print(str(node.ppt) + ": WARNING: " + \
opcode + " out of range, " \ opcode + " out of range, " \
"replacing with " + \ "replacing with " + \
ExtendBranches.reversed[opcode] + \ ExtendBranches.reversed[opcode] + \
"/jmp combo" "/jmp combo", file=sys.stderr)
self.changed = True self.changed = True
node.accept(self, env) node.accept(self, env)
else: else:
@ -763,10 +763,10 @@ class Assembler(Pass):
self.listing.dump() self.listing.dump()
self.mapper.dump() self.mapper.dump()
if Cmd.print_summary and Err.count == 0: if Cmd.print_summary and Err.count == 0:
print>>sys.stderr, "Assembly complete: %s bytes output " \ print("Assembly complete: %s bytes output " \
"(%s code, %s data, %s filler)" \ "(%s code, %s data, %s filler)" \
% (len(self.output), % (len(self.output),
self.code, self.data, self.filler) self.code, self.data, self.filler), file=sys.stderr)
def outputbyte(self, expr, env, tee=None): def outputbyte(self, expr, env, tee=None):
'Outputs a byte, with range checking' 'Outputs a byte, with range checking'
@ -799,7 +799,7 @@ class Assembler(Pass):
'Outputs a little-endian dword, with range checking' 'Outputs a little-endian dword, with range checking'
if self.writeOK: if self.writeOK:
val = expr.value(env) val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL: if val < 0x00000000 or val > 0xFFFFFFFF:
Err.log("DWord constant " + str(expr) + " out of range") Err.log("DWord constant " + str(expr) + " out of range")
val = 0 val = 0
self.output.append(int(val & 0xFF)) self.output.append(int(val & 0xFF))
@ -829,7 +829,7 @@ class Assembler(Pass):
'Outputs a big-endian dword, with range checking' 'Outputs a big-endian dword, with range checking'
if self.writeOK: if self.writeOK:
val = expr.value(env) val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL: if val < 0x00000000 or val > 0xFFFFFFFF:
Err.log("DWord constant " + str(expr) + " out of range") Err.log("DWord constant " + str(expr) + " out of range")
val = 0 val = 0
self.output.append(int((val >> 24) & 0xFF)) self.output.append(int((val >> 24) & 0xFF))
@ -1083,7 +1083,7 @@ class Assembler(Pass):
(pc, target)) (pc, target))
else: else:
created = [] created = []
for i in xrange(target - pc): for i in range(target - pc):
self.outputbyte(node.data[1], env, created) self.outputbyte(node.data[1], env, created)
self.filler += target - pc self.filler += target - pc
self.registerData(created, env.getPC()) self.registerData(created, env.getPC())

View File

@ -1,18 +1,18 @@
#!/usr/bin/env python #!/usr/bin/env python3
import struct import struct
x = ''.join([chr(x) for x in range(256)]) x = bytes(list(range(256)))
bits = struct.unpack("32s32s32s32s32s32s32s32s", x) bits = struct.unpack("32s32s32s32s32s32s32s32s", x)
norm = [4, 5, 6, 7, 0, 1, 2, 3] norm = [4, 5, 6, 7, 0, 1, 2, 3]
ivrs = [4, 1, 0, 7, 6, 5, 2, 3] ivrs = [4, 1, 0, 7, 6, 5, 2, 3]
blnk = [4, 3, 2, 7, 0, 1, 6, 5] blnk = [4, 3, 2, 7, 0, 1, 6, 5]
normmap = ''.join([bits[x] for x in norm]) normmap = b''.join([bits[x] for x in norm])
ivrsmap = ''.join([bits[x] for x in ivrs]) ivrsmap = b''.join([bits[x] for x in ivrs])
blnkmap = ''.join([bits[x] for x in blnk]) blnkmap = b''.join([bits[x] for x in blnk])
def dumpfile(n, m): def dumpfile(n, m):
f = file(n, 'wb') f = open(n, 'wb')
f.write(m) f.write(m)
f.close() f.close()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
verbose = 0 verbose = 0
@ -80,7 +80,7 @@ def decomment(l):
def decomment_readlines(fname): def decomment_readlines(fname):
result = [decomment(x) for x in file(fname).readlines()] result = [decomment(x) for x in open(fname, "rt").readlines()]
return [x for x in result if len(x) > 0] return [x for x in result if len(x) > 0]
@ -103,22 +103,22 @@ def parse_chipset_file(fname):
result[op] = [] result[op] = []
result[op].append((mnem, flatmodes.index(mode))) result[op].append((mnem, flatmodes.index(mode)))
else: else:
print "Unknown mode '%s'" % s_p[1] print("Unknown mode '%s'" % s_p[1])
except ValueError: except ValueError:
print "Illegal opcode '%s'" % l[0] print("Illegal opcode '%s'" % l[0])
return result return result
def collate_chipset_map(cs_list, base): def collate_chipset_map(cs_list, base):
result = {} result = {}
for (opcode, insts) in zip(range(256), cs_list): for (opcode, insts) in zip(list(range(256)), cs_list):
if insts is not None: if insts is not None:
for inst in insts: for inst in insts:
(mnem, mode) = inst (mnem, mode) = inst
if mnem not in result: if mnem not in result:
result[mnem] = [None] * len(modes) result[mnem] = [None] * len(modes)
if result[mnem][mode] is not None: if result[mnem][mode] is not None:
print "Warning: Reassigning %s - %s" % (mnem, modes[mode]) print("Warning: Reassigning %s - %s" % (mnem, modes[mode]))
result[mnem][mode] = opcode result[mnem][mode] = opcode
if base is not None: if base is not None:
todel = [] todel = []
@ -127,9 +127,9 @@ def collate_chipset_map(cs_list, base):
if result[x] == base[x]: if result[x] == base[x]:
todel.append(x) todel.append(x)
elif verbose != 0: elif verbose != 0:
print "# Opcode %s changed" % x print("# Opcode %s changed" % x)
elif verbose != 0: elif verbose != 0:
print "# Opcode %s added" % x print("# Opcode %s added" % x)
for x in todel: for x in todel:
del result[x] del result[x]
return result return result
@ -143,14 +143,14 @@ def mapval(x):
def dump_map(m, prologue=''): def dump_map(m, prologue=''):
mnems = m.keys() mnems = list(m.keys())
mnems.sort() mnems.sort()
for mnem in mnems: for mnem in mnems:
codes = [mapval(x) for x in m[mnem]] codes = [mapval(x) for x in m[mnem]]
print "%s'%s': [%s,\n%s %s]," % (prologue, mnem, print("%s'%s': [%s,\n%s %s]," % (prologue, mnem,
', '.join(codes[:8]), ', '.join(codes[:8]),
prologue + " " * len(mnem), prologue + " " * len(mnem),
', '.join(codes[8:])) ', '.join(codes[8:])))
if __name__ == '__main__': if __name__ == '__main__':
@ -165,18 +165,18 @@ if __name__ == '__main__':
for y in [z.split(':', 1) for z in decomment_readlines(x)]] for y in [z.split(':', 1) for z in decomment_readlines(x)]]
for l in ls: for l in ls:
if len(l) != 2: if len(l) != 2:
print "Could not parse the chipset line '%s'" % ":".join(l) print("Could not parse the chipset line '%s'" % ":".join(l))
else: else:
archs.append((l[0], l[1])) archs.append((l[0], l[1]))
except IOError: except IOError:
print "Could not read file %s" % x print("Could not read file %s" % x)
print prologue print(prologue)
baseset = None baseset = None
for (field, fname) in archs: for (field, fname) in archs:
chipset_list = parse_chipset_file(fname) chipset_list = parse_chipset_file(fname)
instruction_map = collate_chipset_map(chipset_list, baseset) instruction_map = collate_chipset_map(chipset_list, baseset)
if baseset is None: if baseset is None:
baseset = instruction_map baseset = instruction_map
print "%s = {" % field print("%s = {" % field)
dump_map(instruction_map, ' ' * (len(field) + 4)) dump_map(instruction_map, ' ' * (len(field) + 4))
print "%s}" % (' ' * (len(field) + 3)) print("%s}" % (' ' * (len(field) + 3)))

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
import subprocess import subprocess
@ -20,14 +20,14 @@ failed = 0
def assembled(raw): def assembled(raw):
return ' '.join(["%02X" % ord(c) for c in raw]) return ' '.join(["%02X" % c for c in raw])
def assemble_raw(asm="", options=[]): def assemble_raw(asm="", options=[]):
p = subprocess.Popen([pythonpath, ophispath] + options, p = subprocess.Popen([pythonpath, ophispath] + options,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
return p.communicate(asm) return p.communicate(asm.encode("UTF-8"))
def assemble_string(asm, options=[]): def assemble_string(asm, options=[]):
@ -37,15 +37,15 @@ def assemble_string(asm, options=[]):
def test_string(test_name, asm, expected, options=[]): def test_string(test_name, asm, expected, options=[]):
(out, err) = assemble_string(asm, options) (out, err) = assemble_string(asm, options)
if out == expected: if out == expected:
print "%s: SUCCESS" % test_name print("%s: SUCCESS" % test_name)
else: else:
global failed global failed
failed += 1 failed += 1
print "%s: FAILED" % test_name print("%s: FAILED" % test_name)
print "Assembled code: ", assembled(out) print("Assembled code: ", assembled(out))
print "Expected code: ", assembled(expected) print("Expected code: ", assembled(expected))
if err != '': if err != '':
print "Error output:\n%s" % err print("Error output:\n%s" % err.decode(sys.stderr.encoding))
def test_file(test_name, fname, ename, options=[]): def test_file(test_name, fname, ename, options=[]):
@ -57,7 +57,7 @@ def test_file(test_name, fname, ename, options=[]):
expected = f.read() expected = f.read()
f.close() f.close()
else: # a test where we expect failure else: # a test where we expect failure
expected = '' expected = b''
test_string(test_name, asm, expected, options) test_string(test_name, asm, expected, options)
@ -66,16 +66,16 @@ def test_file(test_name, fname, ename, options=[]):
# on, then we start running through the features. # on, then we start running through the features.
def test_basic(): def test_basic():
print print()
print "==== BASIC OPERATION ====" print("==== BASIC OPERATION ====")
test_string('Basic Ophis operation', '.byte "Hello, world!"', test_string('Basic Ophis operation', '.byte "Hello, world!"',
'Hello, world!') b'Hello, world!')
test_string('Newline/EOF passthrough', '.byte 10,26,13,4,0,"Hi",13,10', test_string('Newline/EOF passthrough', '.byte 10,26,13,4,0,"Hi",13,10',
'\n\x1a\r\x04\x00Hi\r\n') b'\n\x1a\r\x04\x00Hi\r\n')
# Normally these would go in Expressions but we need them to run the # Normally these would go in Expressions but we need them to run the
# tests for relative instructions. # tests for relative instructions.
test_string('Program counter recognition', '.org $41\nlda #^\n', '\xa9A') test_string('Program counter recognition', '.org $41\nlda #^\n', b'\xa9A')
test_string('Program counter math', '.org $41\nlda #^+3\n', '\xa9D') test_string('Program counter math', '.org $41\nlda #^+3\n', b'\xa9D')
if failed == 0: if failed == 0:
test_file('Basic instructions', 'testbase.oph', 'testbase.bin') test_file('Basic instructions', 'testbase.oph', 'testbase.bin')
test_file('Basic data pragmas', 'testdata.oph', 'testdata.bin') test_file('Basic data pragmas', 'testdata.oph', 'testdata.bin')
@ -100,13 +100,13 @@ def test_basic():
def test_outfile(): def test_outfile():
global failed global failed
print "\n==== INPUT AND OUTPUT ====" print("\n==== INPUT AND OUTPUT ====")
if os.path.exists("ophis.bin"): if os.path.exists("ophis.bin"):
print "TEST SUITE FAILED: unclean test environment (ophis.bin exists)" print("TEST SUITE FAILED: unclean test environment (ophis.bin exists)")
failed += 1 failed += 1
return return
elif os.path.exists("custom.bin"): elif os.path.exists("custom.bin"):
print "TEST SUITE FAILED: unclean test environment (custom.bin exists)" print("TEST SUITE FAILED: unclean test environment (custom.bin exists)")
failed += 1 failed += 1
return return
@ -114,45 +114,46 @@ def test_outfile():
try: try:
assemble_raw('.byte "Hello, world!", 10', ['-']) assemble_raw('.byte "Hello, world!", 10', ['-'])
f = open('ophis.bin', 'rb') f = open('ophis.bin', 'rb')
if f.read() != 'Hello, world!\n': if f.read() != b'Hello, world!\n':
print "Default output filename: FAILED (bad output)" print("Default output filename: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Default output filename: SUCCESS" print("Default output filename: SUCCESS")
f.close() f.close()
os.unlink('ophis.bin') os.unlink('ophis.bin')
except: except:
print "Default output filename: FAILED (exception)" print("Default output filename: FAILED (exception)")
failed += 1 failed += 1
raise
# Test 2: Command line override # Test 2: Command line override
try: try:
assemble_raw('.byte "Hello, world!", 10', ['-', '-o', 'custom.bin']) assemble_raw('.byte "Hello, world!", 10', ['-', '-o', 'custom.bin'])
f = open('custom.bin', 'rb') f = open('custom.bin', 'rb')
if f.read() != 'Hello, world!\n': if f.read() != b'Hello, world!\n':
print "Commandline output filename: FAILED (bad output)" print("Commandline output filename: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Commandline output filename: SUCCESS" print("Commandline output filename: SUCCESS")
f.close() f.close()
os.unlink('custom.bin') os.unlink('custom.bin')
except: except:
print "Commandline output filename: FAILED (exception)" print("Commandline output filename: FAILED (exception)")
failed += 1 failed += 1
# Test 3: Pragma override # Test 3: Pragma override
try: try:
assemble_raw('.outfile "custom.bin"\n.byte "Hello, world!", 10', ['-']) assemble_raw('.outfile "custom.bin"\n.byte "Hello, world!", 10', ['-'])
f = open('custom.bin', 'rb') f = open('custom.bin', 'rb')
if f.read() != 'Hello, world!\n': if f.read() != b'Hello, world!\n':
print "Commandline output filename: FAILED (bad output)" print("Commandline output filename: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Commandline output filename: SUCCESS" print("Commandline output filename: SUCCESS")
f.close() f.close()
os.unlink('custom.bin') os.unlink('custom.bin')
except: except:
print "Commandline output filename: FAILED (exception)" print("Commandline output filename: FAILED (exception)")
failed += 1 failed += 1
# Test 4: Command line override of .outfile # Test 4: Command line override of .outfile
@ -160,15 +161,15 @@ def test_outfile():
assemble_raw('.outfile "custom2.bin"\n' assemble_raw('.outfile "custom2.bin"\n'
'.byte "Hello, world!", 10', ['-', '-o', 'custom.bin']) '.byte "Hello, world!", 10', ['-', '-o', 'custom.bin'])
f = open('custom.bin', 'rb') f = open('custom.bin', 'rb')
if f.read() != 'Hello, world!\n': if f.read() != b'Hello, world!\n':
print "Commandline override of pragma: FAILED (bad output)" print("Commandline override of pragma: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Commandline override of pragma: SUCCESS" print("Commandline override of pragma: SUCCESS")
f.close() f.close()
os.unlink('custom.bin') os.unlink('custom.bin')
except: except:
print "Commandline override of pragma: FAILED (exception)" print("Commandline override of pragma: FAILED (exception)")
failed += 1 failed += 1
# Test 5: Pragma repetition priority # Test 5: Pragma repetition priority
@ -177,15 +178,15 @@ def test_outfile():
'.outfile "custom2.bin"\n' '.outfile "custom2.bin"\n'
'.byte "Hello, world!", 10', ['-']) '.byte "Hello, world!", 10', ['-'])
f = open('custom.bin', 'rb') f = open('custom.bin', 'rb')
if f.read() != 'Hello, world!\n': if f.read() != b'Hello, world!\n':
print "Pragma repetition: FAILED (bad output)" print("Pragma repetition: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Pragma repetition: SUCCESS" print("Pragma repetition: SUCCESS")
f.close() f.close()
os.unlink('custom.bin') os.unlink('custom.bin')
except: except:
print "Pragma repetition: FAILED (exception)" print("Pragma repetition: FAILED (exception)")
failed += 1 failed += 1
# Test 6: multiple input files # Test 6: multiple input files
@ -200,73 +201,73 @@ def test_outfile():
s += f.read() s += f.read()
f.close() f.close()
if out != s: if out != s:
print "Multiple input files: FAILED (bad output)" print("Multiple input files: FAILED (bad output)")
failed += 1 failed += 1
else: else:
print "Multiple input files: SUCCESS" print("Multiple input files: SUCCESS")
except: except:
print "Multiple input files: FAILED (exception)" print("Multiple input files: FAILED (exception)")
failed += 1 failed += 1
def test_transforms(): def test_transforms():
print "\n==== BINARY TRANSFORM PASSES ====" print("\n==== BINARY TRANSFORM PASSES ====")
print "Simple zero page selection: SUCCESS (covered in basic tests)" print("Simple zero page selection: SUCCESS (covered in basic tests)")
test_string('Chained collapse', '.org $fa \n lda + \n lda ^ \n * rts \n', test_string('Chained collapse', '.org $fa \n lda + \n lda ^ \n * rts \n',
'\xa5\xfe\xa5\xfc\x60') b'\xa5\xfe\xa5\xfc\x60')
test_string('Reversible collapse', '.org $fb \n bne ^+200 \n lda ^ \n', test_string('Reversible collapse', '.org $fb \n bne ^+200 \n lda ^ \n',
'\xf0\x03\x4c\xc5\x01\xad\x00\x01') b'\xf0\x03\x4c\xc5\x01\xad\x00\x01')
def test_expressions(): def test_expressions():
print "\n==== EXPRESSIONS AND LABELS ====" print("\n==== EXPRESSIONS AND LABELS ====")
test_string('Basic addition', '.byte 3+2', '\x05') test_string('Basic addition', '.byte 3+2', b'\x05')
test_string('Basic subtraction', '.byte 3-2', '\x01') test_string('Basic subtraction', '.byte 3-2', b'\x01')
test_string('Basic multiplication', '.byte 3*2', '\x06') test_string('Basic multiplication', '.byte 3*2', b'\x06')
test_string('Basic division', '.byte 6/2', '\x03') test_string('Basic division', '.byte 6/2', b'\x03')
test_string('Basic bit-union', '.byte 5|9', '\x0d') test_string('Basic bit-union', '.byte 5|9', b'\x0d')
test_string('Basic bit-intersection', '.byte 5&9', '\x01') test_string('Basic bit-intersection', '.byte 5&9', b'\x01')
test_string('Basic bit-toggle', '.byte 5^9', '\x0c') test_string('Basic bit-toggle', '.byte 5^9', b'\x0c')
test_string('Division truncation', '.byte 5/2', '\x02') test_string('Division truncation', '.byte 5/2', b'\x02')
test_string('Overflow', '.byte $FF*$10', '') test_string('Overflow', '.byte $FF*$10', b'')
test_string('Multibyte overflow', '.word $FF*$10', '\xf0\x0f') test_string('Multibyte overflow', '.word $FF*$10', b'\xf0\x0f')
test_string('Masked overflow', '.byte $FF*$10&$FF', '\xf0') test_string('Masked overflow', '.byte $FF*$10&$FF', b'\xf0')
test_string('Underflow', '.byte 2-3', '') test_string('Underflow', '.byte 2-3', b'')
test_string('Masked underflow', '.byte 2-3&$FF', '\xff') test_string('Masked underflow', '.byte 2-3&$FF', b'\xff')
test_string('Arithmetic precedence', '.byte 2+3*4-6/2', '\x0b') test_string('Arithmetic precedence', '.byte 2+3*4-6/2', b'\x0b')
test_string('Parentheses', '.byte [2+3]*[4-6/2]', '\x05') test_string('Parentheses', '.byte [2+3]*[4-6/2]', b'\x05')
test_string('String escapes', test_string('String escapes',
'.byte "The man said, \\"The \\\\ is Windowsy.\\""', '.byte "The man said, \\"The \\\\ is Windowsy.\\""',
'The man said, "The \\ is Windowsy."') b'The man said, "The \\ is Windowsy."')
test_string('Byte selector precedence', test_string('Byte selector precedence',
'.byte >$d000+32,>[$d000+32],<[$D000-275]', '.byte >$d000+32,>[$d000+32],<[$D000-275]',
'\xf0\xd0\xed') b'\xf0\xd0\xed')
test_string('Named labels', '.org $6948\nl: .word l', 'Hi') test_string('Named labels', '.org $6948\nl: .word l', b'Hi')
test_string('.alias directive (basic)', '.alias hi $6948\n.word hi', 'Hi') test_string('.alias directive (basic)', '.alias hi $6948\n.word hi', b'Hi')
test_string('.alias directive (derived)', test_string('.alias directive (derived)',
'.alias hi $6948\n.alias ho hi+$600\n.word hi,ho', 'HiHo') '.alias hi $6948\n.alias ho hi+$600\n.word hi,ho', b'HiHo')
test_string('.alias directive (circular)', test_string('.alias directive (circular)',
'.alias a c+1\n.alias b a+3\n.alias c b-4\n.word a, b, c', '.alias a c+1\n.alias b a+3\n.alias c b-4\n.word a, b, c',
'') b'')
test_string('.advance directive (basic)', test_string('.advance directive (basic)',
'lda #$05\n.advance $05\n.byte ^', 'lda #$05\n.advance $05\n.byte ^',
'\xa9\x05\x00\x00\x00\x05') b'\xa9\x05\x00\x00\x00\x05')
test_string('.advance directive (filler)', test_string('.advance directive (filler)',
'lda #$05\nf: .advance $05,f+3\n.byte ^', 'lda #$05\nf: .advance $05,f+3\n.byte ^',
'\xa9\x05\x05\x05\x05\x05') b'\xa9\x05\x05\x05\x05\x05')
test_string('.advance no-op', 'lda #$05\n.advance $02\n.byte ^', test_string('.advance no-op', 'lda #$05\n.advance $02\n.byte ^',
'\xa9\x05\x02') b'\xa9\x05\x02')
test_string('.advance failure', 'lda #$05\n.advance $01\n.byte ^', '') test_string('.advance failure', 'lda #$05\n.advance $01\n.byte ^', b'')
test_string('.checkpc, space > 0', 'lda #$05\n.checkpc $10', '\xa9\x05') test_string('.checkpc, space > 0', 'lda #$05\n.checkpc $10', b'\xa9\x05')
test_string('.checkpc, space = 0', 'lda #$05\n.checkpc 2', '\xa9\x05') test_string('.checkpc, space = 0', 'lda #$05\n.checkpc 2', b'\xa9\x05')
test_string('.checkpc, space < 0', 'lda $05\n.checkpc 1', '') test_string('.checkpc, space < 0', 'lda $05\n.checkpc 1', b'')
test_string('A X Y usable as labels', test_string('A X Y usable as labels',
'.alias a 1\n.alias x 2\n.alias y 3\n' '.alias a 1\n.alias x 2\n.alias y 3\n'
'lda (a+x+y),y\nlda (x+y,x)', 'lda (a+x+y),y\nlda (x+y,x)',
'\xb1\x06\xa1\x05') b'\xb1\x06\xa1\x05')
test_string('Opcodes usable as labels', test_string('Opcodes usable as labels',
'ldy #$00\n dey: dey\n bne dey', 'ldy #$00\n dey: dey\n bne dey',
'\xa0\x00\x88\xd0\xfd') b'\xa0\x00\x88\xd0\xfd')
def test_segments(): def test_segments():
@ -277,18 +278,18 @@ def test_segments():
'.org $61\n' '.org $61\n'
'd:\n' 'd:\n'
'.text\n' '.text\n'
'l: .byte l, d', 'Aa') 'l: .byte l, d', b'Aa')
test_string('Data cleanliness', '.byte 65\n.data\n.byte 65', '') test_string('Data cleanliness', '.byte 65\n.data\n.byte 65', b'')
test_string('.space directive', test_string('.space directive',
'.data\n.org $41\n.space a 2\n.space b 1\n.space c 1\n' '.data\n.org $41\n.space a 2\n.space b 1\n.space c 1\n'
'.text\n.byte a, b, c\n', 'ACD') '.text\n.byte a, b, c\n', b'ACD')
test_string('Multiple named segments', test_string('Multiple named segments',
'.data\n.org $41\n.data a\n.org $61\n.data b\n.org $4a\n' '.data\n.org $41\n.data a\n.org $61\n.data b\n.org $4a\n'
'.data\n.space q 1\n.data a\n.space r 1\n.data b\n.space s 1\n' '.data\n.space q 1\n.data a\n.space r 1\n.data b\n.space s 1\n'
'.text\n.org $10\n.text a\n.org $20\n' '.text\n.org $10\n.text a\n.org $20\n'
'.text\n.byte ^,q,r,s\n' '.text\n.byte ^,q,r,s\n'
'.text a\n.byte ^,q,r,s\n', '.text a\n.byte ^,q,r,s\n',
'\x10AaJ\x20AaJ') b'\x10AaJ\x20AaJ')
def test_scopes(): def test_scopes():
@ -300,13 +301,13 @@ def test_scopes():
'.scend\n' '.scend\n'
'.scope\n' '.scope\n'
'_l: .byte _l\n' '_l: .byte _l\n'
'.scend\n', 'AB') '.scend\n', b'AB')
test_string('Data hiding outside of scope', test_string('Data hiding outside of scope',
'.org $41\n' '.org $41\n'
'.scope\n' '.scope\n'
'_l: .byte _l\n' '_l: .byte _l\n'
'.scend\n' '.scend\n'
' .byte _l\n', '') ' .byte _l\n', b'')
test_string('Repeated labels, nested scopes', test_string('Repeated labels, nested scopes',
'.org $41\n' '.org $41\n'
'.scope\n' '.scope\n'
@ -315,17 +316,17 @@ def test_scopes():
'_l: .byte _l\n' '_l: .byte _l\n'
'.scend\n' '.scend\n'
' .byte _l\n' ' .byte _l\n'
'.scend\n', 'ABA') '.scend\n', b'ABA')
test_string('Anonymous labels (basic)', test_string('Anonymous labels (basic)',
'.org $41\n' '.org $41\n'
'* .byte -, +\n' '* .byte -, +\n'
'* .byte -, --\n', 'ACCA') '* .byte -, --\n', b'ACCA')
test_string('Anonymous labels (across scopes)', test_string('Anonymous labels (across scopes)',
'.org $41\n' '.org $41\n'
'* .byte -, +\n' '* .byte -, +\n'
'.scope\n' '.scope\n'
'* .byte -, --\n' '* .byte -, --\n'
'.scend\n', 'ACCA') '.scend\n', b'ACCA')
def test_macros(): def test_macros():
@ -334,12 +335,12 @@ def test_macros():
'.macro greet\n' '.macro greet\n'
' .byte "hi"\n' ' .byte "hi"\n'
'.macend\n' '.macend\n'
'`greet\n.invoke greet', "hihi") '`greet\n.invoke greet', b"hihi")
test_string('Macros with arguments', test_string('Macros with arguments',
'.macro greet\n' '.macro greet\n'
' .byte "hi",_1\n' ' .byte "hi",_1\n'
'.macend\n' '.macend\n'
"`greet 'A\n.invoke greet 'B", "hiAhiB") "`greet 'A\n.invoke greet 'B", b"hiAhiB")
test_string('Macros invoking macros', test_string('Macros invoking macros',
'.macro inner\n' '.macro inner\n'
' .byte " there"\n' ' .byte " there"\n'
@ -348,7 +349,7 @@ def test_macros():
' .byte "hi"\n' ' .byte "hi"\n'
' `inner\n' ' `inner\n'
'.macend\n' '.macend\n'
"`greet", "hi there") "`greet", b"hi there")
test_string('Macros defining macros (illegal)', test_string('Macros defining macros (illegal)',
'.macro greet\n' '.macro greet\n'
'.macro inner\n' '.macro inner\n'
@ -357,7 +358,7 @@ def test_macros():
' .byte "hi"\n' ' .byte "hi"\n'
' `inner\n' ' `inner\n'
'.macend\n' '.macend\n'
"`greet", "") "`greet", b"")
test_string('Fail on infinite recursion', test_string('Fail on infinite recursion',
'.macro inner\n' '.macro inner\n'
' .byte " there"\n' ' .byte " there"\n'
@ -367,75 +368,75 @@ def test_macros():
' .byte "hi"\n' ' .byte "hi"\n'
' `inner\n' ' `inner\n'
'.macend\n' '.macend\n'
"`greet", "") "`greet", b"")
def test_subfiles(): def test_subfiles():
print("\n==== COMPILATION UNITS ====") print("\n==== COMPILATION UNITS ====")
test_string(".include pragma", '.include "baseinc.oph"', 'BASIC\n') test_string(".include pragma", '.include "baseinc.oph"', b'BASIC\n')
test_string(".include repeated", test_string(".include repeated",
'.include "baseinc.oph"\n.include "baseinc.oph"', '.include "baseinc.oph"\n.include "baseinc.oph"',
'BASIC\nBASIC\n') b'BASIC\nBASIC\n')
test_string(".require pragma", '.require "baseinc.oph"', 'BASIC\n') test_string(".require pragma", '.require "baseinc.oph"', b'BASIC\n')
test_string(".include before .require", test_string(".include before .require",
'.include "baseinc.oph"\n.require "baseinc.oph"', '.include "baseinc.oph"\n.require "baseinc.oph"',
'BASIC\n') b'BASIC\n')
test_string(".require before .include", test_string(".require before .include",
'.require "baseinc.oph"\n.include "baseinc.oph"', '.require "baseinc.oph"\n.include "baseinc.oph"',
'BASIC\nBASIC\n') b'BASIC\nBASIC\n')
test_string(".require same file twice with different paths", test_string(".require same file twice with different paths",
'.include "baseinc.oph"\n.include "sub/baseinc.oph"', '.include "baseinc.oph"\n.include "sub/baseinc.oph"',
'BASIC\nSUB 1 START\nSUB 1 END\n') b'BASIC\nSUB 1 START\nSUB 1 END\n')
test_string(".require different files with identical paths", test_string(".require different files with identical paths",
'.include "sub/sub/sub.oph"', '.include "sub/sub/sub.oph"',
'SUB 2 START\nSUB 1 START\nBASIC\nSUB 1 END\nSUB 2 END\n') b'SUB 2 START\nSUB 1 START\nBASIC\nSUB 1 END\nSUB 2 END\n')
test_string(".charmap (basic)", test_string(".charmap (basic)",
'.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n' '.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n'
'.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n' '.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n'
'.byte "hELLO, wORLD!"', "Hello, World!") '.byte "hELLO, wORLD!"', b"Hello, World!")
test_string(".charmap (reset)", test_string(".charmap (reset)",
'.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n' '.charmap \'A, "abcdefghijklmnopqrstuvwxyz"\n'
'.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n' '.charmap \'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n'
'.byte "hELLO, wORLD!",10\n' '.byte "hELLO, wORLD!",10\n'
'.charmap\n' '.charmap\n'
'.byte "hELLO, wORLD!",10\n', '.byte "hELLO, wORLD!",10\n',
"Hello, World!\nhELLO, wORLD!\n") b"Hello, World!\nhELLO, wORLD!\n")
test_string(".charmap (out of range)", test_string(".charmap (out of range)",
'.charmap 250, "ABCDEFGHIJKLM"\n.byte 250,251', '.charmap 250, "ABCDEFGHIJKLM"\n.byte 250,251',
'') b'')
test_string(".charmapbin (basic)", test_string(".charmapbin (basic)",
'.charmapbin "../examples/petscii.map"\n.byte "hELLO, wORLD!"', '.charmapbin "../examples/petscii.map"\n.byte "hELLO, wORLD!"',
"Hello, World!") b"Hello, World!")
test_string(".charmapbin (illegal)", test_string(".charmapbin (illegal)",
'.charmapbin "baseinc.bin"\n.byte "hELLO, wORLD!"', '') '.charmapbin "baseinc.bin"\n.byte "hELLO, wORLD!"', b'')
test_string(".incbin (basic)", '.incbin "baseinc.bin"', "BASIC\n") test_string(".incbin (basic)", '.incbin "baseinc.bin"', b"BASIC\n")
test_string(".incbin (hardcoded offset)", test_string(".incbin (hardcoded offset)",
'.incbin "baseinc.bin",3', "IC\n") '.incbin "baseinc.bin",3', b"IC\n")
test_string(".incbin (hardcoded offset and length)", test_string(".incbin (hardcoded offset and length)",
'.incbin "baseinc.bin",3,2', "IC") '.incbin "baseinc.bin",3,2', b"IC")
test_string(".incbin (softcoded offset and length)", test_string(".incbin (softcoded offset and length)",
'.alias off len+1\n.alias len 2\n' '.alias off len+1\n.alias len 2\n'
'.incbin "baseinc.bin",off,len', "IC") '.incbin "baseinc.bin",off,len', b"IC")
test_string(".incbin (length too long)", test_string(".incbin (length too long)",
'.byte 65\n.incbin "baseinc.bin",3,4', "") '.byte 65\n.incbin "baseinc.bin",3,4', b"")
test_string(".incbin (negative offset)", test_string(".incbin (negative offset)",
'.byte 65\n.incbin "baseinc.bin",1-5,4', "") '.byte 65\n.incbin "baseinc.bin",1-5,4', b"")
test_string(".incbin (offset = size)", test_string(".incbin (offset = size)",
'.byte 65\n.incbin "baseinc.bin",6', "A") '.byte 65\n.incbin "baseinc.bin",6', b"A")
test_string(".incbin (offset > size)", test_string(".incbin (offset > size)",
'.byte 65\n.incbin "baseinc.bin",7', "") '.byte 65\n.incbin "baseinc.bin",7', b"")
test_string(".incbin (softcoded length too long)", test_string(".incbin (softcoded length too long)",
'.alias off len\n.alias len 4\n' '.alias off len\n.alias len 4\n'
'.byte 65\n.incbin "baseinc.bin",off,len', "") '.byte 65\n.incbin "baseinc.bin",off,len', b"")
test_string(".incbin (softcoded negative offset)", test_string(".incbin (softcoded negative offset)",
'.alias off 1-5\n' '.alias off 1-5\n'
'.byte 65\n.incbin "baseinc.bin",off,4', "") '.byte 65\n.incbin "baseinc.bin",off,4', b"")
test_string(".incbin (softcoded offset = size)", test_string(".incbin (softcoded offset = size)",
'.alias off 6\n' '.alias off 6\n'
'.byte 65\n.incbin "baseinc.bin",off', "A") '.byte 65\n.incbin "baseinc.bin",off', b"A")
test_string(".incbin (softcoded offset > size)", test_string(".incbin (softcoded offset > size)",
'.alias off 7\n' '.alias off 7\n'
'.byte 65\n.incbin "baseinc.bin",off', "") '.byte 65\n.incbin "baseinc.bin",off', b"")
def test_systematic(): def test_systematic():
@ -449,18 +450,18 @@ def test_systematic():
if __name__ == '__main__': if __name__ == '__main__':
print "Using Python interpreter:", pythonpath print("Using Python interpreter:", pythonpath)
test_basic() test_basic()
os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) os.chdir(os.path.dirname(os.path.realpath(sys.argv[0])))
if failed == 0: if failed == 0:
test_systematic() test_systematic()
else: else:
print "\nBasic test cases failed, aborting test." print("\nBasic test cases failed, aborting test.")
if failed > 0: if failed > 0:
print "\nTotal test case failures: %d" % failed print("\nTotal test case failures: %d" % failed)
sys.exit(1) sys.exit(1)
else: else:
print "\nAll test cases succeeded" print("\nAll test cases succeeded")
sys.exit(0) sys.exit(0)