#!/usr/bin/python import sys, os import math from getopt import getopt from l3dis import l3dis verbose = 1 addr = 0 names = {} out = sys.stdout log = sys.stderr form = "hex" # -O recursive options allow to do #! ./l3.py -O -g verilog -v -o foo.l3v def parseoptions(argv): global verbose, addr, out, log, form opt,files = getopt(argv, "qva:o:I:l:g:O:") for o,v in opt: if o=='-O': ff = parseoptions(v.split()) files += ff if o=='-v': verbose += 1 if o=='-q': verbose -= 1 if o=='-a': addr=int(v,0) if o=='-o': if v=='-': out = sys.stdout else: out = open(v, "w") if o=='-l': if v=='-': log = sys.stdout else: log = open(v, "w") if o=='-I': vv = v.strip().split("=",1) names[vv[0]] = int(vv[1],0) if o=="-g": form=v return files files = parseoptions(sys.argv[1:]) import fileinput inp = fileinput.input(files) mnems = { "ADDI" : (0b100000, 1), "PHA" : (0b000111, 1), "ADD" : (0b001000, 2), "SUB" : (0b001001, 2), "HIST" : (0b001100, 2), "HISTS": (0b001101, 2), "MULI" : (0b000110, 1), "LOG" : (0b000001, 1), "CMP" : (0b010000, 2), "TRIM" : (0b000101, 1), "BRNG" : (0b000100, 1), "BITC" : (0b000011, 1, 0x0000), "BITS" : (0b000011, 1, 0x4000), "STOP" : (0b000000, 0, 0x0000), "NOP" : (0b000000, 0, 0x8000), "GOTO" : (0b000000, 0, 0xc000), "POKE" : (0b000010, 0), } opers = [ "==", "<=", ">=", "<<", ">>", ">", "<", "!=", "=", "+", "-", "(", ")", "{", "}", "[", "]", ",", "~", "#", "*", ",", ":", ] cmpopers = { "=0=": 0b000, # unused "<" : 0b001, "==" : 0b010, "<=" : 0b011, ">" : 0b100, "!=" : 0b101, ">=" : 0b110, "=1=": 0b111, # unused } keyw = { "if1" : 0b00, "ifS" : 0b01, "ifN" : 0b10, "ifC" : 0b11, "R" : 0, "C" : 0b0001, "S" : 0b0010, } ccodes = ["if1", "ifS", "ifN", "ifC"] directives = [".include", ".forward", ".forwardfile", ".print"] # list of forwarded names forwards = [] # number of changed forward symbols forwardconflicts = 0 # file to save forwarded declarations forwardfile = None linenumber = 0 def lex(s): s=s.lstrip() if verbose>=6: log.write("LEX: '%s'\n" % (s.strip())) if not s: return ("", (None,None)) if s[0].isdigit(): return lexnumber(s) if s[0]=="." or s[0].isalpha(): return lexid(s) for o in opers: if s[0:len(o)]==o: if verbose>=5: log.write("LEX operator: '%s'\n" % o) return s[len(o):], (1,o) raise ValueError("Syntax Error LEX:%s:%d: %s\n" % (fname,lineno, s)) def lexnumber(s): for i in range(len(s)): try: n = int(s[0:i+1], 0) ii = i+1 nn = n except ValueError: if i>2: break for i in range(len(s)): try: f = float(s[0:i+1]) fi = i+1 ff = f except ValueError: if i>2: break if fi>ii: if verbose>=5: log.write("LEX float: '%.3f'\n" % ff) return s[fi:], (3,ff) if verbose>=5: log.write("LEX number: '%d'\n" % nn) return s[ii:], (0,nn) def lexid(s): i = 0 while i=5: log.write("LEX id: '%s'\n" % s[0:i]) return s[i:], (2,s[0:i]) def parse_char(s, c): ss,t = lex(s) if t[1] != c: raise ValueError("Syntax Error:%s:%d: '%s' expected:%s\n" % (fname,lineno,c,s)) return ss # LINE: {ASSIGN | COMMAND | DIRECTIVE} # ASSIGN: ID '=' EXPR # COMMAND: [ID '='] COND INSTR # EXPR: [EXPR EOPER] [-] {ID | NUMBER} # EOPER: {'+' | '-' | '<<' | '>>'} # COND: [{'ifC' | 'ifS' | 'ifN' | 'if1'}] # INSTR: {ADDI | ADD | MULI | LOG | CMP | TRIM | BIT | BRNG | STOP | GOTO | POKE} # ADDI: {'ADDI' | 'PHA'} RSPEC [{'+' | '-' | ','} EXPR] # ADD: {'ADD' | 'SUB' | 'HIST'} RSPEC [BSHIFT] {',' | '+' | '-'} RSPEC [BSHIFT] # MULI: 'MULI' RSPEC ['*' ENUMBER] [BSHIFT] # LOG: 'LOG' RSPEC # CMP: 'CMP' RSPEC ['+' EXPR] COP RSPEC ['+' EXPR] # TRIM: 'TRIM' RSPEC ',' EXPR ',' EXPR # BRNG: 'BRNG' RSPEC '{' EXPR ':' EXPR '}' # BIT: {'BITS' | 'BITC'} ['~'] {RSPEC '{' EXPR '}' | 'C' | 'S'} # STOP: 'STOP' | 'NOP' # GOTO: 'GOTO' EXPR # POKE: 'POKE' RSPEC '=' RSPEC # BSHIFT: {'<<' | '>>'} ENUMBER # ENUMBER: {NUMBER | ID | '(' EXPR ')'} # RSPEC: {ID | 'R[' EXPR ']'} def parse_LINE(s): global addr names["."] = addr ss,t = lex(s) if t[1]==None or t[1]=='#': return s if t[0]!=2: raise ValueError("Syntax Error:%s:%d: Id or Mmem expected:%s\n" % (fname,lineno,s)) if t[1] in mnems or t[1] in keyw: return parse_COMMAND(ss, t) if t[1] in directives: return parse_DIRECTIVE(ss, t) n = t[1] ss = parse_char(ss, '=') ss,t = lex(ss) if t[1] in mnems or t[1] in keyw: a = addr r = parse_COMMAND(ss, t) set_name(n, a) return r ss, nn = parse_EXPR(ss, t) set_name(n, nn) if n=='.': if verbose>=2: log.write(".=0x%03x # was 0x%03x\n" % (nn, addr)) addr = nn elif verbose>=3: log.write("%s = 0x%x\n" % (n, nn)) return ss def set_name(n, a): if n in forwards: if verbose>=0 and names[n] != a: log.write("Warning: forwarded name redefined: %s = 0x%03x, was 0x%03x\n" % (n, a, names[n])) global forwardconflicts forwardconflicts += 1 if type(a)==float: forwardfile.write("%s = %g\n" % (n,a)) else: forwardfile.write("%s = 0x%03x\n" % (n,a)) names[n] = a def parse_EEXPR(s, t=None): if verbose>=7: log.write("parse_EEXPR %s\n" % repr(t)) if not t: s,t = lex(s) if t[1]=='(': s,n = parse_EXPR(s) s = parse_char(s, ')') elif t[0]==0 or t[0]==3: n = t[1] elif t[0]==2: if not t[1] in names: raise ValueError("Name Error:%s:%d: Name not defined '%s':%s\n" % (fname,lineno, t[1], s)) n = names[t[1]] else: raise ValueError("Syntax Error:%s:%d: NUMBER or ID expected:%s\n" % (fname,lineno,s)) return s, n def parse_SEXPR(s, t=None): if verbose>=7: log.write("parse_SEXPR %s\n" % repr(t)) s, n = parse_EEXPR(s, t) ss, t = lex(s) while t[1]=='>>' or t[1]=='<<': s, nn = parse_EEXPR(ss) if t[1]=='>>': n >>= nn else: n <<= nn ss, t = lex(s) return s, n def parse_EXPR(s, t=None): if verbose>=7: log.write("parse_EXPR %s\n" % repr(t)) if not t: s,t = lex(s) if t[1]=='-': neg = True s,t = lex(s) else: neg = False s, n = parse_SEXPR(s, t) if neg: n = -n ss, t = lex(s) while t[1]=='+' or t[1]=='-': s, nn = parse_SEXPR(ss) if t[1]=='+': n += nn else: n -= nn ss, t = lex(s) return s, n def parse_RSPEC(s, t=None): ss = s if not t: s,t = lex(s) if t[1]=='R': s = parse_char(s, '[') s,n = parse_EXPR(s) s = parse_char(s, ']') else: if not t[1] in names: raise ValueError("Syntax Error:%s:%d: register name expected:%s\n" % (fname,lineno,ss)) n = names[t[1]] if n<0 or n>=0x400: raise ValueError("Value Error:%s:%d: Register number out of range 0x%02x:%s\n" % (fname,lineno, n, ss)) return s, n&0xff def parse_COMMAND(s, t=None): if not t: s,t = lex(s) cc = keyw["if1"] if t[1] in ccodes: cc = keyw[t[1]] s, t = lex(s) if t[1] not in mnems: raise ValueError("Syntax Error:%s:%d: MNEMONIC expected:%s\n" % (fname,lineno,s)) m = t[1] if m=='ADDI' or m=='PHA': return parse_ADDI(s, m, cc) if m=='ADD' or m=='SUB' or m=='HIST': return parse_ADD(s, m, cc) if m=='MULI': return parse_MUL(s, m, cc) if m=='LOG': return parse_LOG(s, m, cc) if m=='CMP': return parse_CMP(s, m, cc) if m=='BRNG': return parse_BRNG(s, m, cc) if m=='TRIM': return parse_TRIM(s, m, cc) if m=='BITS' or m=='BITC': return parse_BIT(s, m, cc) if m=='STOP' or m=='NOP': return parse_STOP(s, m, cc) if m=='GOTO': return parse_GOTO(s, m, cc) if m=='POKE': return parse_POKE(s, m, cc) raise ValueError("This Cannot Happen") def output_format_printf(addr, op): out.write(output_format_string % (addr, op)) def output_format_error(addr, op): raise IndexError("""error: unknown output format: '%s' -g hex hex dump -g verilog verilog l3_code task call """ % form) def output_format(addr, op): global output_format, output_format_string output_format = output_format_error if form=="hex": output_format = output_format_printf output_format_string = "@%03x %08x\n" if form=="verilog": output_format = output_format_printf output_format_string = " l3_code(10'h %03x, 32'h %08x);\n" if form=="python": output_format = output_format_printf output_format_string = "l3_code(0x%03x, 0x%08x)\n" output_format(addr, op) def issue_cmd(cc, oo, x=0, y=0, i=0, ii=0): global addr op = cc << 30 op |= oo << 24 op |= x & 0xff op |= (y & 0xff) << 16 op |= i << 8 op |= ii << 12 output_format(addr, op) if verbose>=3: log.write(l3dis(op, addr=addr, form='@%(addr)-3d %(cond)-3s %(mnem)-5s %(args)s\n')) addr += 1 def parse_STOP(s, m, cc): issue_cmd(cc, mnems[m][0], i=mnems[m][2]) return s def parse_GOTO(s, m, cc): ss, n = parse_EXPR(s) if n<0 or n>=0x400: raise ValueError("Value Error:%s:%d: GOTO target out of range 0x%03x:%s\n" % ( fname,lineno, n, s)) issue_cmd(cc, mnems[m][0], i=mnems[m][2] | n) return ss def parse_POKE(s, m, cc): s, d = parse_RSPEC(s) s = parse_char(s, '=') s, x = parse_RSPEC(s) issue_cmd(cc, mnems[m][0], x=x, i=d) return s def parse_ADDI(s, m, cc): s, x = parse_RSPEC(s) ss, t = lex(s) if t[1] in ['+', '-', ',']: s, n = parse_EXPR(ss, t if t[1]=='-' else None) if m=='PHA': if n<0 or n>0x10000: raise ValueError("Value Error:%s:%d: PHA constant out of range 0x%04x:%s\n" % ( fname,lineno, n, s)) else: if n<-1048576 or n>1048575: raise ValueError("Value Error:%s:%d: ADDI constant out of range 0x%06x:%s\n" % ( fname,lineno, n, s)) n &= 0x1fffff else: n = 0 issue_cmd(cc, mnems[m][0], x=x, i=n) return s def parse_BSHIFT(s): ss, t = lex(s) if t[1]=='>>' or t[1]=='<<': s, n = parse_EEXPR(ss) if t[1]=='<<': n = -n if not -8 <= n <= 7: raise ValueError("Value Error:%s:%d: ADD SHIFT constant out of range [-8..7] %d:%s\n" % ( fname,lineno, n, ss)) n &= 0xf else: n = 0 return s, n def parse_ADD(s, m, cc): s, x = parse_RSPEC(s) s, nx = parse_BSHIFT(s) ss, t = lex(s) if t[1]=='+' or t[1]=='-' or t[1]==',': s, y = parse_RSPEC(ss) s, ny = parse_BSHIFT(s) if t[1]=='-': if m=='HIST' or m=='HISTS': m = 'HISTS' else: m = 'SUB' if t[1]=='+': if m=='HIST' or m=='HISTS': m = 'HIST' else: m = 'ADD' else: y = names['Z'] ny = 0 issue_cmd(cc, mnems[m][0], x=x, i=nx, y=y, ii=ny) return s def parse_MUL(s, m, cc): s, x = parse_RSPEC(s) s = parse_char(s, '*') ns = 0 s, n = parse_EEXPR(s) if type(n)==float: f = n ff = n n = int(ff) while n<0x7ff and ns<15: ff *= 2 ns += 1 n = int(ff) if n<=0 or n>0x1000: raise ValueError("Value Error:%s:%d: MUL float constant out of range %g:%s\n" % ( fname,lineno, f, s)) else: if n<0 or n>=0x1000: raise ValueError("Value Error:%s:%d: MUL constant out of range 0x%03x:%s\n" % ( fname,lineno, n, s)) ss, t = lex(s) if t[1]=='>>': s, ns = parse_EXPR(ss) if ns<0 or ns>=16: raise ValueError("Value Error:%s:%d: MUL SHIFT constant out of range [0..15] %d:%s\n" % ( fname,lineno, ns, ss)) if n<0 or n>=0x1000: raise ValueError("Value Error:%s:%d: MUL constant out of range 0x%03x:%s\n" % ( fname,lineno, n, s)) issue_cmd(cc, mnems[m][0], x=x, i=n | (ns<<12)) return s def parse_LOG(s, m, cc): s, x = parse_RSPEC(s) issue_cmd(cc, mnems[m][0], x=x) return s def parse_OFFS(s): ss, t = lex(s) if t[1]=='+': s, n = parse_EXPR(ss) if n<0 or n>=0x100: raise ValueError("Value Error:%s:%d: CMP OFFSET out of range [0..255] %d:%s\n" % ( fname,lineno, n, ss)) else: n = 0 return s, n def parse_CMP(s, m, cc): s, x = parse_RSPEC(s) s, ox = parse_OFFS(s) s, t = lex(s) if t[1] not in cmpopers: raise ValueError("Syntax Error:%s:%d: comp operator expected expected:%s\n" % (fname,lineno,s)) s, y = parse_RSPEC(s) s, oy = parse_OFFS(s) if ox != 0 and oy != 0: raise ValueError("Value Error:%s:%d: CMP OFFSETs cannot be both != 0:%s\n" % ( fname,lineno, ss)) ccc = cmpopers[t[1]] if oy != 0: ccc |= 0b1000 issue_cmd(cc, mnems[m][0] | ccc, x=x, y=y, i=ox|oy) return s def parse_BND(s): s = parse_char(s, ',') s, n = parse_EXPR(s) if n<0 or n>=0x100: raise ValueError("Value Error:%s:%d: TRIM boundary out of range [0..255] %d:%s\n" % ( fname,lineno, n, ss)) return s, n def parse_TRIM(s, m, cc): s, x = parse_RSPEC(s) s, b1 = parse_BND(s) s, b2 = parse_BND(s) issue_cmd(cc, mnems[m][0], x=x, i=b1 | (b2<<8)) return s def parse_BRNG(s, m, cc): s, x = parse_RSPEC(s) s, t = lex(s) if t[1]!='{': raise ValueError("Syntax Error:%s:%d: '{' expected:%s\n" % (fname,lineno,s)) s, n1 = parse_EXPR(s) if n1<0 or n1>=32: raise ValueError("Value Error:%s:%d: First bit number out of range [0..28] {%d}:%s\n" % (fname,lineno, n1, s)) s, t = lex(s) if t[1]!=':': n2 = n1 else: s, n2 = parse_EXPR(s) if n2<0 or n2>=32: raise ValueError("Value Error:%s:%d: second bit number out of range [0..28] {%d}:%s\n" % (fname,lineno, n2, s)) s, t = lex(s) if t[1]!='}': raise ValueError("Syntax Error:%s:%d: '}' expected:%s\n" % (fname,lineno,s)) issue_cmd(cc, mnems[m][0], x=x, i=n2 | (n1<<5)) return s def parse_BIT(s, m, cc): s, t = lex(s) n = 0 x = 0 cccc = 0 if t[1]=='~': cccc = 0b1000 s, t = lex(s) if t[1]=='C': cccc |= 0b0001 elif t[1]=='S': cccc |= 0b0010 elif t[1]==0: cccc |= 0b0011 elif t[1]==1: cccc ^= 0b1011 else: s, x = parse_RSPEC(s, t) s, t = lex(s) if t[1]!='{': raise ValueError("Syntax Error:%s:%d: '{' expected:%s\n" % (fname,lineno,s)) s, n = parse_EXPR(s) s, t = lex(s) if t[1]!='}': raise ValueError("Syntax Error:%s:%d: '}' expected:%s\n" % (fname,lineno,s)) if n<0 or n>=32: raise ValueError("Value Error:%s:%d: Bit number out of range [0..28] {%d}:%s\n" % (fname,lineno, n, s)) issue_cmd(cc, mnems[m][0], x=x, i=mnems[m][2] | n | (cccc<<12)) return s includestack = [] def parse_DIRECTIVE(s, t): if t[1] == ".include": fn = s.split()[0].strip('"') f = open(fn) includestack[0:0] = [[f, fn, 0]] if verbose >= 2: log.write("opening include file %s at level %d\n" % (fn, len(includestack))) return "" global forwardfile, forwardfilename if t[1] == ".forwardfile": fn = s.split()[0].strip('"') try: f = open(fn) includestack[0:0] = [[f, fn, 0]] if verbose >= 2: log.write("opening forward file %s for include at level %d\n" % (fn, len(includestack))) except IOError: pass forwardfile = open(fn+".new", "w") forwardfilename = fn if verbose >= 2: log.write("creating forward file %s.new\n" % (fn,)) return "" if t[1] == ".forward": s,t = lex(s) if t[0] != 2: raise ValueError("Syntax Error:%s:%d: ID expected:%s\n" % (fname,lineno,s)) if not forwardfile: raise ValueError("No .forwardfile opened\n") if not t[1] in names: names[t[1]] = 0 forwards.append(t[1]) return s if t[1] == ".print": sss = s.strip().split('$') ss = sss[0] for ssss in sss[1:]: if not ssss: ss += '$' else: if ssss[0]=='{': sssss,t = lex(ssss[1:]) if sssss[0]=='}': sssss=sssss[1:] else: sssss,t = lex(ssss) if t[0]==2 and t[1] in names: ss += ("0x%03x" % names[t[1]])+sssss else: ss += ssss log.write(ss+'\n') return "" raise ValueError("This Cannot Happen") def parser(s): try: s = parse_LINE(s) ss, t = lex(s) if t[1]!=None and t[1]!='#': raise ValueError("Syntax Error:%s:%d: '#' expected:%s\n" % (fname,lineno,s)) except ValueError, e: log.write(str(e)) for s in inp: global fname, lineno fname = inp.filename() lineno = inp.filelineno() parser(s) while includestack: i = includestack[0] try: s = i[0].next() except StopIteration: if verbose >= 2: log.write("closing include file %s, %d lines, at level %d\n" % (i[1], i[2], len(includestack))) i[0].close() includestack[0:1]=[] continue fname = i[1] lineno = i[2]+1 i[2] = lineno parser(s) if forwardconflicts: if verbose >= 2: log.write("replacing forward file %s\n" % (forwardfilename,)) forwardfile.close() os.rename(forwardfilename+".new", forwardfilename)