diff --git a/src/Ophis/Frontend.py b/src/Ophis/Frontend.py index 0bd3c19..0c418a1 100644 --- a/src/Ophis/Frontend.py +++ b/src/Ophis/Frontend.py @@ -307,7 +307,7 @@ def parse_line(ppt, lexemelist): return IR.SequenceNode(ppt, result) def parse_file(ppt, filename): - "Loads a .P65 source file, and returns an IR list." + "Loads an Ophis source file, and returns an IR list." Err.currentpoint = ppt if Cmd.verbose > 0: print "Loading "+filename try: diff --git a/src/Ophis/IR.py b/src/Ophis/IR.py index a0255e2..6e017f6 100644 --- a/src/Ophis/IR.py +++ b/src/Ophis/IR.py @@ -39,7 +39,7 @@ def SequenceNode(ppt, nodelist): return Node(ppt, "SEQUENCE", *nodelist) class Expr: - """Base class for P65 expressions + """Base class for Ophis 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.""" diff --git a/src/Ophis/Main.py b/src/Ophis/Main.py index 6cc43f4..0977e0a 100644 --- a/src/Ophis/Main.py +++ b/src/Ophis/Main.py @@ -40,6 +40,7 @@ def run_all(infile, outfile): l_basic = Ophis.Passes.UpdateLabels() l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0) c = Ophis.Passes.Collapse() + b = Ophis.Passes.ExtendBranches() a = Ophis.Passes.Assembler() passes = [] @@ -47,7 +48,7 @@ def run_all(infile, outfile): 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.append(Ophis.Passes.FixPoint("instruction selection", [l, c, b], lambda: c.collapsed == 0 and b.expanded == 0)) passes.extend([Ophis.Passes.NormalizeModes(), Ophis.Passes.UpdateLabels(), a]) for p in passes: p.go(z, env) diff --git a/src/Ophis/Opcodes.py b/src/Ophis/Opcodes.py index 5a8270f..0dc45a7 100644 --- a/src/Ophis/Opcodes.py +++ b/src/Ophis/Opcodes.py @@ -9,20 +9,20 @@ # 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] diff --git a/src/Ophis/Passes.py b/src/Ophis/Passes.py index 7a2ff70..7b6fd25 100644 --- a/src/Ophis/Passes.py +++ b/src/Ophis/Passes.py @@ -227,11 +227,9 @@ class EasyModes(Pass): 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 +class PCTracker(Pass): + "Superclass for passes that need an accurate program counter." + name = "**BUG** PC Tracker Superpass used directly" def visitSetPC(self, node, env): env.setPC(node.data[0].value(env)) def visitAdvance(self, node, env): env.setPC(node.data[0].value(env)) def visitImplied(self, node, env): env.incPC(1) @@ -256,17 +254,24 @@ class UpdateLabels(Pass): def visitPointerX(self, node, env): env.incPC(3) def visitPointerY(self, node, env): env.incPC(3) def visitCheckPC(self, node, env): pass + def visitLabel(self, node, env): pass + def visitByte(self, node, env): env.incPC(len(node.data)) + def visitWord(self, node, env): env.incPC(len(node.data)*2) + def visitDword(self, node, env): env.incPC(len(node.data)*4) + def visitWordBE(self, node, env): env.incPC(len(node.data)*2) + def visitDwordBE(self, node, env): env.incPC(len(node.data)*4) + +class UpdateLabels(PCTracker): + "Computes the new values for all entries in the symbol table" + name = "Label Update Pass" + def prePass(self): + self.changed = 0 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 @@ -344,6 +349,65 @@ def collapse_y_ind(node, env): else: return 0 +class ExtendBranches(PCTracker): + """Eliminates any branch instructions that would end up going past + the 128-byte range, and replaces them with a branch-jump + pair. Also tracks how many elements where changed this pass.""" + name = "Branch Expansion Pass" + 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', + 'bbs2': 'bbr2', + 'bbr3': 'bbs3', + 'bbs3': 'bbr3', + 'bbr4': 'bbs4', + 'bbs4': 'bbr4', + 'bbr5': 'bbs5', + 'bbs5': 'bbr5', + 'bbr6': 'bbs6', + 'bbs6': 'bbr6', + 'bbr7': 'bbs7', + 'bbs7': 'bbr7' + } + def prePass(self): + self.expanded = 0 + def visitRelative(self, node, env): + (opcode, expr) = node.data + arg = expr.value(env) + arg = arg-(env.getPC()+2) + if arg < -128 or arg > 127: + if opcode == 'bra': + # If BRA - BRanch Always - is out of range, it's a JMP. + node.data = ('jmp', expr) + node.nodetype = "Absolute" + if Cmd.verbose > 0: + print str(node.ppt) + ": WARNING: bra out of range, replacing with jmp" + else: + # Otherwise, we replace it with a 'macro' of sorts by hand: + # $branch LOC -> $reversed_branch ^+5; JMP LOC + # We don't use temp labels here because labels need to have been fixed + # in place by this point, and JMP is always 3 bytes long. + expansion = [IR.Node(node.ppt, "Relative", ExtendBranches.reversed[opcode], IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(5)])), + IR.Node(node.ppt, "Absolute", 'jmp', expr)] + node.nodetype='SEQUENCE' + node.data = expansion + if Cmd.verbose > 0: + print str(node.ppt) + ": WARNING: "+opcode+" out of range, replacing with "+ExtendBranches.reversed[opcode] +"/jmp combo" + self.expanded += 1 + node.accept(self, env) + else: + env.incPC(2) class NormalizeModes(Pass): """Eliminates the intermediate "Memory" and "Pointer" nodes, diff --git a/tests/branch_c02.bin b/tests/branch_c02.bin new file mode 100644 index 0000000..0683ba6 Binary files /dev/null and b/tests/branch_c02.bin differ diff --git a/tests/branch_c02.oph b/tests/branch_c02.oph new file mode 100644 index 0000000..6040ef3 --- /dev/null +++ b/tests/branch_c02.oph @@ -0,0 +1,74 @@ +.text +.org $0800 +early: + bbr0 late + bbr1 late + bbr2 late + bbr3 late + bbr4 late + bbr5 late + bbr6 late + bbr7 late + bra late + bbs0 late + bbs1 late + bbs2 late + bbs3 late + bbs4 late + bbs5 late + bbs6 late + bbs7 late + bbr0 early + bbr1 early + bbr2 early + bbr3 early + bbr4 early + bbr5 early + bbr6 early + bbr7 early + bra early + bbs0 early + bbs1 early + bbs2 early + bbs3 early + bbs4 early + bbs5 early + bbs6 early + bbs7 early + .advance ^+256 +late: + bbr0 late + bbr1 late + bbr2 late + bbr3 late + bbr4 late + bbr5 late + bbr6 late + bbr7 late + bra late + bbs0 late + bbs1 late + bbs2 late + bbs3 late + bbs4 late + bbs5 late + bbs6 late + bbs7 late + bbr0 early + bbr1 early + bbr2 early + bbr3 early + bbr4 early + bbr5 early + bbr6 early + bbr7 early + bra early + bbs0 early + bbs1 early + bbs2 early + bbs3 early + bbs4 early + bbs5 early + bbs6 early + bbs7 early + diff --git a/tests/branch_c02_ref.oph b/tests/branch_c02_ref.oph new file mode 100644 index 0000000..0c8e0c2 --- /dev/null +++ b/tests/branch_c02_ref.oph @@ -0,0 +1,106 @@ +.text +.org $0800 +early: + bbs0 + + jmp late +* bbs1 + + jmp late +* bbs2 + + jmp late +* bbs3 + + jmp late +* bbs4 + + jmp late +* bbs5 + + jmp late +* bbs6 + + jmp late +* bbs7 + + jmp late +* jmp late +* bbr0 + + jmp late +* bbr1 + + jmp late +* bbr2 + + jmp late +* bbr3 + + jmp late +* bbr4 + + jmp late +* bbr5 + + jmp late +* bbr6 + + jmp late +* bbr7 + + jmp late +* bbr0 early + bbr1 early + bbr2 early + bbr3 early + bbr4 early + bbr5 early + bbr6 early + bbr7 early + bra early + bbs0 early + bbs1 early + bbs2 early + bbs3 early + bbs4 early + bbs5 early + bbs6 early + bbs7 early +.advance ^+256 +late: + bbr0 late + bbr1 late + bbr2 late + bbr3 late + bbr4 late + bbr5 late + bbr6 late + bbr7 late + bra late + bbs0 late + bbs1 late + bbs2 late + bbs3 late + bbs4 late + bbs5 late + bbs6 late + bbs7 late + bbs0 + + jmp early +* bbs1 + + jmp early +* bbs2 + + jmp early +* bbs3 + + jmp early +* bbs4 + + jmp early +* bbs5 + + jmp early +* bbs6 + + jmp early +* bbs7 + + jmp early +* jmp early + bbr0 + + jmp early +* bbr1 + + jmp early +* bbr2 + + jmp early +* bbr3 + + jmp early +* bbr4 + + jmp early +* bbr5 + + jmp early +* bbr6 + + jmp early +* bbr7 + + jmp early +* diff --git a/tests/longbranch.bin b/tests/longbranch.bin new file mode 100644 index 0000000..06b78c7 Binary files /dev/null and b/tests/longbranch.bin differ diff --git a/tests/longbranch.oph b/tests/longbranch.oph new file mode 100644 index 0000000..c5c9e7a --- /dev/null +++ b/tests/longbranch.oph @@ -0,0 +1,37 @@ +.text +.org $0800 +early: + bpl late + bmi late + bvc late + bvs late + bcc late + bcs late + bne late + beq late + bpl early + bmi early + bvc early + bvs early + bcc early + bcs early + bne early + beq early +.advance $0900 +late: + bpl late + bmi late + bvc late + bvs late + bcc late + bcs late + bne late + beq late + bpl early + bmi early + bvc early + bvs early + bcc early + bcs early + bne early + beq early diff --git a/tests/longbranch_ref.oph b/tests/longbranch_ref.oph new file mode 100644 index 0000000..72f585d --- /dev/null +++ b/tests/longbranch_ref.oph @@ -0,0 +1,54 @@ +.text +.org $0800 +early: + bmi + + jmp late +* bpl + + jmp late +* bvs + + jmp late +* bvc + + jmp late +* bcs + + jmp late +* bcc + + jmp late +* beq + + jmp late +* bne + + jmp late +* bpl early + bmi early + bvc early + bvs early + bcc early + bcs early + bne early + beq early +.advance $0900 +late: + bpl late + bmi late + bvc late + bvs late + bcc late + bcs late + bne late + beq late + bmi + + jmp early +* bpl + + jmp early +* bvs + + jmp early +* bvc + + jmp early +* bcs + + jmp early +* bcc + + jmp early +* beq + + jmp early +* bne + + jmp early +* \ No newline at end of file