def visitZPRelative(self, node, env): (opcode, tested, expr) = node.data arg = expr.value(env) arg = arg - (env.getPC() + 3) if arg < -128 or arg > 127: # Otherwise, we replace it with a 'macro' of sorts by hand: # $branch LOC -> $reversed_branch ^+6; 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, "ZPRelative", ExtendBranches.reversed[opcode], tested, IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(6)])), IR.Node(node.ppt, "Absolute", 'jmp', expr, None)] node.nodetype = 'SEQUENCE' node.data = expansion if Cmd.warn_on_branch_extend: print>>sys.stderr, str(node.ppt) + ": WARNING: " + \ opcode + " out of range, " \ "replacing with " + \ ExtendBranches.reversed[opcode] + \ "/jmp combo" self.changed = True node.accept(self, env) else: PCTracker.visitZPRelative(self, node, env)
def pragmaIncbin(ppt, line, result): "Includes a binary file" filename = line.expect("STRING").value offset = IR.ConstantExpr(0) size = None if str(line.lookahead(0)) == ",": line.pop() offset = FE.parse_expr(line) if str(line.lookahead(0)) == ",": line.pop() size = FE.parse_expr(line) line.expect("EOL") if type(filename) == str: try: f = file(os.path.join(FE.context_directory, filename), "rb") if offset.hardcoded and (size is None or size.hardcoded): # We know how big it will be, we can just use the values. # First check to make sure they're sane if offset.value() < 0: Err.log("Offset may not be negative") f.close() return f.seek(0, 2) # Seek to end of file if offset.value() > f.tell(): Err.log("Offset runs past end of file") f.close() return if size is not None: if size.value() < 0: Err.log("Length may not be negative") f.close() return if offset.value() + size.value() > f.tell(): Err.log(".incbin length too long") f.close() return if size is None: size = IR.ConstantExpr(-1) f.seek(offset.value()) bytes = f.read(size.value()) bytes = [IR.ConstantExpr(ord(x)) for x in bytes] result.append(IR.Node(ppt, "Byte", *bytes)) else: # offset or length could change based on label placement. # This seems like an unbelievably bad idea, but since we # don't have constant prop it will happen for any symbolic # alias. Don't use symbolic aliases when extracting tiny # pieces out of humongous files, I guess. bytes = f.read() bytes = [IR.ConstantExpr(ord(x)) for x in bytes] if size is None: size = IR.SequenceExpr([IR.ConstantExpr(len(bytes)), "-", offset]) result.append(IR.Node(ppt, "ByteRange", offset, size, *bytes)) f.close() except IOError: Err.log("Could not read " + filename) return
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)])))
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))
def pragmaCbmfloat(ppt, line, result): "Parses a string into a CBM BASIC format floating point number" data = [] while True: try: v_str = line.expect("STRING").value v = float(v_str) if v == 0.0: data.extend([0, 0, 0, 0, 0]) else: if v < 0.0: sign = 128 v = -v else: sign = 0 expt = math.floor(math.log(v, 2)) if expt >= -128 and expt <= 126: mantissa = v / (2**expt) m1 = (mantissa - 1.0) * 128 + sign m2 = m1 * 256 m3 = m2 * 256 m4 = m3 * 256 data.extend( [int(x) % 256 for x in [expt + 129, m1, m2, m3, m4]]) else: Err.log("Floating point constant out of range") except ValueError: Err.log("Expected: floating point") next = line.expect(',', 'EOL').type if next == 'EOL': break bytes = [IR.ConstantExpr(x) for x in data] result.append(IR.Node(ppt, "Byte", *bytes))
def visitRelative(self, node, env): (opcode, expr) = node.data[:2] arg = expr.value(env) arg = arg - (env.getPC() + 2) if arg < -128 or arg > 127: if Cmd.enable_4502_exts: node.nodetype = "RelativeLong" if Cmd.warn_on_branch_extend: print(str(node.ppt) + ": WARNING: " \ "branch out of range, replacing with 16-bit relative branch", file=sys.stderr) else: if opcode == 'bra': # If BRA - BRanch Always - is out of range, it's a JMP. node.data = ('jmp', expr, None) node.nodetype = "Absolute" if Cmd.warn_on_branch_extend: print(str(node.ppt) + ": WARNING: " \ "bra out of range, replacing with jmp", file=sys.stderr) 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)]), None), IR.Node(node.ppt, "Absolute", 'jmp', expr, None) ] node.nodetype = 'SEQUENCE' node.data = expansion if Cmd.warn_on_branch_extend: print(str(node.ppt) + ": WARNING: " + \ opcode + " out of range, " \ "replacing with " + \ ExtendBranches.reversed[opcode] + \ "/jmp combo", file=sys.stderr) self.changed = True node.accept(self, env) else: PCTracker.visitRelative(self, node, env)
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)
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))
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))
def pragmaIncbin(ppt, line, result): "Includes a binary file" filename = line.expect("STRING").value line.expect("EOL") if type(filename) == str: f = file(filename, "rb") bytes = f.read() f.close() bytes = [IR.ConstantExpr(ord(x)) for x in bytes] result.append(IR.Node(ppt, "Byte", *bytes))
def pragmaAdvance(ppt, line, result): "Outputs filler until reaching the target PC" newPC = FE.parse_expr(line) if str(line.lookahead(0)) == ",": line.pop() fillexpr = FE.parse_expr(line) else: fillexpr = IR.ConstantExpr(0) line.expect("EOL") result.append(IR.Node(ppt, "Advance", newPC, fillexpr))
def pragmaMacro(ppt, line, result): "Begin a macro definition" lbl = line.expect("LABEL").value line.expect("EOL") result.append(IR.Node(ppt, "MacroBegin", lbl))
def pragmaScend(ppt, line, result): "End the innermost lexical scoping block" line.expect("EOL") result.append(IR.Node(ppt, "ScopeEnd"))
def pragmaScope(ppt, line, result): "Create a new lexical scoping block" line.expect("EOL") result.append(IR.Node(ppt, "ScopeBegin"))
def pragmaDwordbe(ppt, line, result): "Raw data, a dword at a time, big-endian" dwords = readData(line) result.append(IR.Node(ppt, "DwordBE", *dwords))
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))
def pragmaWord(ppt, line, result): "Raw data, a word at a time, little-endian" words = readData(line) result.append(IR.Node(ppt, "Word", *words))
def pragmaByte(ppt, line, result): "Raw data, a byte at a time" bytes = readData(line) result.append(IR.Node(ppt, "Byte", *bytes))
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))
def registerNode(node): global currentbody currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data))
def pragmaMacend(ppt, line, result): "End a macro definition" line.expect("EOL") result.append(IR.Node(ppt, "MacroEnd"))
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))
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))
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 arg2 = None 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 == ",": line.expect(",") if line.lookahead(0).type == "X": mode = "PointerX" line.expect("X") line.expect(")") line.expect("EOL") else: mode = "PointerSPY" line.expect("SP") line.expect(")") line.expect(",") line.expect("Y") line.expect("EOL") else: line.expect(")") tok = line.expect(",", "EOL").type if tok == "EOL": mode = "Pointer" else: if line.lookahead(0).type == "Y": mode = "PointerY" line.expect("Y") line.expect("EOL") else: mode = "PointerZ" line.expect("Z") 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 == ",": # Parser has to special-case the BBXn instructions, # Which uniquely take two addresses if opcode[:3] in ["bbs", "bbr"]: arg2 = parse_expr(line) mode = "Memory2" else: tok = line.expect("X", "Y", "Z").type if tok == "X": mode = "MemoryX" elif tok == "Y": mode = "MemoryY" else: mode = "MemoryZ" line.expect("EOL") else: mode = "Memory" result.append(IR.Node(ppt, mode, opcode, arg, arg2))
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))
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 = "IndirectX" line.expect(",") line.expect("X") line.expect(")") line.expect("EOL") else: line.expect(")") tok = line.expect(",", "EOL").type if tok == "EOL": mode = "Indirect" else: mode = "IndirectY" 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))