Пример #1
0
def run_all(infile, outfile):
	"Transforms the source infile to a binary outfile."
	Err.count = 0
	z = Ophis.Frontend.parse(infile)
	env = Ophis.Environment.Environment()

	
	m = Ophis.Passes.ExpandMacros()
	i = Ophis.Passes.InitLabels()
	l_basic = Ophis.Passes.UpdateLabels()
	l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0)
	c = Ophis.Passes.Collapse()
	a = Ophis.Passes.Assembler()

	passes = []
	passes.append(Ophis.Passes.DefineMacros())
	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.extend([Ophis.Passes.NormalizeModes(), Ophis.Passes.UpdateLabels(), a])

	for p in passes: p.go(z, env)

	if Err.count == 0:
		try:
			output = file(outfile, 'wb')
			output.write("".join(map(chr, a.output)))
		except IOError:
			print "Could not write to "+outfile
	else:
		Err.report()
Пример #2
0
 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
Пример #3
0
 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
Пример #4
0
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))
Пример #5
0
	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
Пример #6
0
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
Пример #7
0
	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
Пример #8
0
 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")
Пример #9
0
 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
Пример #10
0
 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]
Пример #11
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")
Пример #12
0
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)
Пример #13
0
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
Пример #14
0
 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")
Пример #15
0
	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)
Пример #16
0
	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
Пример #17
0
 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)
Пример #18
0
 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")
Пример #19
0
	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)
Пример #20
0
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 = []
Пример #21
0
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")
Пример #22
0
 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")
Пример #23
0
 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)
Пример #24
0
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)
Пример #25
0
 def outputdword_be(self, expr, env, tee=None):
     'Outputs a big-endian dword, with range checking'
     if self.writeOK:
         val = expr.value(env)
         if val < 0x00000000 or val > 0xFFFFFFFFL:
             Err.log("DWord constant " + str(expr) + " out of range")
             val = 0
         self.output.append(int((val >> 24) & 0xFF))
         self.output.append(int((val >> 16) & 0xFF))
         self.output.append(int((val >> 8) & 0xFF))
         self.output.append(int(val & 0xFF))
         if tee is not None:
             tee.extend(self.output[-4:])
     else:
         Err.log("Attempt to write to data segment")
Пример #26
0
 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
Пример #27
0
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")
Пример #28
0
 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
Пример #30
0
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
Пример #31
0
 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
Пример #32
0
 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"
Пример #33
0
 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
Пример #34
0
 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.print_pass:
             print>>sys.stderr, "Fixpoint failed, looping back"
     else:
         Err.log("Can't make %s converge!  Maybe there's a recursive "
                 "dependency somewhere?" % self.name)
Пример #35
0
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")
Пример #36
0
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")
Пример #37
0
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")
Пример #38
0
def run_all(infile, outfile):
    "Transforms the source infile to a binary outfile."
    Err.count = 0
    z = Ophis.Frontend.parse(infile)
    env = Ophis.Environment.Environment()

    m = Ophis.Passes.ExpandMacros()
    i = Ophis.Passes.InitLabels()
    l_basic = Ophis.Passes.UpdateLabels()
    l = Ophis.Passes.FixPoint("label update", [l_basic],
                              lambda: l_basic.changed == 0)
    c = Ophis.Passes.Collapse()
    a = Ophis.Passes.Assembler()

    passes = []
    passes.append(Ophis.Passes.DefineMacros())
    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.extend(
        [Ophis.Passes.NormalizeModes(),
         Ophis.Passes.UpdateLabels(), a])

    for p in passes:
        p.go(z, env)

    if Err.count == 0:
        try:
            output = open(outfile, 'wb')
            output.write(bytes(a.output))
            output.close()
        except IOError:
            print("Could not write to " + outfile)
    else:
        Err.report()
Пример #39
0
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
Пример #40
0
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
Пример #41
0
 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
Пример #42
0
 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')
Пример #43
0
 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
Пример #44
0
 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 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))
Пример #46
0
 def visitAdvance(self, node, env):
     PCvalid = self.PCvalid
     self.PCvalid = node.data[0].valid(env, self.PCvalid)
     if not node.data[0].valid(env, PCvalid):
         Err.log("Undefined or circular reference on .advance")
Пример #47
0
 def postPass(self):
     if self.inDef:
         Err.log("Unmatched .macro")
     elif Cmd.print_ir:
         print>>sys.stderr, "Macro definitions:"
         Macro.dump()
Пример #48
0
def run_all():
    """Transforms the source infiles to a binary outfile.

    Returns a shell-style exit code: 1 if there were errors, 0 if there
    were no errors.

    """
    Err.count = 0
    z = Ophis.Frontend.parse(Ophis.CmdLine.infiles)
    env = Ophis.Environment.Environment()

    m = Ophis.Passes.ExpandMacros()
    i = Ophis.Passes.InitLabels()
    l_basic = Ophis.Passes.UpdateLabels()
    l = Ophis.Passes.FixPoint("label update", [l_basic],
                              lambda: not l_basic.changed)

    # The instruction selector is a bunch of fixpoints, and which
    # passes run depends on the command line options a bit.
    c_basic = Ophis.Passes.Collapse()
    c = Ophis.Passes.FixPoint("instruction selection 1", [l, c_basic],
                              lambda: not c_basic.changed)

    if Ophis.CmdLine.enable_branch_extend:
        b = Ophis.Passes.ExtendBranches()
        instruction_select = Ophis.Passes.FixPoint("instruction selection 2",
                                                   [c, b],
                                                   lambda: not b.changed)
    else:
        instruction_select = c
    a = Ophis.Passes.Assembler()

    passes = []
    passes.append(Ophis.Passes.DefineMacros())
    passes.append(
        Ophis.Passes.FixPoint("macro expansion", [m], lambda: not m.changed))
    passes.append(
        Ophis.Passes.FixPoint("label initialization", [i],
                              lambda: not i.changed))
    passes.extend([
        Ophis.Passes.CircularityCheck(),
        Ophis.Passes.CheckExprs(),
        Ophis.Passes.EasyModes()
    ])
    passes.append(instruction_select)
    passes.extend(
        [Ophis.Passes.NormalizeModes(),
         Ophis.Passes.UpdateLabels(), a])

    for p in passes:
        p.go(z, env)

    if Err.count == 0:
        try:
            outfile = Ophis.CmdLine.outfile
            if outfile == '-':
                output = sys.stdout
                if sys.platform == "win32":
                    # We can't dump our binary in text mode; that would be
                    # disastrous. So, we'll do some platform-specific
                    # things here to force our stdout to binary mode.
                    import msvcrt
                    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
            elif outfile is None:
                output = file('ophis.bin', 'wb')
            else:
                output = file(outfile, 'wb')
            output.write("".join(map(chr, a.output)))
            output.flush()
            if outfile != '-':
                output.close()
            return 0
        except IOError:
            print >> sys.stderr, "Could not write to " + outfile
            return 1
    else:
        Err.report()
        return 1
Пример #49
0
 def visitUnknown(self, node, env):
     Err.log("Internal error!  " + self.name +
             " cannot understand node type " + node.nodetype)
Пример #50
0
 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))
Пример #51
0
 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))
Пример #52
0
 def visitLabel(self, node, env):
     (label, val) = node.data
     if not val.valid(env, self.PCvalid):
         Err.log("Undefined or circular dependency for label '%s'" % label)
Пример #53
0
 def visitCheckPC(self, node, env):
     if not node.data[0].valid(env, self.PCvalid):
         Err.log("Undefined or circular reference on program counter check")
Пример #54
0
 def endscope(self):
     "Leaves a scope."
     if len(self.stack) == 1:
         Err.log("Unmatched .scend")
     self.stack.pop(0)
Пример #55
0
def lex(point, line):
    """Turns a line of source into a sequence of lexemes."""
    Err.currentpoint = point
    result = []

    def is_opcode(op):
        "Tests whether a string is an opcode or an identifier"
        return op in Ops.opcodes

    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 add_EOL():
        "Adds an end-of-line lexeme"
        result.append(Lexeme("EOL"))

    # Actual routine begins here
    value = ""
    quotemode = False
    backslashmode = False
    for c in line.strip():
        if backslashmode:
            backslashmode = False
            value = value + c
        elif c == "\\":
            backslashmode = True
        elif quotemode:
            if c == '"':
                quotemode = False
            else:
                value = value + c
        elif c == ';':
            add_token(value)
            value = ""
            break
        elif c == '.' and value != "":
            value = value + c
        elif c.isspace():
            add_token(value)
            value = ""
        elif c in punctuation:
            add_token(value)
            add_token(c)
            value = ""
        elif c == '"':
            add_token(value)
            value = '"'
            quotemode = True
        else:
            value = value + c
    if backslashmode:
        Err.log("Backslashed newline")
    if quotemode:
        Err.log("Unterminated string constant")
    add_token(value)
    add_EOL()
    return result
Пример #56
0
 def postPass(self):
     if self.inDef:
         Err.log("Unmatched .macro")
     elif Cmd.verbose > 2:
         print("Macro definitions:")
         Macro.dump()
Пример #57
0
 def visitSetPC(self, node, env):
     PCvalid = self.PCvalid
     self.PCvalid = node.data[0].valid(env, self.PCvalid)
     if not node.data[0].valid(env, PCvalid):
         Err.log("Undefined or circular reference on program counter set")