Major formatting fixes:

* No more tabs
 * Fix copyright notices to point at right files and name the license right
This commit is contained in:
Michael C. Martin 2012-05-06 20:06:28 -07:00
parent d5ec7bdacd
commit 1df8ad465d
12 changed files with 1508 additions and 1514 deletions

View File

@ -9,9 +9,9 @@
6510 compatibility and deprecation are handled in Ophis.Main."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
verbose = 1;

View File

@ -1,13 +1,10 @@
"""Core pragmas
Provides the core assembler directives. It does not guarantee
compatibility with older versions of P65-Perl."""
Provides the core assembler directives."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
from __future__ import nested_scopes
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
import Ophis.IR as IR
import Ophis.Frontend as FE
@ -18,193 +15,193 @@ basecharmap = "".join([chr(x) for x in range(256)])
currentcharmap = basecharmap
def reset():
global loadedfiles, currentcharmap, basecharmap
loadedfiles={}
currentcharmap = basecharmap
global loadedfiles, currentcharmap, basecharmap
loadedfiles={}
currentcharmap = basecharmap
def pragmaInclude(ppt, line, result):
"Includes a source file"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str: result.append(FE.parse_file(ppt, filename))
"Includes a source file"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str: result.append(FE.parse_file(ppt, filename))
def pragmaRequire(ppt, line, result):
"Includes a source file at most one time"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
global loadedfiles
if filename not in loadedfiles:
loadedfiles[filename]=1
result.append(FE.parse_file(ppt, filename))
"Includes a source file at most one time"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
global loadedfiles
if filename not in loadedfiles:
loadedfiles[filename]=1
result.append(FE.parse_file(ppt, filename))
def pragmaIncbin(ppt, line, result):
"Includes a binary file"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
try:
f = file(filename, "rb")
bytes = f.read()
f.close()
except IOError:
Err.log ("Could not read "+filename)
return
bytes = [IR.ConstantExpr(ord(x)) for x in bytes]
result.append(IR.Node(ppt, "Byte", *bytes))
"Includes a binary file"
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
try:
f = file(filename, "rb")
bytes = f.read()
f.close()
except IOError:
Err.log ("Could not read "+filename)
return
bytes = [IR.ConstantExpr(ord(x)) for x in bytes]
result.append(IR.Node(ppt, "Byte", *bytes))
def pragmaCharmap(ppt, line, result):
"Modify the character map."
global currentcharmap, basecharmap
bytes = readData(line)
if len(bytes) == 0:
currentcharmap = basecharmap
else:
try:
base = bytes[0].data
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
currentcharmap = currentcharmap[:base] + newsubstr + currentcharmap[base+len(newsubstr):]
if len(currentcharmap) != 256 or base < 0 or base > 255:
Err.log("Charmap replacement out of range")
currentcharmap = currentcharmap[:256]
except ValueError:
Err.log("Illegal character in .charmap directive")
"Modify the character map."
global currentcharmap, basecharmap
bytes = readData(line)
if len(bytes) == 0:
currentcharmap = basecharmap
else:
try:
base = bytes[0].data
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
currentcharmap = currentcharmap[:base] + newsubstr + currentcharmap[base+len(newsubstr):]
if len(currentcharmap) != 256 or base < 0 or base > 255:
Err.log("Charmap replacement out of range")
currentcharmap = currentcharmap[:256]
except ValueError:
Err.log("Illegal character in .charmap directive")
def pragmaCharmapbin(ppt, line, result):
"Load a new character map from a file"
global currentcharmap
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
try:
f = file(filename, "rb")
bytes = f.read()
f.close()
except IOError:
Err.log ("Could not read "+filename)
return
if len(bytes)==256:
currentcharmap = bytes
else:
Err.log("Character map "+filename+" not 256 bytes long")
"Load a new character map from a file"
global currentcharmap
filename = line.expect("STRING").value
line.expect("EOL")
if type(filename)==str:
try:
f = file(filename, "rb")
bytes = f.read()
f.close()
except IOError:
Err.log ("Could not read "+filename)
return
if len(bytes)==256:
currentcharmap = bytes
else:
Err.log("Character map "+filename+" not 256 bytes long")
def pragmaOrg(ppt, line, result):
"Relocates the PC with no output"
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "SetPC", newPC))
"Relocates the PC with no output"
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "SetPC", newPC))
def pragmaAdvance(ppt, line, result):
"Outputs filler until reaching the target PC"
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "Advance", newPC))
"Outputs filler until reaching the target PC"
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "Advance", newPC))
def pragmaCheckpc(ppt, line, result):
"Enforces that the PC has not exceeded a certain point"
target = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "CheckPC", target))
"Enforces that the PC has not exceeded a certain point"
target = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "CheckPC", target))
def pragmaAlias(ppt, line, result):
"Assigns an arbitrary label"
lbl = line.expect("LABEL").value
target = FE.parse_expr(line)
result.append(IR.Node(ppt, "Label", lbl, target))
"Assigns an arbitrary label"
lbl = line.expect("LABEL").value
target = FE.parse_expr(line)
result.append(IR.Node(ppt, "Label", lbl, target))
def pragmaSpace(ppt, line, result):
"Reserves space in a data segment for a variable"
lbl = line.expect("LABEL").value
size = line.expect("NUM").value
line.expect("EOL")
result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr()))
result.append(IR.Node(ppt, "SetPC", IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(size)])))
"Reserves space in a data segment for a variable"
lbl = line.expect("LABEL").value
size = line.expect("NUM").value
line.expect("EOL")
result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr()))
result.append(IR.Node(ppt, "SetPC", IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(size)])))
def pragmaText(ppt, line, result):
"Switches to a text segment"
next = line.expect("LABEL", "EOL")
if next.type == "LABEL":
line.expect("EOL")
segment = next.value
else:
segment = "*text-default*"
result.append(IR.Node(ppt, "TextSegment", segment))
"Switches to a text segment"
next = line.expect("LABEL", "EOL")
if next.type == "LABEL":
line.expect("EOL")
segment = next.value
else:
segment = "*text-default*"
result.append(IR.Node(ppt, "TextSegment", segment))
def pragmaData(ppt, line, result):
"Switches to a data segment (no output allowed)"
next = line.expect("LABEL", "EOL")
if next.type == "LABEL":
line.expect("EOL")
segment = next.value
else:
segment = "*data-default*"
result.append(IR.Node(ppt, "DataSegment", segment))
"Switches to a data segment (no output allowed)"
next = line.expect("LABEL", "EOL")
if next.type == "LABEL":
line.expect("EOL")
segment = next.value
else:
segment = "*data-default*"
result.append(IR.Node(ppt, "DataSegment", segment))
def readData(line):
"Read raw data from a comma-separated list"
if line.lookahead(0).type == "STRING":
data = [IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value.translate(currentcharmap)]
else:
data = [FE.parse_expr(line)]
next = line.expect(',', 'EOL').type
while next == ',':
if line.lookahead(0).type == "STRING":
data.extend([IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value])
else:
data.append(FE.parse_expr(line))
next = line.expect(',', 'EOL').type
return data
"Read raw data from a comma-separated list"
if line.lookahead(0).type == "STRING":
data = [IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value.translate(currentcharmap)]
else:
data = [FE.parse_expr(line)]
next = line.expect(',', 'EOL').type
while next == ',':
if line.lookahead(0).type == "STRING":
data.extend([IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value])
else:
data.append(FE.parse_expr(line))
next = line.expect(',', 'EOL').type
return data
def pragmaByte(ppt, line, result):
"Raw data, a byte at a time"
bytes = readData(line)
result.append(IR.Node(ppt, "Byte", *bytes))
"Raw data, a byte at a time"
bytes = readData(line)
result.append(IR.Node(ppt, "Byte", *bytes))
def pragmaWord(ppt, line, result):
"Raw data, a word at a time, little-endian"
words = readData(line)
result.append(IR.Node(ppt, "Word", *words))
"Raw data, a word at a time, little-endian"
words = readData(line)
result.append(IR.Node(ppt, "Word", *words))
def pragmaDword(ppt, line, result):
"Raw data, a double-word at a time, little-endian"
dwords = readData(line)
result.append(IR.Node(ppt, "Dword", *dwords))
"Raw data, a double-word at a time, little-endian"
dwords = readData(line)
result.append(IR.Node(ppt, "Dword", *dwords))
def pragmaWordbe(ppt, line, result):
"Raw data, a word at a time, big-endian"
words = readData(line)
result.append(IR.Node(ppt, "WordBE", *words))
"Raw data, a word at a time, big-endian"
words = readData(line)
result.append(IR.Node(ppt, "WordBE", *words))
def pragmaDwordbe(ppt, line, result):
"Raw data, a dword at a time, big-endian"
dwords = readData(line)
result.append(IR.Node(ppt, "DwordBE", *dwords))
"Raw data, a dword at a time, big-endian"
dwords = readData(line)
result.append(IR.Node(ppt, "DwordBE", *dwords))
def pragmaScope(ppt, line, result):
"Create a new lexical scoping block"
line.expect("EOL")
result.append(IR.Node(ppt, "ScopeBegin"))
"Create a new lexical scoping block"
line.expect("EOL")
result.append(IR.Node(ppt, "ScopeBegin"))
def pragmaScend(ppt, line, result):
"End the innermost lexical scoping block"
line.expect("EOL")
result.append(IR.Node(ppt, "ScopeEnd"))
"End the innermost lexical scoping block"
line.expect("EOL")
result.append(IR.Node(ppt, "ScopeEnd"))
def pragmaMacro(ppt, line, result):
"Begin a macro definition"
lbl = line.expect("LABEL").value
line.expect("EOL")
result.append(IR.Node(ppt, "MacroBegin", lbl))
"Begin a macro definition"
lbl = line.expect("LABEL").value
line.expect("EOL")
result.append(IR.Node(ppt, "MacroBegin", lbl))
def pragmaMacend(ppt, line, result):
"End a macro definition"
line.expect("EOL")
result.append(IR.Node(ppt, "MacroEnd"))
"End a macro definition"
line.expect("EOL")
result.append(IR.Node(ppt, "MacroEnd"))
def pragmaInvoke(ppt, line, result):
macro = line.expect("LABEL").value
if line.lookahead(0).type == "EOL":
args = []
else:
args = readData(line)
result.append(IR.Node(ppt, "MacroInvoke", macro, *args))
macro = line.expect("LABEL").value
if line.lookahead(0).type == "EOL":
args = []
else:
args = readData(line)
result.append(IR.Node(ppt, "MacroInvoke", macro, *args))

View File

@ -1,75 +1,74 @@
"""Symbol tables and environments for P65.
"""Symbol tables and environments for Ophis.
Implements the symbol lookup, through nested environments -
any non-temporary variable is stored at the top level."""
Implements the symbol lookup, through nested environments -
any non-temporary variable is stored at the top level."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
from __future__ import nested_scopes
import Ophis.Errors as Err
class Environment:
"""Environment class.
Controls the various scopes and global abstract execution variables."""
def __init__(self):
self.dicts = [{}]
self.stack = [0]
self.pc = 0
self.segmentdict = {}
self.segment = "*text-default*"
self.scopecount = 0
def __contains__(self, item):
if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return 1
return 0
return item in self.dicts[0]
def __getitem__(self, item):
if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return dict[item]
else:
if item in self.dicts[0]: return self.dicts[0][item]
Err.log("Unknown label '%s'" % item)
return 0
def __setitem__(self, item, value):
if item[0] == '_':
self.dicts[self.stack[0]][item] = value
else:
self.dicts[0][item] = value
def __str__(self):
return str(self.dicts)
def getPC(self):
return self.pc
def setPC(self, value):
self.pc = value
def incPC(self, amount):
self.pc += amount
def getsegment(self):
return self.segment
def setsegment(self, segment):
self.segmentdict[self.segment] = self.pc
self.segment = segment
self.pc = self.segmentdict.get(segment, 0)
def reset(self):
"Clears out program counter, segment, and scoping information"
self.pc = 0
self.segmentdict = {}
self.segment = "*text-default*"
self.scopecount = 0
if len(self.stack) > 1:
Err.log("Unmatched .scope")
self.stack = [0]
def newscope(self):
"Enters a new scope for temporary labels."
self.scopecount += 1
self.stack.insert(0, self.scopecount)
if len(self.dicts) <= self.scopecount: self.dicts.append({})
def endscope(self):
"Leaves a scope."
if len(self.stack) == 1:
Err.log("Unmatched .scend")
self.stack.pop(0)
"""Environment class.
Controls the various scopes and global abstract execution variables."""
def __init__(self):
self.dicts = [{}]
self.stack = [0]
self.pc = 0
self.segmentdict = {}
self.segment = "*text-default*"
self.scopecount = 0
def __contains__(self, item):
if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return 1
return 0
return item in self.dicts[0]
def __getitem__(self, item):
if item[0] == '_':
for dict in [self.dicts[i] for i in self.stack]:
if item in dict: return dict[item]
else:
if item in self.dicts[0]: return self.dicts[0][item]
Err.log("Unknown label '%s'" % item)
return 0
def __setitem__(self, item, value):
if item[0] == '_':
self.dicts[self.stack[0]][item] = value
else:
self.dicts[0][item] = value
def __str__(self):
return str(self.dicts)
def getPC(self):
return self.pc
def setPC(self, value):
self.pc = value
def incPC(self, amount):
self.pc += amount
def getsegment(self):
return self.segment
def setsegment(self, segment):
self.segmentdict[self.segment] = self.pc
self.segment = segment
self.pc = self.segmentdict.get(segment, 0)
def reset(self):
"Clears out program counter, segment, and scoping information"
self.pc = 0
self.segmentdict = {}
self.segment = "*text-default*"
self.scopecount = 0
if len(self.stack) > 1:
Err.log("Unmatched .scope")
self.stack = [0]
def newscope(self):
"Enters a new scope for temporary labels."
self.scopecount += 1
self.stack.insert(0, self.scopecount)
if len(self.dicts) <= self.scopecount: self.dicts.append({})
def endscope(self):
"Leaves a scope."
if len(self.stack) == 1:
Err.log("Unmatched .scend")
self.stack.pop(0)

View File

@ -1,24 +1,24 @@
"""Error logging
Keeps track of the number of errors inflicted so far, and
where in the assembly the errors are occurring."""
Keeps track of the number of errors inflicted so far, and
where in the assembly the errors are occurring."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
count = 0
currentpoint = "<Top Level>"
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."""
global count
count = count+1
print currentpoint+": "+err
global count
count = count+1
print currentpoint+": "+err
def report():
"Print out the number of errors."
if count == 0: print "No errors"
elif count == 1: print "1 error"
else: print str(count)+" errors"
"Print out the number of errors."
if count == 0: print "No errors"
elif count == 1: print "1 error"
else: print str(count)+" errors"

View File

@ -1,34 +1,32 @@
"""Lexer and Parser
Constructs a list of IR nodes from a list of input strings."""
Constructs a list of IR nodes from a list of input strings."""
from __future__ import nested_scopes
import Ophis.Errors as Err
import Ophis.Opcodes as Ops
import Ophis.IR as IR
import Ophis.CmdLine as Cmd
import os
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
class Lexeme:
"Class for lexer tokens. Used by lexer and parser."
def __init__(self, type="UNKNOWN", value=None):
self.type = type.upper()
self.value = value
def __str__(self):
if self.value == None:
return self.type
else:
return self.type+":"+str(self.value)
def __repr__(self):
return "Lexeme("+`self.type`+", "+`self.value`+")"
def matches(self, other):
"1 if Lexemes a and b have the same type."
return self.type == other.type
"Class for lexer tokens. Used by lexer and parser."
def __init__(self, type="UNKNOWN", value=None):
self.type = type.upper()
self.value = value
def __str__(self):
if self.value == None:
return self.type
else:
return self.type+":"+str(self.value)
def __repr__(self):
return "Lexeme("+`self.type`+", "+`self.value`+")"
def matches(self, other):
"1 if Lexemes a and b have the same type."
return self.type == other.type
bases = {"$":("hexadecimal", 16),
"%":("binary", 2),
@ -37,297 +35,297 @@ bases = {"$":("hexadecimal", 16),
punctuation = "#,`<>():.+-*/&|^[]"
def lex(point, line):
"""Turns a line of source into a sequence of lexemes."""
Err.currentpoint = point
result = []
def is_opcode(op):
"Tests whether a string is an opcode or an identifier"
return op in Ops.opcodes
def add_token(token):
"Converts a substring into a single lexeme"
if token == "":
return
if token == "0":
result.append(Lexeme("NUM", 0))
return
firstchar = token[0]
rest = token[1:]
if firstchar == '"':
result.append(Lexeme("STRING", rest))
return
elif firstchar in bases:
try:
result.append(Lexeme("NUM", long(rest, bases[firstchar][1])))
return
except ValueError:
Err.log('Invalid '+bases[firstchar][0]+' constant: '+rest)
result.append(Lexeme("NUM", 0))
return
elif firstchar.isdigit():
try:
result.append(Lexeme("NUM", long(token)))
except ValueError:
Err.log('Identifiers may not begin with a number')
result.append(Lexeme("LABEL", "ERROR"))
return
elif firstchar == "'":
if len(rest) == 1:
result.append(Lexeme("NUM", ord(rest)))
else:
Err.log("Invalid character constant '"+rest+"'")
result.append(Lexeme("NUM", 0))
return
elif firstchar in punctuation:
if rest != "":
Err.log("Internal lexer error! '"+token+"' can't happen!")
result.append(Lexeme(firstchar))
return
else: # Label, opcode, or index register
id = token.lower()
if is_opcode(id):
result.append(Lexeme("OPCODE", id))
elif id == "x":
result.append(Lexeme("X"))
elif id == "y":
result.append(Lexeme("Y"))
else:
result.append(Lexeme("LABEL", id))
return
# should never reach here
Err.log("Internal lexer error: add_token fall-through")
def add_EOL():
"Adds an end-of-line lexeme"
result.append(Lexeme("EOL"))
# Actual routine begins here
value = ""
quotemode = 0
backslashmode = 0
for c in line.strip():
if backslashmode:
backslashmode = 0
value = value + c
elif c == "\\":
backslashmode = 1
elif quotemode:
if c == '"':
quotemode = 0
else:
value = value + c
elif c == ';':
add_token(value)
value = ""
break
elif c.isspace():
add_token(value)
value = ""
elif c in punctuation:
add_token(value)
add_token(c)
value = ""
elif c == '"':
add_token(value)
value = '"'
quotemode = 1
else:
value = value + c
if backslashmode:
Err.log("Backslashed newline")
if quotemode:
Err.log("Unterminated string constant")
add_token(value)
add_EOL()
return result
"""Turns a line of source into a sequence of lexemes."""
Err.currentpoint = point
result = []
def is_opcode(op):
"Tests whether a string is an opcode or an identifier"
return op in Ops.opcodes
def add_token(token):
"Converts a substring into a single lexeme"
if token == "":
return
if token == "0":
result.append(Lexeme("NUM", 0))
return
firstchar = token[0]
rest = token[1:]
if firstchar == '"':
result.append(Lexeme("STRING", rest))
return
elif firstchar in bases:
try:
result.append(Lexeme("NUM", long(rest, bases[firstchar][1])))
return
except ValueError:
Err.log('Invalid '+bases[firstchar][0]+' constant: '+rest)
result.append(Lexeme("NUM", 0))
return
elif firstchar.isdigit():
try:
result.append(Lexeme("NUM", long(token)))
except ValueError:
Err.log('Identifiers may not begin with a number')
result.append(Lexeme("LABEL", "ERROR"))
return
elif firstchar == "'":
if len(rest) == 1:
result.append(Lexeme("NUM", ord(rest)))
else:
Err.log("Invalid character constant '"+rest+"'")
result.append(Lexeme("NUM", 0))
return
elif firstchar in punctuation:
if rest != "":
Err.log("Internal lexer error! '"+token+"' can't happen!")
result.append(Lexeme(firstchar))
return
else: # Label, opcode, or index register
id = token.lower()
if is_opcode(id):
result.append(Lexeme("OPCODE", id))
elif id == "x":
result.append(Lexeme("X"))
elif id == "y":
result.append(Lexeme("Y"))
else:
result.append(Lexeme("LABEL", id))
return
# should never reach here
Err.log("Internal lexer error: add_token fall-through")
def add_EOL():
"Adds an end-of-line lexeme"
result.append(Lexeme("EOL"))
# Actual routine begins here
value = ""
quotemode = 0
backslashmode = 0
for c in line.strip():
if backslashmode:
backslashmode = 0
value = value + c
elif c == "\\":
backslashmode = 1
elif quotemode:
if c == '"':
quotemode = 0
else:
value = value + c
elif c == ';':
add_token(value)
value = ""
break
elif c.isspace():
add_token(value)
value = ""
elif c in punctuation:
add_token(value)
add_token(c)
value = ""
elif c == '"':
add_token(value)
value = '"'
quotemode = 1
else:
value = value + c
if backslashmode:
Err.log("Backslashed newline")
if quotemode:
Err.log("Unterminated string constant")
add_token(value)
add_EOL()
return result
class ParseLine:
"Maintains the parse state of a line of code. Enables arbitrary lookahead."
def __init__(self, lexemes):
self.lexemes = lexemes
self.location = 0
def lookahead(self, i):
"""Returns the token i units ahead in the parse.
lookahead(0) returns the next token; trying to read off the end of
the sequence returns the last token in the sequence (usually EOL)."""
target = self.location+i
if target >= len(self.lexemes): target = -1
return self.lexemes[target]
def pop(self):
"Returns and removes the next element in the line."
old = self.location
if self.location < len(self.lexemes)-1: self.location += 1
return self.lexemes[old]
def expect(self, *tokens):
"""Reads a token from the ParseLine line and returns it if it's of a type
in the sequence tokens. Otherwise, it logs an error."""
token = self.pop()
if token.type not in tokens:
Err.log('Expected: "'+'", "'.join(tokens)+'"')
return token
"Maintains the parse state of a line of code. Enables arbitrary lookahead."
def __init__(self, lexemes):
self.lexemes = lexemes
self.location = 0
def lookahead(self, i):
"""Returns the token i units ahead in the parse.
lookahead(0) returns the next token; trying to read off the end of
the sequence returns the last token in the sequence (usually EOL)."""
target = self.location+i
if target >= len(self.lexemes): target = -1
return self.lexemes[target]
def pop(self):
"Returns and removes the next element in the line."
old = self.location
if self.location < len(self.lexemes)-1: self.location += 1
return self.lexemes[old]
def expect(self, *tokens):
"""Reads a token from the ParseLine line and returns it if it's of a type
in the sequence tokens. Otherwise, it logs an error."""
token = self.pop()
if token.type not in tokens:
Err.log('Expected: "'+'", "'.join(tokens)+'"')
return token
pragma_modules = []
def parse_expr(line):
"Parses an Ophis arithmetic expression."
def atom():
"Parses lowest-priority expression components."
next = line.lookahead(0).type
if next == "NUM":
return IR.ConstantExpr(line.expect("NUM").value)
elif next == "LABEL":
return IR.LabelExpr(line.expect("LABEL").value)
elif next == "^":
line.expect("^")
return IR.PCExpr()
elif next == "[":
line.expect("[")
result = parse_expr(line)
line.expect("]")
return result
elif next == "+":
offset = 0
while next == "+":
offset += 1
line.expect("+")
next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset))
elif next == "-":
offset = 1
while next == "-":
offset -= 1
line.expect("-")
next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset))
elif next == ">":
line.expect(">")
return IR.HighByteExpr(atom())
elif next == "<":
line.expect("<")
return IR.LowByteExpr(atom())
else:
Err.log('Expected: expression')
def precedence_read(constructor, reader, separators):
"""Handles precedence. The reader argument is a function that returns
expressions that bind more tightly than these; separators is a list
of strings naming the operators at this precedence level. The
constructor argument is a class, indicating what node type holds
objects of this precedence level.
"Parses an Ophis arithmetic expression."
def atom():
"Parses lowest-priority expression components."
next = line.lookahead(0).type
if next == "NUM":
return IR.ConstantExpr(line.expect("NUM").value)
elif next == "LABEL":
return IR.LabelExpr(line.expect("LABEL").value)
elif next == "^":
line.expect("^")
return IR.PCExpr()
elif next == "[":
line.expect("[")
result = parse_expr(line)
line.expect("]")
return result
elif next == "+":
offset = 0
while next == "+":
offset += 1
line.expect("+")
next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset))
elif next == "-":
offset = 1
while next == "-":
offset -= 1
line.expect("-")
next = line.lookahead(0).type
return IR.LabelExpr("*"+str(templabelcount+offset))
elif next == ">":
line.expect(">")
return IR.HighByteExpr(atom())
elif next == "<":
line.expect("<")
return IR.LowByteExpr(atom())
else:
Err.log('Expected: expression')
def precedence_read(constructor, reader, separators):
"""Handles precedence. The reader argument is a function that returns
expressions that bind more tightly than these; separators is a list
of strings naming the operators at this precedence level. The
constructor argument is a class, indicating what node type holds
objects of this precedence level.
Returns a list of Expr objects with separator strings between them."""
result = [reader()] # first object
nextop = line.lookahead(0).type
while (nextop in separators):
line.expect(nextop)
result.append(nextop)
result.append(reader())
nextop = line.lookahead(0).type
if len(result) == 1: return result[0]
return constructor(result)
def term():
"Parses * and /"
return precedence_read(IR.SequenceExpr, atom, ["*", "/"])
def arith():
"Parses + and -"
return precedence_read(IR.SequenceExpr, term, ["+", "-"])
def bits():
"Parses &, |, and ^"
return precedence_read(IR.SequenceExpr, arith, ["&", "|", "^"])
return bits()
Returns a list of Expr objects with separator strings between them."""
result = [reader()] # first object
nextop = line.lookahead(0).type
while (nextop in separators):
line.expect(nextop)
result.append(nextop)
result.append(reader())
nextop = line.lookahead(0).type
if len(result) == 1: return result[0]
return constructor(result)
def term():
"Parses * and /"
return precedence_read(IR.SequenceExpr, atom, ["*", "/"])
def arith():
"Parses + and -"
return precedence_read(IR.SequenceExpr, term, ["+", "-"])
def bits():
"Parses &, |, and ^"
return precedence_read(IR.SequenceExpr, arith, ["&", "|", "^"])
return bits()
def parse_line(ppt, lexemelist):
"Turn a line of source into an IR Node."
Err.currentpoint = ppt
result = []
line = ParseLine(lexemelist)
def aux():
"Accumulates all IR nodes defined by this line."
if line.lookahead(0).type == "EOL":
pass
elif line.lookahead(1).type == ":":
newlabel=line.expect("LABEL").value
line.expect(":")
result.append(IR.Node(ppt, "Label", newlabel, IR.PCExpr()))
aux()
elif line.lookahead(0).type == "*":
global templabelcount
templabelcount = templabelcount + 1
result.append(IR.Node(ppt, "Label", "*"+str(templabelcount), IR.PCExpr()))
line.expect("*")
aux()
elif line.lookahead(0).type == "." or line.lookahead(0).type == "`":
which = line.expect(".", "`").type
if (which == "."): pragma = line.expect("LABEL").value
else: pragma = "invoke"
pragmaFunction = "pragma"+pragma.title()
for mod in pragma_modules:
if hasattr(mod, pragmaFunction):
getattr(mod, pragmaFunction)(ppt, line, result)
break
else:
Err.log("Unknown pragma "+pragma)
"Turn a line of source into an IR Node."
Err.currentpoint = ppt
result = []
line = ParseLine(lexemelist)
def aux():
"Accumulates all IR nodes defined by this line."
if line.lookahead(0).type == "EOL":
pass
elif line.lookahead(1).type == ":":
newlabel=line.expect("LABEL").value
line.expect(":")
result.append(IR.Node(ppt, "Label", newlabel, IR.PCExpr()))
aux()
elif line.lookahead(0).type == "*":
global templabelcount
templabelcount = templabelcount + 1
result.append(IR.Node(ppt, "Label", "*"+str(templabelcount), IR.PCExpr()))
line.expect("*")
aux()
elif line.lookahead(0).type == "." or line.lookahead(0).type == "`":
which = line.expect(".", "`").type
if (which == "."): pragma = line.expect("LABEL").value
else: pragma = "invoke"
pragmaFunction = "pragma"+pragma.title()
for mod in pragma_modules:
if hasattr(mod, pragmaFunction):
getattr(mod, pragmaFunction)(ppt, line, result)
break
else:
Err.log("Unknown pragma "+pragma)
else: # Instruction
opcode = line.expect("OPCODE").value
if line.lookahead(0).type == "#":
mode = "Immediate"
line.expect("#")
arg = parse_expr(line)
line.expect("EOL")
elif line.lookahead(0).type == "(":
line.expect("(")
arg = parse_expr(line)
if line.lookahead(0).type == ",":
mode = "PointerX"
line.expect(",")
line.expect("X")
line.expect(")")
line.expect("EOL")
else:
line.expect(")")
tok = line.expect(",", "EOL").type
if tok == "EOL":
mode = "Pointer"
else:
mode = "PointerY"
line.expect("Y")
line.expect("EOL")
elif line.lookahead(0).type == "EOL":
mode = "Implied"
arg = None
else:
arg = parse_expr(line)
tok = line.expect("EOL", ",").type
if tok == ",":
tok = line.expect("X", "Y").type
if tok == "X": mode = "MemoryX"
else: mode = "MemoryY"
line.expect("EOL")
else: mode = "Memory"
result.append(IR.Node(ppt, mode, opcode, arg))
aux()
result = [node for node in result if node is not IR.NullNode]
if len(result) == 0: return IR.NullNode
if len(result) == 1: return result[0]
return IR.SequenceNode(ppt, result)
else: # Instruction
opcode = line.expect("OPCODE").value
if line.lookahead(0).type == "#":
mode = "Immediate"
line.expect("#")
arg = parse_expr(line)
line.expect("EOL")
elif line.lookahead(0).type == "(":
line.expect("(")
arg = parse_expr(line)
if line.lookahead(0).type == ",":
mode = "PointerX"
line.expect(",")
line.expect("X")
line.expect(")")
line.expect("EOL")
else:
line.expect(")")
tok = line.expect(",", "EOL").type
if tok == "EOL":
mode = "Pointer"
else:
mode = "PointerY"
line.expect("Y")
line.expect("EOL")
elif line.lookahead(0).type == "EOL":
mode = "Implied"
arg = None
else:
arg = parse_expr(line)
tok = line.expect("EOL", ",").type
if tok == ",":
tok = line.expect("X", "Y").type
if tok == "X": mode = "MemoryX"
else: mode = "MemoryY"
line.expect("EOL")
else: mode = "Memory"
result.append(IR.Node(ppt, mode, opcode, arg))
aux()
result = [node for node in result if node is not IR.NullNode]
if len(result) == 0: return IR.NullNode
if len(result) == 1: return result[0]
return IR.SequenceNode(ppt, result)
def parse_file(ppt, filename):
"Loads a .P65 source file, and returns an IR list."
Err.currentpoint = ppt
if Cmd.verbose > 0: print "Loading "+filename
try:
f = file(filename)
linelist = f.readlines()
f.close()
pptlist = ["%s:%d" % (filename, i+1) for i in range(len(linelist))]
lexlist = map(lex, pptlist, linelist)
IRlist = map(parse_line, pptlist, lexlist)
IRlist = [node for node in IRlist if node is not IR.NullNode]
return IR.SequenceNode(ppt, IRlist)
except IOError:
Err.log ("Could not read "+filename)
return IR.NullNode
"Loads a .P65 source file, and returns an IR list."
Err.currentpoint = ppt
if Cmd.verbose > 0: print "Loading "+filename
try:
f = file(filename)
linelist = f.readlines()
f.close()
pptlist = ["%s:%d" % (filename, i+1) for i in range(len(linelist))]
lexlist = map(lex, pptlist, linelist)
IRlist = map(parse_line, pptlist, lexlist)
IRlist = [node for node in IRlist if node is not IR.NullNode]
return IR.SequenceNode(ppt, IRlist)
except IOError:
Err.log ("Could not read "+filename)
return IR.NullNode
def parse(filename):
"Top level parsing routine, taking a source file name and returning an IR list."
global templabelcount
templabelcount = 0
return parse_file("<Top Level>", filename)
"Top level parsing routine, taking a source file name and returning an IR list."
global templabelcount
templabelcount = 0
return parse_file("<Top Level>", filename)

View File

@ -1,161 +1,160 @@
"""P65 Intermediate Representation
"""Ophis Intermediate Representation
Classes for representing the Intermediate nodes upon which the
assembler passes operate."""
Classes for representing the Intermediate nodes upon which the
assembler passes operate."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
from __future__ import nested_scopes
import Ophis.Errors as Err
class Node:
"""The default IR Node
Instances of Node always have the three fields ppt(Program Point),
nodetype(a string), and data (a list)."""
def __init__(self, ppt, nodetype, *data):
self.ppt = ppt
self.nodetype = nodetype
self.data = list(data)
def accept(self, asmpass, env=None):
"""Implements the Visitor pattern for an assembler pass.
Calls the routine 'asmpass.visitTYPE(self, env)' where
TYPE is the value of self.nodetype."""
Err.currentpoint = self.ppt
routine = getattr(asmpass, "visit"+self.nodetype, asmpass.visitUnknown)
routine(self, env)
def __str__(self):
if self.nodetype != "SEQUENCE":
return str(self.ppt)+": "+self.nodetype+" - "+" ".join(map(str, self.data))
else:
return "\n".join(map(str, self.data))
def __repr__(self):
args = [self.ppt, self.nodetype] + self.data
return "Node(" + ", ".join(map(repr, args)) + ")"
"""The default IR Node
Instances of Node always have the three fields ppt(Program Point),
nodetype(a string), and data (a list)."""
def __init__(self, ppt, nodetype, *data):
self.ppt = ppt
self.nodetype = nodetype
self.data = list(data)
def accept(self, asmpass, env=None):
"""Implements the Visitor pattern for an assembler pass.
Calls the routine 'asmpass.visitTYPE(self, env)' where
TYPE is the value of self.nodetype."""
Err.currentpoint = self.ppt
routine = getattr(asmpass, "visit"+self.nodetype, asmpass.visitUnknown)
routine(self, env)
def __str__(self):
if self.nodetype != "SEQUENCE":
return str(self.ppt)+": "+self.nodetype+" - "+" ".join(map(str, self.data))
else:
return "\n".join(map(str, self.data))
def __repr__(self):
args = [self.ppt, self.nodetype] + self.data
return "Node(" + ", ".join(map(repr, args)) + ")"
NullNode = Node("<none>", "None")
def SequenceNode(ppt, nodelist):
return Node(ppt, "SEQUENCE", *nodelist)
return Node(ppt, "SEQUENCE", *nodelist)
class Expr:
"""Base class for P65 expressions
All expressions have a field called "data" and a boolean field
called "hardcoded". An expression is hardcoded if it has no
symbolic values in it."""
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return "<UNKNOWN: "+`self.data`+">"
def valid(self, env=None, PCvalid=0):
"""Returns true if the the expression can be successfully
evaluated in the specified environment."""
return 0
def value(self, env=None):
"Evaluates this expression in the given environment."
return None
"""Base class for P65 expressions
All expressions have a field called "data" and a boolean field
called "hardcoded". An expression is hardcoded if it has no
symbolic values in it."""
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return "<UNKNOWN: "+`self.data`+">"
def valid(self, env=None, PCvalid=0):
"""Returns true if the the expression can be successfully
evaluated in the specified environment."""
return 0
def value(self, env=None):
"Evaluates this expression in the given environment."
return None
class ConstantExpr(Expr):
"Represents a numeric constant"
def __init__(self, data):
self.data = data
self.hardcoded = 1
def __str__(self):
return str(self.data)
def valid(self, env=None, PCvalid=0):
return 1
def value(self, env=None):
return self.data
"Represents a numeric constant"
def __init__(self, data):
self.data = data
self.hardcoded = 1
def __str__(self):
return str(self.data)
def valid(self, env=None, PCvalid=0):
return 1
def value(self, env=None):
return self.data
class LabelExpr(Expr):
"Represents a symbolic constant"
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return self.data
def valid(self, env=None, PCvalid=0):
return (env is not None) and self.data in env
def value(self, env=None):
return env[self.data]
"Represents a symbolic constant"
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return self.data
def valid(self, env=None, PCvalid=0):
return (env is not None) and self.data in env
def value(self, env=None):
return env[self.data]
class PCExpr(Expr):
"Represents the current program counter: ^"
def __init__(self):
self.hardcoded = 0
def __str__(self):
return "^"
def valid(self, env=None, PCvalid=0):
return env is not None and PCvalid
def value(self, env=None):
return env.getPC()
"Represents the current program counter: ^"
def __init__(self):
self.hardcoded = 0
def __str__(self):
return "^"
def valid(self, env=None, PCvalid=0):
return env is not None and PCvalid
def value(self, env=None):
return env.getPC()
class HighByteExpr(Expr):
"Represents the expression >{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return ">"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return (val >> 8) & 0xff
"Represents the expression >{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return ">"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return (val >> 8) & 0xff
class LowByteExpr(Expr):
"Represents the expression <{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return "<"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return val & 0xff
"Represents the expression <{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return "<"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return val & 0xff
class SequenceExpr(Expr):
"""Represents an interleaving of operands (of type Expr) and
operators (of type String). Subclasses must provide a routine
operate(self, firstarg, op, secondarg) that evaluates the
operator."""
def __init__(self, data):
"""Constructor for Sequence Expressions. Results will be
screwy if the data inpot isn't a list with types
[Expr, str, Expr, str, Expr, str, ... Expr, str, Expr]."""
self.data = data
self.operands = [x for x in data if isinstance(x, Expr)]
self.operators = [x for x in data if type(x)==str]
for i in self.operands:
if not i.hardcoded:
self.hardcoded = 0
break
else:
self.hardcoded = 1
def __str__(self):
return "["+" ".join(map(str, self.data))+"]"
def valid(self, env=None, PCvalid=0):
for i in self.operands:
if not i.valid(env, PCvalid):
return 0
return 1
def value(self, env=None):
subs = map((lambda x: x.value(env)), self.operands)
result = subs[0]
index = 1
for op in self.operators:
result = self.operate(result, op, subs[index])
index += 1
return result
def operate(self, start, op, 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
if op=="|": return start | other
if op=="^": return start ^ other
"""Represents an interleaving of operands (of type Expr) and
operators (of type String). Subclasses must provide a routine
operate(self, firstarg, op, secondarg) that evaluates the
operator."""
def __init__(self, data):
"""Constructor for Sequence Expressions. Results will be
screwy if the data inpot isn't a list with types
[Expr, str, Expr, str, Expr, str, ... Expr, str, Expr]."""
self.data = data
self.operands = [x for x in data if isinstance(x, Expr)]
self.operators = [x for x in data if type(x)==str]
for i in self.operands:
if not i.hardcoded:
self.hardcoded = 0
break
else:
self.hardcoded = 1
def __str__(self):
return "["+" ".join(map(str, self.data))+"]"
def valid(self, env=None, PCvalid=0):
for i in self.operands:
if not i.valid(env, PCvalid):
return 0
return 1
def value(self, env=None):
subs = map((lambda x: x.value(env)), self.operands)
result = subs[0]
index = 1
for op in self.operators:
result = self.operate(result, op, subs[index])
index += 1
return result
def operate(self, start, op, 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
if op=="|": return start | other
if op=="^": return start ^ other

View File

@ -1,8 +1,12 @@
"""Macro support for P65.
"""Macro support for Ophis.
P65 Macros are cached SequenceNodes with arguments
set via .alias commands and prevented from escaping
with .scope and .scend commands."""
Ophis Macros are cached SequenceNodes with arguments
set via .alias commands and prevented from escaping
with .scope and .scend commands."""
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
import sys
@ -15,48 +19,48 @@ currentname = None
currentbody = None
def newMacro(name):
"Start creating a new macro with the specified name."
global currentname
global currentbody
global macros
if currentname is not None:
Err.log("Internal error! Nested macro attempt!")
else:
if name in macros:
Err.log("Duplicate macro definition '%s'" % name)
currentname = name
currentbody = []
"Start creating a new macro with the specified name."
global currentname
global currentbody
global macros
if currentname is not None:
Err.log("Internal error! Nested macro attempt!")
else:
if name in macros:
Err.log("Duplicate macro definition '%s'" % name)
currentname = name
currentbody = []
def registerNode(node):
global currentbody
currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data))
global currentbody
currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data))
def endMacro():
global currentname
global currentbody
global macros
if currentname is None:
Err.log("Internal error! Ended a non-existent macro!")
else:
macros[currentname] = currentbody
currentname = None
currentbody = None
global currentname
global currentbody
global macros
if currentname is None:
Err.log("Internal error! Ended a non-existent macro!")
else:
macros[currentname] = currentbody
currentname = None
currentbody = None
def expandMacro(ppt, name, arglist):
global macros
if name not in macros:
Err.log("Undefined macro '%s'" % name)
return IR.NullNode
argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg) for (i, arg) in zip(xrange(1, sys.maxint), arglist)]
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) 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)
global macros
if name not in macros:
Err.log("Undefined macro '%s'" % name)
return IR.NullNode
argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg) for (i, arg) in zip(xrange(1, sys.maxint), arglist)]
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) 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)
def dump():
global macros
for mac in macros:
body = macros[mac]
print "Macro: "+mac
for node in body: print node
print ""
global macros
for mac in macros:
body = macros[mac]
print "Macro: "+mac
for node in body: print node
print ""

View File

@ -1,13 +1,12 @@
"""Main controller routines for the P65 assembler.
"""Main controller routines for the Ophis assembler.
When invoked as main, interprets its command line and goes from there.
Otherwise, use run_all to interpret a file set."""
When invoked as main, interprets its command line and goes from there.
Otherwise, use run_all to interpret a file set."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
from __future__ import nested_scopes
import sys
import Ophis.Frontend
import Ophis.IR
@ -21,104 +20,104 @@ import Ophis.Opcodes
def usage():
"Prints a usage message and quits."
print "Usage:"
print "\tOphis [options] infile outfile"
print ""
print "Options:"
print "\t-6510 Allow 6510 undocumented opcodes"
print "\t-65c02 Enable 65c02 extensions"
print "\t-d Allow deprecated pragmas"
print "\t-v n Set verbosity to n (0-4, 1=default)"
sys.exit(1)
"Prints a usage message and quits."
print "Usage:"
print "\tOphis [options] infile outfile"
print ""
print "Options:"
print "\t-6510 Allow 6510 undocumented opcodes"
print "\t-65c02 Enable 65c02 extensions"
print "\t-d Allow deprecated pragmas"
print "\t-v n Set verbosity to n (0-4, 1=default)"
sys.exit(1)
def run_all(infile, outfile):
"Transforms the source infile to a binary outfile."
Err.count = 0
z = Ophis.Frontend.parse(infile)
env = Ophis.Environment.Environment()
"Transforms the source infile to a binary outfile."
Err.count = 0
z = Ophis.Frontend.parse(infile)
env = Ophis.Environment.Environment()
m = Ophis.Passes.ExpandMacros()
i = Ophis.Passes.InitLabels()
l_basic = Ophis.Passes.UpdateLabels()
l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0)
c = Ophis.Passes.Collapse()
a = Ophis.Passes.Assembler()
m = Ophis.Passes.ExpandMacros()
i = Ophis.Passes.InitLabels()
l_basic = Ophis.Passes.UpdateLabels()
l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0)
c = Ophis.Passes.Collapse()
a = Ophis.Passes.Assembler()
passes = []
passes.append(Ophis.Passes.DefineMacros())
passes.append(Ophis.Passes.FixPoint("macro expansion", [m], lambda: m.changed == 0))
passes.append(Ophis.Passes.FixPoint("label initialization", [i], lambda: i.changed == 0))
passes.extend([Ophis.Passes.CircularityCheck(), Ophis.Passes.CheckExprs(), Ophis.Passes.EasyModes()])
passes.append(Ophis.Passes.FixPoint("instruction selection", [l, c], lambda: c.collapsed == 0))
passes.extend([Ophis.Passes.NormalizeModes(), Ophis.Passes.UpdateLabels(), a])
passes = []
passes.append(Ophis.Passes.DefineMacros())
passes.append(Ophis.Passes.FixPoint("macro expansion", [m], lambda: m.changed == 0))
passes.append(Ophis.Passes.FixPoint("label initialization", [i], lambda: i.changed == 0))
passes.extend([Ophis.Passes.CircularityCheck(), Ophis.Passes.CheckExprs(), Ophis.Passes.EasyModes()])
passes.append(Ophis.Passes.FixPoint("instruction selection", [l, c], lambda: c.collapsed == 0))
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:
try:
output = file(outfile, 'wb')
output.write("".join(map(chr, a.output)))
except IOError:
print "Could not write to "+outfile
else:
Err.report()
if Err.count == 0:
try:
output = file(outfile, 'wb')
output.write("".join(map(chr, a.output)))
except IOError:
print "Could not write to "+outfile
else:
Err.report()
def run_ophis():
infile = None
outfile = None
infile = None
outfile = None
p65_compatibility_mode = 0
chip_extension = None
p65_compatibility_mode = 0
chip_extension = None
reading_arg = 0
reading_arg = 0
for x in sys.argv[1:]:
if reading_arg:
try:
Ophis.CmdLine.verbose = int(x)
reading_arg = 0
except ValueError:
print "FATAL: Non-integer passed as argument to -v"
usage()
elif x[0] == '-':
if x == '-v':
reading_arg = 1
elif x == '-6510':
chip_extension = Ophis.Opcodes.undocops
elif x == '-65c02':
chip_extension = Ophis.Opcodes.c02extensions
elif x == '-d':
p65_compatibility_mode = 1
else:
print "FATAL: Unknown option "+x
usage()
elif infile == None:
infile = x
elif outfile == None:
outfile = x
else:
print "FATAL: Too many files specified"
usage()
for x in sys.argv[1:]:
if reading_arg:
try:
Ophis.CmdLine.verbose = int(x)
reading_arg = 0
except ValueError:
print "FATAL: Non-integer passed as argument to -v"
usage()
elif x[0] == '-':
if x == '-v':
reading_arg = 1
elif x == '-6510':
chip_extension = Ophis.Opcodes.undocops
elif x == '-65c02':
chip_extension = Ophis.Opcodes.c02extensions
elif x == '-d':
p65_compatibility_mode = 1
else:
print "FATAL: Unknown option "+x
usage()
elif infile == None:
infile = x
elif outfile == None:
outfile = x
else:
print "FATAL: Too many files specified"
usage()
if infile is None:
print "FATAL: No files specified"
usage()
if infile is None:
print "FATAL: No files specified"
usage()
if outfile is None:
print "FATAL: No output file specified"
usage()
if outfile is None:
print "FATAL: No output file specified"
usage()
Ophis.Frontend.pragma_modules.append(Ophis.CorePragmas)
Ophis.Frontend.pragma_modules.append(Ophis.CorePragmas)
if p65_compatibility_mode:
Ophis.Frontend.pragma_modules.append(Ophis.OldPragmas)
if p65_compatibility_mode:
Ophis.Frontend.pragma_modules.append(Ophis.OldPragmas)
if chip_extension is not None:
Ophis.Opcodes.opcodes.update(chip_extension)
if chip_extension is not None:
Ophis.Opcodes.opcodes.update(chip_extension)
Ophis.CorePragmas.reset()
run_all(infile, outfile)
Ophis.CorePragmas.reset()
run_all(infile, outfile)
if __name__ == '__main__':
run_ophis()
run_ophis()

View File

@ -1,16 +1,16 @@
"""P65-Perl compatibility pragmas
Additional assembler directives to permit assembly of
old P65-Perl sources. This is not, in itself, sufficient,
as the precedence of < and > vs. + and - has changed
between P65-Perl and P65-Ophis.
Additional assembler directives to permit assembly of
old P65-Perl sources. This is not, in itself, sufficient,
as the precedence of < and > vs. + and - has changed
between P65-Perl and Ophis.
Supported pragmas are: .ascii (byte), .address (word),
.segment (text), .code (text), and .link."""
Supported pragmas are: .ascii (byte), .address (word),
.segment (text), .code (text), and .link."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
import Ophis.CorePragmas as core
@ -20,9 +20,9 @@ pragmaSegment = core.pragmaText
pragmaCode = core.pragmaText
def pragmaLink(ppt, line, result):
"Load a file in a precise memory location."
filename = line.expect("STRING").value
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "SetPC", newPC))
if type(filename)==str: result.append(FE.parse_file(ppt, filename))
"Load a file in a precise memory location."
filename = line.expect("STRING").value
newPC = FE.parse_expr(line)
line.expect("EOL")
result.append(IR.Node(ppt, "SetPC", newPC))
if type(filename)==str: result.append(FE.parse_file(ppt, filename))

View File

@ -1,28 +1,28 @@
"""Opcodes file.
Tables for the assembly of 6502-family instructions, mapping
opcodes and addressing modes to binary instructions."""
Tables for the assembly of 6502-family instructions, mapping
opcodes and addressing modes to binary instructions."""
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
# Names of addressing modes
modes = ["Implied", # 0
"Immediate", # 1
"Zero Page", # 2
"Zero Page, X", # 3
"Zero Page, Y", # 4
"Absolute", # 5
"Absolute, X", # 6
"Absolute, Y", # 7
"(Absolute)", # 8
"Immediate", # 1
"Zero Page", # 2
"Zero Page, X", # 3
"Zero Page, Y", # 4
"Absolute", # 5
"Absolute, X", # 6
"Absolute, Y", # 7
"(Absolute)", # 8
"(Absolute, X)", # 9
"(Absolute), Y", # 10
"(Zero Page)", # 11
"(Zero Page, X)", # 12
"(Zero Page), Y", # 13
"Relative"] # 14
"(Zero Page, X)", # 12
"(Zero Page), Y", # 13
"Relative"] # 14
# Lengths of the argument
lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1]

View File

@ -1,18 +1,16 @@
"""The P65 Assembler passes
"""The Ophis Assembler passes
P65's design philosophy is to build the IR once, then run a great
many assembler passes over the result. Thus, each pass does a
single, specialized job. When strung together, the full
translation occurs. This structure also makes the assembler
very extensible; additional analyses or optimizations may be
added as new subclasses of Pass."""
Ophis's design philosophy is to build the IR once, then run a great
many assembler passes over the result. Thus, each pass does a
single, specialized job. When strung together, the full
translation occurs. This structure also makes the assembler
very extensible; additional analyses or optimizations may be
added as new subclasses of Pass."""
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
from __future__ import nested_scopes
import Ophis.Errors as Err
import Ophis.IR as IR
import Ophis.Opcodes as Ops
@ -22,497 +20,497 @@ import Ophis.Macro as Macro
# The passes themselves
class Pass:
"""Superclass for all assembler passes. Automatically handles IR
types that modify the environent's structure, and by default
raises an error on anything else. Override visitUnknown in your
extension pass to produce a pass that accepts everything."""
name = "Default Pass"
def __init__(self):
self.writeOK = 1
def visitNone(self, node, env):
pass
def visitSEQUENCE(self, node, env):
Err.currentpoint = node.ppt
for n in node.data:
n.accept(self, env)
def visitDataSegment(self, node, env):
self.writeOK = 0
env.setsegment(node.data[0])
def visitTextSegment(self, node, env):
self.writeOK = 1
env.setsegment(node.data[0])
def visitScopeBegin(self, node, env):
env.newscope()
def visitScopeEnd(self, node, env):
env.endscope()
def visitUnknown(self, node, env):
Err.log("Internal error! "+self.name+" cannot understand node type "+node.nodetype)
def prePass(self):
pass
def postPass(self):
pass
def go(self, node, env):
"""Prepares the environment and runs this pass, possibly
printing debugging information."""
if Err.count == 0:
if Cmd.verbose > 1: print "Running: "+self.name
env.reset()
self.prePass()
node.accept(self, env)
self.postPass()
env.reset()
if Cmd.verbose > 3:
print "Current labels:"
print env
if Cmd.verbose > 2:
print "Current IR:"
print node
"""Superclass for all assembler passes. Automatically handles IR
types that modify the environent's structure, and by default
raises an error on anything else. Override visitUnknown in your
extension pass to produce a pass that accepts everything."""
name = "Default Pass"
def __init__(self):
self.writeOK = 1
def visitNone(self, node, env):
pass
def visitSEQUENCE(self, node, env):
Err.currentpoint = node.ppt
for n in node.data:
n.accept(self, env)
def visitDataSegment(self, node, env):
self.writeOK = 0
env.setsegment(node.data[0])
def visitTextSegment(self, node, env):
self.writeOK = 1
env.setsegment(node.data[0])
def visitScopeBegin(self, node, env):
env.newscope()
def visitScopeEnd(self, node, env):
env.endscope()
def visitUnknown(self, node, env):
Err.log("Internal error! "+self.name+" cannot understand node type "+node.nodetype)
def prePass(self):
pass
def postPass(self):
pass
def go(self, node, env):
"""Prepares the environment and runs this pass, possibly
printing debugging information."""
if Err.count == 0:
if Cmd.verbose > 1: print "Running: "+self.name
env.reset()
self.prePass()
node.accept(self, env)
self.postPass()
env.reset()
if Cmd.verbose > 3:
print "Current labels:"
print env
if Cmd.verbose > 2:
print "Current IR:"
print node
class FixPoint:
"""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."""
def __init__(self, name, passes, fixpoint):
self.name = name
self.passes = passes
self.fixpoint = fixpoint
def go(self, node, env):
"""Runs this FixPoint's passes, in order, until the fixpoint
is true. Always runs the passes at least once."""
for i in xrange(100):
if Err.count != 0: break
for p in self.passes:
p.go(node, env)
if Err.count != 0: break
if self.fixpoint(): break
if Cmd.verbose > 1: print "Fixpoint failed, looping back"
else:
Err.log("Can't make %s converge! Maybe there's a recursive dependency somewhere?" % self.name)
"""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."""
def __init__(self, name, passes, fixpoint):
self.name = name
self.passes = passes
self.fixpoint = fixpoint
def go(self, node, env):
"""Runs this FixPoint's passes, in order, until the fixpoint
is true. Always runs the passes at least once."""
for i in xrange(100):
if Err.count != 0: break
for p in self.passes:
p.go(node, env)
if Err.count != 0: break
if self.fixpoint(): break
if Cmd.verbose > 1: print "Fixpoint failed, looping back"
else:
Err.log("Can't make %s converge! Maybe there's a recursive dependency somewhere?" % self.name)
class DefineMacros(Pass):
"Extract macro definitions and remove them from the IR"
name = "Macro definition pass"
def prePass(self):
self.inDef = 0
self.nestedError = 0
def postPass(self):
if self.inDef:
Err.log("Unmatched .macro")
elif Cmd.verbose > 2:
print "Macro definitions:"
Macro.dump()
def visitMacroBegin(self, node, env):
if self.inDef:
Err.log("Nested macro definition")
self.nestedError = 1
else:
Macro.newMacro(node.data[0])
node.nodetype = "None"
node.data = []
self.inDef = 1
def visitMacroEnd(self, node, env):
if self.inDef:
Macro.endMacro()
node.nodetype = "None"
node.data = []
self.inDef = 0
elif not self.nestedError:
Err.log("Unmatched .macend")
def visitUnknown(self, node, env):
if self.inDef:
Macro.registerNode(node)
node.nodetype = "None"
node.data = []
"Extract macro definitions and remove them from the IR"
name = "Macro definition pass"
def prePass(self):
self.inDef = 0
self.nestedError = 0
def postPass(self):
if self.inDef:
Err.log("Unmatched .macro")
elif Cmd.verbose > 2:
print "Macro definitions:"
Macro.dump()
def visitMacroBegin(self, node, env):
if self.inDef:
Err.log("Nested macro definition")
self.nestedError = 1
else:
Macro.newMacro(node.data[0])
node.nodetype = "None"
node.data = []
self.inDef = 1
def visitMacroEnd(self, node, env):
if self.inDef:
Macro.endMacro()
node.nodetype = "None"
node.data = []
self.inDef = 0
elif not self.nestedError:
Err.log("Unmatched .macend")
def visitUnknown(self, node, env):
if self.inDef:
Macro.registerNode(node)
node.nodetype = "None"
node.data = []
class ExpandMacros(Pass):
"Replace macro invocations with the appropriate text"
name = "Macro expansion pass"
def prePass(self):
self.changed = 0
def visitMacroInvoke(self, node, env):
replacement = Macro.expandMacro(node.ppt, node.data[0], node.data[1:])
node.nodetype = replacement.nodetype
node.data = replacement.data
self.changed = 1
def visitUnknown(self, node, env):
pass
"Replace macro invocations with the appropriate text"
name = "Macro expansion pass"
def prePass(self):
self.changed = 0
def visitMacroInvoke(self, node, env):
replacement = Macro.expandMacro(node.ppt, node.data[0], node.data[1:])
node.nodetype = replacement.nodetype
node.data = replacement.data
self.changed = 1
def visitUnknown(self, node, env):
pass
class InitLabels(Pass):
"Finds all reachable labels"
name = "Label initialization pass"
def __init__(self):
Pass.__init__(self)
self.labelmap = {}
def prePass(self):
self.changed = 0
self.PCvalid = 1
def visitAdvance(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid)
def visitSetPC(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid)
def visitLabel(self, node, env):
(label, val) = node.data
fulllabel = "%d:%s" % (env.stack[0], label)
if fulllabel in self.labelmap and self.labelmap[fulllabel] is not node:
Err.log("Duplicate label definition '%s'" % label)
if fulllabel not in self.labelmap:
self.labelmap[fulllabel] = node
if val.valid(env, self.PCvalid) and label not in env:
env[label]=0
self.changed=1
def visitUnknown(self, node, env):
pass
"Finds all reachable labels"
name = "Label initialization pass"
def __init__(self):
Pass.__init__(self)
self.labelmap = {}
def prePass(self):
self.changed = 0
self.PCvalid = 1
def visitAdvance(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid)
def visitSetPC(self, node, env):
self.PCvalid=node.data[0].valid(env, self.PCvalid)
def visitLabel(self, node, env):
(label, val) = node.data
fulllabel = "%d:%s" % (env.stack[0], label)
if fulllabel in self.labelmap and self.labelmap[fulllabel] is not node:
Err.log("Duplicate label definition '%s'" % label)
if fulllabel not in self.labelmap:
self.labelmap[fulllabel] = node
if val.valid(env, self.PCvalid) and label not in env:
env[label]=0
self.changed=1
def visitUnknown(self, node, env):
pass
class CircularityCheck(Pass):
"Checks for circular label dependencies"
name = "Circularity check pass"
def prePass(self):
self.changed=0
self.PCvalid=1
def visitAdvance(self, node, env):
PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on .advance")
def visitSetPC(self, node, env):
PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on program counter set")
def visitCheckPC(self, node, env):
if not node.data[0].valid(env, self.PCvalid):
Err.log("Undefined or circular reference on program counter check")
def visitLabel(self, node, env):
(label, val) = node.data
if not val.valid(env, self.PCvalid):
Err.log("Undefined or circular dependency for label '%s'" % label)
def visitUnknown(self, node, env):
pass
"Checks for circular label dependencies"
name = "Circularity check pass"
def prePass(self):
self.changed=0
self.PCvalid=1
def visitAdvance(self, node, env):
PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on .advance")
def visitSetPC(self, node, env):
PCvalid = self.PCvalid
self.PCvalid=node.data[0].valid(env, self.PCvalid)
if not node.data[0].valid(env, PCvalid):
Err.log("Undefined or circular reference on program counter set")
def visitCheckPC(self, node, env):
if not node.data[0].valid(env, self.PCvalid):
Err.log("Undefined or circular reference on program counter check")
def visitLabel(self, node, env):
(label, val) = node.data
if not val.valid(env, self.PCvalid):
Err.log("Undefined or circular dependency for label '%s'" % label)
def visitUnknown(self, node, env):
pass
class CheckExprs(Pass):
"Ensures all expressions can resolve"
name = "Expression checking pass"
def visitUnknown(self, node, env):
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
"Ensures all expressions can resolve"
name = "Expression checking pass"
def visitUnknown(self, node, env):
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
class EasyModes(Pass):
"Assigns address modes to hardcoded and branch instructions"
name = "Easy addressing modes pass"
def visitMemory(self, node, env):
if Ops.opcodes[node.data[0]][14] is not None:
node.nodetype = "Relative"
return
if node.data[1].hardcoded:
if not collapse_no_index(node, env):
node.nodetype = "Absolute"
def visitMemoryX(self, node, env):
if node.data[1].hardcoded:
if not collapse_x(node, env):
node.nodetype = "AbsoluteX"
def visitMemoryY(self, node, env):
if node.data[1].hardcoded:
if not collapse_y(node, env):
node.nodetype = "AbsoluteY"
def visitPointer(self, node, env):
if node.data[1].hardcoded:
if not collapse_no_index_ind(node, env):
node.nodetype = "Indirect"
def visitPointerX(self, node, env):
if node.data[1].hardcoded:
if not collapse_x_ind(node, env):
node.nodetype = "AbsIndX"
def visitPointerY(self, node, env):
if node.data[1].hardcoded:
if not collapse_y_ind(node, env):
node.nodetype = "AbsIndY"
def visitUnknown(self, node, env):
pass
"Assigns address modes to hardcoded and branch instructions"
name = "Easy addressing modes pass"
def visitMemory(self, node, env):
if Ops.opcodes[node.data[0]][14] is not None:
node.nodetype = "Relative"
return
if node.data[1].hardcoded:
if not collapse_no_index(node, env):
node.nodetype = "Absolute"
def visitMemoryX(self, node, env):
if node.data[1].hardcoded:
if not collapse_x(node, env):
node.nodetype = "AbsoluteX"
def visitMemoryY(self, node, env):
if node.data[1].hardcoded:
if not collapse_y(node, env):
node.nodetype = "AbsoluteY"
def visitPointer(self, node, env):
if node.data[1].hardcoded:
if not collapse_no_index_ind(node, env):
node.nodetype = "Indirect"
def visitPointerX(self, node, env):
if node.data[1].hardcoded:
if not collapse_x_ind(node, env):
node.nodetype = "AbsIndX"
def visitPointerY(self, node, env):
if node.data[1].hardcoded:
if not collapse_y_ind(node, env):
node.nodetype = "AbsIndY"
def visitUnknown(self, node, env):
pass
class UpdateLabels(Pass):
"Computes the new values for all entries in the symbol table"
name = "Label Update Pass"
def prePass(self):
self.changed = 0
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 visitImplied(self, node, env): env.incPC(1)
def visitImmediate(self, node, env): env.incPC(2)
def visitIndirectX(self, node, env): env.incPC(2)
def visitIndirectY(self, node, env): env.incPC(2)
def visitZPIndirect(self, node, env): env.incPC(2)
def visitZeroPage(self, node, env): env.incPC(2)
def visitZeroPageX(self, node, env): env.incPC(2)
def visitZeroPageY(self, node, env): 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):
(label, val) = node.data
old = env[label]
env[label] = val.value(env)
if old != env[label]:
self.changed = 1
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)
"Computes the new values for all entries in the symbol table"
name = "Label Update Pass"
def prePass(self):
self.changed = 0
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 visitImplied(self, node, env): env.incPC(1)
def visitImmediate(self, node, env): env.incPC(2)
def visitIndirectX(self, node, env): env.incPC(2)
def visitIndirectY(self, node, env): env.incPC(2)
def visitZPIndirect(self, node, env): env.incPC(2)
def visitZeroPage(self, node, env): env.incPC(2)
def visitZeroPageX(self, node, env): env.incPC(2)
def visitZeroPageY(self, node, env): 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):
(label, val) = node.data
old = env[label]
env[label] = val.value(env)
if old != env[label]:
self.changed = 1
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 Collapse(Pass):
"""Selects as many zero-page instructions to convert as
possible, and tracks how many instructions have been
converted this pass."""
name = "Instruction Collapse Pass"
def prePass(self):
self.collapsed = 0
def visitMemory(self, node, env):
if collapse_no_index(node, env): self.collapsed += 1
def visitMemoryX(self, node, env):
if collapse_x(node, env): self.collapsed += 1
def visitMemoryY(self, node, env):
if collapse_y(node, env): self.collapsed += 1
def visitPointer(self, node, env):
if collapse_no_index_ind(node, env): self.collapsed += 1
def visitPointerX(self, node, env):
if collapse_x_ind(node, env): self.collapsed += 1
def visitPointerY(self, node, env):
if collapse_y_ind(node, env): self.collapsed += 1
def visitUnknown(self, node, env):
pass
"""Selects as many zero-page instructions to convert as
possible, and tracks how many instructions have been
converted this pass."""
name = "Instruction Collapse Pass"
def prePass(self):
self.collapsed = 0
def visitMemory(self, node, env):
if collapse_no_index(node, env): self.collapsed += 1
def visitMemoryX(self, node, env):
if collapse_x(node, env): self.collapsed += 1
def visitMemoryY(self, node, env):
if collapse_y(node, env): self.collapsed += 1
def visitPointer(self, node, env):
if collapse_no_index_ind(node, env): self.collapsed += 1
def visitPointerX(self, node, env):
if collapse_x_ind(node, env): self.collapsed += 1
def visitPointerY(self, node, env):
if collapse_y_ind(node, env): self.collapsed += 1
def visitUnknown(self, node, env):
pass
def collapse_no_index(node, env):
"""Transforms a Memory node into a ZeroPage one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][2] is not None:
node.nodetype = "ZeroPage"
return 1
else:
return 0
"""Transforms a Memory node into a ZeroPage one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][2] is not None:
node.nodetype = "ZeroPage"
return 1
else:
return 0
def collapse_x(node, env):
"""Transforms a MemoryX node into a ZeroPageX one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][3] is not None:
node.nodetype = "ZeroPageX"
return 1
else:
return 0
"""Transforms a MemoryX node into a ZeroPageX one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][3] is not None:
node.nodetype = "ZeroPageX"
return 1
else:
return 0
def collapse_y(node, env):
"""Transforms a MemoryY node into a ZeroPageY one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][4] is not None:
node.nodetype = "ZeroPageY"
return 1
else:
return 0
"""Transforms a MemoryY node into a ZeroPageY one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][4] is not None:
node.nodetype = "ZeroPageY"
return 1
else:
return 0
def collapse_no_index_ind(node, env):
"""Transforms a Pointer node into a ZPIndirect one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][11] is not None:
node.nodetype = "ZPIndirect"
return 1
else:
return 0
"""Transforms a Pointer node into a ZPIndirect one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][11] is not None:
node.nodetype = "ZPIndirect"
return 1
else:
return 0
def collapse_x_ind(node, env):
"""Transforms a PointerX node into an IndirectX one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][12] is not None:
node.nodetype = "IndirectX"
return 1
else:
return 0
"""Transforms a PointerX node into an IndirectX one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][12] is not None:
node.nodetype = "IndirectX"
return 1
else:
return 0
def collapse_y_ind(node, env):
"""Transforms a PointerY node into an IndirectY one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][13] is not None:
node.nodetype = "IndirectY"
return 1
else:
return 0
"""Transforms a PointerY node into an IndirectY one if possible.
Returns 1 if it made the collapse, false otherwise."""
if node.data[1].value(env) < 0x100 and Ops.opcodes[node.data[0]][13] is not None:
node.nodetype = "IndirectY"
return 1
else:
return 0
class NormalizeModes(Pass):
"""Eliminates the intermediate "Memory" and "Pointer" nodes,
converting them to "Absolute"."""
name = "Mode Normalization pass"
def visitMemory(self, node, env): node.nodetype = "Absolute"
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.
def visitPointerY(self, node, env): node.nodetype = "AbsIndY"
def visitUnknown(self, node, env): pass
"""Eliminates the intermediate "Memory" and "Pointer" nodes,
converting them to "Absolute"."""
name = "Mode Normalization pass"
def visitMemory(self, node, env): node.nodetype = "Absolute"
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.
def visitPointerY(self, node, env): node.nodetype = "AbsIndY"
def visitUnknown(self, node, env): pass
class Assembler(Pass):
"""Converts the IR into a list of bytes, suitable for writing to
a file."""
name = "Assembler"
"""Converts the IR into a list of bytes, suitable for writing to
a file."""
name = "Assembler"
def prePass(self):
self.output = []
self.code = 0
self.data = 0
self.filler = 0
def prePass(self):
self.output = []
self.code = 0
self.data = 0
self.filler = 0
def postPass(self):
if Cmd.verbose > 0 and Err.count == 0:
print "Assembly complete: %s bytes output (%s code, %s data, %s filler)" \
% (len(self.output), self.code, self.data, self.filler)
def postPass(self):
if Cmd.verbose > 0 and Err.count == 0:
print "Assembly complete: %s bytes output (%s code, %s data, %s filler)" \
% (len(self.output), self.code, self.data, self.filler)
def outputbyte(self, expr, env):
'Outputs a byte, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00 or val > 0xff:
Err.log("Byte constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val))
else:
Err.log("Attempt to write to data segment")
def outputword(self, expr, env):
'Outputs a little-endian word, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputdword(self, expr, env):
'Outputs a little-endian dword, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL:
Err.log("DWord constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int((val >> 16) & 0xFF))
self.output.append(int((val >> 24) & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputbyte(self, expr, env):
'Outputs a byte, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00 or val > 0xff:
Err.log("Byte constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val))
else:
Err.log("Attempt to write to data segment")
def outputword(self, expr, env):
'Outputs a little-endian word, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputdword(self, expr, env):
'Outputs a little-endian dword, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL:
Err.log("DWord constant "+str(expr)+" out of range")
val = 0
self.output.append(int(val & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int((val >> 16) & 0xFF))
self.output.append(int((val >> 24) & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputword_be(self, expr, env):
'Outputs a big-endian word, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range")
val = 0
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int(val & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputdword_be(self, expr, env):
'Outputs a big-endian dword, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL:
Err.log("DWord constant "+str(expr)+" out of range")
val = 0
self.output.append(int((val >> 24) & 0xFF))
self.output.append(int((val >> 16) & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int(val & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputword_be(self, expr, env):
'Outputs a big-endian word, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x0000 or val > 0xFFFF:
Err.log("Word constant "+str(expr)+" out of range")
val = 0
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int(val & 0xFF))
else:
Err.log("Attempt to write to data segment")
def outputdword_be(self, expr, env):
'Outputs a big-endian dword, with range checking'
if self.writeOK:
val = expr.value(env)
if val < 0x00000000 or val > 0xFFFFFFFFL:
Err.log("DWord constant "+str(expr)+" out of range")
val = 0
self.output.append(int((val >> 24) & 0xFF))
self.output.append(int((val >> 16) & 0xFF))
self.output.append(int((val >> 8) & 0xFF))
self.output.append(int(val & 0xFF))
else:
Err.log("Attempt to write to data segment")
def assemble(self, node, mode, env):
"A generic instruction called by the visitor methods themselves"
(opcode, expr) = node.data
bin_op = Ops.opcodes[opcode][mode]
if bin_op is None:
Err.log('%s does not have mode "%s"' % (opcode.upper(), Ops.modes[mode]))
return
self.outputbyte(IR.ConstantExpr(bin_op), env)
arglen = Ops.lengths[mode]
if mode == 14: # Special handling for relative mode
arg = expr.value(env)
arg = arg-(env.getPC()+2)
if arg < -128 or arg > 127:
Err.log("Branch target out of bounds")
arg = 0
if arg < 0: arg += 256
expr = IR.ConstantExpr(arg)
if arglen == 1: self.outputbyte(expr, env)
if arglen == 2: self.outputword(expr, env)
env.incPC(1+arglen)
self.code += 1+arglen
def assemble(self, node, mode, env):
"A generic instruction called by the visitor methods themselves"
(opcode, expr) = node.data
bin_op = Ops.opcodes[opcode][mode]
if bin_op is None:
Err.log('%s does not have mode "%s"' % (opcode.upper(), Ops.modes[mode]))
return
self.outputbyte(IR.ConstantExpr(bin_op), env)
arglen = Ops.lengths[mode]
if mode == 14: # Special handling for relative mode
arg = expr.value(env)
arg = arg-(env.getPC()+2)
if arg < -128 or arg > 127:
Err.log("Branch target out of bounds")
arg = 0
if arg < 0: arg += 256
expr = IR.ConstantExpr(arg)
if arglen == 1: self.outputbyte(expr, env)
if arglen == 2: 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 visitByte(self, node, env):
for expr in node.data:
self.outputbyte(expr, env)
env.incPC(len(node.data))
self.data += len(node.data)
def visitWord(self, node, env):
for expr in node.data:
self.outputword(expr, env)
env.incPC(len(node.data)*2)
self.data += len(node.data)*2
def visitDword(self, node, env):
for expr in node.data:
self.outputdword(expr, env)
env.incPC(len(node.data)*4)
self.data += len(node.data)*4
def visitWordBE(self, node, env):
for expr in node.data:
self.outputword_be(expr, env)
env.incPC(len(node.data)*2)
self.data += len(node.data)*2
def visitDwordBE(self, node, env):
for expr in node.data:
self.outputdword_be(expr, env)
env.incPC(len(node.data)*4)
self.data += len(node.data)*4
def visitSetPC(self, node, env):
env.setPC(node.data[0].value(env))
def visitCheckPC(self, node, env):
pc = env.getPC()
target = node.data[0].value(env)
if (pc > target):
Err.log(".checkpc assertion failed: $%x > $%x" % (pc, target))
def visitAdvance(self, node, env):
pc = env.getPC()
target = node.data[0].value(env)
if (pc > target):
Err.log("Attempted to .advance backwards: $%x to $%x" % (pc, target))
else:
zero = IR.ConstantExpr(0)
for i in xrange(target-pc): self.outputbyte(zero, env)
self.filler += target-pc
env.setPC(target)
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):
for expr in node.data:
self.outputbyte(expr, env)
env.incPC(len(node.data))
self.data += len(node.data)
def visitWord(self, node, env):
for expr in node.data:
self.outputword(expr, env)
env.incPC(len(node.data)*2)
self.data += len(node.data)*2
def visitDword(self, node, env):
for expr in node.data:
self.outputdword(expr, env)
env.incPC(len(node.data)*4)
self.data += len(node.data)*4
def visitWordBE(self, node, env):
for expr in node.data:
self.outputword_be(expr, env)
env.incPC(len(node.data)*2)
self.data += len(node.data)*2
def visitDwordBE(self, node, env):
for expr in node.data:
self.outputdword_be(expr, env)
env.incPC(len(node.data)*4)
self.data += len(node.data)*4
def visitSetPC(self, node, env):
env.setPC(node.data[0].value(env))
def visitCheckPC(self, node, env):
pc = env.getPC()
target = node.data[0].value(env)
if (pc > target):
Err.log(".checkpc assertion failed: $%x > $%x" % (pc, target))
def visitAdvance(self, node, env):
pc = env.getPC()
target = node.data[0].value(env)
if (pc > target):
Err.log("Attempted to .advance backwards: $%x to $%x" % (pc, target))
else:
zero = IR.ConstantExpr(0)
for i in xrange(target-pc): self.outputbyte(zero, env)
self.filler += target-pc
env.setPC(target)

View File

@ -1,5 +1,5 @@
"P65 - a cross-assembler for the 6502 series of chips"
"Ophis - a cross-assembler for the 6502 series of chips"
# Copyright 2002 Michael C. Martin.
# You may use, modify, and distribute this file under the BSD
# license: See LICENSE.txt for details.
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.