def exit_proc(self, lineno): """ Exits current procedure. Local labels are transferred to global scope unless they have been marked as local ones. Raises an error if no current local context (stack underflow) """ __DEBUG__('Exiting current scope from lineno %i' % lineno) if len(self.local_labels) <= 1: error(lineno, 'ENDP in global scope (with no PROC)') return for label in self.local_labels[-1].values(): if label.local: if not label.defined: error(lineno, "Undefined LOCAL label '%s'" % label.name) return continue name = label.name _lineno = label.lineno value = label.value if name not in self.global_labels.keys(): self.global_labels[name] = label else: self.global_labels[name].define(value, _lineno) self.local_labels.pop() # Removes current context self.scopes.pop()
def p_asm_ld8(p): """ asm : LD reg8 COMMA reg8_hl | LD reg8_hl COMMA reg8 | LD reg8 COMMA reg8 | LD SP COMMA HL | LD SP COMMA reg16i | LD A COMMA reg8 | LD reg8 COMMA A | LD reg8_hl COMMA A | LD A COMMA reg8_hl | LD A COMMA A | LD A COMMA I | LD I COMMA A | LD A COMMA R | LD R COMMA A | LD A COMMA reg8i | LD reg8i COMMA A | LD reg8 COMMA reg8i | LD reg8i COMMA regBCDE | LD reg8i COMMA reg8i """ if p[2] in ('H', 'L') and p[4] in ('IXH', 'IXL', 'IYH', 'IYL'): p[0] = None error(p.lineno(0), "Unexpected token '%s'" % p[4]) else: p[0] = Asm(p.lineno(1), 'LD %s,%s' % (p[2], p[4]))
def p_ind8_I(p): """ reg8_I : LP IX expr RP | LP IY expr RP | LP IX PLUS pexpr RP | LP IX MINUS pexpr RP | LP IY PLUS pexpr RP | LP IY MINUS pexpr RP """ if len(p) == 6: expr = p[4] sign = p[3] else: expr = p[3] gen_ = expr.inorder() first_expr = next(gen_, '') if first_expr and first_expr.parent: if len(first_expr.parent.children) == 2: first_token = first_expr.symbol.item else: first_token = first_expr.parent.symbol.item else: first_token = '<nothing>' if first_token not in ('-', '+'): error( p.lineno(2), "Unexpected token '{}'. Expected '+' or '-'".format( first_token)) sign = '+' if sign == '-': expr = Expr.makenode(Container(sign, p.lineno(2)), expr) p[0] = ('(%s+N)' % p[2], expr)
def set_org(self, value, lineno): """ Sets a new ORG value """ if value < 0 or value > MAX_MEM: error(lineno, "Memory ORG out of range [0 .. 65535]. Current value: %i" % value) self.index = self.ORG = value
def __init__(self, lineno, asm, arg=None): self.lineno = lineno if asm not in ('DEFB', 'DEFS', 'DEFW'): try: super(Asm, self).__init__(asm, arg) except Error as v: error(lineno, v.msg) return self.pending = len([ x for x in self.arg if isinstance(x, Expr) and x.try_eval() is None ]) > 0 if not self.pending: self.arg = self.argval() else: self.asm = asm self.pending = True if isinstance(arg, str): self.arg = tuple( [Expr(Container(ord(x), lineno)) for x in arg]) else: self.arg = arg self.arg_num = len(self.arg)
def p_error(p): if p is not None: if p.type != 'NEWLINE': error(p.lineno, "Syntax error. Unexpected token '%s' [%s]" % (p.value, p.type)) else: error(p.lineno, "Syntax error. Unexpected end of line [NEWLINE]") else: OPTIONS.stderr.value.write("General syntax error at assembler (unexpected End of File?)") gl.has_errors += 1
def __set_byte(self, byte, lineno): """ Sets a byte at the current location, and increments org in one. Raises an error if org > MAX_MEMORY """ if byte < 0 or byte > 255: error(lineno, 'Invalid byte value %i' % byte) self.memory_bytes[self.org] = byte self.index += 1 # Increment current memory pointer
def define(self, value, lineno, namespace=None): """ Defines label value. It can be anything. Even an AST """ if self.defined: error(lineno, "label '%s' already defined at line %i" % (self.name, self.lineno)) self.value = value self.lineno = lineno self.namespace = NAMESPACE if namespace is None else namespace
def p_im(p): """ asm : IM expr """ val = p[2].eval() if val not in (0, 1, 2): error(p.lineno(1), 'Invalid IM number %i' % val) p[0] = None return p[0] = Asm(p.lineno(1), 'IM %i' % val)
def p_align(p): """ asm : ALIGN expr | ALIGN pexpr """ align = p[2].eval() if align < 2: error(p.lineno(1), "ALIGN value must be greater than 1") return MEMORY.set_org(MEMORY.org + (align - MEMORY.org % align) % align, p.lineno(1))
def resolve(self, lineno): """ Evaluates label value. Exits with error (unresolved) if value is none """ if not self.defined: error(lineno, "Undeclared label '%s'" % self.name) if isinstance(self.value, Expr): return self.value.eval() return self.value
def p_DEFS(p): # Define bytes """ asm : DEFS number_list """ if len(p[2]) > 2: error(p.lineno(1), "too many arguments for DEFS") if len(p[2]) < 2: num = Expr.makenode(Container(0, p.lineno(1))) # Defaults to 0 p[2] = p[2] + (num, ) p[0] = Asm(p.lineno(1), 'DEFS', p[2])
def p_rst(p): """ asm : RST expr """ val = p[2].eval() if val not in (0, 8, 16, 24, 32, 40, 48, 56): error(p.lineno(1), 'Invalid RST number %i' % val) p[0] = None return p[0] = Asm(p.lineno(1), 'RST %XH' % val)
def p_BIT_ix(p): """ asm : bitop expr COMMA reg8_I | bitop pexpr COMMA reg8_I """ bit = p[2].eval() if bit < 0 or bit > 7: error(p.lineno(3), 'Invalid bit position %i. Must be in [0..7]' % bit) p[0] = None return p[0] = Asm(p.lineno(3), '%s %i,%s' % (p[1], bit, p[4][0]), p[4][1])
def assemble(input_): """ Assembles input string, and leave the result in the MEMORY global object """ global MEMORY if MEMORY is None: MEMORY = Memory() parser.parse(input_, lexer=LEXER, debug=OPTIONS.Debug.value > 2) if len(MEMORY.scopes): error(MEMORY.scopes[-1], 'Missing ENDP to close this scope') return gl.has_errors
def try_eval(self): """ Recursively evals the node. Returns None if it is still unresolved. """ item = self.symbol.item if isinstance(item, int): return item if isinstance(item, Label): if item.defined: if isinstance(item.value, Expr): return item.value.try_eval() else: return item.value else: if Expr.ignore: return None # Try to resolve into the global namespace error(self.symbol.lineno, "Undefined label '%s'" % item.name) return None try: if isinstance(item, tuple): return tuple([x.try_eval() for x in item]) if isinstance(item, list): return [x.try_eval() for x in item] if item == '-' and len(self.children) == 1: return -self.left.try_eval() if item == '+' and len(self.children) == 1: return self.left.try_eval() try: return self.funct[item](self.left.try_eval(), self.right.try_eval()) except ZeroDivisionError: error(self.symbol.lineno, 'Division by 0') except KeyError: pass except TypeError: pass return None
def p_incbin(p): """ asm : INCBIN STRING """ try: fname = zxbpp.search_filename(p[2], p.lineno(2), local_first=True) if not fname: p[0] = None return with api.utils.open_file(fname, 'rb') as f: filecontent = f.read() except IOError: error(p.lineno(2), "cannot read file '%s'" % p[2]) p[0] = None return p[0] = Asm(p.lineno(1), 'DEFB', filecontent)
def dump(self): """ Returns a tuple containing code ORG (origin address), and a list of bytes (OUTPUT) """ org = min(self.memory_bytes.keys()) # Org is the lowest one OUTPUT = [] align = [] for label in self.global_labels.values(): if not label.defined: error(label.lineno, "Undefined GLOBAL label '%s'" % label.name) for i in range(org, max(self.memory_bytes.keys()) + 1): if gl.has_errors: return org, OUTPUT try: try: a = [x for x in self.orgs[i] if isinstance(x, Asm)] # search for asm instructions if not a: align.append( 0) # Fill with ZEROes not used memory regions continue OUTPUT += align align = [] a = a[0] if a.pending: a.arg = a.argval() a.pending = False tmp = a.bytes() for r in range(len(tmp)): self.memory_bytes[i + r] = tmp[r] except KeyError: pass OUTPUT.append(self.memory_bytes[i]) except KeyError: OUTPUT.append(0) # Fill with ZEROes not used memory regions return org, OUTPUT
def argval(self): """ Solve args values or raise errors if not defined yet """ if gl.has_errors: return [None] if self.asm in ('DEFB', 'DEFS', 'DEFW'): return tuple( [x.eval() if isinstance(x, Expr) else x for x in self.arg]) self.arg = tuple( [x if not isinstance(x, Expr) else x.eval() for x in self.arg]) if self.asm.split(' ')[0] in ('JR', 'DJNZ'): # A relative jump? if self.arg[0] < -128 or self.arg[0] > 127: error(self.lineno, 'Relative jump out of range') return [None] return super(Asm, self).argval()