2012-05-07 03:06:28 +00:00
|
|
|
"""The Ophis Assembler passes
|
|
|
|
|
|
|
|
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."""
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2024-07-20 04:06:02 +00:00
|
|
|
# Copyright 2002-2024 Michael C. Martin and additional contributors.
|
2012-05-07 03:06:28 +00:00
|
|
|
# You may use, modify, and distribute this file under the MIT
|
|
|
|
# license: See README for details.
|
|
|
|
|
2012-05-29 02:19:08 +00:00
|
|
|
import sys
|
2012-05-07 03:06:28 +00:00
|
|
|
import Ophis.Errors as Err
|
|
|
|
import Ophis.IR as IR
|
|
|
|
import Ophis.Opcodes as Ops
|
|
|
|
import Ophis.CmdLine as Cmd
|
2013-03-25 01:26:48 +00:00
|
|
|
import Ophis.Listing as Listing
|
2012-05-07 03:06:28 +00:00
|
|
|
import Ophis.Macro as Macro
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-06-01 07:24:51 +00:00
|
|
|
class Pass(object):
|
2012-05-07 03:06:28 +00:00
|
|
|
"""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"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def __init__(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.writeOK = True
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitNone(self, node, env):
|
|
|
|
pass
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitSEQUENCE(self, node, env):
|
|
|
|
Err.currentpoint = node.ppt
|
|
|
|
for n in node.data:
|
|
|
|
n.accept(self, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitDataSegment(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.writeOK = False
|
2012-05-07 03:06:28 +00:00
|
|
|
env.setsegment(node.data[0])
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitTextSegment(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.writeOK = True
|
2012-05-07 03:06:28 +00:00
|
|
|
env.setsegment(node.data[0])
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitScopeBegin(self, node, env):
|
|
|
|
env.newscope()
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitScopeEnd(self, node, env):
|
|
|
|
env.endscope()
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
Err.log("Internal error! " + self.name +
|
|
|
|
" cannot understand node type " + node.nodetype)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
|
|
|
pass
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def postPass(self):
|
|
|
|
pass
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def go(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
"""Prepares the environment and runs this pass, possibly
|
2012-05-07 03:06:28 +00:00
|
|
|
printing debugging information."""
|
|
|
|
if Err.count == 0:
|
2012-06-01 17:25:48 +00:00
|
|
|
if Cmd.print_pass:
|
2019-01-10 04:45:01 +00:00
|
|
|
print("Running: " + self.name, file=sys.stderr)
|
2012-05-07 03:06:28 +00:00
|
|
|
env.reset()
|
|
|
|
self.prePass()
|
|
|
|
node.accept(self, env)
|
|
|
|
self.postPass()
|
|
|
|
env.reset()
|
2012-05-31 05:18:45 +00:00
|
|
|
if Cmd.print_labels:
|
2019-01-10 04:45:01 +00:00
|
|
|
print("Current labels:", file=sys.stderr)
|
|
|
|
print(env, file=sys.stderr)
|
2012-05-31 05:18:45 +00:00
|
|
|
if Cmd.print_ir:
|
2019-01-10 04:45:01 +00:00
|
|
|
print("Current IR:", file=sys.stderr)
|
|
|
|
print(node, file=sys.stderr)
|
2012-05-07 03:06:28 +00:00
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-06-01 07:24:51 +00:00
|
|
|
class FixPoint(object):
|
2012-05-07 03:06:28 +00:00
|
|
|
"""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
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def go(self, node, env):
|
|
|
|
"""Runs this FixPoint's passes, in order, until the fixpoint
|
|
|
|
is true. Always runs the passes at least once."""
|
2019-01-10 04:45:01 +00:00
|
|
|
for i in range(100):
|
2012-06-01 17:25:48 +00:00
|
|
|
if Err.count != 0:
|
|
|
|
break
|
2012-05-07 03:06:28 +00:00
|
|
|
for p in self.passes:
|
|
|
|
p.go(node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
if Err.count != 0:
|
|
|
|
break
|
|
|
|
if self.fixpoint():
|
|
|
|
break
|
|
|
|
if Cmd.print_pass:
|
2019-01-10 04:45:01 +00:00
|
|
|
print("Fixpoint failed, looping back", file=sys.stderr)
|
2012-05-07 03:06:28 +00:00
|
|
|
else:
|
2012-06-01 17:25:48 +00:00
|
|
|
Err.log("Can't make %s converge! Maybe there's a recursive "
|
|
|
|
"dependency somewhere?" % self.name)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
class DefineMacros(Pass):
|
|
|
|
"Extract macro definitions and remove them from the IR"
|
|
|
|
name = "Macro definition pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.inDef = False
|
|
|
|
self.nestedError = False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def postPass(self):
|
|
|
|
if self.inDef:
|
|
|
|
Err.log("Unmatched .macro")
|
2012-05-31 05:18:45 +00:00
|
|
|
elif Cmd.print_ir:
|
2019-01-10 04:45:01 +00:00
|
|
|
print("Macro definitions:", file=sys.stderr)
|
2012-05-07 03:06:28 +00:00
|
|
|
Macro.dump()
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMacroBegin(self, node, env):
|
|
|
|
if self.inDef:
|
|
|
|
Err.log("Nested macro definition")
|
2012-06-01 17:25:48 +00:00
|
|
|
self.nestedError = True
|
2012-05-07 03:06:28 +00:00
|
|
|
else:
|
|
|
|
Macro.newMacro(node.data[0])
|
|
|
|
node.nodetype = "None"
|
|
|
|
node.data = []
|
2012-06-01 17:25:48 +00:00
|
|
|
self.inDef = True
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMacroEnd(self, node, env):
|
|
|
|
if self.inDef:
|
|
|
|
Macro.endMacro()
|
|
|
|
node.nodetype = "None"
|
|
|
|
node.data = []
|
2012-06-01 17:25:48 +00:00
|
|
|
self.inDef = False
|
2012-05-07 03:06:28 +00:00
|
|
|
elif not self.nestedError:
|
|
|
|
Err.log("Unmatched .macend")
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
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"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMacroInvoke(self, node, env):
|
|
|
|
replacement = Macro.expandMacro(node.ppt, node.data[0], node.data[1:])
|
|
|
|
node.nodetype = replacement.nodetype
|
|
|
|
node.data = replacement.data
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = True
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
|
|
|
pass
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
class InitLabels(Pass):
|
|
|
|
"Finds all reachable labels"
|
|
|
|
name = "Label initialization pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def __init__(self):
|
|
|
|
Pass.__init__(self)
|
|
|
|
self.labelmap = {}
|
2012-06-12 13:27:11 +00:00
|
|
|
self.runcount = 0
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = False
|
|
|
|
self.PCvalid = True
|
2012-06-12 13:27:11 +00:00
|
|
|
self.runcount += 1
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitAdvance(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.PCvalid = node.data[0].valid(env, self.PCvalid)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitSetPC(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.PCvalid = node.data[0].valid(env, self.PCvalid)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitLabel(self, node, env):
|
|
|
|
(label, val) = node.data
|
|
|
|
fulllabel = "%d:%s" % (env.stack[0], label)
|
2012-06-01 17:25:48 +00:00
|
|
|
if fulllabel in self.labelmap and self.labelmap[fulllabel] is not node:
|
2012-05-07 03:06:28 +00:00
|
|
|
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:
|
2012-06-01 17:25:48 +00:00
|
|
|
env[label] = 0
|
|
|
|
self.changed = True
|
2012-06-12 13:27:11 +00:00
|
|
|
if label in ['a', 'x', 'y'] and self.runcount == 1:
|
2019-01-10 04:45:01 +00:00
|
|
|
print(str(node.ppt) + ": WARNING: " \
|
|
|
|
"using register name as label", file=sys.stderr)
|
2012-06-12 13:27:11 +00:00
|
|
|
if label in Ops.opcodes and self.runcount == 1:
|
2019-01-10 04:45:01 +00:00
|
|
|
print(str(node.ppt) + ": WARNING: " \
|
|
|
|
"using opcode name as label", file=sys.stderr)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
|
|
|
pass
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
class CircularityCheck(Pass):
|
|
|
|
"Checks for circular label dependencies"
|
|
|
|
name = "Circularity check pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = False
|
|
|
|
self.PCvalid = True
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitAdvance(self, node, env):
|
|
|
|
PCvalid = self.PCvalid
|
2012-06-01 17:25:48 +00:00
|
|
|
self.PCvalid = node.data[0].valid(env, self.PCvalid)
|
2012-05-07 03:06:28 +00:00
|
|
|
if not node.data[0].valid(env, PCvalid):
|
|
|
|
Err.log("Undefined or circular reference on .advance")
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitSetPC(self, node, env):
|
|
|
|
PCvalid = self.PCvalid
|
2012-06-01 17:25:48 +00:00
|
|
|
self.PCvalid = node.data[0].valid(env, self.PCvalid)
|
2012-05-07 03:06:28 +00:00
|
|
|
if not node.data[0].valid(env, PCvalid):
|
|
|
|
Err.log("Undefined or circular reference on program counter set")
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitCheckPC(self, node, env):
|
|
|
|
if not node.data[0].valid(env, self.PCvalid):
|
|
|
|
Err.log("Undefined or circular reference on program counter check")
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitLabel(self, node, env):
|
|
|
|
(label, val) = node.data
|
2012-06-01 17:25:48 +00:00
|
|
|
if not val.valid(env, self.PCvalid):
|
2012-05-07 03:06:28 +00:00
|
|
|
Err.log("Undefined or circular dependency for label '%s'" % label)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
|
|
|
pass
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
class CheckExprs(Pass):
|
|
|
|
"Ensures all expressions can resolve"
|
|
|
|
name = "Expression checking pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
# Throw away result, just confirm validity of all expressions
|
2012-05-07 03:06:28 +00:00
|
|
|
for i in [x for x in node.data if isinstance(x, IR.Expr)]:
|
2012-06-01 17:25:48 +00:00
|
|
|
i.value(env)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
class EasyModes(Pass):
|
|
|
|
"Assigns address modes to hardcoded and branch instructions"
|
|
|
|
name = "Easy addressing modes pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMemory(self, node, env):
|
2014-02-07 09:52:06 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Relative")] is not None:
|
2012-05-07 03:06:28 +00:00
|
|
|
node.nodetype = "Relative"
|
|
|
|
return
|
2014-02-07 09:52:06 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("RelativeLong")] is not None:
|
|
|
|
node.nodetype = "RelativeLong"
|
|
|
|
return
|
2012-06-01 17:25:48 +00:00
|
|
|
if node.data[1].hardcoded:
|
2012-05-07 03:06:28 +00:00
|
|
|
if not collapse_no_index(node, env):
|
|
|
|
node.nodetype = "Absolute"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
|
|
|
def visitMemoryX(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
2012-05-07 03:06:28 +00:00
|
|
|
if not collapse_x(node, env):
|
|
|
|
node.nodetype = "AbsoluteX"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
|
|
|
def visitMemoryY(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
2012-05-07 03:06:28 +00:00
|
|
|
if not collapse_y(node, env):
|
|
|
|
node.nodetype = "AbsoluteY"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2013-01-28 01:09:56 +00:00
|
|
|
def visitMemory2(self, node, env):
|
|
|
|
node.nodetype = "ZPRelative"
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointer(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
|
|
|
if not collapse_no_index_ind(node, env):
|
|
|
|
node.nodetype = "Indirect"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointerX(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
|
|
|
if not collapse_x_ind(node, env):
|
|
|
|
node.nodetype = "AbsIndX"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointerY(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
|
|
|
if not collapse_y_ind(node, env):
|
|
|
|
node.nodetype = "AbsIndY"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2014-02-07 10:22:11 +00:00
|
|
|
def visitPointerSPY(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
|
|
|
if not collapse_spy_ind(node, env):
|
|
|
|
node.nodetype = "AbsIndSPY"
|
|
|
|
|
|
|
|
def visitPointerZ(self, node, env):
|
|
|
|
if node.data[1].hardcoded:
|
|
|
|
if not collapse_z_ind(node, env):
|
|
|
|
node.nodetype = "AbsIndZ"
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitUnknown(self, node, env):
|
|
|
|
pass
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-25 01:12:35 +00:00
|
|
|
class PCTracker(Pass):
|
|
|
|
"Superclass for passes that need an accurate program counter."
|
|
|
|
name = "**BUG** PC Tracker Superpass used directly"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2014-02-07 16:21:42 +00:00
|
|
|
def visitImmediateLong(self, node, env):
|
2014-02-07 16:25:26 +00:00
|
|
|
env.incPC(3)
|
2014-02-07 16:21:42 +00:00
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
def visitIndirectX(self, node, env):
|
|
|
|
env.incPC(2)
|
|
|
|
|
|
|
|
def visitIndirectY(self, node, env):
|
|
|
|
env.incPC(2)
|
|
|
|
|
2014-02-07 09:52:06 +00:00
|
|
|
def visitIndirectSPY(self, node, env):
|
|
|
|
env.incPC(2)
|
|
|
|
|
|
|
|
def visitIndirectZ(self, node, env):
|
|
|
|
env.incPC(2)
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
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)
|
|
|
|
|
2014-02-07 09:52:06 +00:00
|
|
|
def visitRelativeLong(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
2013-01-28 01:09:56 +00:00
|
|
|
def visitZPRelative(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
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)
|
|
|
|
|
2014-02-07 16:21:42 +00:00
|
|
|
def visitAbsIndZ(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
def visitMemory(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
|
|
|
def visitMemoryX(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
|
|
|
def visitMemoryY(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
2014-02-07 10:22:11 +00:00
|
|
|
def visitMemoryZ(self, node, env):
|
|
|
|
env.incPC(3)
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
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))
|
|
|
|
|
2012-06-04 06:50:29 +00:00
|
|
|
def visitByteRange(self, node, env):
|
|
|
|
if node.data[1].valid(env):
|
|
|
|
env.incPC(node.data[1].value(env))
|
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
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)
|
|
|
|
|
2012-05-25 01:12:35 +00:00
|
|
|
|
|
|
|
class UpdateLabels(PCTracker):
|
|
|
|
"Computes the new values for all entries in the symbol table"
|
|
|
|
name = "Label Update Pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-25 01:12:35 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitLabel(self, node, env):
|
|
|
|
(label, val) = node.data
|
|
|
|
old = env[label]
|
|
|
|
env[label] = val.value(env)
|
|
|
|
if old != env[label]:
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = True
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
2012-06-09 05:21:50 +00:00
|
|
|
class Collapse(PCTracker):
|
2012-06-01 17:25:48 +00:00
|
|
|
"Selects as many zero-page instructions to convert as possible."
|
2012-05-07 03:06:28 +00:00
|
|
|
name = "Instruction Collapse Pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def prePass(self):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed = False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMemory(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_no_index(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitMemory(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMemoryX(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_x(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitMemoryX(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitMemoryY(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_y(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitMemoryY(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2014-02-07 10:22:11 +00:00
|
|
|
def visitMemoryZ(self, node, env):
|
|
|
|
PCTracker.visitMemoryZ(self, node, env)
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointer(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_no_index_ind(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitPointer(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointerX(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_x_ind(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitPointerX(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def visitPointerY(self, node, env):
|
2012-06-01 17:25:48 +00:00
|
|
|
self.changed |= collapse_y_ind(node, env)
|
2012-06-09 05:21:50 +00:00
|
|
|
PCTracker.visitPointerY(self, node, env)
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-06-09 05:21:50 +00:00
|
|
|
# Previously zero-paged elements may end up un-zero-paged by
|
|
|
|
# the branch extension pass. Force them to Absolute equivalents
|
|
|
|
# if this happens.
|
|
|
|
|
2014-02-07 16:25:26 +00:00
|
|
|
def visitImmediate(self, node, env):
|
|
|
|
if node.data[1].value(env) >= 0x100:
|
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("ImmediateLong")] is not None:
|
|
|
|
node.nodetype = "ImmediateLong"
|
|
|
|
PCTracker.visitImmediateLong(self, node, env)
|
|
|
|
self.changed = True
|
|
|
|
return
|
|
|
|
PCTracker.visitImmediate(self, node, env)
|
|
|
|
|
2012-06-09 05:21:50 +00:00
|
|
|
def visitZeroPage(self, node, env):
|
|
|
|
if node.data[1].value(env) >= 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Absolute")] is not None:
|
2012-06-09 05:21:50 +00:00
|
|
|
node.nodetype = "Absolute"
|
|
|
|
PCTracker.visitAbsolute(self, node, env)
|
|
|
|
self.changed = True
|
|
|
|
return
|
|
|
|
PCTracker.visitZeroPage(self, node, env)
|
|
|
|
|
|
|
|
def visitZeroPageX(self, node, env):
|
|
|
|
if node.data[1].value(env) >= 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Absolute, X")] is not None:
|
2012-06-09 05:21:50 +00:00
|
|
|
node.nodetype = "AbsoluteX"
|
|
|
|
PCTracker.visitAbsoluteX(self, node, env)
|
|
|
|
self.changed = True
|
|
|
|
return
|
|
|
|
PCTracker.visitZeroPageX(self, node, env)
|
|
|
|
|
|
|
|
def visitZeroPageY(self, node, env):
|
|
|
|
if node.data[1].value(env) >= 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Absolute, Y")] is not None:
|
2012-06-09 05:21:50 +00:00
|
|
|
node.nodetype = "AbsoluteY"
|
|
|
|
PCTracker.visitAbsoluteY(self, node, env)
|
|
|
|
self.changed = True
|
|
|
|
return
|
|
|
|
PCTracker.visitZeroPageY(self, node, env)
|
2012-05-07 03:06:28 +00:00
|
|
|
|
2012-06-01 17:25:48 +00:00
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def collapse_no_index(node, env):
|
|
|
|
"""Transforms a Memory node into a ZeroPage one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Zero Page")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "ZeroPage"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
def collapse_x(node, env):
|
|
|
|
"""Transforms a MemoryX node into a ZeroPageX one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Zero Page, X")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "ZeroPageX"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
def collapse_y(node, env):
|
|
|
|
"""Transforms a MemoryY node into a ZeroPageY one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("Zero Page, Y")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "ZeroPageY"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
def collapse_no_index_ind(node, env):
|
|
|
|
"""Transforms a Pointer node into a ZPIndirect one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("(Zero Page)")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "ZPIndirect"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
def collapse_x_ind(node, env):
|
|
|
|
"""Transforms a PointerX node into an IndirectX one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("(Zero Page, X)")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "IndirectX"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
|
|
|
def collapse_y_ind(node, env):
|
|
|
|
"""Transforms a PointerY node into an IndirectY one if possible.
|
2012-06-01 17:25:48 +00:00
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("(Zero Page), Y")] is not None:
|
2012-06-01 17:25:48 +00:00
|
|
|
node.nodetype = "IndirectY"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2014-02-07 10:22:11 +00:00
|
|
|
def collapse_spy_ind(node, env):
|
|
|
|
"""Transforms a PointerSPY node into an IndirectY one if possible.
|
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("(Zero Page, SP), Y")] is not None:
|
2014-02-07 10:22:11 +00:00
|
|
|
node.nodetype = "IndirectSPY"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def collapse_z_ind(node, env):
|
|
|
|
"""Transforms a PointerZ node into an IndirectZ one if possible.
|
|
|
|
Returns boolean indicating whether or not it made the collapse."""
|
|
|
|
if node.data[1].value(env) < 0x100:
|
2014-02-07 15:49:14 +00:00
|
|
|
if Ops.opcodes[node.data[0]][Ops.modes.index("(Zero Page), Z")] is not None:
|
2014-02-07 10:22:11 +00:00
|
|
|
node.nodetype = "IndirectZ"
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2012-05-07 03:06:28 +00:00
|
|
|
|
2012-05-25 01:12:35 +00:00
|
|
|
class ExtendBranches(PCTracker):
|
|
|
|
"""Eliminates any branch instructions that would end up going past
|
2012-06-01 17:25:48 +00:00
|
|
|
the 128-byte range, and replaces them with a branch-jump pair."""
|
2012-05-25 01:12:35 +00:00
|
|
|
name = "Branch Expansion Pass"
|
2012-06-01 17:25:48 +00:00
|
|
|
reversed = {'bcc': 'bcs',
|
|
|
|
'bcs': 'bcc',
|
|
|
|
'beq': 'bne',
|
|
|
|
'bmi': 'bpl',
|
|
|
|
'bne': 'beq',
|
|
|
|
'bpl': 'bmi',
|
|
|
|
'bvc': 'bvs',
|
|
|
|
'bvs': 'bvc',
|
|
|
|
# 65c02 ones. 'bra' is special, though, having no inverse
|
|
|
|
'bbr0': 'bbs0',
|
|
|
|
'bbs0': 'bbr0',
|
|
|
|
'bbr1': 'bbs1',
|
|
|
|
'bbs1': 'bbr1',
|
|
|
|
'bbr2': 'bbs2',
|
|