! Copyright (c) May 1989
! Rainer W Thonnes, Computer Science Dept, University of Edinburgh
! All rights reserved

! Assembler for Intel 8051 family of microcontrollers
! Last revised August 1990

! The user source file should be structured as follows:
!
! %begin
! %include "<this file>"
!   <main body of user program>
!   %end {of DO PASS routine which begins at the end of this file}
! %end {matching the %begin}

%option "-nonstandard-nodiag-nocheck-nolist"
%string(63)filename
%externalroutinespec phex4(%integer x)
%externalroutinespec phex2(%integer x)
%constinteger blank = 255 {0 for on-chip rom, 255 for separate eprom}
%constinteger promsize=65536
%bytearray codebuffer(0:promsize-1)
%integer pass,maxpc,pc,errors,orgno,labelno,instno,bitloc,byteloc
%integername orglab


! Operand codes:
! Literals in the signed/unsigned byte range -128:255 stand for themselves.
! Other operands are represented by values less than -256:
! Registers R0:7                                by -264+0:7,
! Indirect byte operands  @R0:1 (written XR0:1) by -266+0:1,
! Special registers A, C, and DPTR              by -267/8/9,
! Indirect memory operand @DPTR (written XDPTR) by -270,
! Table lookup operand @A+DPTR (written XADPTR) by -271,
! Table lookup operand @A+PC (written XAPC)     by -272,
! Direct byte operands 0:255                    by -1024+0:255,
! Direct bit operands 0:255                     by -1024-512+0:255.

%constinteger -
  minnum=-128,maxnum=255,
  r7=-257,r6=-258,r5=-259,r4=-260,
  r3=-261,r2=-262,r1=-263,r0=-264,
  xr1=-265,xr0=-266,
  a=-267,c=-268,dptr=-269,
  xdptr=-270,xadptr=-271,xapc=-272,
  byte0=-1024,byte255=byte0+255,
  bit0=-1536,bit255=bit0+255

! Special Function Registers

%constinteger -
  P0     = byte0+16_80,  SP     = byte0+16_81,
  DPL    = byte0+16_82,  DPH    = byte0+16_83,
  PCON   = byte0+16_87,
  TCON   = byte0+16_88,  TMOD   = byte0+16_89,
  TL0    = byte0+16_8A,  TL1    = byte0+16_8B,
  TH0    = byte0+16_8C,  TH1    = byte0+16_8D,
  P1     = byte0+16_90,
  SCON   = byte0+16_98,  SBUF   = byte0+16_99,
  P2     = byte0+16_A0,  IE     = byte0+16_A8,
  P3     = byte0+16_B0,  IP     = byte0+16_B8,
  T2CON  = byte0+16_C8,
  RCAP2L = byte0+16_CA,  RCAP2H = byte0+16_CB,
  TL2    = byte0+16_CC,  TH2    = byte0+16_CD,
  PSW    = byte0+16_D0,
  ACC    = byte0+16_E0,  B      = byte0+16_F0

! SFR addressable bits

%constinteger -
  p0 7 = bit0+16_87, p0 6 = bit0+16_86, p0 5 = bit0+16_85, p0 4 = bit0+16_84,
  p0 3 = bit0+16_83, p0 2 = bit0+16_82, p0 1 = bit0+16_81, p0 0 = bit0+16_80,
  tcon TF1 = bit0+16_8F, tcon TR1 = bit0+16_8E,
  tcon TF0 = bit0+16_8D, tcon TR0 = bit0+16_8C,
  tcon IE1 = bit0+16_8B, tcon IT1 = bit0+16_8A,
  tcon IE0 = bit0+16_89, tcon IT0 = bit0+16_88,
  p1 7 = bit0+16_97, p1 6 = bit0+16_96, p1 5 = bit0+16_95, p1 4 = bit0+16_94,
  p1 3 = bit0+16_93, p1 2 = bit0+16_92, p1 1 = bit0+16_91, p1 0 = bit0+16_90,
  scon SM0 = bit0+16_9F, scon SM1 = bit0+16_9E,
  scon SM2 = bit0+16_9D, scon REN = bit0+16_9C,
  scon TB8 = bit0+16_9B, scon RB8 = bit0+16_9A,
  scon TI  = bit0+16_99, scon RI  = bit0+16_98,
  p2 7 = bit0+16_A7, p2 6 = bit0+16_A6, p2 5 = bit0+16_A5, p2 4 = bit0+16_A4,
  p2 3 = bit0+16_A3, p2 2 = bit0+16_A2, p2 1 = bit0+16_A1, p2 0 = bit0+16_A0,
  ie EA  = bit0+16_AF,
  ie ET2 = bit0+16_AD, ie ES  = bit0+16_AC,
  ie ET1 = bit0+16_AB, ie EX1 = bit0+16_AA,
  ie ET0 = bit0+16_A9, ie EX0 = bit0+16_A8,
  p3 7 = bit0+16_B7, p3 6 = bit0+16_B6, p3 5 = bit0+16_B5, p3 4 = bit0+16_B4,
  p3 3 = bit0+16_B3, p3 2 = bit0+16_B2, p3 1 = bit0+16_B1, p3 0 = bit0+16_B0,
  ip PT2 = bit0+16_BD, ip PS  = bit0+16_BC,
  ip PT1 = bit0+16_BB, ip PX1 = bit0+16_BA,
  ip PT0 = bit0+16_B9, ip PX0 = bit0+16_B8,
  t2con TF2   = bit0+16_CF, t2con EXF2  = bit0+16_CE,
  t2con RCLK  = bit0+16_CD, t2con TCLK  = bit0+16_CC,
  t2con EXEN2 = bit0+16_CB, t2con TR2   = bit0+16_CA,
  t2con CT2   = bit0+16_C9, t2con CPRL2 = bit0+16_C8,
  psw CY = bit0+16_D7, psw AC = bit0+16_D6,
  psw F0 = bit0+16_D5, psw RS1 = bit0+16_D4,
  psw RS0 = bit0+16_D3, psw OV = bit0+16_D2,
  psw P = bit0+16_D0,
  acc7 = bit0+16_E7, acc6 = bit0+16_E6, acc5 = bit0+16_E5, acc4 = bit0+16_E4,
  acc3 = bit0+16_E3, acc2 = bit0+16_E2, acc1 = bit0+16_E1, acc0 = bit0+16_E0,
  b7 = bit0+16_F7, b6 = bit0+16_F6, b5 = bit0+16_F5, b4 = bit0+16_F4,
  b3 = bit0+16_F3, b2 = bit0+16_F2, b1 = bit0+16_F1, b0 = bit0+16_F0

%routine error(%string(255)s)
! Print error message (NL deferred to allow for error details)
  %routine report
    phex4(pc)
    %unless orglab==nil %start
      space
      %if orglab==orgno %then printsymbol('O') %else printsymbol('L')
      write(orglab,0)
    %finish
    printstring(" I"); write(instno,0)
    %if s="" %start
      write(errors,1); printstring(" error")
      printsymbol('s') %unless errors=1
    %else
      printstring("  *****  ")
      printstring(s)
    %finish
  %end
  %returnif pass=1
  selectoutput(0); report; newline
  selectoutput(1); newline; report
  errors = errors+1
%end

%routine put(%integer x)
  codebuffer(pc) = x&255 %unless pc>=promsize
  pc = pc+1; maxpc = pc %if pc>maxpc
%end

%routine dump(%integer x)
  put(x); instno = instno+1
%end

%routine dump2(%integer x,y)
  put(x); put(y); instno = instno+1
%end

%routine dump3(%integer x,y,z)
  put(x); put(y); put(z); instno = instno+1
%end

! Checkers

%predicate r(%integer n); %trueif n!7=r7; %false; %end
%predicate xr(%integer n); %trueif n!1=xr1; %false; %end
%predicate bit(%integer n); %trueif n!255=bit255; %false; %end
%predicate byte(%integer n); %trueif n!255=byte255; %false; %end
%predicate literal(%integer n); %trueif minnum<=n<=maxnum; %false; %end

%routine must be r(%integer n); %returnif r(n)
  error("Operand R0-R7 expected"); %end
%routine must be xr(%integer n); %returnif xr(n)
  error("Operand XR0-XR1 expected"); %end
%routine must be bit(%integer n); %returnif bit(n)
  error("Bit variable expected"); %end
%routine must be byte(%integer n); %returnif byte(n)
  error("Byte variable expected"); %end
%routine must be literal(%integer n); %returnif literal(n)
  error("Literal operand expected"); %end

%routine rel(%integer label)
  %unless pass=1 %start
    error("Label not declared") %if label=0; label = label-pc-1
    error("Label too far for relative jump") %unless -128<=label<=127
  %finish
  dump(label)
%end

%predicate reachable(%integer x)
  %trueif (x!!(pc+2))&(-16_800) = 0; %false
%end
  
%routine reach(%integer x)
  error("Label not declared") %if x=0
  error("Label out of reach") %unless reachable(x)
%end

! Declarators

%routine bitvar(%integername var, %integer bits)
! Declare VAR as a bit variable of size BITS (normally 1)
  var = bitloc; bitloc = bitloc+bits
%end

%routine bytevar(%integername var, %integer bytes)
! Declare VAR as a byte variables of size BYTES (normally 1)
  var = byteloc; byteloc = byteloc+bytes
%end

%integerfn bytebit(%integer byte,bit)
! Construct (i.e. return as result) a bit variable which
! is bit number BIT within the existing byte variable BYTE.
  mustbebyte(byte)
  error("Invalid bit number") %unless bit>>3=0
  error("Byte variable not bit-addressable") %unless byte&7=0
  %result = byte-byte0+bit0+bit&7
%end

%routine bytevarbit(%integername var, %integer byte,bit)
! Declare VAR as a bit variable, being bit BIT in byte variable BYTE.
  must be byte(byte)
  var = bytebit(byte,bit)
%end

%routine label(%integername x)
! Define a label at the current location counter (PC) value.
  orglab == labelno; labelno = labelno+1; instno = 1
  %if pass=1 %start
    x = pc
  %else
    %unless x=pc %start
      error("Duplicate label "); phex4(x)
    %finish
  %finish
%end

! Instructions

%routine nop;     dump(16_00); %end
%routine swap a;  dump(16_c4); %end
%routine rl a;    dump(16_23); %end
%routine rlc a;   dump(16_33); %end
%routine rr a;    dump(16_03); %end
%routine rrc a;   dump(16_13); %end
%routine ret;     dump(16_22); %end
%routine reti;    dump(16_32); %end
%routine mul ab;  dump(16_a4); %end
%routine div ab;  dump(16_84); %end
%routine daa;     dump(16_d4); %end
%routine jmp xad; dump(16_73); %end

%routine clr a;    dump(16_e4); %end
%routine clr c;    dump(16_c3); %end
%routine setb c;   dump(16_d3); %end
%routine cpl a;    dump(16_f4); %end
%routine cpl c;    dump(16_b3); %end
%routine inc dptr; dump(16_a3); %end
%routine inc a;    dump(16_04); %end
%routine dec a;    dump(16_14); %end

%routine push(%integer x); mustbebyte(x); dump2(16_c0,x); %end
%routine pop (%integer x); mustbebyte(x); dump2(16_d0,x); %end

%routine xchd(%integer p)
  must be xr(p); dump(16_d4+p&1)
%end

%routine clr(%integer x)
  %if x=a %then clr a %else-
  %if x=c %then clr c %elsestart
    must be bit(x); dump2(16_c2,x)
  %finish
%end

%routine setb(%integer x)
  %if x=c %then setb c %elsestart
    must be bit(x); dump2(16_d2,x)
  %finish
%end

%routine cpl(%integer x)
  %if x=a %then cpl a %else-
  %if x=c %then cpl c %elsestart
    must be bit(x); dump2(16_b2,x)
  %finish
%end

%routine inc(%integer x)
  %if x=dptr %then inc dptr %else-
  %if x=a %then inc a %else-
  %if r(x) %then dump(16_08+x&7) %else-
  %if xr(x) %then dump(16_06+x&1) %elsestart
    must be byte(x); dump2(16_05,x)
  %finish
%end

%routine dec(%integer x)
  %if x=a %then dec a %else-
  %if r(x) %then dump(16_18+x&7) %else-
  %if xr(x) %then dump(16_16+x&1) %elsestart
    must be byte(x); dump2(16_15,x)
  %finish
%end

%routine binary(%integer o,p)
! Auxiliary routine for ADD/ADDC/SUBB/XCH
  %if literal(p) %start
    dump2(o+4,p)
  %elseif r(p)
    dump(o+8+p&7)
  %elseif xr(p)
    dump(o+6+p&1)
  %else
    must be byte(p); dump2(o+5,p)
  %finish
%end

%routine add(%integer x);  binary(16_20,x); %end
%routine addc(%integer x); binary(16_30,x); %end
%routine subb(%integer x); binary(16_90,x); %end

%routine xch(%integer x)
  %if literal(x) %start
    x = byte0; error("XCH: no literal allowed")
  %finish
  binary(16_c0,x)
%end

%routine aox(%integer o,d,s)
! Auxiliary routine for ANL/ORL/XRL (and/or/xor)
  %if d=a %then binary(o,s) %else-
  %if d=c %start
    %if bit(\s) %start
      s = s!!255; o = o+16_60-16_32
    %elseunless bit(s)
      s = bit0
      error("Invalid source for destination C")
    %finish
    dump2(o+16_32,s)
  %else
    error("Invalid destination") %unless byte(d)
    %if s=a %then dump2(o+2,d) %elsestart
      must be literal(s); dump3(o+3,d,s)
    %finish
  %finish
%end

%routine anl(%integer d,s); aox(16_50,d,s); %end
%routine orl(%integer d,s); aox(16_40,d,s); %end

%routine xrl(%integer d,s)
  error("Invalid destination") %if d=c
  aox(16_60,d,s)
%end

%routine acall(%integer x)
  reach(x); dump2(x>>8<<5+17,x)
%end

%routine ajmp(%integer x)
  reach(x); dump2(x>>8<<5+1,x)
%end

%routine sjmp(%integer x)
  put(16_80); rel(x)
%end

%routine lcall(%integer x)
  dump3(18,x>>8,x)
%end

%routine ljmp(%integer x)
  dump3(2,x>>8,x)
%end

%routine call(%integer x)
  %if reachable(x) %then acall(x) %else lcall(x)
%end

%routine jmp(%integer x)
  dump(16_73) %andreturnif x=xadptr
  %if -128<=x-pc-2<=127 %then sjmp(x) %else-
  %if reachable(x) %then ajmp(x) %else ljmp(x)
%end

%routine djnz(%integer x,l)
  %if r(x) %then put(16_d8+x&7) %elsestart
    must be byte(x); put(16_d5); put(x)
  %finish
  rel(l)
%end

%routine cjne(%integer ria,x,l)
  %if ria=a %and byte(x) %start
    put(16_b5)
  %else
    %if ria=a %start
      put(16_b4)
    %elseif xr(ria)
      put(16_b6+ria&1)
    %else
      must be r(ria)
      put(16_b8+ria&7)
    %finish
    must be literal(x)
  %finish
  put(x); rel(l)
%end

%routine jb(%integer b,l);  must be bit(b); put(16_20); put(b); rel(l); %end
%routine jnb(%integer b,l); must be bit(b); put(16_30); put(b); rel(l); %end
%routine jbc(%integer b,l); must be bit(b); put(16_10); put(b); rel(l); %end
%routine jc(%integer l);  put(16_40); rel(l); %end
%routine jnc(%integer l); put(16_50); rel(l); %end
%routine jz(%integer l);  put(16_60); rel(l); %end
%routine jnz(%integer l); put(16_70); rel(l); %end

%routine mov(%integer d,s)
  %if xr(d) %start
    %if s=a %then dump(16_f6+d&1) %else-
    %if byte(s) %then dump2(16_a6+d&1,s) %elsestart
      must be literal(s); dump2(16_76+d&1,s)
    %finish
  %elseif d=a
    %if r(s) %then dump(16_e8+s&7) %else-
    %if xr(s) %then dump(16_e6+s&1) %else-
    %if byte(s) %and s#acc %then dump2(16_e5,s) %elsestart
      must be literal(s); dump2(16_74,s)
    %finish
  %elseif s=c
    must be bit(d); dump2(16_92,d)
  %elseif d=c
    must be bit(s); dump2(16_a2,s)
  %elseif d=dptr
    dump3(16_90,s>>8,s)
  %elseif r(d)
    %if s=a %then dump(16_f8+d&7) %else-
    %if byte(s) %then dump2(16_a8+d&7,s) %elsestart
      must be literal(s); dump2(16_78+d&7,s)
    %finish
  %elseif byte(d)
    %if s=a %then dump2(16_f5,d) %else-
    %if r(s) %then dump2(16_88+s&7,d) %else-
    %if xr(s) %then dump2(16_86+s&1,d) %else-
    %if byte(s) %then dump3(16_85,s,d) %elsestart
      must be literal(s); dump3(16_75,d,s)
    %finish
  %else
    error("Invalid operands for MOV"); nop
  %finish
%end

%routine movc(%integer x)
  %if x=xapc %then dump(16_83) %elseif x=xadptr %then dump(16_93)-
  %else error("Invalid operand for MOVC") %and nop
%end

%routine movx(%integer d,s)
  %if s=a %start
    %if xr(d) %then dump(16_f2+d&1) %else-
    %if d=xdptr %then dump(16_f0) %else-
      error("Invalid MOVX destination") %and nop
  %elseif d=a
    %if xr(s) %then dump(16_e2+s&1) %else-
    %if s=xdptr %then dump(16_e0) %else-
      error("Invalid MOVX source") %and nop
  %else
    error("Invalid MOVX operands"); nop
  %finish
%end

! Directives

%routine org(%integer x)
! Set location counter to X
  error("ORG backwards?") %if x<pc
  pc = x
  orglab == orgno; orgno = orgno+1; instno = 1
%end

%routine data(%integer x)
! Plant in-line data
  dump(x)
%end

%routinespec do pass
%routinespec actually do pass

! Main program begins here

  codebuffer = 0
  filename = cliparam; filename = "$" %if filename=""

  openoutput(1,":N"); selectoutput(1)
  pass = 1; do pass
  closeoutput

  openoutput(1,filename.".lis"); selectoutput(1)
  pass = 2; do pass
  error("Code buffer full") %if maxpc>promsize
  newline; error("")
  newline; closeoutput

  openoutput(1,filename.".obj"); selectoutput(1)
  maxpc = promsize %if maxpc>promsize
  maxpc = maxpc-1 %while maxpc>0 %and codebuffer(maxpc-1)=blank
  printsymbol(codebuffer(pc)) %for pc = 0,1,maxpc-1
  closeoutput

  %routine do pass
    bitloc = bit0; byteloc = byte0
    errors = 0; maxpc = 0; pc = 0
    labelno=0; orgno=0; orglab == nil; instno=1
    actually do pass
  %end

  %routine actually do pass
{END of include file}
