! (Intel, AMD, etc) 8051 family assembler
! (c) May 1989 RWT - all rights reserved

! The user source file should be structured as follows:
!
! %begin
! %include "<this file>"
!   program name = "<whatever>"  {optional but recommended}
!   <main body of user program>
!   %end {of DO PASS routine which begins at the end of this file}
! %end {matching the %begin}
!
! The contents of PROGRAM NAME are used to name the listing and code files.

%option "-nons"
%ownstring(31)program name = "8051"
%externalroutinespec phex4(%integer x)
%externalroutinespec phex2(%integer x)
%constinteger blank = 255 {0 for on-chip rom, 255 for separate eprom}

%constinteger -
  eprom2732=4096,
  eprom2764=8192,
  eprom27128=16384,
  eprom27256=32768,
  eprom27512=65536,
  promsize=eprom2764
%ownbytearray codebuffer(0:promsize-1)
%owninteger pass,maxpc,pc,errors,orgno,labelno,instno
%ownintegername orglab

! Operand codes:
! Literals in the signed/unsigned byte range stand for themselves.
! Other operands are represented by values greater than 1023.

%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=255-1024,
  bit0=-1536,bit255=255-1536,
  p0=byte0+16_80, p1=byte0+16_90,
  p2=byte0+16_a0, p3=byte0+16_b0,
  sp=byte0+16_81, pcon=byte0+16_87,
  dpl=byte0+16_82, dph=byte0+16_83,
  tcon=byte0+16_88, tmod=byte0+16_89,
  tl0=byte0+16_8a, tl1=byte0+16_8b,
  th0=byte0+16_8c, th1=byte0+16_8d,
  scon=byte0+16_98, sbuf=byte0+16+99,
  ie=byte0+16_a8, ip=byte0+16_b8,
  psw=byte0+16_d0, acc=byte0+16_e0,
  b=byte0+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

%owninteger bitloc=0,byteloc=0

%routine bitvar(%integername var, %integer bits)
  var = bitloc; bitloc = bitloc+bits
%end

%routine bytevar(%integername var, %integer bytes)
  var = byteloc; byteloc = byteloc+bytes
%end

%integerfn bytebit(%integer base,bit)
  mustbebyte(base); error("Invalid bit number") %unless bit>>3=0
  %result = base-byte0+bit0+bit&7
%end

%routine bytevarbit(%integername var, %integer base,bit)
  must be byte(base)
  error("Byte variable not bit-addressable") %unless base&7=0
  var = bytebit(base,bit)
%end

%routine label(%integername x)
  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 xchd(%integer p)
  must be xr(p); dump(16_d4+p&1)
%end

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

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

%routine inc(%integer x)
  %if x=dptr %then dump(16_a3) %else-
  %if x=a %then dump(16_04) %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 dump(16_14) %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)
  %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(p+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("No literal allowed")
  %finish
  binary(16_c0,x)
%end

%routine aox(%integer o,d,s)
  %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 ra,x,l)
  %if ra=a %start
    %if byte(x) %then put(16_b5) %elsestart
      must be literal(x); put(16_b4)
    %finish
    put(x); rel(l)
  %else
    error("Invalid operands") %unless r(ra)
    must be literal(x); put(16_b8+ra&7); put(x); rel(l)
  %finish
%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 byte(d); dump2(16_92,d)
  %elseif d=c
    must be byte(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

%routine push(%integer x); must be byte(x); dump2(16_c0,x); %end
%routine pop(%integer x);  must be byte(x); dump2(16_d0,x); %end

%routine setb(%integer x)
  %if b=c %then dump(16_d3) %elsestart
    must be bit(x); dump2(16_d2,x)
  %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

! Main program begins here

  codebuffer(pc) = blank %for pc = 0,1,promsize-1

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

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

  openoutput(1,program name.".obj"); selectoutput(1)
  maxpc = promsize %if maxpc>promsize
  printsymbol(codebuffer(pc)) %for pc = 0,1,maxpc-1
  closeoutput

  %routine do pass
    errors = 0; maxpc = 0; pc = 0; labelno=0; orgno=0; orglab == nil; instno=1
{END of include file}
