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 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] 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 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 relativizelong(self, expr, env, arglen): "Convert an expression into one for use in relative addressing" arg = expr.value(env) arg = arg - (env.getPC() + arglen) if arg < 0: arg += 65536 return IR.ConstantExpr(arg)
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 assemble(self, node, mode, env): "A generic instruction called by the visitor methods themselves" (opcode, expr, expr2) = 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 inst_bytes = [] self.outputbyte(IR.ConstantExpr(bin_op), env, inst_bytes) arglen = Ops.lengths[mode] val1 = None val2 = None if expr is not None: val1 = expr.value(env) if expr2 is not None: val2 = expr2.value(env) if mode == Ops.modes.index("Zero Page, Relative"): expr2 = self.relativize(expr2, env, arglen) self.outputbyte(expr, env, inst_bytes) self.outputbyte(expr2, env, inst_bytes) else: if mode == Ops.modes.index("Relative"): expr = self.relativize(expr, env, arglen) elif mode == Ops.modes.index("RelativeLong"): expr = self.relativizelong(expr, env, arglen) if arglen == 1: self.outputbyte(expr, env, inst_bytes) elif arglen == 2: self.outputword(expr, env, inst_bytes) self.listing.listInstruction( self.listing_string(env.getPC(), inst_bytes, mode, opcode, val1, val2)) env.incPC(1 + arglen) self.code += 1 + arglen
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 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 relativize(self, expr, env, arglen): "Convert an expression into one for use in relative addressing" arg = expr.value(env) arg = arg - (env.getPC() + arglen + 1) if arg < -128 or arg > 127: Err.log("Branch target out of bounds") arg = 0 if arg < 0: arg += 256 return IR.ConstantExpr(arg)
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 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 range(target - pc): self.outputbyte(zero, env) self.filler += target - pc env.setPC(target)
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 == 11: # 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 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 atom(): "Parses lowest-priority expression components." global templabelcount next = line.lookahead(0).type if next == "NUM": return IR.ConstantExpr(line.expect("NUM").value) elif next in ["LABEL", "X", "Y", "Z", "SP", "OPCODE"]: 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')