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 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 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 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 == 15: # ZP Relative mode is wildly nonstandard expr2 = self.relativize(expr2, env, arglen) self.outputbyte(expr, env, inst_bytes) self.outputbyte(expr2, env, inst_bytes) else: if mode == 14: expr = self.relativize(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 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 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 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
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
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 visitMacroEnd(self, node, env): if self.inDef: Macro.endMacro() node.nodetype = "None" node.data = [] self.inDef = False elif not self.nestedError: Err.log("Unmatched .macend")
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 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 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 visitMacroBegin(self, node, env): if self.inDef: Err.log("Nested macro definition") self.nestedError = True else: Macro.newMacro(node.data[0]) node.nodetype = "None" node.data = [] self.inDef = True
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 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")) elif id == "z": result.append(Lexeme("Z")) elif id == "sp": result.append(Lexeme("SP")) else: result.append(Lexeme("LABEL", id)) return # should never reach here Err.log("Internal lexer error: add_token fall-through")
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", int(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", int(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")) elif id == "z": result.append(Lexeme("Z")) elif id == "sp": result.append(Lexeme("SP")) else: result.append(Lexeme("LABEL", id)) return # should never reach here Err.log("Internal lexer error: add_token fall-through")
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
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 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 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 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 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 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 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 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 = []
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)
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 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: created = [] for i in xrange(target - pc): self.outputbyte(node.data[1], env, created) self.filler += target - pc self.registerData(created, env.getPC()) env.setPC(target)
def outputword(self, expr, env, tee=None): '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)) if tee is not None: tee.extend(self.output[-2:]) else: Err.log("Attempt to write to data segment")
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: f = file(filename, "rb") bytes = f.read() f.close() if len(bytes)==256: currentcharmap = bytes else: Err.log("Character map "+filename+" not 256 bytes long")
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 range(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)
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")
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 in tokens: return token if "LABEL" in tokens: if token.type in ["X", "Y"]: token.value = token.type.lower() token.type = "LABEL" return token elif token.type == "OPCODE": token.type = "LABEL" return token Err.log('Expected: "' + '", "'.join(tokens) + '"') return token
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
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 = True if label in ['a', 'x', 'y'] and self.runcount == 1: print>>sys.stderr, str(node.ppt) + ": WARNING: " \ "using register name as label" if label in Ops.opcodes and self.runcount == 1: print>>sys.stderr, str(node.ppt) + ": WARNING: " \ "using opcode name as label"
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 = open(filename, 'r', encoding='utf-8') 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 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 in tokens: return token if 'LABEL' in tokens: if token.type in ['X', 'Y', 'Z', 'SP']: token.value = token.type.lower() token.type = 'LABEL' return token elif token.type == 'OPCODE': token.type = 'LABEL' return token Err.log('Expected: "' + '", "'.join(tokens) + '"') return token
def pragmaCharmap(ppt, line, result): "Modify the character map." global currentcharmap, basecharmap if str(line.lookahead(0)) == "EOL": currentcharmap = basecharmap else: bytes = readData(line) 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(os.path.join(FE.context_directory, 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 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 = open(os.path.join(FE.context_directory, 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 parse_file(ppt, filename, load_once=False): "Loads an Ophis source file, and returns an IR list." global context_directory, loadedfiles Err.currentpoint = ppt old_context = context_directory if filename != '-': if context_directory is not None: filename = os.path.abspath( os.path.join(context_directory, filename)) if load_once and filename in loadedfiles: if Cmd.print_loaded_files: print("Skipping " + filename, file=sys.stderr) return IR.NullNode loadedfiles[filename] = True if Cmd.print_loaded_files: if filename != '-': print("Loading " + filename, file=sys.stderr) else: print("Loading from standard input", file=sys.stderr) try: if filename != '-': if context_directory is not None: filename = os.path.join(context_directory, filename) f = open(filename, "rt") linelist = f.readlines() f.close() context_directory = os.path.abspath(os.path.dirname(filename)) else: context_directory = os.getcwd() linelist = sys.stdin.readlines() pptlist = ["%s:%d" % (filename, i + 1) for i in range(len(linelist))] lexlist = list(map(lex, pptlist, linelist)) IRlist = list(map(parse_line, pptlist, lexlist)) IRlist = [node for node in IRlist if node is not IR.NullNode] context_directory = old_context return IR.SequenceNode(ppt, IRlist) except IOError: Err.log("Could not read " + filename) context_directory = old_context return IR.NullNode
def parse_file(ppt, filename, load_once=False): "Loads an Ophis source file, and returns an IR list." global context_directory, loadedfiles Err.currentpoint = ppt old_context = context_directory if filename != '-': if context_directory is not None: filename = os.path.abspath(os.path.join(context_directory, filename)) if load_once and filename in loadedfiles: if Cmd.print_loaded_files: print>>sys.stderr, "Skipping " + filename return IR.NullNode loadedfiles[filename] = True if Cmd.print_loaded_files: if filename != '-': print>>sys.stderr, "Loading " + filename else: print>>sys.stderr, "Loading from standard input" try: if filename != '-': if context_directory is not None: filename = os.path.join(context_directory, filename) f = file(filename) linelist = f.readlines() f.close() context_directory = os.path.abspath(os.path.dirname(filename)) else: context_directory = os.getcwd() linelist = sys.stdin.readlines() 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] context_directory = old_context return IR.SequenceNode(ppt, IRlist) except IOError: Err.log("Could not read " + filename) context_directory = old_context return IR.NullNode
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 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')
def visitByteRange(self, node, env): offset = node.data[0].value(env) + 2 length = node.data[1].value(env) if offset < 2: Err.log("Negative offset in .incbin") elif offset > len(node.data): Err.log("Offset extends past end of file") elif length < 0: Err.log("Negative length") elif offset + length > len(node.data): Err.log("File too small for .incbin subrange") else: created = [] for expr in node.data[offset:(offset + length)]: self.outputbyte(expr, env, created) self.registerData(created, env.getPC()) env.incPC(length) self.data += length
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))