Massive code modernization spree.

Full PEP8 compliance. Also, booleans have been inserted where
they make sense (introduced in 2.3!) and I haven't knowingly
added anything that will break 2.3 compatibility.

At this point the code really doesn't look like it was written
ten years ago. Hooray!
This commit is contained in:
Michael C. Martin 2012-06-01 10:25:48 -07:00
parent f83379287f
commit 14a37ca879
38 changed files with 1459 additions and 925 deletions

View File

@ -22,28 +22,43 @@ print_labels = False
infile = None infile = None
outfile = None outfile = None
def parse_args(raw_args): def parse_args(raw_args):
"Populate the module's globals based on the command-line options given." "Populate the module's globals based on the command-line options given."
global enable_collapse, enable_branch_extend, enable_undoc_ops, enable_65c02_exts global enable_collapse, enable_branch_extend
global enable_undoc_ops, enable_65c02_exts
global warn_on_branch_extend global warn_on_branch_extend
global print_summary, print_loaded_files, print_pass, print_ir, print_labels global print_summary, print_loaded_files
global print_pass, print_ir, print_labels
global infile, outfile global infile, outfile
parser = optparse.OptionParser(usage= "Usage: %prog [options] srcfile outfile", version="Ophis 6502 cross-assembler, version 2.0") parser = optparse.OptionParser(
usage="Usage: %prog [options] srcfile outfile",
version="Ophis 6502 cross-assembler, version 2.0")
ingrp = optparse.OptionGroup(parser, "Input options") ingrp = optparse.OptionGroup(parser, "Input options")
ingrp.add_option("-u", "--undoc", action="store_true", default=False, help="Enable 6502 undocumented opcodes") ingrp.add_option("-u", "--undoc", action="store_true", default=False,
ingrp.add_option("-c", "--65c02", action="store_true", default=False, dest="c02", help="Enable 65c02 extended instruction set") help="Enable 6502 undocumented opcodes")
ingrp.add_option("-c", "--65c02", action="store_true", default=False,
dest="c02", help="Enable 65c02 extended instruction set")
outgrp = optparse.OptionGroup(parser, "Console output options") outgrp = optparse.OptionGroup(parser, "Console output options")
outgrp.add_option("-v", "--verbose", action="store_const", const=2, help="Verbose mode", default=1) outgrp.add_option("-v", "--verbose", action="store_const", const=2,
outgrp.add_option("-q", "--quiet", action="store_const", help="Quiet mode", dest="verbose", const=0) help="Verbose mode", default=1)
outgrp.add_option("-d", "--debug", action="count", dest="verbose", help=optparse.SUPPRESS_HELP) outgrp.add_option("-q", "--quiet", action="store_const", help="Quiet mode",
outgrp.add_option("--no-warn", action="store_false", dest="warn", default=True, help="Do not print warnings") dest="verbose", const=0)
outgrp.add_option("-d", "--debug", action="count", dest="verbose",
help=optparse.SUPPRESS_HELP)
outgrp.add_option("--no-warn", action="store_false", dest="warn",
default=True, help="Do not print warnings")
bingrp = optparse.OptionGroup(parser, "Binary output options") bingrp = optparse.OptionGroup(parser, "Compilation options")
bingrp.add_option("--no-collapse", action="store_false", dest="enable_collapse", default="True", help="Disable zero-page collapse pass") bingrp.add_option("--no-collapse", action="store_false",
bingrp.add_option("--no-branch-extend", action="store_false", dest="enable_branch_extend", default="True", help="Disable branch-extension pass") dest="enable_collapse", default="True",
help="Disable zero-page collapse pass")
bingrp.add_option("--no-branch-extend", action="store_false",
dest="enable_branch_extend", default="True",
help="Disable branch-extension pass")
parser.add_option_group(ingrp) parser.add_option_group(ingrp)
parser.add_option_group(outgrp) parser.add_option_group(outgrp)

View File

@ -10,46 +10,52 @@ 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
loadedfiles={} loadedfiles = {}
basecharmap = "".join([chr(x) for x in range(256)]) basecharmap = "".join([chr(x) for x in range(256)])
currentcharmap = basecharmap currentcharmap = basecharmap
def reset(): def reset():
global loadedfiles, currentcharmap, basecharmap global loadedfiles, currentcharmap, basecharmap
loadedfiles={} loadedfiles = {}
currentcharmap = basecharmap currentcharmap = basecharmap
def pragmaInclude(ppt, line, result): def pragmaInclude(ppt, line, result):
"Includes a source file" "Includes a source file"
filename = line.expect("STRING").value filename = line.expect("STRING").value
line.expect("EOL") line.expect("EOL")
if type(filename)==str: result.append(FE.parse_file(ppt, filename)) if type(filename) == str:
result.append(FE.parse_file(ppt, filename))
def pragmaRequire(ppt, line, result): def pragmaRequire(ppt, line, result):
"Includes a source file at most one time" "Includes a source file at most one time"
filename = line.expect("STRING").value filename = line.expect("STRING").value
line.expect("EOL") line.expect("EOL")
if type(filename)==str: if type(filename) == str:
global loadedfiles global loadedfiles
if filename not in loadedfiles: if filename not in loadedfiles:
loadedfiles[filename]=1 loadedfiles[filename] = True
result.append(FE.parse_file(ppt, filename)) result.append(FE.parse_file(ppt, filename))
def pragmaIncbin(ppt, line, result): def pragmaIncbin(ppt, line, result):
"Includes a binary file" "Includes a binary file"
filename = line.expect("STRING").value filename = line.expect("STRING").value
line.expect("EOL") line.expect("EOL")
if type(filename)==str: if type(filename) == str:
try: try:
f = file(filename, "rb") f = file(filename, "rb")
bytes = f.read() bytes = f.read()
f.close() f.close()
except IOError: except IOError:
Err.log ("Could not read "+filename) Err.log("Could not read " + filename)
return return
bytes = [IR.ConstantExpr(ord(x)) for x in bytes] bytes = [IR.ConstantExpr(ord(x)) for x in bytes]
result.append(IR.Node(ppt, "Byte", *bytes)) result.append(IR.Node(ppt, "Byte", *bytes))
def pragmaCharmap(ppt, line, result): def pragmaCharmap(ppt, line, result):
"Modify the character map." "Modify the character map."
global currentcharmap, basecharmap global currentcharmap, basecharmap
@ -60,30 +66,33 @@ def pragmaCharmap(ppt, line, result):
try: try:
base = bytes[0].data base = bytes[0].data
newsubstr = "".join([chr(x.data) for x in bytes[1:]]) newsubstr = "".join([chr(x.data) for x in bytes[1:]])
currentcharmap = currentcharmap[:base] + newsubstr + currentcharmap[base+len(newsubstr):] currentcharmap = currentcharmap[:base] + newsubstr + \
currentcharmap[base + len(newsubstr):]
if len(currentcharmap) != 256 or base < 0 or base > 255: if len(currentcharmap) != 256 or base < 0 or base > 255:
Err.log("Charmap replacement out of range") Err.log("Charmap replacement out of range")
currentcharmap = currentcharmap[:256] currentcharmap = currentcharmap[:256]
except ValueError: except ValueError:
Err.log("Illegal character in .charmap directive") Err.log("Illegal character in .charmap directive")
def pragmaCharmapbin(ppt, line, result): def pragmaCharmapbin(ppt, line, result):
"Load a new character map from a file" "Load a new character map from a file"
global currentcharmap global currentcharmap
filename = line.expect("STRING").value filename = line.expect("STRING").value
line.expect("EOL") line.expect("EOL")
if type(filename)==str: if type(filename) == str:
try: try:
f = file(filename, "rb") f = file(filename, "rb")
bytes = f.read() bytes = f.read()
f.close() f.close()
except IOError: except IOError:
Err.log ("Could not read "+filename) Err.log("Could not read " + filename)
return return
if len(bytes)==256: if len(bytes) == 256:
currentcharmap = bytes currentcharmap = bytes
else: else:
Err.log("Character map "+filename+" not 256 bytes long") Err.log("Character map " + filename + " not 256 bytes long")
def pragmaOrg(ppt, line, result): def pragmaOrg(ppt, line, result):
"Relocates the PC with no output" "Relocates the PC with no output"
@ -91,6 +100,7 @@ def pragmaOrg(ppt, line, result):
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "SetPC", newPC)) result.append(IR.Node(ppt, "SetPC", newPC))
def pragmaAdvance(ppt, line, result): def pragmaAdvance(ppt, line, result):
"Outputs filler until reaching the target PC" "Outputs filler until reaching the target PC"
newPC = FE.parse_expr(line) newPC = FE.parse_expr(line)
@ -102,25 +112,31 @@ def pragmaAdvance(ppt, line, result):
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "Advance", newPC, fillexpr)) result.append(IR.Node(ppt, "Advance", newPC, fillexpr))
def pragmaCheckpc(ppt, line, result): def pragmaCheckpc(ppt, line, result):
"Enforces that the PC has not exceeded a certain point" "Enforces that the PC has not exceeded a certain point"
target = FE.parse_expr(line) target = FE.parse_expr(line)
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "CheckPC", target)) result.append(IR.Node(ppt, "CheckPC", target))
def pragmaAlias(ppt, line, result): def pragmaAlias(ppt, line, result):
"Assigns an arbitrary label" "Assigns an arbitrary label"
lbl = line.expect("LABEL").value lbl = line.expect("LABEL").value
target = FE.parse_expr(line) target = FE.parse_expr(line)
result.append(IR.Node(ppt, "Label", lbl, target)) result.append(IR.Node(ppt, "Label", lbl, target))
def pragmaSpace(ppt, line, result): def pragmaSpace(ppt, line, result):
"Reserves space in a data segment for a variable" "Reserves space in a data segment for a variable"
lbl = line.expect("LABEL").value lbl = line.expect("LABEL").value
size = line.expect("NUM").value size = line.expect("NUM").value
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr())) result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr()))
result.append(IR.Node(ppt, "SetPC", IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(size)]))) result.append(IR.Node(ppt, "SetPC",
IR.SequenceExpr([IR.PCExpr(), "+",
IR.ConstantExpr(size)])))
def pragmaText(ppt, line, result): def pragmaText(ppt, line, result):
"Switches to a text segment" "Switches to a text segment"
@ -132,6 +148,7 @@ def pragmaText(ppt, line, result):
segment = "*text-default*" segment = "*text-default*"
result.append(IR.Node(ppt, "TextSegment", segment)) result.append(IR.Node(ppt, "TextSegment", segment))
def pragmaData(ppt, line, result): def pragmaData(ppt, line, result):
"Switches to a data segment (no output allowed)" "Switches to a data segment (no output allowed)"
next = line.expect("LABEL", "EOL") next = line.expect("LABEL", "EOL")
@ -142,67 +159,80 @@ def pragmaData(ppt, line, result):
segment = "*data-default*" segment = "*data-default*"
result.append(IR.Node(ppt, "DataSegment", segment)) result.append(IR.Node(ppt, "DataSegment", segment))
def readData(line): def readData(line):
"Read raw data from a comma-separated list" "Read raw data from a comma-separated list"
if line.lookahead(0).type == "STRING": if line.lookahead(0).type == "STRING":
data = [IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value.translate(currentcharmap)] data = [IR.ConstantExpr(ord(x))
for x in line.expect("STRING").value.translate(currentcharmap)]
else: else:
data = [FE.parse_expr(line)] data = [FE.parse_expr(line)]
next = line.expect(',', 'EOL').type next = line.expect(',', 'EOL').type
while next == ',': while next == ',':
if line.lookahead(0).type == "STRING": if line.lookahead(0).type == "STRING":
data.extend([IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value]) data.extend([IR.ConstantExpr(ord(x))
for x in line.expect("STRING").value])
else: else:
data.append(FE.parse_expr(line)) data.append(FE.parse_expr(line))
next = line.expect(',', 'EOL').type next = line.expect(',', 'EOL').type
return data return data
def pragmaByte(ppt, line, result): def pragmaByte(ppt, line, result):
"Raw data, a byte at a time" "Raw data, a byte at a time"
bytes = readData(line) bytes = readData(line)
result.append(IR.Node(ppt, "Byte", *bytes)) result.append(IR.Node(ppt, "Byte", *bytes))
def pragmaWord(ppt, line, result): def pragmaWord(ppt, line, result):
"Raw data, a word at a time, little-endian" "Raw data, a word at a time, little-endian"
words = readData(line) words = readData(line)
result.append(IR.Node(ppt, "Word", *words)) result.append(IR.Node(ppt, "Word", *words))
def pragmaDword(ppt, line, result): def pragmaDword(ppt, line, result):
"Raw data, a double-word at a time, little-endian" "Raw data, a double-word at a time, little-endian"
dwords = readData(line) dwords = readData(line)
result.append(IR.Node(ppt, "Dword", *dwords)) result.append(IR.Node(ppt, "Dword", *dwords))
def pragmaWordbe(ppt, line, result): def pragmaWordbe(ppt, line, result):
"Raw data, a word at a time, big-endian" "Raw data, a word at a time, big-endian"
words = readData(line) words = readData(line)
result.append(IR.Node(ppt, "WordBE", *words)) result.append(IR.Node(ppt, "WordBE", *words))
def pragmaDwordbe(ppt, line, result): def pragmaDwordbe(ppt, line, result):
"Raw data, a dword at a time, big-endian" "Raw data, a dword at a time, big-endian"
dwords = readData(line) dwords = readData(line)
result.append(IR.Node(ppt, "DwordBE", *dwords)) result.append(IR.Node(ppt, "DwordBE", *dwords))
def pragmaScope(ppt, line, result): def pragmaScope(ppt, line, result):
"Create a new lexical scoping block" "Create a new lexical scoping block"
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "ScopeBegin")) result.append(IR.Node(ppt, "ScopeBegin"))
def pragmaScend(ppt, line, result): def pragmaScend(ppt, line, result):
"End the innermost lexical scoping block" "End the innermost lexical scoping block"
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "ScopeEnd")) result.append(IR.Node(ppt, "ScopeEnd"))
def pragmaMacro(ppt, line, result): def pragmaMacro(ppt, line, result):
"Begin a macro definition" "Begin a macro definition"
lbl = line.expect("LABEL").value lbl = line.expect("LABEL").value
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "MacroBegin", lbl)) result.append(IR.Node(ppt, "MacroBegin", lbl))
def pragmaMacend(ppt, line, result): def pragmaMacend(ppt, line, result):
"End a macro definition" "End a macro definition"
line.expect("EOL") line.expect("EOL")
result.append(IR.Node(ppt, "MacroEnd")) result.append(IR.Node(ppt, "MacroEnd"))
def pragmaInvoke(ppt, line, result): def pragmaInvoke(ppt, line, result):
macro = line.expect("LABEL").value macro = line.expect("LABEL").value
if line.lookahead(0).type == "EOL": if line.lookahead(0).type == "EOL":

View File

@ -9,6 +9,7 @@
import Ophis.Errors as Err import Ophis.Errors as Err
class Environment(object): class Environment(object):
"""Environment class. """Environment class.
Controls the various scopes and global abstract execution variables.""" Controls the various scopes and global abstract execution variables."""
@ -19,39 +20,52 @@ class Environment(object):
self.segmentdict = {} self.segmentdict = {}
self.segment = "*text-default*" self.segment = "*text-default*"
self.scopecount = 0 self.scopecount = 0
def __contains__(self, item): def __contains__(self, item):
if item[0] == '_': if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]: for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return 1 if item in dict:
return 0 return True
return False
return item in self.dicts[0] return item in self.dicts[0]
def __getitem__(self, item): def __getitem__(self, item):
if item[0] == '_': if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]: for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return dict[item] if item in dict:
return dict[item]
else: else:
if item in self.dicts[0]: return self.dicts[0][item] if item in self.dicts[0]:
return self.dicts[0][item]
Err.log("Unknown label '%s'" % item) Err.log("Unknown label '%s'" % item)
return 0 return 0
def __setitem__(self, item, value): def __setitem__(self, item, value):
if item[0] == '_': if item[0] == '_':
self.dicts[self.stack[0]][item] = value self.dicts[self.stack[0]][item] = value
else: else:
self.dicts[0][item] = value self.dicts[0][item] = value
def __str__(self): def __str__(self):
return str(self.dicts) return str(self.dicts)
def getPC(self): def getPC(self):
return self.pc return self.pc
def setPC(self, value): def setPC(self, value):
self.pc = value self.pc = value
def incPC(self, amount): def incPC(self, amount):
self.pc += amount self.pc += amount
def getsegment(self): def getsegment(self):
return self.segment return self.segment
def setsegment(self, segment): def setsegment(self, segment):
self.segmentdict[self.segment] = self.pc self.segmentdict[self.segment] = self.pc
self.segment = segment self.segment = segment
self.pc = self.segmentdict.get(segment, 0) self.pc = self.segmentdict.get(segment, 0)
def reset(self): def reset(self):
"Clears out program counter, segment, and scoping information" "Clears out program counter, segment, and scoping information"
self.pc = 0 self.pc = 0
@ -61,14 +75,16 @@ class Environment(object):
if len(self.stack) > 1: if len(self.stack) > 1:
Err.log("Unmatched .scope") Err.log("Unmatched .scope")
self.stack = [0] self.stack = [0]
def newscope(self): def newscope(self):
"Enters a new scope for temporary labels." "Enters a new scope for temporary labels."
self.scopecount += 1 self.scopecount += 1
self.stack.insert(0, self.scopecount) self.stack.insert(0, self.scopecount)
if len(self.dicts) <= self.scopecount: self.dicts.append({}) if len(self.dicts) <= self.scopecount:
self.dicts.append({})
def endscope(self): def endscope(self):
"Leaves a scope." "Leaves a scope."
if len(self.stack) == 1: if len(self.stack) == 1:
Err.log("Unmatched .scend") Err.log("Unmatched .scend")
self.stack.pop(0) self.stack.pop(0)

View File

@ -12,15 +12,20 @@ import sys
count = 0 count = 0
currentpoint = "<Top Level>" currentpoint = "<Top Level>"
def log(err): def log(err):
"""Reports an error at the current program point, and increases """Reports an error at the current program point, and increases
the global error count.""" the global error count."""
global count global count
count = count+1 count = count + 1
print>>sys.stderr, currentpoint+": "+err print>>sys.stderr, currentpoint + ": " + err
def report(): def report():
"Print out the number of errors." "Print out the number of errors."
if count == 0: print>>sys.stderr, "No errors" if count == 0:
elif count == 1: print>>sys.stderr, "1 error" print>>sys.stderr, "No errors"
else: print>>sys.stderr, str(count)+" errors" elif count == 1:
print>>sys.stderr, "1 error"
else:
print>>sys.stderr, str(count) + " errors"

View File

@ -13,35 +13,44 @@ import os
# You may use, modify, and distribute this file under the MIT # You may use, modify, and distribute this file under the MIT
# license: See README for details. # license: See README for details.
class Lexeme(object): class Lexeme(object):
"Class for lexer tokens. Used by lexer and parser." "Class for lexer tokens. Used by lexer and parser."
def __init__(self, type="UNKNOWN", value=None): def __init__(self, type="UNKNOWN", value=None):
self.type = type.upper() self.type = type.upper()
self.value = value self.value = value
def __str__(self): def __str__(self):
if self.value == None: if self.value is None:
return self.type return self.type
else: else:
return self.type+":"+str(self.value) return self.type + ":" + str(self.value)
def __repr__(self): def __repr__(self):
return "Lexeme("+`self.type`+", "+`self.value`+")" return "Lexeme(" + repr(self.type) + ", " + repr(self.value) + ")"
def matches(self, other): def matches(self, other):
"1 if Lexemes a and b have the same type." "1 if Lexemes a and b have the same type."
return self.type == other.type return self.type == other.type
bases = {"$":("hexadecimal", 16),
"%":("binary", 2), bases = {"$": ("hexadecimal", 16),
"0":("octal", 8)} "%": ("binary", 2),
"0": ("octal", 8)}
punctuation = "#,`<>():.+-*/&|^[]" punctuation = "#,`<>():.+-*/&|^[]"
def lex(point, line): def lex(point, line):
"""Turns a line of source into a sequence of lexemes.""" """Turns a line of source into a sequence of lexemes."""
Err.currentpoint = point Err.currentpoint = point
result = [] result = []
def is_opcode(op): def is_opcode(op):
"Tests whether a string is an opcode or an identifier" "Tests whether a string is an opcode or an identifier"
return op in Ops.opcodes return op in Ops.opcodes
def add_token(token): def add_token(token):
"Converts a substring into a single lexeme" "Converts a substring into a single lexeme"
if token == "": if token == "":
@ -59,7 +68,8 @@ def lex(point, line):
result.append(Lexeme("NUM", long(rest, bases[firstchar][1]))) result.append(Lexeme("NUM", long(rest, bases[firstchar][1])))
return return
except ValueError: except ValueError:
Err.log('Invalid '+bases[firstchar][0]+' constant: '+rest) Err.log('Invalid ' + bases[firstchar][0] + ' constant: ' +
rest)
result.append(Lexeme("NUM", 0)) result.append(Lexeme("NUM", 0))
return return
elif firstchar.isdigit(): elif firstchar.isdigit():
@ -73,12 +83,12 @@ def lex(point, line):
if len(rest) == 1: if len(rest) == 1:
result.append(Lexeme("NUM", ord(rest))) result.append(Lexeme("NUM", ord(rest)))
else: else:
Err.log("Invalid character constant '"+rest+"'") Err.log("Invalid character constant '" + rest + "'")
result.append(Lexeme("NUM", 0)) result.append(Lexeme("NUM", 0))
return return
elif firstchar in punctuation: elif firstchar in punctuation:
if rest != "": if rest != "":
Err.log("Internal lexer error! '"+token+"' can't happen!") Err.log("Internal lexer error! '" + token + "' can't happen!")
result.append(Lexeme(firstchar)) result.append(Lexeme(firstchar))
return return
else: # Label, opcode, or index register else: # Label, opcode, or index register
@ -94,22 +104,24 @@ def lex(point, line):
return return
# should never reach here # should never reach here
Err.log("Internal lexer error: add_token fall-through") Err.log("Internal lexer error: add_token fall-through")
def add_EOL(): def add_EOL():
"Adds an end-of-line lexeme" "Adds an end-of-line lexeme"
result.append(Lexeme("EOL")) result.append(Lexeme("EOL"))
# Actual routine begins here # Actual routine begins here
value = "" value = ""
quotemode = 0 quotemode = False
backslashmode = 0 backslashmode = False
for c in line.strip(): for c in line.strip():
if backslashmode: if backslashmode:
backslashmode = 0 backslashmode = False
value = value + c value = value + c
elif c == "\\": elif c == "\\":
backslashmode = 1 backslashmode = True
elif quotemode: elif quotemode:
if c == '"': if c == '"':
quotemode = 0 quotemode = False
else: else:
value = value + c value = value + c
elif c == ';': elif c == ';':
@ -126,7 +138,7 @@ def lex(point, line):
elif c == '"': elif c == '"':
add_token(value) add_token(value)
value = '"' value = '"'
quotemode = 1 quotemode = True
else: else:
value = value + c value = value + c
if backslashmode: if backslashmode:
@ -137,35 +149,45 @@ def lex(point, line):
add_EOL() add_EOL()
return result return result
class ParseLine(object): class ParseLine(object):
"Maintains the parse state of a line of code. Enables arbitrary lookahead." "Maintains the parse state of a line of code. Enables arbitrary lookahead."
def __init__(self, lexemes): def __init__(self, lexemes):
self.lexemes = lexemes self.lexemes = lexemes
self.location = 0 self.location = 0
def lookahead(self, i): def lookahead(self, i):
"""Returns the token i units ahead in the parse. """Returns the token i units ahead in the parse.
lookahead(0) returns the next token; trying to read off the end of lookahead(0) returns the next token; trying to read off the end of
the sequence returns the last token in the sequence (usually EOL).""" the sequence returns the last token in the sequence (usually EOL)."""
target = self.location+i target = self.location + i
if target >= len(self.lexemes): target = -1 if target >= len(self.lexemes):
target = -1
return self.lexemes[target] return self.lexemes[target]
def pop(self): def pop(self):
"Returns and removes the next element in the line." "Returns and removes the next element in the line."
old = self.location old = self.location
if self.location < len(self.lexemes)-1: self.location += 1 if self.location < len(self.lexemes) - 1:
self.location += 1
return self.lexemes[old] return self.lexemes[old]
def expect(self, *tokens): def expect(self, *tokens):
"""Reads a token from the ParseLine line and returns it if it's of a type """Reads a token from the ParseLine line and returns it if it's of a
in the sequence tokens. Otherwise, it logs an error.""" type in the sequence tokens. Otherwise, it logs an error."""
token = self.pop() token = self.pop()
if token.type not in tokens: if token.type not in tokens:
Err.log('Expected: "'+'", "'.join(tokens)+'"') Err.log('Expected: "' + '", "'.join(tokens) + '"')
return token return token
pragma_modules = [] pragma_modules = []
def parse_expr(line): def parse_expr(line):
"Parses an Ophis arithmetic expression." "Parses an Ophis arithmetic expression."
def atom(): def atom():
"Parses lowest-priority expression components." "Parses lowest-priority expression components."
next = line.lookahead(0).type next = line.lookahead(0).type
@ -187,14 +209,14 @@ def parse_expr(line):
offset += 1 offset += 1
line.expect("+") line.expect("+")
next = line.lookahead(0).type next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset)) return IR.LabelExpr("*" + str(templabelcount + offset))
elif next == "-": elif next == "-":
offset = 1 offset = 1
while next == "-": while next == "-":
offset -= 1 offset -= 1
line.expect("-") line.expect("-")
next = line.lookahead(0).type next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset)) return IR.LabelExpr("*" + str(templabelcount + offset))
elif next == ">": elif next == ">":
line.expect(">") line.expect(">")
return IR.HighByteExpr(atom()) return IR.HighByteExpr(atom())
@ -203,6 +225,7 @@ def parse_expr(line):
return IR.LowByteExpr(atom()) return IR.LowByteExpr(atom())
else: else:
Err.log('Expected: expression') Err.log('Expected: expression')
def precedence_read(constructor, reader, separators): def precedence_read(constructor, reader, separators):
"""Handles precedence. The reader argument is a function that returns """Handles precedence. The reader argument is a function that returns
expressions that bind more tightly than these; separators is a list expressions that bind more tightly than these; separators is a list
@ -218,51 +241,60 @@ def parse_expr(line):
result.append(nextop) result.append(nextop)
result.append(reader()) result.append(reader())
nextop = line.lookahead(0).type nextop = line.lookahead(0).type
if len(result) == 1: return result[0] if len(result) == 1:
return result[0]
return constructor(result) return constructor(result)
def term(): def term():
"Parses * and /" "Parses * and /"
return precedence_read(IR.SequenceExpr, atom, ["*", "/"]) return precedence_read(IR.SequenceExpr, atom, ["*", "/"])
def arith(): def arith():
"Parses + and -" "Parses + and -"
return precedence_read(IR.SequenceExpr, term, ["+", "-"]) return precedence_read(IR.SequenceExpr, term, ["+", "-"])
def bits(): def bits():
"Parses &, |, and ^" "Parses &, |, and ^"
return precedence_read(IR.SequenceExpr, arith, ["&", "|", "^"]) return precedence_read(IR.SequenceExpr, arith, ["&", "|", "^"])
return bits() return bits()
def parse_line(ppt, lexemelist): def parse_line(ppt, lexemelist):
"Turn a line of source into an IR Node." "Turn a line of source into an IR Node."
Err.currentpoint = ppt Err.currentpoint = ppt
result = [] result = []
line = ParseLine(lexemelist) line = ParseLine(lexemelist)
def aux(): def aux():
"Accumulates all IR nodes defined by this line." "Accumulates all IR nodes defined by this line."
if line.lookahead(0).type == "EOL": if line.lookahead(0).type == "EOL":
pass pass
elif line.lookahead(1).type == ":": elif line.lookahead(1).type == ":":
newlabel=line.expect("LABEL").value newlabel = line.expect("LABEL").value
line.expect(":") line.expect(":")
result.append(IR.Node(ppt, "Label", newlabel, IR.PCExpr())) result.append(IR.Node(ppt, "Label", newlabel, IR.PCExpr()))
aux() aux()
elif line.lookahead(0).type == "*": elif line.lookahead(0).type == "*":
global templabelcount global templabelcount
templabelcount = templabelcount + 1 templabelcount = templabelcount + 1
result.append(IR.Node(ppt, "Label", "*"+str(templabelcount), IR.PCExpr())) result.append(IR.Node(ppt, "Label", "*" + str(templabelcount),
IR.PCExpr()))
line.expect("*") line.expect("*")
aux() aux()
elif line.lookahead(0).type == "." or line.lookahead(0).type == "`": elif line.lookahead(0).type == "." or line.lookahead(0).type == "`":
which = line.expect(".", "`").type which = line.expect(".", "`").type
if (which == "."): pragma = line.expect("LABEL").value if (which == "."):
else: pragma = "invoke" pragma = line.expect("LABEL").value
pragmaFunction = "pragma"+pragma.title() else:
pragma = "invoke"
pragmaFunction = "pragma" + pragma.title()
for mod in pragma_modules: for mod in pragma_modules:
if hasattr(mod, pragmaFunction): if hasattr(mod, pragmaFunction):
getattr(mod, pragmaFunction)(ppt, line, result) getattr(mod, pragmaFunction)(ppt, line, result)
break break
else: else:
Err.log("Unknown pragma "+pragma) Err.log("Unknown pragma " + pragma)
else: # Instruction else: # Instruction
opcode = line.expect("OPCODE").value opcode = line.expect("OPCODE").value
if line.lookahead(0).type == "#": if line.lookahead(0).type == "#":
@ -296,23 +328,30 @@ def parse_line(ppt, lexemelist):
tok = line.expect("EOL", ",").type tok = line.expect("EOL", ",").type
if tok == ",": if tok == ",":
tok = line.expect("X", "Y").type tok = line.expect("X", "Y").type
if tok == "X": mode = "MemoryX" if tok == "X":
else: mode = "MemoryY" mode = "MemoryX"
else:
mode = "MemoryY"
line.expect("EOL") line.expect("EOL")
else: mode = "Memory" else:
mode = "Memory"
result.append(IR.Node(ppt, mode, opcode, arg)) result.append(IR.Node(ppt, mode, opcode, arg))
aux() aux()
result = [node for node in result if node is not IR.NullNode] result = [node for node in result if node is not IR.NullNode]
if len(result) == 0: return IR.NullNode if len(result) == 0:
if len(result) == 1: return result[0] return IR.NullNode
if len(result) == 1:
return result[0]
return IR.SequenceNode(ppt, result) return IR.SequenceNode(ppt, result)
def parse_file(ppt, filename): def parse_file(ppt, filename):
"Loads an Ophis source file, and returns an IR list." "Loads an Ophis source file, and returns an IR list."
Err.currentpoint = ppt Err.currentpoint = ppt
if Cmd.print_loaded_files: if Cmd.print_loaded_files:
if filename != '-': if filename != '-':
print>>sys.stderr, "Loading "+filename print>>sys.stderr, "Loading " + filename
else: else:
print>>sys.stderr, "Loading from standard input" print>>sys.stderr, "Loading from standard input"
try: try:
@ -322,18 +361,19 @@ def parse_file(ppt, filename):
f.close() f.close()
else: else:
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 = map(lex, pptlist, linelist)
IRlist = map(parse_line, pptlist, lexlist) IRlist = 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]
return IR.SequenceNode(ppt, IRlist) return IR.SequenceNode(ppt, IRlist)
except IOError: except IOError:
Err.log ("Could not read "+filename) Err.log("Could not read " + filename)
return IR.NullNode return IR.NullNode
def parse(filename): def parse(filename):
"Top level parsing routine, taking a source file name and returning an IR list." """Top level parsing routine, taking a source file name
and returning an IR list."""
global templabelcount global templabelcount
templabelcount = 0 templabelcount = 0
return parse_file("<Top Level>", filename) return parse_file("<Top Level>", filename)

View File

@ -9,6 +9,7 @@
import Ophis.Errors as Err import Ophis.Errors as Err
class Node(object): class Node(object):
"""The default IR Node """The default IR Node
Instances of Node always have the three fields ppt(Program Point), Instances of Node always have the three fields ppt(Program Point),
@ -17,27 +18,35 @@ class Node(object):
self.ppt = ppt self.ppt = ppt
self.nodetype = nodetype self.nodetype = nodetype
self.data = list(data) self.data = list(data)
def accept(self, asmpass, env=None): def accept(self, asmpass, env=None):
"""Implements the Visitor pattern for an assembler pass. """Implements the Visitor pattern for an assembler pass.
Calls the routine 'asmpass.visitTYPE(self, env)' where Calls the routine 'asmpass.visitTYPE(self, env)' where
TYPE is the value of self.nodetype.""" TYPE is the value of self.nodetype."""
Err.currentpoint = self.ppt Err.currentpoint = self.ppt
routine = getattr(asmpass, "visit"+self.nodetype, asmpass.visitUnknown) routine = getattr(asmpass, "visit" + self.nodetype,
asmpass.visitUnknown)
routine(self, env) routine(self, env)
def __str__(self): def __str__(self):
if self.nodetype != "SEQUENCE": if self.nodetype != "SEQUENCE":
return str(self.ppt)+": "+self.nodetype+" - "+" ".join(map(str, self.data)) return str(self.ppt) + ": " + self.nodetype + " - " + \
" ".join(map(str, self.data))
else: else:
return "\n".join(map(str, self.data)) return "\n".join(map(str, self.data))
def __repr__(self): def __repr__(self):
args = [self.ppt, self.nodetype] + self.data args = [self.ppt, self.nodetype] + self.data
return "Node(" + ", ".join(map(repr, args)) + ")" return "Node(" + ", ".join(map(repr, args)) + ")"
NullNode = Node("<none>", "None") NullNode = Node("<none>", "None")
def SequenceNode(ppt, nodelist): def SequenceNode(ppt, nodelist):
return Node(ppt, "SEQUENCE", *nodelist) return Node(ppt, "SEQUENCE", *nodelist)
class Expr(object): class Expr(object):
"""Base class for Ophis expressions """Base class for Ophis expressions
All expressions have a field called "data" and a boolean field All expressions have a field called "data" and a boolean field
@ -45,78 +54,102 @@ class Expr(object):
symbolic values in it.""" symbolic values in it."""
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
self.hardcoded = 0 self.hardcoded = False
def __str__(self): def __str__(self):
return "<UNKNOWN: "+`self.data`+">" return "<UNKNOWN: " + repr(self.data) + ">"
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
"""Returns true if the the expression can be successfully """Returns true if the the expression can be successfully
evaluated in the specified environment.""" evaluated in the specified environment."""
return 0 return False
def value(self, env=None): def value(self, env=None):
"Evaluates this expression in the given environment." "Evaluates this expression in the given environment."
return None return None
class ConstantExpr(Expr): class ConstantExpr(Expr):
"Represents a numeric constant" "Represents a numeric constant"
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
self.hardcoded = 1 self.hardcoded = True
def __str__(self): def __str__(self):
return str(self.data) return str(self.data)
def valid(self, env=None, PCvalid=0):
return 1 def valid(self, env=None, PCvalid=False):
return True
def value(self, env=None): def value(self, env=None):
return self.data return self.data
class LabelExpr(Expr): class LabelExpr(Expr):
"Represents a symbolic constant" "Represents a symbolic constant"
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
self.hardcoded = 0 self.hardcoded = False
def __str__(self): def __str__(self):
return self.data return self.data
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
return (env is not None) and self.data in env return (env is not None) and self.data in env
def value(self, env=None): def value(self, env=None):
return env[self.data] return env[self.data]
class PCExpr(Expr): class PCExpr(Expr):
"Represents the current program counter: ^" "Represents the current program counter: ^"
def __init__(self): def __init__(self):
self.hardcoded = 0 self.hardcoded = False
def __str__(self): def __str__(self):
return "^" return "^"
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
return env is not None and PCvalid return env is not None and PCvalid
def value(self, env=None): def value(self, env=None):
return env.getPC() return env.getPC()
class HighByteExpr(Expr): class HighByteExpr(Expr):
"Represents the expression >{data}" "Represents the expression >{data}"
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
self.hardcoded = data.hardcoded self.hardcoded = data.hardcoded
def __str__(self): def __str__(self):
return ">"+str(self.data) return ">" + str(self.data)
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
return self.data.valid(env, PCvalid) return self.data.valid(env, PCvalid)
def value(self, env=None): def value(self, env=None):
val = self.data.value(env) val = self.data.value(env)
return (val >> 8) & 0xff return (val >> 8) & 0xff
class LowByteExpr(Expr): class LowByteExpr(Expr):
"Represents the expression <{data}" "Represents the expression <{data}"
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
self.hardcoded = data.hardcoded self.hardcoded = data.hardcoded
def __str__(self): def __str__(self):
return "<"+str(self.data) return "<" + str(self.data)
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
return self.data.valid(env, PCvalid) return self.data.valid(env, PCvalid)
def value(self, env=None): def value(self, env=None):
val = self.data.value(env) val = self.data.value(env)
return val & 0xff return val & 0xff
class SequenceExpr(Expr): class SequenceExpr(Expr):
"""Represents an interleaving of operands (of type Expr) and """Represents an interleaving of operands (of type Expr) and
operators (of type String). Subclasses must provide a routine operators (of type String). Subclasses must provide a routine
@ -128,20 +161,23 @@ class SequenceExpr(Expr):
[Expr, str, Expr, str, Expr, str, ... Expr, str, Expr].""" [Expr, str, Expr, str, Expr, str, ... Expr, str, Expr]."""
self.data = data self.data = data
self.operands = [x for x in data if isinstance(x, Expr)] self.operands = [x for x in data if isinstance(x, Expr)]
self.operators = [x for x in data if type(x)==str] self.operators = [x for x in data if type(x) == str]
for i in self.operands: for i in self.operands:
if not i.hardcoded: if not i.hardcoded:
self.hardcoded = 0 self.hardcoded = False
break break
else: else:
self.hardcoded = 1 self.hardcoded = True
def __str__(self): def __str__(self):
return "["+" ".join(map(str, self.data))+"]" return "[" + " ".join(map(str, self.data)) + "]"
def valid(self, env=None, PCvalid=0):
def valid(self, env=None, PCvalid=False):
for i in self.operands: for i in self.operands:
if not i.valid(env, PCvalid): if not i.valid(env, PCvalid):
return 0 return False
return 1 return True
def value(self, env=None): def value(self, env=None):
subs = map((lambda x: x.value(env)), self.operands) subs = map((lambda x: x.value(env)), self.operands)
result = subs[0] result = subs[0]
@ -150,11 +186,19 @@ class SequenceExpr(Expr):
result = self.operate(result, op, subs[index]) result = self.operate(result, op, subs[index])
index += 1 index += 1
return result return result
def operate(self, start, op, other): def operate(self, start, op, other):
if op=="*": return start * other if op == "*":
if op=="/": return start // other return start * other
if op=="+": return start + other if op == "/":
if op=="-": return start - other return start // other
if op=="&": return start & other if op == "+":
if op=="|": return start | other return start + other
if op=="^": return start ^ other if op == "-":
return start - other
if op == "&":
return start & other
if op == "|":
return start | other
if op == "^":
return start ^ other

View File

@ -18,6 +18,7 @@ macros = {}
currentname = None currentname = None
currentbody = None currentbody = None
def newMacro(name): def newMacro(name):
"Start creating a new macro with the specified name." "Start creating a new macro with the specified name."
global currentname global currentname
@ -31,10 +32,12 @@ def newMacro(name):
currentname = name currentname = name
currentbody = [] currentbody = []
def registerNode(node): def registerNode(node):
global currentbody global currentbody
currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data)) currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data))
def endMacro(): def endMacro():
global currentname global currentname
global currentbody global currentbody
@ -46,21 +49,29 @@ def endMacro():
currentname = None currentname = None
currentbody = None currentbody = None
def expandMacro(ppt, name, arglist): def expandMacro(ppt, name, arglist):
global macros global macros
if name not in macros: if name not in macros:
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) for (i, arg) in zip(xrange(1, sys.maxint), arglist)] argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg)
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) for i in range(1, len(arglist)+1)] for (i, arg) in zip(xrange(1, sys.maxint), arglist)]
body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data) for node in macros[name]] bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i))
invocation = [IR.Node(ppt, "ScopeBegin")] + argexprs + [IR.Node(ppt, "ScopeBegin")] + bindexprs + body + [IR.Node(ppt, "ScopeEnd"), IR.Node(ppt, "ScopeEnd")] for i in range(1, len(arglist) + 1)]
body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data)
for node in macros[name]]
invocation = [IR.Node(ppt, "ScopeBegin")] + argexprs + \
[IR.Node(ppt, "ScopeBegin")] + bindexprs + body + \
[IR.Node(ppt, "ScopeEnd"), IR.Node(ppt, "ScopeEnd")]
return IR.SequenceNode(ppt, invocation) return IR.SequenceNode(ppt, invocation)
def dump(): 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>>sys.stderr, "Macro: " + mac
for node in body: print>>sys.stderr, node for node in body:
print>>sys.stderr, node
print>>sys.stderr, "" print>>sys.stderr, ""

View File

@ -17,6 +17,7 @@ import Ophis.Environment
import Ophis.CmdLine import Ophis.CmdLine
import Ophis.Opcodes import Ophis.Opcodes
def run_all(infile, outfile): def run_all(infile, outfile):
"Transforms the source infile to a binary outfile." "Transforms the source infile to a binary outfile."
Err.count = 0 Err.count = 0
@ -26,21 +27,31 @@ def run_all(infile, outfile):
m = Ophis.Passes.ExpandMacros() m = Ophis.Passes.ExpandMacros()
i = Ophis.Passes.InitLabels() i = Ophis.Passes.InitLabels()
l_basic = Ophis.Passes.UpdateLabels() l_basic = Ophis.Passes.UpdateLabels()
l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0) l = Ophis.Passes.FixPoint("label update", [l_basic],
lambda: not l_basic.changed)
c_basic = Ophis.Passes.Collapse() c_basic = Ophis.Passes.Collapse()
c = Ophis.Passes.FixPoint("instruction selection 1", [l, c_basic], lambda: c_basic.collapsed == 0) c = Ophis.Passes.FixPoint("instruction selection 1", [l, c_basic],
lambda: not c_basic.changed)
b = Ophis.Passes.ExtendBranches() b = Ophis.Passes.ExtendBranches()
a = Ophis.Passes.Assembler() a = Ophis.Passes.Assembler()
passes = [] passes = []
passes.append(Ophis.Passes.DefineMacros()) passes.append(Ophis.Passes.DefineMacros())
passes.append(Ophis.Passes.FixPoint("macro expansion", [m], lambda: m.changed == 0)) passes.append(Ophis.Passes.FixPoint("macro expansion", [m],
passes.append(Ophis.Passes.FixPoint("label initialization", [i], lambda: i.changed == 0)) lambda: not m.changed))
passes.extend([Ophis.Passes.CircularityCheck(), Ophis.Passes.CheckExprs(), Ophis.Passes.EasyModes()]) passes.append(Ophis.Passes.FixPoint("label initialization", [i],
passes.append(Ophis.Passes.FixPoint("instruction selection 2", [c, b], lambda: b.expanded == 0)) lambda: not i.changed))
passes.extend([Ophis.Passes.NormalizeModes(), Ophis.Passes.UpdateLabels(), a]) passes.extend([Ophis.Passes.CircularityCheck(),
Ophis.Passes.CheckExprs(),
Ophis.Passes.EasyModes()])
passes.append(Ophis.Passes.FixPoint("instruction selection 2", [c, b],
lambda: not b.changed))
passes.extend([Ophis.Passes.NormalizeModes(),
Ophis.Passes.UpdateLabels(),
a])
for p in passes: p.go(z, env) for p in passes:
p.go(z, env)
if Err.count == 0: if Err.count == 0:
try: try:
@ -53,10 +64,11 @@ def run_all(infile, outfile):
if outfile != '-': if outfile != '-':
output.close() output.close()
except IOError: except IOError:
print>>sys.stderr, "Could not write to "+outfile print>>sys.stderr, "Could not write to " + outfile
else: else:
Err.report() Err.report()
def run_ophis(args): def run_ophis(args):
Ophis.CmdLine.parse_args(args) Ophis.CmdLine.parse_args(args)
Ophis.Frontend.pragma_modules.append(Ophis.CorePragmas) Ophis.Frontend.pragma_modules.append(Ophis.CorePragmas)
@ -69,5 +81,6 @@ def run_ophis(args):
Ophis.CorePragmas.reset() Ophis.CorePragmas.reset()
run_all(Ophis.CmdLine.infile, Ophis.CmdLine.outfile) run_all(Ophis.CmdLine.infile, Ophis.CmdLine.outfile)
if __name__ == '__main__': if __name__ == '__main__':
run_ophis(sys.argv[1:]) run_ophis(sys.argv[1:])

View File

@ -28,146 +28,275 @@ modes = ["Implied", # 0
"(Zero Page), Y", # 13 "(Zero Page), Y", # 13
"Relative"] # 14 "Relative"] # 14
# Lengths of the argument # Lengths of the argument
lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1] lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1]
opcodes = { opcodes = {
'adc': [None, 0x69, 0x65, 0x75, None, 0x6D, 0x7D, 0x79, None, None, None, None, 0x61, 0x71, None], 'adc': [None, 0x69, 0x65, 0x75, None, 0x6D, 0x7D, 0x79,
'and': [None, 0x29, 0x25, 0x35, None, 0x2D, 0x3D, 0x39, None, None, None, None, 0x21, 0x31, None], None, None, None, None, 0x61, 0x71, None],
'asl': [0x0A, None, 0x06, 0x16, None, 0x0E, 0x1E, None, None, None, None, None, None, None, None], 'and': [None, 0x29, 0x25, 0x35, None, 0x2D, 0x3D, 0x39,
'bcc': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x90], None, None, None, None, 0x21, 0x31, None],
'bcs': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xB0], 'asl': [0x0A, None, 0x06, 0x16, None, 0x0E, 0x1E, None,
'beq': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xF0], None, None, None, None, None, None, None],
'bit': [None, None, 0x24, None, None, 0x2C, None, None, None, None, None, None, None, None, None], 'bcc': [None, None, None, None, None, None, None, None,
'bmi': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x30], None, None, None, None, None, None, 0x90],
'bne': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xD0], 'bcs': [None, None, None, None, None, None, None, None,
'bpl': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x10], None, None, None, None, None, None, 0xB0],
'brk': [0x00, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'beq': [None, None, None, None, None, None, None, None,
'bvc': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x50], None, None, None, None, None, None, 0xF0],
'bvs': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x70], 'bit': [None, None, 0x24, None, None, 0x2C, None, None,
'clc': [0x18, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'cld': [0xD8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bmi': [None, None, None, None, None, None, None, None,
'cli': [0x58, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0x30],
'clv': [0xB8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bne': [None, None, None, None, None, None, None, None,
'cmp': [None, 0xC9, 0xC5, 0xD5, None, 0xCD, 0xDD, 0xD9, None, None, None, None, 0xC1, 0xD1, None], None, None, None, None, None, None, 0xD0],
'cpx': [None, 0xE0, 0xE4, None, None, 0xEC, None, None, None, None, None, None, None, None, None], 'bpl': [None, None, None, None, None, None, None, None,
'cpy': [None, 0xC0, 0xC4, None, None, 0xCC, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0x10],
'dec': [None, None, 0xC6, 0xD6, None, 0xCE, 0xDE, None, None, None, None, None, None, None, None], 'brk': [0x00, None, None, None, None, None, None, None,
'dex': [0xCA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'dey': [0x88, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bvc': [None, None, None, None, None, None, None, None,
'eor': [None, 0x49, 0x45, 0x55, None, 0x4D, 0x5D, 0x59, None, None, None, None, 0x41, 0x51, None], None, None, None, None, None, None, 0x50],
'inc': [None, None, 0xE6, 0xF6, None, 0xEE, 0xFE, None, None, None, None, None, None, None, None], 'bvs': [None, None, None, None, None, None, None, None,
'inx': [0xE8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0x70],
'iny': [0xC8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'clc': [0x18, None, None, None, None, None, None, None,
'jmp': [None, None, None, None, None, 0x4C, None, None, 0x6C, None, None, None, None, None, None], None, None, None, None, None, None, None],
'jsr': [None, None, None, None, None, 0x20, None, None, None, None, None, None, None, None, None], 'cld': [0xD8, None, None, None, None, None, None, None,
'lda': [None, 0xA9, 0xA5, 0xB5, None, 0xAD, 0xBD, 0xB9, None, None, None, None, 0xA1, 0xB1, None], None, None, None, None, None, None, None],
'ldx': [None, 0xA2, 0xA6, None, 0xB6, 0xAE, None, 0xBE, None, None, None, None, None, None, None], 'cli': [0x58, None, None, None, None, None, None, None,
'ldy': [None, 0xA0, 0xA4, 0xB4, None, 0xAC, 0xBC, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'lsr': [0x4A, None, 0x46, 0x56, None, 0x4E, 0x5E, None, None, None, None, None, None, None, None], 'clv': [0xB8, None, None, None, None, None, None, None,
'nop': [0xEA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'ora': [None, 0x09, 0x05, 0x15, None, 0x0D, 0x1D, 0x19, None, None, None, None, 0x01, 0x11, None], 'cmp': [None, 0xC9, 0xC5, 0xD5, None, 0xCD, 0xDD, 0xD9,
'pha': [0x48, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, 0xC1, 0xD1, None],
'php': [0x08, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'cpx': [None, 0xE0, 0xE4, None, None, 0xEC, None, None,
'pla': [0x68, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'plp': [0x28, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'cpy': [None, 0xC0, 0xC4, None, None, 0xCC, None, None,
'rol': [0x2A, None, 0x26, 0x36, None, 0x2E, 0x3E, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'ror': [0x6A, None, 0x66, 0x76, None, 0x6E, 0x7E, None, None, None, None, None, None, None, None], 'dec': [None, None, 0xC6, 0xD6, None, 0xCE, 0xDE, None,
'rti': [0x40, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'rts': [0x60, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'dex': [0xCA, None, None, None, None, None, None, None,
'sbc': [None, 0xE9, 0xE5, 0xF5, None, 0xED, 0xFD, 0xF9, None, None, None, None, 0xE1, 0xF1, None], None, None, None, None, None, None, None],
'sec': [0x38, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'dey': [0x88, None, None, None, None, None, None, None,
'sed': [0xF8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'sei': [0x78, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'eor': [None, 0x49, 0x45, 0x55, None, 0x4D, 0x5D, 0x59,
'sta': [None, None, 0x85, 0x95, None, 0x8D, 0x9D, 0x99, None, None, None, None, 0x81, 0x91, None], None, None, None, None, 0x41, 0x51, None],
'stx': [None, None, 0x86, None, 0x96, 0x8E, None, None, None, None, None, None, None, None, None], 'inc': [None, None, 0xE6, 0xF6, None, 0xEE, 0xFE, None,
'sty': [None, None, 0x84, 0x94, None, 0x8C, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'tax': [0xAA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'inx': [0xE8, None, None, None, None, None, None, None,
'tay': [0xA8, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'tsx': [0xBA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'iny': [0xC8, None, None, None, None, None, None, None,
'txa': [0x8A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'txs': [0x9A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'jmp': [None, None, None, None, None, 0x4C, None, None,
'tya': [0x98, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 0x6C, None, None, None, None, None, None],
'jsr': [None, None, None, None, None, 0x20, None, None,
None, None, None, None, None, None, None],
'lda': [None, 0xA9, 0xA5, 0xB5, None, 0xAD, 0xBD, 0xB9,
None, None, None, None, 0xA1, 0xB1, None],
'ldx': [None, 0xA2, 0xA6, None, 0xB6, 0xAE, None, 0xBE,
None, None, None, None, None, None, None],
'ldy': [None, 0xA0, 0xA4, 0xB4, None, 0xAC, 0xBC, None,
None, None, None, None, None, None, None],
'lsr': [0x4A, None, 0x46, 0x56, None, 0x4E, 0x5E, None,
None, None, None, None, None, None, None],
'nop': [0xEA, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'ora': [None, 0x09, 0x05, 0x15, None, 0x0D, 0x1D, 0x19,
None, None, None, None, 0x01, 0x11, None],
'pha': [0x48, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'php': [0x08, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'pla': [0x68, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'plp': [0x28, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'rol': [0x2A, None, 0x26, 0x36, None, 0x2E, 0x3E, None,
None, None, None, None, None, None, None],
'ror': [0x6A, None, 0x66, 0x76, None, 0x6E, 0x7E, None,
None, None, None, None, None, None, None],
'rti': [0x40, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'rts': [0x60, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'sbc': [None, 0xE9, 0xE5, 0xF5, None, 0xED, 0xFD, 0xF9,
None, None, None, None, 0xE1, 0xF1, None],
'sec': [0x38, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'sed': [0xF8, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'sei': [0x78, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'sta': [None, None, 0x85, 0x95, None, 0x8D, 0x9D, 0x99,
None, None, None, None, 0x81, 0x91, None],
'stx': [None, None, 0x86, None, 0x96, 0x8E, None, None,
None, None, None, None, None, None, None],
'sty': [None, None, 0x84, 0x94, None, 0x8C, None, None,
None, None, None, None, None, None, None],
'tax': [0xAA, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'tay': [0xA8, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'tsx': [0xBA, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'txa': [0x8A, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'txs': [0x9A, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'tya': [0x98, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
} }
undocops = { undocops = {
'anc': [None, 0x0B, None, None, None, None, None, None, None, None, None, None, None, None, None], 'anc': [None, 0x0B, None, None, None, None, None, None,
'ane': [None, 0x8B, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'arr': [None, 0x6B, None, None, None, None, None, None, None, None, None, None, None, None, None], 'ane': [None, 0x8B, None, None, None, None, None, None,
'asr': [None, 0x4B, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'dcp': [None, None, 0xC7, 0xD7, None, 0xCF, 0xDF, 0xDB, None, None, None, None, 0xC3, 0xD3, None], 'arr': [None, 0x6B, None, None, None, None, None, None,
'isb': [None, None, 0xE7, 0xF7, None, 0xEF, 0xFF, 0xFB, None, None, None, None, 0xE3, 0xF3, None], None, None, None, None, None, None, None],
'las': [None, None, None, None, None, None, None, 0xBB, None, None, None, None, None, None, None], 'asr': [None, 0x4B, None, None, None, None, None, None,
'lax': [None, None, 0xA7, None, 0xB7, 0xAF, None, 0xBF, None, None, None, None, 0xA3, 0xB3, None], None, None, None, None, None, None, None],
'lxa': [None, 0xAB, None, None, None, None, None, None, None, None, None, None, None, None, None], 'dcp': [None, None, 0xC7, 0xD7, None, 0xCF, 0xDF, 0xDB,
'nop': [0xEA, None, 0x04, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, 0xC3, 0xD3, None],
'rla': [None, None, 0x27, 0x37, None, 0x2F, 0x3F, 0x3B, None, None, None, None, 0x23, 0x33, None], 'isb': [None, None, 0xE7, 0xF7, None, 0xEF, 0xFF, 0xFB,
'rra': [None, None, 0x67, 0x77, None, 0x6F, 0x7F, 0x7B, None, None, None, None, 0x63, 0x73, None], None, None, None, None, 0xE3, 0xF3, None],
'sax': [None, None, 0x87, None, 0x97, 0x8F, None, None, None, None, None, None, 0x83, None, None], 'las': [None, None, None, None, None, None, None, 0xBB,
'sbx': [None, 0xCB, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'sha': [None, None, None, None, None, None, None, 0x9F, None, None, None, None, None, 0x93, None], 'lax': [None, None, 0xA7, None, 0xB7, 0xAF, None, 0xBF,
'shs': [None, None, None, None, None, None, None, 0x9B, None, None, None, None, None, None, None], None, None, None, None, 0xA3, 0xB3, None],
'shx': [None, None, None, None, None, None, None, 0x9E, None, None, None, None, None, None, None], 'lxa': [None, 0xAB, None, None, None, None, None, None,
'slo': [None, None, 0x07, 0x17, None, 0x0F, 0x1F, 0x1B, None, None, None, None, 0x03, 0x13, None], None, None, None, None, None, None, None],
'sre': [None, None, 0x47, 0x57, None, 0x4F, 0x5F, 0x5B, None, None, None, None, 0x43, 0x53, None], 'nop': [0xEA, None, 0x04, None, None, None, None, None,
None, None, None, None, None, None, None],
'rla': [None, None, 0x27, 0x37, None, 0x2F, 0x3F, 0x3B,
None, None, None, None, 0x23, 0x33, None],
'rra': [None, None, 0x67, 0x77, None, 0x6F, 0x7F, 0x7B,
None, None, None, None, 0x63, 0x73, None],
'sax': [None, None, 0x87, None, 0x97, 0x8F, None, None,
None, None, None, None, 0x83, None, None],
'sbx': [None, 0xCB, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'sha': [None, None, None, None, None, None, None, 0x9F,
None, None, None, None, None, 0x93, None],
'shs': [None, None, None, None, None, None, None, 0x9B,
None, None, None, None, None, None, None],
'shx': [None, None, None, None, None, None, None, 0x9E,
None, None, None, None, None, None, None],
'slo': [None, None, 0x07, 0x17, None, 0x0F, 0x1F, 0x1B,
None, None, None, None, 0x03, 0x13, None],
'sre': [None, None, 0x47, 0x57, None, 0x4F, 0x5F, 0x5B,
None, None, None, None, 0x43, 0x53, None],
} }
c02extensions = { c02extensions = {
'adc': [None, 0x69, 0x65, 0x75, None, 0x6D, 0x7D, 0x79, None, None, None, 0x72, 0x61, 0x71, None], 'adc': [None, 0x69, 0x65, 0x75, None, 0x6D, 0x7D, 0x79,
'and': [None, 0x29, 0x25, 0x35, None, 0x2D, 0x3D, 0x39, None, None, None, 0x32, 0x21, 0x31, None], None, None, None, 0x72, 0x61, 0x71, None],
'bbr0': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x0F], 'and': [None, 0x29, 0x25, 0x35, None, 0x2D, 0x3D, 0x39,
'bbr1': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x1F], None, None, None, 0x32, 0x21, 0x31, None],
'bbr2': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x2F], 'bbr0': [None, None, None, None, None, None, None, None,
'bbr3': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x3F], None, None, None, None, None, None, 0x0F],
'bbr4': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x4F], 'bbr1': [None, None, None, None, None, None, None, None,
'bbr5': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x5F], None, None, None, None, None, None, 0x1F],
'bbr6': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x6F], 'bbr2': [None, None, None, None, None, None, None, None,
'bbr7': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x7F], None, None, None, None, None, None, 0x2F],
'bbs0': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x8F], 'bbr3': [None, None, None, None, None, None, None, None,
'bbs1': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x9F], None, None, None, None, None, None, 0x3F],
'bbs2': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xAF], 'bbr4': [None, None, None, None, None, None, None, None,
'bbs3': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xBF], None, None, None, None, None, None, 0x4F],
'bbs4': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xCF], 'bbr5': [None, None, None, None, None, None, None, None,
'bbs5': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xDF], None, None, None, None, None, None, 0x5F],
'bbs6': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xEF], 'bbr6': [None, None, None, None, None, None, None, None,
'bbs7': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0xFF], None, None, None, None, None, None, 0x6F],
'bit': [None, 0x89, 0x24, 0x34, None, 0x2C, 0x3C, None, None, None, None, None, None, None, None], 'bbr7': [None, None, None, None, None, None, None, None,
'bra': [None, None, None, None, None, None, None, None, None, None, None, None, None, None, 0x80], None, None, None, None, None, None, 0x7F],
'cmp': [None, 0xC9, 0xC5, 0xD5, None, 0xCD, 0xDD, 0xD9, None, None, None, 0xD2, 0xC1, 0xD1, None], 'bbs0': [None, None, None, None, None, None, None, None,
'dea': [0x3A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0x8F],
'dec': [0x3A, None, 0xC6, 0xD6, None, 0xCE, 0xDE, None, None, None, None, None, None, None, None], 'bbs1': [None, None, None, None, None, None, None, None,
'eor': [None, 0x49, 0x45, 0x55, None, 0x4D, 0x5D, 0x59, None, None, None, 0x52, 0x41, 0x51, None], None, None, None, None, None, None, 0x9F],
'ina': [0x1A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bbs2': [None, None, None, None, None, None, None, None,
'inc': [0x1A, None, 0xE6, 0xF6, None, 0xEE, 0xFE, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0xAF],
'jmp': [None, None, None, None, None, 0x4C, None, None, 0x6C, 0x7C, None, None, None, None, None], 'bbs3': [None, None, None, None, None, None, None, None,
'lda': [None, 0xA9, 0xA5, 0xB5, None, 0xAD, 0xBD, 0xB9, None, None, None, 0xB2, 0xA1, 0xB1, None], None, None, None, None, None, None, 0xBF],
'ora': [None, 0x09, 0x05, 0x15, None, 0x0D, 0x1D, 0x19, None, None, None, 0x12, 0x01, 0x11, None], 'bbs4': [None, None, None, None, None, None, None, None,
'phx': [0xDA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0xCF],
'phy': [0x5A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bbs5': [None, None, None, None, None, None, None, None,
'plx': [0xFA, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0xDF],
'ply': [0x7A, None, None, None, None, None, None, None, None, None, None, None, None, None, None], 'bbs6': [None, None, None, None, None, None, None, None,
'rmb0': [None, None, 0x07, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0xEF],
'rmb1': [None, None, 0x17, None, None, None, None, None, None, None, None, None, None, None, None], 'bbs7': [None, None, None, None, None, None, None, None,
'rmb2': [None, None, 0x27, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0xFF],
'rmb3': [None, None, 0x37, None, None, None, None, None, None, None, None, None, None, None, None], 'bit': [None, 0x89, 0x24, 0x34, None, 0x2C, 0x3C, None,
'rmb4': [None, None, 0x47, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'rmb5': [None, None, 0x57, None, None, None, None, None, None, None, None, None, None, None, None], 'bra': [None, None, None, None, None, None, None, None,
'rmb6': [None, None, 0x67, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, 0x80],
'rmb7': [None, None, 0x77, None, None, None, None, None, None, None, None, None, None, None, None], 'cmp': [None, 0xC9, 0xC5, 0xD5, None, 0xCD, 0xDD, 0xD9,
'sbc': [None, 0xE9, 0xE5, 0xF5, None, 0xED, 0xFD, 0xF9, None, None, None, 0xF2, 0xE1, 0xF1, None], None, None, None, 0xD2, 0xC1, 0xD1, None],
'smb0': [None, None, 0x87, None, None, None, None, None, None, None, None, None, None, None, None], 'dea': [0x3A, None, None, None, None, None, None, None,
'smb1': [None, None, 0x97, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'smb2': [None, None, 0xA7, None, None, None, None, None, None, None, None, None, None, None, None], 'dec': [0x3A, None, 0xC6, 0xD6, None, 0xCE, 0xDE, None,
'smb3': [None, None, 0xB7, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'smb4': [None, None, 0xC7, None, None, None, None, None, None, None, None, None, None, None, None], 'eor': [None, 0x49, 0x45, 0x55, None, 0x4D, 0x5D, 0x59,
'smb5': [None, None, 0xD7, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, 0x52, 0x41, 0x51, None],
'smb6': [None, None, 0xE7, None, None, None, None, None, None, None, None, None, None, None, None], 'ina': [0x1A, None, None, None, None, None, None, None,
'smb7': [None, None, 0xF7, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'sta': [None, None, 0x85, 0x95, None, 0x8D, 0x9D, 0x99, None, None, None, 0x92, 0x81, 0x91, None], 'inc': [0x1A, None, 0xE6, 0xF6, None, 0xEE, 0xFE, None,
'stp': [0xDB, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, None, None, None, None],
'stz': [None, None, 0x64, 0x74, None, 0x9C, 0x9E, None, None, None, None, None, None, None, None], 'jmp': [None, None, None, None, None, 0x4C, None, None,
'trb': [None, None, 0x14, None, None, 0x1C, None, None, None, None, None, None, None, None, None], 0x6C, 0x7C, None, None, None, None, None],
'tsb': [None, None, 0x04, None, None, 0x0C, None, None, None, None, None, None, None, None, None], 'lda': [None, 0xA9, 0xA5, 0xB5, None, 0xAD, 0xBD, 0xB9,
'wai': [0xCB, None, None, None, None, None, None, None, None, None, None, None, None, None, None], None, None, None, 0xB2, 0xA1, 0xB1, None],
'ora': [None, 0x09, 0x05, 0x15, None, 0x0D, 0x1D, 0x19,
None, None, None, 0x12, 0x01, 0x11, None],
'phx': [0xDA, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'phy': [0x5A, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'plx': [0xFA, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'ply': [0x7A, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb0': [None, None, 0x07, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb1': [None, None, 0x17, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb2': [None, None, 0x27, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb3': [None, None, 0x37, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb4': [None, None, 0x47, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb5': [None, None, 0x57, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb6': [None, None, 0x67, None, None, None, None, None,
None, None, None, None, None, None, None],
'rmb7': [None, None, 0x77, None, None, None, None, None,
None, None, None, None, None, None, None],
'sbc': [None, 0xE9, 0xE5, 0xF5, None, 0xED, 0xFD, 0xF9,
None, None, None, 0xF2, 0xE1, 0xF1, None],
'smb0': [None, None, 0x87, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb1': [None, None, 0x97, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb2': [None, None, 0xA7, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb3': [None, None, 0xB7, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb4': [None, None, 0xC7, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb5': [None, None, 0xD7, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb6': [None, None, 0xE7, None, None, None, None, None,
None, None, None, None, None, None, None],
'smb7': [None, None, 0xF7, None, None, None, None, None,
None, None, None, None, None, None, None],
'sta': [None, None, 0x85, 0x95, None, 0x8D, 0x9D, 0x99,
None, None, None, 0x92, 0x81, 0x91, None],
'stp': [0xDB, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
'stz': [None, None, 0x64, 0x74, None, 0x9C, 0x9E, None,
None, None, None, None, None, None, None],
'trb': [None, None, 0x14, None, None, 0x1C, None, None,
None, None, None, None, None, None, None],
'tsb': [None, None, 0x04, None, None, 0x0C, None, None,
None, None, None, None, None, None, None],
'wai': [0xCB, None, None, None, None, None, None, None,
None, None, None, None, None, None, None],
} }

View File

@ -20,41 +20,55 @@ import Ophis.Macro as Macro
# The passes themselves # The passes themselves
class Pass(object): class Pass(object):
"""Superclass for all assembler passes. Automatically handles IR """Superclass for all assembler passes. Automatically handles IR
types that modify the environent's structure, and by default types that modify the environent's structure, and by default
raises an error on anything else. Override visitUnknown in your raises an error on anything else. Override visitUnknown in your
extension pass to produce a pass that accepts everything.""" extension pass to produce a pass that accepts everything."""
name = "Default Pass" name = "Default Pass"
def __init__(self): def __init__(self):
self.writeOK = 1 self.writeOK = True
def visitNone(self, node, env): def visitNone(self, node, env):
pass pass
def visitSEQUENCE(self, node, env): def visitSEQUENCE(self, node, env):
Err.currentpoint = node.ppt Err.currentpoint = node.ppt
for n in node.data: for n in node.data:
n.accept(self, env) n.accept(self, env)
def visitDataSegment(self, node, env): def visitDataSegment(self, node, env):
self.writeOK = 0 self.writeOK = False
env.setsegment(node.data[0]) env.setsegment(node.data[0])
def visitTextSegment(self, node, env): def visitTextSegment(self, node, env):
self.writeOK = 1 self.writeOK = True
env.setsegment(node.data[0]) env.setsegment(node.data[0])
def visitScopeBegin(self, node, env): def visitScopeBegin(self, node, env):
env.newscope() env.newscope()
def visitScopeEnd(self, node, env): def visitScopeEnd(self, node, env):
env.endscope() env.endscope()
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
Err.log("Internal error! "+self.name+" cannot understand node type "+node.nodetype) Err.log("Internal error! " + self.name +
" cannot understand node type " + node.nodetype)
def prePass(self): def prePass(self):
pass pass
def postPass(self): def postPass(self):
pass pass
def go(self, node, env): def go(self, node, env):
"""Prepares the environment and runs this pass, possibly """Prepares the environment and runs this pass, possibly
printing debugging information.""" printing debugging information."""
if Err.count == 0: if Err.count == 0:
if Cmd.print_pass: print>>sys.stderr, "Running: "+self.name if Cmd.print_pass:
print>>sys.stderr, "Running: " + self.name
env.reset() env.reset()
self.prePass() self.prePass()
node.accept(self, env) node.accept(self, env)
@ -67,6 +81,7 @@ class Pass(object):
print>>sys.stderr, "Current IR:" print>>sys.stderr, "Current IR:"
print>>sys.stderr, node print>>sys.stderr, node
class FixPoint(object): class FixPoint(object):
"""A specialized class that is not a pass but can be run like one. """A specialized class that is not a pass but can be run like one.
This class takes a list of passes and a "fixpoint" function.""" This class takes a list of passes and a "fixpoint" function."""
@ -74,48 +89,60 @@ class FixPoint(object):
self.name = name self.name = name
self.passes = passes self.passes = passes
self.fixpoint = fixpoint self.fixpoint = fixpoint
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 xrange(100):
if Err.count != 0: break if Err.count != 0:
break
for p in self.passes: for p in self.passes:
p.go(node, env) p.go(node, env)
if Err.count != 0: break if Err.count != 0:
if self.fixpoint(): break break
if Cmd.print_pass: print>>sys.stderr, "Fixpoint failed, looping back" if self.fixpoint():
break
if Cmd.print_pass:
print>>sys.stderr, "Fixpoint failed, looping back"
else: else:
Err.log("Can't make %s converge! Maybe there's a recursive dependency somewhere?" % self.name) Err.log("Can't make %s converge! Maybe there's a recursive "
"dependency somewhere?" % self.name)
class DefineMacros(Pass): class DefineMacros(Pass):
"Extract macro definitions and remove them from the IR" "Extract macro definitions and remove them from the IR"
name = "Macro definition pass" name = "Macro definition pass"
def prePass(self): def prePass(self):
self.inDef = 0 self.inDef = False
self.nestedError = 0 self.nestedError = False
def postPass(self): def postPass(self):
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>>sys.stderr, "Macro definitions:"
Macro.dump() Macro.dump()
def visitMacroBegin(self, node, env): def visitMacroBegin(self, node, env):
if self.inDef: if self.inDef:
Err.log("Nested macro definition") Err.log("Nested macro definition")
self.nestedError = 1 self.nestedError = True
else: else:
Macro.newMacro(node.data[0]) Macro.newMacro(node.data[0])
node.nodetype = "None" node.nodetype = "None"
node.data = [] node.data = []
self.inDef = 1 self.inDef = True
def visitMacroEnd(self, node, env): def visitMacroEnd(self, node, env):
if self.inDef: if self.inDef:
Macro.endMacro() Macro.endMacro()
node.nodetype = "None" node.nodetype = "None"
node.data = [] node.data = []
self.inDef = 0 self.inDef = False
elif not self.nestedError: elif not self.nestedError:
Err.log("Unmatched .macend") Err.log("Unmatched .macend")
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
if self.inDef: if self.inDef:
Macro.registerNode(node) Macro.registerNode(node)
@ -126,29 +153,38 @@ class DefineMacros(Pass):
class ExpandMacros(Pass): class ExpandMacros(Pass):
"Replace macro invocations with the appropriate text" "Replace macro invocations with the appropriate text"
name = "Macro expansion pass" name = "Macro expansion pass"
def prePass(self): def prePass(self):
self.changed = 0 self.changed = False
def visitMacroInvoke(self, node, env): def visitMacroInvoke(self, node, env):
replacement = Macro.expandMacro(node.ppt, node.data[0], node.data[1:]) replacement = Macro.expandMacro(node.ppt, node.data[0], node.data[1:])
node.nodetype = replacement.nodetype node.nodetype = replacement.nodetype
node.data = replacement.data node.data = replacement.data
self.changed = 1 self.changed = True
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
class InitLabels(Pass): class InitLabels(Pass):
"Finds all reachable labels" "Finds all reachable labels"
name = "Label initialization pass" name = "Label initialization pass"
def __init__(self): def __init__(self):
Pass.__init__(self) Pass.__init__(self)
self.labelmap = {} self.labelmap = {}
def prePass(self): def prePass(self):
self.changed = 0 self.changed = False
self.PCvalid = 1 self.PCvalid = True
def visitAdvance(self, node, env): def visitAdvance(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid) self.PCvalid = node.data[0].valid(env, self.PCvalid)
def visitSetPC(self, node, env): def visitSetPC(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid) self.PCvalid = node.data[0].valid(env, self.PCvalid)
def visitLabel(self, node, env): def visitLabel(self, node, env):
(label, val) = node.data (label, val) = node.data
fulllabel = "%d:%s" % (env.stack[0], label) fulllabel = "%d:%s" % (env.stack[0], label)
@ -157,47 +193,60 @@ class InitLabels(Pass):
if fulllabel not in self.labelmap: if fulllabel not in self.labelmap:
self.labelmap[fulllabel] = node self.labelmap[fulllabel] = node
if val.valid(env, self.PCvalid) and label not in env: if val.valid(env, self.PCvalid) and label not in env:
env[label]=0 env[label] = 0
self.changed=1 self.changed = True
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
class CircularityCheck(Pass): class CircularityCheck(Pass):
"Checks for circular label dependencies" "Checks for circular label dependencies"
name = "Circularity check pass" name = "Circularity check pass"
def prePass(self): def prePass(self):
self.changed=0 self.changed = False
self.PCvalid=1 self.PCvalid = True
def visitAdvance(self, node, env): def visitAdvance(self, node, env):
PCvalid = self.PCvalid PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid) self.PCvalid = node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid): if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on .advance") Err.log("Undefined or circular reference on .advance")
def visitSetPC(self, node, env): def visitSetPC(self, node, env):
PCvalid = self.PCvalid PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid) self.PCvalid = node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid): if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on program counter set") Err.log("Undefined or circular reference on program counter set")
def visitCheckPC(self, node, env): def visitCheckPC(self, node, env):
if not node.data[0].valid(env, self.PCvalid): if not node.data[0].valid(env, self.PCvalid):
Err.log("Undefined or circular reference on program counter check") Err.log("Undefined or circular reference on program counter check")
def visitLabel(self, node, env): def visitLabel(self, node, env):
(label, val) = node.data (label, val) = node.data
if not val.valid(env, self.PCvalid): if not val.valid(env, self.PCvalid):
Err.log("Undefined or circular dependency for label '%s'" % label) Err.log("Undefined or circular dependency for label '%s'" % label)
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
class CheckExprs(Pass): class CheckExprs(Pass):
"Ensures all expressions can resolve" "Ensures all expressions can resolve"
name = "Expression checking pass" name = "Expression checking pass"
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
# Throw away result, just confirm validity of all expressions
for i in [x for x in node.data if isinstance(x, IR.Expr)]: for i in [x for x in node.data if isinstance(x, IR.Expr)]:
i.value(env) # Throw away result, just confirm validity of all expressions i.value(env)
class EasyModes(Pass): class EasyModes(Pass):
"Assigns address modes to hardcoded and branch instructions" "Assigns address modes to hardcoded and branch instructions"
name = "Easy addressing modes pass" name = "Easy addressing modes pass"
def visitMemory(self, node, env): def visitMemory(self, node, env):
if Ops.opcodes[node.data[0]][14] is not None: if Ops.opcodes[node.data[0]][14] is not None:
node.nodetype = "Relative" node.nodetype = "Relative"
@ -205,157 +254,240 @@ class EasyModes(Pass):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_no_index(node, env): if not collapse_no_index(node, env):
node.nodetype = "Absolute" node.nodetype = "Absolute"
def visitMemoryX(self, node, env): def visitMemoryX(self, node, env):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_x(node, env): if not collapse_x(node, env):
node.nodetype = "AbsoluteX" node.nodetype = "AbsoluteX"
def visitMemoryY(self, node, env): def visitMemoryY(self, node, env):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_y(node, env): if not collapse_y(node, env):
node.nodetype = "AbsoluteY" node.nodetype = "AbsoluteY"
def visitPointer(self, node, env): def visitPointer(self, node, env):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_no_index_ind(node, env): if not collapse_no_index_ind(node, env):
node.nodetype = "Indirect" node.nodetype = "Indirect"
def visitPointerX(self, node, env): def visitPointerX(self, node, env):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_x_ind(node, env): if not collapse_x_ind(node, env):
node.nodetype = "AbsIndX" node.nodetype = "AbsIndX"
def visitPointerY(self, node, env): def visitPointerY(self, node, env):
if node.data[1].hardcoded: if node.data[1].hardcoded:
if not collapse_y_ind(node, env): if not collapse_y_ind(node, env):
node.nodetype = "AbsIndY" node.nodetype = "AbsIndY"
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
class PCTracker(Pass): class PCTracker(Pass):
"Superclass for passes that need an accurate program counter." "Superclass for passes that need an accurate program counter."
name = "**BUG** PC Tracker Superpass used directly" name = "**BUG** PC Tracker Superpass used directly"
def visitSetPC(self, node, env): env.setPC(node.data[0].value(env))
def visitAdvance(self, node, env): env.setPC(node.data[0].value(env)) def visitSetPC(self, node, env):
def visitImplied(self, node, env): env.incPC(1) env.setPC(node.data[0].value(env))
def visitImmediate(self, node, env): env.incPC(2)
def visitIndirectX(self, node, env): env.incPC(2) def visitAdvance(self, node, env):
def visitIndirectY(self, node, env): env.incPC(2) env.setPC(node.data[0].value(env))
def visitZPIndirect(self, node, env): env.incPC(2)
def visitZeroPage(self, node, env): env.incPC(2) def visitImplied(self, node, env):
def visitZeroPageX(self, node, env): env.incPC(2) env.incPC(1)
def visitZeroPageY(self, node, env): env.incPC(2)
def visitRelative(self, node, env): env.incPC(2) def visitImmediate(self, node, env):
def visitIndirect(self, node, env): env.incPC(3) env.incPC(2)
def visitAbsolute(self, node, env): env.incPC(3)
def visitAbsoluteX(self, node, env): env.incPC(3) def visitIndirectX(self, node, env):
def visitAbsoluteY(self, node, env): env.incPC(3) env.incPC(2)
def visitAbsIndX(self, node, env): env.incPC(3)
def visitAbsIndY(self, node, env): env.incPC(3) def visitIndirectY(self, node, env):
def visitMemory(self, node, env): env.incPC(3) env.incPC(2)
def visitMemoryX(self, node, env): env.incPC(3)
def visitMemoryY(self, node, env): env.incPC(3) def visitZPIndirect(self, node, env):
def visitPointer(self, node, env): env.incPC(3) env.incPC(2)
def visitPointerX(self, node, env): env.incPC(3)
def visitPointerY(self, node, env): env.incPC(3) def visitZeroPage(self, node, env):
def visitCheckPC(self, node, env): pass env.incPC(2)
def visitLabel(self, node, env): pass
def visitByte(self, node, env): env.incPC(len(node.data)) def visitZeroPageX(self, node, env):
def visitWord(self, node, env): env.incPC(len(node.data)*2) env.incPC(2)
def visitDword(self, node, env): env.incPC(len(node.data)*4)
def visitWordBE(self, node, env): env.incPC(len(node.data)*2) def visitZeroPageY(self, node, env):
def visitDwordBE(self, node, env): env.incPC(len(node.data)*4) env.incPC(2)
def visitRelative(self, node, env):
env.incPC(2)
def visitIndirect(self, node, env):
env.incPC(3)
def visitAbsolute(self, node, env):
env.incPC(3)
def visitAbsoluteX(self, node, env):
env.incPC(3)
def visitAbsoluteY(self, node, env):
env.incPC(3)
def visitAbsIndX(self, node, env):
env.incPC(3)
def visitAbsIndY(self, node, env):
env.incPC(3)
def visitMemory(self, node, env):
env.incPC(3)
def visitMemoryX(self, node, env):
env.incPC(3)
def visitMemoryY(self, node, env):
env.incPC(3)
def visitPointer(self, node, env):
env.incPC(3)
def visitPointerX(self, node, env):
env.incPC(3)
def visitPointerY(self, node, env):
env.incPC(3)
def visitCheckPC(self, node, env):
pass
def visitLabel(self, node, env):
pass
def visitByte(self, node, env):
env.incPC(len(node.data))
def visitWord(self, node, env):
env.incPC(len(node.data) * 2)
def visitDword(self, node, env):
env.incPC(len(node.data) * 4)
def visitWordBE(self, node, env):
env.incPC(len(node.data) * 2)
def visitDwordBE(self, node, env):
env.incPC(len(node.data) * 4)
class UpdateLabels(PCTracker): class UpdateLabels(PCTracker):
"Computes the new values for all entries in the symbol table" "Computes the new values for all entries in the symbol table"
name = "Label Update Pass" name = "Label Update Pass"
def prePass(self): def prePass(self):
self.changed = 0 self.changed = False
def visitLabel(self, node, env): def visitLabel(self, node, env):
(label, val) = node.data (label, val) = node.data
old = env[label] old = env[label]
env[label] = val.value(env) env[label] = val.value(env)
if old != env[label]: if old != env[label]:
self.changed = 1 self.changed = True
class Collapse(Pass): class Collapse(Pass):
"""Selects as many zero-page instructions to convert as "Selects as many zero-page instructions to convert as possible."
possible, and tracks how many instructions have been
converted this pass."""
name = "Instruction Collapse Pass" name = "Instruction Collapse Pass"
def prePass(self): def prePass(self):
self.collapsed = 0 self.changed = False
def visitMemory(self, node, env): def visitMemory(self, node, env):
if collapse_no_index(node, env): self.collapsed += 1 self.changed |= collapse_no_index(node, env)
def visitMemoryX(self, node, env): def visitMemoryX(self, node, env):
if collapse_x(node, env): self.collapsed += 1 self.changed |= collapse_x(node, env)
def visitMemoryY(self, node, env): def visitMemoryY(self, node, env):
if collapse_y(node, env): self.collapsed += 1 self.changed |= collapse_y(node, env)
def visitPointer(self, node, env): def visitPointer(self, node, env):
if collapse_no_index_ind(node, env): self.collapsed += 1 self.changed |= collapse_no_index_ind(node, env)
def visitPointerX(self, node, env): def visitPointerX(self, node, env):
if collapse_x_ind(node, env): self.collapsed += 1 self.changed |= collapse_x_ind(node, env)
def visitPointerY(self, node, env): def visitPointerY(self, node, env):
if collapse_y_ind(node, env): self.collapsed += 1 self.changed |= collapse_y_ind(node, env)
def visitUnknown(self, node, env): def visitUnknown(self, node, env):
pass pass
def collapse_no_index(node, env): def collapse_no_index(node, env):
"""Transforms a Memory node into a ZeroPage one if possible. """Transforms a Memory node into a ZeroPage one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][2] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][2] is not None:
node.nodetype = "ZeroPage" node.nodetype = "ZeroPage"
return 1 return True
else: return False
return 0
def collapse_x(node, env): def collapse_x(node, env):
"""Transforms a MemoryX node into a ZeroPageX one if possible. """Transforms a MemoryX node into a ZeroPageX one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][3] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][3] is not None:
node.nodetype = "ZeroPageX" node.nodetype = "ZeroPageX"
return 1 return True
else: return False
return 0
def collapse_y(node, env): def collapse_y(node, env):
"""Transforms a MemoryY node into a ZeroPageY one if possible. """Transforms a MemoryY node into a ZeroPageY one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][4] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][4] is not None:
node.nodetype = "ZeroPageY" node.nodetype = "ZeroPageY"
return 1 return True
else: return False
return 0
def collapse_no_index_ind(node, env): def collapse_no_index_ind(node, env):
"""Transforms a Pointer node into a ZPIndirect one if possible. """Transforms a Pointer node into a ZPIndirect one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][11] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][11] is not None:
node.nodetype = "ZPIndirect" node.nodetype = "ZPIndirect"
return 1 return True
else: return False
return 0
def collapse_x_ind(node, env): def collapse_x_ind(node, env):
"""Transforms a PointerX node into an IndirectX one if possible. """Transforms a PointerX node into an IndirectX one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][12] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][12] is not None:
node.nodetype = "IndirectX" node.nodetype = "IndirectX"
return 1 return True
else: return False
return 0
def collapse_y_ind(node, env): def collapse_y_ind(node, env):
"""Transforms a PointerY node into an IndirectY one if possible. """Transforms a PointerY node into an IndirectY one if possible.
Returns 1 if it made the collapse, false otherwise.""" Returns boolean indicating whether or not it made the collapse."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][13] is not None: if node.data[1].value(env) < 0x100:
if Ops.opcodes[node.data[0]][13] is not None:
node.nodetype = "IndirectY" node.nodetype = "IndirectY"
return 1 return True
else: return False
return 0
class ExtendBranches(PCTracker): class ExtendBranches(PCTracker):
"""Eliminates any branch instructions that would end up going past """Eliminates any branch instructions that would end up going past
the 128-byte range, and replaces them with a branch-jump the 128-byte range, and replaces them with a branch-jump pair."""
pair. Also tracks how many elements where changed this pass."""
name = "Branch Expansion Pass" name = "Branch Expansion Pass"
reversed = { 'bcc': 'bcs', reversed = {'bcc': 'bcs',
'bcs': 'bcc', 'bcs': 'bcc',
'beq': 'bne', 'beq': 'bne',
'bmi': 'bpl', 'bmi': 'bpl',
@ -381,47 +513,75 @@ class ExtendBranches(PCTracker):
'bbr7': 'bbs7', 'bbr7': 'bbs7',
'bbs7': 'bbr7' 'bbs7': 'bbr7'
} }
def prePass(self): def prePass(self):
self.expanded = 0 self.changed = False
def visitRelative(self, node, env): def visitRelative(self, node, env):
(opcode, expr) = node.data (opcode, expr) = node.data
arg = expr.value(env) arg = expr.value(env)
arg = arg-(env.getPC()+2) arg = arg - (env.getPC() + 2)
if arg < -128 or arg > 127: if arg < -128 or arg > 127:
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) node.data = ('jmp', expr)
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: bra out of range, replacing with jmp" print>>sys.stderr, str(node.ppt) + ": WARNING: " \
"bra out of range, replacing with jmp"
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
# We don't use temp labels here because labels need to have been fixed # We don't use temp labels here because labels need to have
# in place by this point, and JMP is always 3 bytes long. # been fixed in place by this point, and JMP is always 3
expansion = [IR.Node(node.ppt, "Relative", ExtendBranches.reversed[opcode], IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(5)])), # bytes long.
expansion = [IR.Node(node.ppt, "Relative",
ExtendBranches.reversed[opcode],
IR.SequenceExpr([IR.PCExpr(), "+",
IR.ConstantExpr(5)])),
IR.Node(node.ppt, "Absolute", 'jmp', expr)] IR.Node(node.ppt, "Absolute", 'jmp', expr)]
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: "+opcode+" out of range, replacing with "+ExtendBranches.reversed[opcode] +"/jmp combo" print>>sys.stderr, str(node.ppt) + ": WARNING: " + \
self.expanded += 1 opcode + " out of range, " \
"replacing with " + \
ExtendBranches.reversed[opcode] + \
"/jmp combo"
self.changed = True
node.accept(self, env) node.accept(self, env)
else: else:
env.incPC(2) env.incPC(2)
class NormalizeModes(Pass): class NormalizeModes(Pass):
"""Eliminates the intermediate "Memory" and "Pointer" nodes, """Eliminates the intermediate "Memory" and "Pointer" nodes,
converting them to "Absolute".""" converting them to "Absolute"."""
name = "Mode Normalization pass" name = "Mode Normalization pass"
def visitMemory(self, node, env): node.nodetype = "Absolute"
def visitMemoryX(self, node, env): node.nodetype = "AbsoluteX" def visitMemory(self, node, env):
def visitMemoryY(self, node, env): node.nodetype = "AbsoluteY" node.nodetype = "Absolute"
def visitPointer(self, node, env): node.nodetype = "Indirect"
def visitPointerX(self, node, env): node.nodetype = "AbsIndX" def visitMemoryX(self, node, env):
node.nodetype = "AbsoluteX"
def visitMemoryY(self, node, env):
node.nodetype = "AbsoluteY"
def visitPointer(self, node, env):
node.nodetype = "Indirect"
def visitPointerX(self, node, env):
node.nodetype = "AbsIndX"
# If we ever hit a PointerY by this point, we have a bug. # If we ever hit a PointerY by this point, we have a bug.
def visitPointerY(self, node, env): node.nodetype = "AbsIndY"
def visitUnknown(self, node, env): pass def visitPointerY(self, node, env):
node.nodetype = "AbsIndY"
def visitUnknown(self, node, env):
pass
class Assembler(Pass): class Assembler(Pass):
"""Converts the IR into a list of bytes, suitable for writing to """Converts the IR into a list of bytes, suitable for writing to
@ -436,36 +596,40 @@ class Assembler(Pass):
def postPass(self): def postPass(self):
if Cmd.print_summary and Err.count == 0: if Cmd.print_summary and Err.count == 0:
print>>sys.stderr, "Assembly complete: %s bytes output (%s code, %s data, %s filler)" \ print>>sys.stderr, "Assembly complete: %s bytes output " \
% (len(self.output), self.code, self.data, self.filler) "(%s code, %s data, %s filler)" \
% (len(self.output),
self.code, self.data, self.filler)
def outputbyte(self, expr, env): def outputbyte(self, expr, env):
'Outputs a byte, with range checking' 'Outputs a byte, with range checking'
if self.writeOK: if self.writeOK:
val = expr.value(env) val = expr.value(env)
if val < 0x00 or val > 0xff: if val < 0x00 or val > 0xff:
Err.log("Byte constant "+str(expr)+" out of range") Err.log("Byte constant " + str(expr) + " out of range")
val = 0 val = 0
self.output.append(int(val)) self.output.append(int(val))
else: else:
Err.log("Attempt to write to data segment") Err.log("Attempt to write to data segment")
def outputword(self, expr, env): def outputword(self, expr, env):
'Outputs a little-endian word, with range checking' 'Outputs a little-endian word, with range checking'
if self.writeOK: if self.writeOK:
val = expr.value(env) val = expr.value(env)
if val < 0x0000 or val > 0xFFFF: if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range") Err.log("Word constant " + str(expr) + " out of range")
val = 0 val = 0
self.output.append(int(val & 0xFF)) self.output.append(int(val & 0xFF))
self.output.append(int((val >> 8) & 0xFF)) self.output.append(int((val >> 8) & 0xFF))
else: else:
Err.log("Attempt to write to data segment") Err.log("Attempt to write to data segment")
def outputdword(self, expr, env): def outputdword(self, expr, env):
'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 > 0xFFFFFFFFL:
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))
self.output.append(int((val >> 8) & 0xFF)) self.output.append(int((val >> 8) & 0xFF))
@ -479,18 +643,19 @@ class Assembler(Pass):
if self.writeOK: if self.writeOK:
val = expr.value(env) val = expr.value(env)
if val < 0x0000 or val > 0xFFFF: if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range") Err.log("Word constant " + str(expr) + " out of range")
val = 0 val = 0
self.output.append(int((val >> 8) & 0xFF)) self.output.append(int((val >> 8) & 0xFF))
self.output.append(int(val & 0xFF)) self.output.append(int(val & 0xFF))
else: else:
Err.log("Attempt to write to data segment") Err.log("Attempt to write to data segment")
def outputdword_be(self, expr, env): def outputdword_be(self, expr, env):
'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 > 0xFFFFFFFFL:
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))
self.output.append(int((val >> 16) & 0xFF)) self.output.append(int((val >> 16) & 0xFF))
@ -504,77 +669,122 @@ class Assembler(Pass):
(opcode, expr) = node.data (opcode, expr) = node.data
bin_op = Ops.opcodes[opcode][mode] bin_op = Ops.opcodes[opcode][mode]
if bin_op is None: if bin_op is None:
Err.log('%s does not have mode "%s"' % (opcode.upper(), Ops.modes[mode])) Err.log('%s does not have mode "%s"' % (opcode.upper(),
Ops.modes[mode]))
return return
self.outputbyte(IR.ConstantExpr(bin_op), env) self.outputbyte(IR.ConstantExpr(bin_op), env)
arglen = Ops.lengths[mode] arglen = Ops.lengths[mode]
if mode == 14: # Special handling for relative mode if mode == 14: # Special handling for relative mode
arg = expr.value(env) arg = expr.value(env)
arg = arg-(env.getPC()+2) arg = arg - (env.getPC() + 2)
if arg < -128 or arg > 127: if arg < -128 or arg > 127:
Err.log("Branch target out of bounds") Err.log("Branch target out of bounds")
arg = 0 arg = 0
if arg < 0: arg += 256 if arg < 0:
arg += 256
expr = IR.ConstantExpr(arg) expr = IR.ConstantExpr(arg)
if arglen == 1: self.outputbyte(expr, env) if arglen == 1:
if arglen == 2: self.outputword(expr, env) self.outputbyte(expr, env)
env.incPC(1+arglen) if arglen == 2:
self.code += 1+arglen self.outputword(expr, env)
env.incPC(1 + arglen)
self.code += 1 + arglen
def visitImplied(self, node, env):
self.assemble(node, 0, env)
def visitImmediate(self, node, env):
self.assemble(node, 1, env)
def visitZeroPage(self, node, env):
self.assemble(node, 2, env)
def visitZeroPageX(self, node, env):
self.assemble(node, 3, env)
def visitZeroPageY(self, node, env):
self.assemble(node, 4, env)
def visitAbsolute(self, node, env):
self.assemble(node, 5, env)
def visitAbsoluteX(self, node, env):
self.assemble(node, 6, env)
def visitAbsoluteY(self, node, env):
self.assemble(node, 7, env)
def visitIndirect(self, node, env):
self.assemble(node, 8, env)
def visitAbsIndX(self, node, env):
self.assemble(node, 9, env)
def visitAbsIndY(self, node, env):
self.assemble(node, 10, env)
def visitZPIndirect(self, node, env):
self.assemble(node, 11, env)
def visitIndirectX(self, node, env):
self.assemble(node, 12, env)
def visitIndirectY(self, node, env):
self.assemble(node, 13, env)
def visitRelative(self, node, env):
self.assemble(node, 14, env)
def visitLabel(self, node, env):
pass
def visitImplied(self, node, env): self.assemble(node, 0, env)
def visitImmediate(self, node, env): self.assemble(node, 1, env)
def visitZeroPage(self, node, env): self.assemble(node, 2, env)
def visitZeroPageX(self, node, env): self.assemble(node, 3, env)
def visitZeroPageY(self, node, env): self.assemble(node, 4, env)
def visitAbsolute(self, node, env): self.assemble(node, 5, env)
def visitAbsoluteX(self, node, env): self.assemble(node, 6, env)
def visitAbsoluteY(self, node, env): self.assemble(node, 7, env)
def visitIndirect(self, node, env): self.assemble(node, 8, env)
def visitAbsIndX(self, node, env): self.assemble(node, 9, env)
def visitAbsIndY(self, node, env): self.assemble(node, 10, env)
def visitZPIndirect(self, node, env): self.assemble(node, 11, env)
def visitIndirectX(self, node, env): self.assemble(node, 12, env)
def visitIndirectY(self, node, env): self.assemble(node, 13, env)
def visitRelative(self, node, env): self.assemble(node, 14, env)
def visitLabel(self, node, env): pass
def visitByte(self, node, env): def visitByte(self, node, env):
for expr in node.data: for expr in node.data:
self.outputbyte(expr, env) self.outputbyte(expr, env)
env.incPC(len(node.data)) env.incPC(len(node.data))
self.data += len(node.data) self.data += len(node.data)
def visitWord(self, node, env): def visitWord(self, node, env):
for expr in node.data: for expr in node.data:
self.outputword(expr, env) self.outputword(expr, env)
env.incPC(len(node.data)*2) env.incPC(len(node.data) * 2)
self.data += len(node.data)*2 self.data += len(node.data) * 2
def visitDword(self, node, env): def visitDword(self, node, env):
for expr in node.data: for expr in node.data:
self.outputdword(expr, env) self.outputdword(expr, env)
env.incPC(len(node.data)*4) env.incPC(len(node.data) * 4)
self.data += len(node.data)*4 self.data += len(node.data) * 4
def visitWordBE(self, node, env): def visitWordBE(self, node, env):
for expr in node.data: for expr in node.data:
self.outputword_be(expr, env) self.outputword_be(expr, env)
env.incPC(len(node.data)*2) env.incPC(len(node.data) * 2)
self.data += len(node.data)*2 self.data += len(node.data) * 2
def visitDwordBE(self, node, env): def visitDwordBE(self, node, env):
for expr in node.data: for expr in node.data:
self.outputdword_be(expr, env) self.outputdword_be(expr, env)
env.incPC(len(node.data)*4) env.incPC(len(node.data) * 4)
self.data += len(node.data)*4 self.data += len(node.data) * 4
def visitSetPC(self, node, env): def visitSetPC(self, node, env):
env.setPC(node.data[0].value(env)) env.setPC(node.data[0].value(env))
def visitCheckPC(self, node, env): def visitCheckPC(self, node, env):
pc = env.getPC() pc = env.getPC()
target = node.data[0].value(env) target = node.data[0].value(env)
if (pc > target): if (pc > target):
Err.log(".checkpc assertion failed: $%x > $%x" % (pc, target)) Err.log(".checkpc assertion failed: $%x > $%x" % (pc, target))
def visitAdvance(self, node, env): def visitAdvance(self, node, env):
pc = env.getPC() pc = env.getPC()
target = node.data[0].value(env) target = node.data[0].value(env)
if (pc > target): if (pc > target):
Err.log("Attempted to .advance backwards: $%x to $%x" % (pc, target)) Err.log("Attempted to .advance backwards: $%x to $%x" %
(pc, target))
else: else:
for i in xrange(target-pc): self.outputbyte(node.data[1], env) for i in xrange(target - pc):
self.filler += target-pc self.outputbyte(node.data[1], env)
self.filler += target - pc
env.setPC(target) env.setPC(target)

View File

@ -6,6 +6,11 @@ setup(name='Ophis',
author="Michael Martin", author="Michael Martin",
author_email="mcmartin@gmail.com", author_email="mcmartin@gmail.com",
license="MIT", license="MIT",
long_description="Ophis is a cross-assembler for the 65xx series of chips. It supports the stock 6502 opcodes, the 65c02 extensions, and syntax for the \"undocumented opcodes\" in the 6510 chip used on the Commodore 64. (Syntax for these opcodes matches those given in the VICE team's documentation.)", long_description="Ophis is a cross-assembler for the 65xx series of "
"chips. It supports the stock 6502 opcodes, the 65c02 "
"extensions, and syntax for the \"undocumented "
"opcodes\" in the 6510 chip used on the Commodore 64. "
"(Syntax for these opcodes matches those given in the "
"VICE team's documentation.)",
packages=['Ophis'], packages=['Ophis'],
scripts=['scripts/ophis']) scripts=['scripts/ophis'])

View File

@ -1,9 +1,10 @@
from distutils.core import setup from distutils.core import setup
import py2exe, sys import py2exe
import sys
sys.argv.append('py2exe') sys.argv.append('py2exe')
setup(options = {'py2exe': {'bundle_files': 1}}, setup(options={'py2exe': {'bundle_files': 1}},
packages = ['Ophis'], packages=['Ophis'],
zipfile = None, zipfile=None,
console = [{'script': "scripts/ophis"}]) console=[{'script': "scripts/ophis"}])

View File

@ -10,11 +10,13 @@ normmap = ''.join([bits[x] for x in norm])
ivrsmap = ''.join([bits[x] for x in ivrs]) ivrsmap = ''.join([bits[x] for x in ivrs])
blnkmap = ''.join([bits[x] for x in blnk]) blnkmap = ''.join([bits[x] for x in blnk])
def dumpfile(n, m): def dumpfile(n, m):
f = file(n, 'wb') f = file(n, 'wb')
f.write(m) f.write(m)
f.close() f.close()
dumpfile('a2normal.map', normmap) dumpfile('a2normal.map', normmap)
dumpfile('a2inverse.map', ivrsmap) dumpfile('a2inverse.map', ivrsmap)
dumpfile('a2blink.map', blnkmap) dumpfile('a2blink.map', blnkmap)

View File

@ -3,7 +3,7 @@ import sys
verbose = 0 verbose = 0
prologue = '"""' +"""Opcodes file. prologue = '"""' + """Opcodes file.
Tables for the assembly of 6502-family instructions, mapping Tables for the assembly of 6502-family instructions, mapping
opcodes and addressing modes to binary instructions.""" + '"""' + """ opcodes and addressing modes to binary instructions.""" + '"""' + """
@ -33,6 +33,7 @@ modes = ["Implied", # 0
"(Zero Page), Y", # 13 "(Zero Page), Y", # 13
"Relative"] # 14 "Relative"] # 14
# Lengths of the argument # Lengths of the argument
lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1] lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1]
""" """
@ -56,24 +57,28 @@ modes = ["Implied", # 0
flatmodes = [x.lower() for x in modes] flatmodes = [x.lower() for x in modes]
# WARNING: This decommenter assumes that # never appears anywhere else # WARNING: This decommenter assumes that # never appears anywhere else
# in a line. # in a line.
def decomment (l): def decomment(l):
if '#' in l: if '#' in l:
l = l[:l.index('#')] l = l[:l.index('#')]
return l.strip() return l.strip()
def decomment_readlines (fname):
def decomment_readlines(fname):
result = [decomment(x) for x in file(fname).readlines()] result = [decomment(x) for x in file(fname).readlines()]
return [x for x in result if len(x) > 0] return [x for x in result if len(x) > 0]
def parse_chipset_file (fname):
def parse_chipset_file(fname):
result = [None] * 256 result = [None] * 256
ls = [[x.strip() for x in y] for y in [z.split(':', 1) for z in decomment_readlines (fname)]] ls = [[x.strip() for x in y]
for y in [z.split(':', 1) for z in decomment_readlines(fname)]]
for l in ls: for l in ls:
if len(l) == 2: if len(l) == 2:
try: try:
op = int (l[0], 16) op = int(l[0], 16)
syns = l[1].split(';') syns = l[1].split(';')
for s in syns: for s in syns:
s_p = s.split('-') s_p = s.split('-')
@ -81,7 +86,7 @@ def parse_chipset_file (fname):
mnem = s_p[0].lower().strip() mnem = s_p[0].lower().strip()
mode = s_p[1].lower().strip() mode = s_p[1].lower().strip()
if mode in flatmodes: if mode in flatmodes:
if result[op] == None: if result[op] is None:
result[op] = [] result[op] = []
result[op].append((mnem, flatmodes.index(mode))) result[op].append((mnem, flatmodes.index(mode)))
else: else:
@ -90,10 +95,11 @@ def parse_chipset_file (fname):
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(range(256), cs_list):
if insts != 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:
@ -115,19 +121,26 @@ def collate_chipset_map (cs_list, base):
del result[x] del result[x]
return result return result
def mapval(x): def mapval(x):
if x is None: if x is None:
return "None" return "None"
else: else:
return "0x%02X" % x return "0x%02X" % x
def dump_map (m, prologue = ''):
def dump_map(m, prologue=''):
mnems = m.keys() mnems = m.keys()
mnems.sort() mnems.sort()
for mnem in mnems: for mnem in mnems:
print "%s'%s': [%s]," % (prologue, mnem, ', '.join([mapval(x) for x in m[mnem]])) codes = [mapval(x) for x in m[mnem]]
print "%s'%s': [%s,\n%s %s]," % (prologue, mnem,
', '.join(codes[:8]),
prologue + " " * len(mnem),
', '.join(codes[8:]))
if __name__=='__main__':
if __name__ == '__main__':
if len(sys.argv) > 1: if len(sys.argv) > 1:
chipsets = argv[1:] chipsets = argv[1:]
else: else:
@ -135,7 +148,8 @@ if __name__=='__main__':
archs = [] archs = []
for x in chipsets: for x in chipsets:
try: try:
ls = [[x.strip() for x in y] for y in [z.split(':', 1) for z in decomment_readlines (x)]] ls = [[x.strip() for x in y]
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)
@ -147,10 +161,9 @@ if __name__=='__main__':
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 == 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))
print ""