%include "inc:util.imp" {for PAM} %begin {ARM assembler - RWT October 1988} %constinteger - mincond=1,maxcond=18, mincode=19, and=19,eor=20,sub=21,rsb=22,add=23,adc=24,sbc=25,rsc=26, tst=27,teq=28,cmp=29,cmn=30,orr=31,mov=32,bic=33,mvn=34, mul=35,mla=36,str=37,ldr=38,stm=39,ldm=40,swi=41,b=42,bl=43, end=44,org=45,data=46, maxcode=46, minshift=47,maxshift=51,rrx=52, minmulti=53,maxmulti=60, minop=61,star=68,maxop=70, predeflim=70 %constinteger codelimit = 4096 {words} %integerarray code buffer(0:codelimit-1) %integer lineno=0, pass, origin, codepointer, pos, i,j,k %string(255)source,binary="",listing="",line="",item,label,message="" %routine parterror(%string(255)s) ! Accumulate an error message message = message.s %unless pass=1 %end %routine error(%string(255)s) ! Output an error message. ! NB error lines in the listing appear BEFORE the line to which they refer. ! On the report stream the offending line is printed first (but once only). %owninteger errorline=0 %integer o %returnif pass=1 s = message.s; message = "" %for o = 0,1,1 %cycle selectoutput(o) %if o=0 %and errorline#lineno %start errorline = lineno write(lineno,3); space; printstring(line); newline %finish write(lineno,3); printstring(" ** "); printstring(s); newline %repeat %end %routine printline(%integer which,n2,n1) ! Print a "normal" line to the listing file. ! Bits in WHICH determine which of the numbers N2 and/or N1 should be printed. %returnunless pass=2 write(lineno,3) %if which&2#0 %start space; phex(n2) %elseif which&1#0 %or line#"" spaces(9) %finish %if which&1#0 %start space; phex(n1) space %if line#"" %elseif line#"" spaces(10) %finish printstring(line) newline %end ! Symbol table management %constinteger symbol limit = 1000, char limit = 6000, hash mask = 31 %recordformat hash fm (%integer next,index,value) %bytearray char store (1:char limit) %record(hash fm)%array cell(1:symbol limit) %integerarray hash table(0:hash mask) %integerfn encode(%string(255)s,%integer add,value) ! Code S as a dense number (i.e. the first name will encode as 1, ! the next as 2, etc). If the name is already in the symbol table, ! return the number already allocated to it. ! ADD#0: Insert symbol if not there, check value if there already. ! ADD=0: Lookup only. %record(hash fm)%name c %integer cno,k,i,hashval = 0 %owninteger charpos = 1, freelist = 1 %if charpos+length(s)+1>charlimit %start error("Too many long symbols") %result = freelist-1 %finish charstore(charpos) = length(s) %for i = 1,1,length(s) %cycle k = charno(s,i) charstore(charpos+i) = k hashval = hashval<<1!!k %repeat hashval = hashval&hashmask k = length(s) cno = hashtable(hashval) %while cno#0 %cycle c == cell(cno) %if charstore(c_index)=k %start i = k %cycle %if i=0 %start %if add>0 %and c_value#value %start %if cno<=predeflim %start parterror("Cannot redefine built-in symbol ") %else parterror("Discrepancy "); parterror(itos(value-c_value,0)) parterror(" for symbol ") %finish error(s) %finish %result = cno %finish %exitunless charstore(c_index+i)=charstore(charpos+i) i = i-1 %repeat %finish cno = c_next %repeat %result = -1 %if add=0 cno = freelist %if cno>symbollimit %start error("Symbol table full") %result = cno-1 %finish c == cell(cno); freelist = freelist+1 c_next = hashtable(hashval); hashtable(hashval) = cno c_index = charpos; c_value = value charpos = charpos+k+1 %result = cno %end %routine setup symbol table ! Initialise the raw table and then insert the prefdefined symbols ! into it. The dense encoding scheme means we know what codes each ! of the symbols will have, and these are used later on. %integer i %routine insert(%string(5)s,%integer v) i = encode(s,1,v) %end hashtable(i) = 0 %for i = 0,1,hashmask ! Condition codes insert("EQ",16_00000000); insert("NE",16_10000000) insert("CS",16_20000000); insert("CC",16_30000000) insert("HS",16_20000000); insert("LO",16_30000000) {alternatives} insert("MI",16_40000000); insert("PL",16_50000000) insert("VS",16_60000000); insert("VC",16_70000000) insert("HI",16_80000000); insert("LS",16_90000000) insert("GE",16_A0000000); insert("LT",16_B0000000) insert("GT",16_C0000000); insert("LE",16_D0000000) insert("AL",16_E0000000); insert("NV",16_F0000000) ! Opcodes insert("AND",16_00000000); insert("EOR",16_00200000) insert("SUB",16_00400000); insert("RSB",16_00600000) insert("ADD",16_00800000); insert("ADC",16_00A00000) insert("SBC",16_00C00000); insert("RSC",16_00E00000) insert("TST",16_01000000); insert("TEQ",16_01200000) insert("CMP",16_01400000); insert("CMN",16_01600000) insert("ORR",16_01800000); insert("MOV",16_01A00000) insert("BIC",16_01C00000); insert("MVN",16_01E00000) insert("MUL",16_00000090); insert("MLA",16_00200090) insert("STR",16_04000000); insert("LDR",16_04100000) insert("STM",16_08000000); insert("LDM",16_08100000) insert("SWI",16_0F000000) insert("B",16_0A000000); insert("BL",16_0B000000) ! Miscellaneous directives insert("END",0); insert("ORG",0); insert("DATA",0) ! Shift codes insert("LSL",0); insert("LSR",32) insert("ASR",64); insert("ROR",96) insert("ASL",0); insert("RRX",96) ! STM/LDM suffices insert("DA",16_0000000); insert("IA",16_0800000) insert("DB",16_1000000); insert("IB",16_1800000) insert("ED",16_0000000); insert("EA",16_0800000) insert("FD",16_1000000); insert("FA",16_1800000) ! Operators for literal expressions insert("+",0); insert("-",1); insert("&",2); insert("!",3) insert("!!",4); insert("<<",5); insert(">>",6) insert("*",7); insert("/",8); insert("%",9) ! Ordinary symbols, beginning with register names insert("R0",0); insert("R1",1); insert("R2",2); insert("R3",3) insert("R4",4); insert("R5",5); insert("R6",6); insert("R7",7) insert("R8",8); insert("R9",9); insert("R10",10); insert("R11",11) insert("R12",12); insert("R13",13); insert("R14",14); insert("R15",15) insert("PC",15); insert("LINK",14) %end %integerfn valof(%integer cellno) %result = cell(cellno)_value %end %integerfn value ! Returns the value of ITEM. ! ITEM is either a literal number or a symbol. %integer n=0,b=10,m,k,l=length(item),p=1 k = charno(item,p) %if '0'<=k<='9' %start %if charno(item,l)='H' {"Standard" grotty hex notation} %start b = 16; l = l-1 %finish m = '0'+b-1 m = m+7 %if b>10 %cycle n = n*b-'0'+k; p = p+1; %exitif p>l; k = charno(item,p) %if k='_' %start {preferred notation for arbitrary radix} b = n m = '0'+b-1 m = m+7 %if b>10 n = 0; k = '0' %elseif k<'0' %or k>m %or '9''9' %finish %repeat %result = n %finish k = encode(item,0,0) %if k<=predeflim %start %result = codepointer<<2+origin %if k=star %if k<0 %start parterror("Unknown symbol ") %else parterror("Inappropriate context for symbol ") %finish error(item) %result = 0 %finish %result = valof(k) %end %routine readline ! Increment the line number, then read a new line from the ! source file into string LINE (the NL is not put into the string). ! Set POS to 1 for subsequent scanning. %integer k lineno = lineno+1 line = "" %cycle readsymbol(k); %exitif k=nl line = line.tostring(k) %repeat pos = 1 %end %integerfn getsym ! Read one character out of the stored line, advancing POS past it. ! Return -1 if off end. %result = -1 %if pos>length(line) pos = pos+1; %result = charno(line,pos-1) %end %predicate nextis(%integer k) ! If the next character on the line is K, skip it and return true. ! Ignore spaces (unless K=' '). ! If K=';' we are testing for either start of comment or end of line. %unless k=' ' %start pos = pos+1 %while pos<=length(line) %and charno(line,pos)=' ' %finish %if pos>length(line) %start %trueif k=';'; %false %finish %falseunless charno(line,pos)=k pos = pos+1; %true %end %routine expect(%integer e) ! The next character on the line should be E. If it isn't, report ! everything we encounter until E or end-of-line as spurious. %integer k %returnif nextis(e) k = getsym %unless k<0 %start parterror("Spurious """) %cycle parterror(tostring(k)); k = getsym %repeatuntil k=e %or k<0 error("""") %andreturnif e=';' parterror(""" when ") %finish parterror(""""); parterror(tostring(e)) error(""" expected") %end %routine getitem ! Read into ITEM a string of alphanumerics (upper case, including '_') ! or a single non-alphanumeric symbol or one of the double symbols ! "!!" or "<<" or ">>" or "//". Spaces are ignored. ! Return with ITEM="" if nothing relevant remains on line. %integer k item = "" k = getsym %until k#' ' {skip spaces} pos = pos-1 %andreturnif k=';' {comment reached} %returnif k<0 {end of line reached} %while 'a'<=k!32<='z' - %or '0'<=k<='9' - %or k='_' %cycle {accumulate item} k = k-32 %if 'a'<=k<='z' {force upper case} item = item.tostring(k) k = getsym %repeat %if item="" %start {first K was non-alphanumeric} item = tostring(k) %if (k='!' %or k='/' %or k='<' %or k='>') - %and nextis(k) %start {double symbol} item = item.tostring(k) %unless k='/' %finish %else pos = pos-1 %unless k<0 %finish %end %predicate begins(%integer k) ! If ITEM begins with K, strip it off and return TRUE. %falseif item="" %or charno(item,1)#k item = substring(item,2,length(item)) %true %end %integerfn expr {accumulate literal expression} ! NB operator precedence strictly left to right, an initial ! minus or not sign is allowed and applies to the first item only. %integer val1,val2,op %conststring junk="Literal expected" %switch sw(minop:maxop) %integerfn thing ! Read either a quoted (multi-)character or other literal or a symbol. %integer val,k %if nextis('''') %start val = 0 %cycle k = getsym %if k<0 %start expect(''''); %result = val %finish %if k='''' %start %result = val %unless nextis(k) %finish val = val<<8+k %if val&16_ff000000#0 %start expect(''''); %result = val %finish %repeat %finish getitem %if item="" %or item="," %start pos = pos-1 %unless item="" error(junk) %result = 0 %finish %result = value %end %if nextis('-') %start val1 = -thing %elseif nextis('\') val1 = \thing %finishelse val1 = thing %cycle getitem %result = val1 %if item="" op = encode(item,0,0) %if minop<=op<=maxop %start val2 = thing ->sw(op) %finish pos = pos-length(item) %result = val1 sw(minop+0): val1 = val1+val2; %continue sw(minop+1): val1 = val1-val2; %continue sw(minop+2): val1 = val1&val2; %continue sw(minop+3): val1 = val1!val2; %continue sw(minop+4): val1 = val1!!val2; %continue sw(minop+5): val1 = val1<>val2; %continue sw(minop+7): val1 = val1*val2; %continue sw(minop+8): val1 = val1//val2; %continue sw(minop+9): val1 = rem(val1,val2) %repeat %end %integerfn register ! Read a literal or symbol, expecting it to be in the range 0-15. %integer r getitem %if item="," %start pos = pos-1; item = "" %finish %if item="" %start error("Register expected"); %result = 0 %finish r = value %result = r %if r=r&15 parterror("Register "); parterror(itos(r,0)); error("?") %result = 0 %end %integerfn registerlist ! Read a single register or a list enclosed in {}. ! Within the list individual registers are separated by commas, ! register ranges are indicated by separating two register names with a dash. %integer list=0,r1,r2,t %result = 1<r2 %start t = r1; r1 = r2; r2 = t %finish r1 = 1<32 %start error("Invalid shift count") s = s&31 %elseif s=0 %and t#0 error("Warning: shift type changed to LSL") t = 0 %elseif s=32 s = 0 %if t=96 {ror} %start error("Warning: ROR #32 changed to LSL #0") t = 0 %elseif t=0 error("Warning: LSL #32 means LSL #0") %finish %finish %result = m+t+s<<7 %finish {otherwise shift count in register} %if full=0 %start error("Shift count cannot be register in this context") %result = m+t %finish s = register %result = m+t+s<<8+16 %finish error("Shift type expected") %result = m %end %integerfn operand ! Read operand for DP instructions: 8 bit rotated literal or shifted register %integer imm,rot=0 %if nextis('#') %start imm = expr %cycle %exitif imm=imm&255 imm = imm<<2+imm>>30; rot = (rot+1)&15 %if rot=0 %start parterror(itos(imm,0)) error(" is not a suitable immediate value") imm = 0; %exit %finish %repeat %result = 1<<25+rot<<8+imm %finish %result = shift(1) %end %integerfn address(%integer must be postinc) ! Read address for LDR/STR instruction: PC-relative label ! or pre-indexed: [R{,{#expr|{+|-|}R{,shift}}]{!} ! or post-indexed: [R],{#expr|{+|-|}R{,shift}} %conststring wrongmode="T suffix requires post-increment mode" %integer m,n,o %routine offset %if nextis('#') %start {immediate offset} o = expr o = o-8 %if n>>16&15=15 %else {register offset} n = n+1<<25 %unless nextis('-') %start n = n+1<<23 %if nextis('+') %start; %finish %finish m = register o = shift(0) %if nextis(',') %finish %end m = 0; o = 0 %unless nextis('[') %start {PC relative label} error(wrongmode) %unless mustbepostinc=0 n = 16_10F0000 o = expr-codepointer<<2-8-origin %else {otherwise indexed} n = register<<16 %if nextis(',') %start {pre-indexed with nonzero offset} error(wrongmode) %unless mustbepostinc=0 n = n+1<<24 offset expect(']') n = n+1<<21 %if nextis('!') %else {post-indexed (or pre with zero offset)} expect(']') %if nextis(',') %start {post-indexed} offset %elseif mustbepostinc=0 {zero offset} n = n+1<<24 %finish %finish %finish %if n&1<<25=0 %start %unless |o|<=16_FFF %start parterror("Offset "); parterror(itos(|o|,0)); error(" too big") %finish %if o<0 %then o = -o %else n = n+1<<23 %finish %result = o&16_FFF+n+m %end %integerfn condition(%integer default) ! Strip the (by now leading) condition code characters off the opcode. ! Return the appropriate result if a valid condition was recognised. ! Otherwise return DEFAULT, which is either ALWAYS or INVALID. %integer x %result = default %if length(item)<2 x = encode(substring(item,1,2),0,0) %result = default %unless mincond<=x<=maxcond item = substring(item,3,length(item)) %result = valof(x) %end %routine suffix ! Ensure all characters in the opcode mnemonic have been used up. %returnif item="" parterror("Spurious opcode suffix "); error(item) %end %routine assemble line %constinteger invalid=1,always=16_E0000000,never=16_F0000000,CCbit=1<<20 %constinteger Lbit=CCbit,Wbit=1<<21,BSbit=1<<22 %integer op,cond,d,s,m,n %switch sw(mincode:maxcode) %unless nextis(' ') %start {maybe label} getitem %if item="" %start {no: blank line or comment only} printline(0,0,0) %return %finish label = item %if nextis('=') %start {literal definition} %if label="*" %start {Setting origin} sw(org):op = expr %if origin!codepointer=0 %start error("Origin must be word-aligned") %if op&3#0 origin = op %else error("Origin can only be defined at beginning") %finish %else op = valof(encode(label,1,expr)) %finish expect(';') printline(1,0,op) %return %finish %if nextis(':') %start; %finish {label definition} op = encode(label,1,codepointer<<2+origin) %finish getitem %if item="" %start {null statement} printline(0,0,0) %return %finish cond = invalid op = encode(item,0,0) %if mincode<=op<=maxcode %start cond = always; item = "" %elseif length(item)=3 %and charno(item,1)='B' item = substring(item,2,3) cond = condition(invalid) op = b %elseif length(item)=4 %and substring(item,1,2)="BL" item = substring(item,3,4) cond = condition(invalid) op = bl %elseif length(item)>3 {basic opcode} op = encode(substring(item,1,3),0,0) %if mincode<=opdone %finish ->sw(op) sw(and):sw(eor):sw(sub):sw(rsb): sw(add):sw(adc):sw(sbc):sw(rsc): sw(orr):sw(bic): op = valof(op) op = op+CCbit %if begins('S') suffix; d = register<<12; expect(',') binary: n = register<<16 unary: expect(',') op = op+d+n+operand ->done sw(tst):sw(teq):sw(cmp):sw(cmn): op = valof(op)!CCbit %if begins('S') %start; %finish d = 0; d = 16_F000 {%and op = op!CCbit} %if begins('P') suffix; ->binary sw(mov):sw(mvn): op = valof(op) op = op!CCbit %if begins('S') suffix; d = register<<12 n = 0; ->unary sw(mul):sw(mla): op = valof(op) op = op!CCbit %if begins('S') suffix; d = register expect(','); m = register expect(','); s = register n = 0 %if op&Wbit#0 %start expect(','); n = register %finish %if d=15 %start error("Destination must not be PC") %elseif d=m %if d=s %start error("Destination must differ from first operand") %else m = s; s = d error("Warning: source operands switched round") %finish %finish op = op+((d<<4+n)<<4+s)<<8+m ->done sw(str):sw(ldr): op = valof(op) op = op!BSbit %if begins('B') op = op!Wbit %if begins('T') suffix op = op+register<<12 expect(',') op = op+address(op&Wbit) ->done sw(stm):sw(ldm): op = valof(op) %if length(item)<2 %then d = 0 %else d = encode(substring(item,1,2),0,0) %if minmulti<=d<=maxmulti %start op = op!!16_1800000 %if op&Lbit#0 %and d-minmulti>=4 op = op!!valof(d) item = substring(item,3,length(item)) %elseif item="" error("Suffix expected") %else parterror("Unrecognised suffix "); error(item) %finish op = op+register<<16 op = op+Wbit %if nextis('!') expect(',') op = op+registerlist op = op!BSbit %if nextis('^') ->done sw(swi): suffix op = expr&16_ffffff+valof(op) ->done sw(b):sw(bl): suffix d = expr-codepointer<<2-8-origin error("Destination must be word-aligned") %unless d&3=0 error("Destination too far") %unless -16_4000000<=d<=16_3fffffc op = d>>2&16_ffffff+valof(op) ->done sw(end): expect(';') printline(2,codepointer<<2,0) %signal 9 sw(data): cond = 0; op = expr done: op = op+cond expect(';') printline(3,codepointer<<2+origin,op) codebuffer(codepointer) = op codepointer = codepointer+1 %end %routine extend(%string(255)%name s,%string(3)x) %string(255)aft,temp temp = s temp = aft %while temp -> (":").aft s = s.".".x %unless s -> (".").aft %end ! Acquire parameters defineparam("Source file",source,pamnodefault) defineparam("Binary file",binary,pamnewgroup) defineparam("Listing file",listing,0) processparameters(cliparam) binary = source.".obj" %if binary="" listing = source.".lis" %if listing="" extend(source,"arm") extend(binary,"obj") extend(listing,"lis") ! Initialise pass = 0 setup symbol table ! Process source file twice openinput(1,source); selectinput(1) %begin %on 9 %start %if pass=2 %start closeoutput %return %finish resetinput openoutput(1,listing); selectoutput(1) %finish pass = pass+1; codepointer = 0; origin = 0; lineno = 0 %cycle read line assemble line %repeat %end ! Output code buffer openoutput(1,binary); selectoutput(1) %for i = 0,1,codepointer-1 %cycle k = code buffer(i) printsymbol(k>>j&255) %for j = 24,-8,0 %repeat %end