#include <stdio.h>
#include "io.h"
#include "ex.h"
#include "st.h"
#include "res.h"
#include "op.h"

#define ELEMENTS(Arr) (sizeof(Arr)/sizeof(Arr[0]))

/* Translate controls:
   X: . = x     # = #x      / = /x      n = Rn
      A = A     B = AB      C = C       D = DPTR
      i = @Ri   @ = @DPTR   > = @A+DPTR $ = @A+PC
   Y: (x)  L = Long Code Address
           P = Paged Code Address
           R = Relative Code Address
           D = Direct Register Address
      (x/) B = Bit Address
      (@)  w = Word
           b = Byte
      (i)  i = Register Pointer R0/R1
      (n)  n = Register R0/R1/R2/R3/R4/R5/R6/R7
           x = Reverse Order of Next 2 Operands.
  */

Mode ModeTab[] = {
/* acall */ { 0x11, ".",   "P" },
/* add */   { 0x24, "A#",  "b" },  { 0x26, "Ai",  "i" }, { 0x25, "A.",  "D" },
            { 0x28, "An",  "n" },
/* addc */  { 0x34, "A#",  "b" },  { 0x36, "Ai",  "i" }, { 0x35, "A.",  "D" },
            { 0x38, "An",  "n" },
/* ajmp */  { 0x01, ".",   "P" },
/* anl */   { 0x54, "A#",  "b" },  { 0x56, "Ai",  "i" }, { 0x55, "A.",  "D" },
            { 0x58, "An",  "n" },  { 0xb0, "C/",  "B" }, { 0x82, "C.",  "B" },
            { 0x53, ".#",  "Db" }, { 0x52, ".A",  "D" },
/* cjne */  { 0xb6, "i#.", "ibR" },{ 0xb4, "A#.", "bR" },{ 0xb5, "A..", "DR" },
            { 0xb8, "n#.", "nbR" },
/* clr */   { 0xe4, "A",   "" },   { 0xc2, ".",   "B" }, { 0xc3, "C",   "" },
/* cpl */   { 0xf4, "A",   "" },   { 0xb2, ".",   "B" }, { 0xb3, "C",   "" },
/* da */    { 0xd4, "A",   "" },
/* dec */   { 0x16, "i",   "i" },  { 0x14, "A",   ""},   { 0x15, ".",   "D" },
            { 0x18, "n",   "n" },
/* div */   { 0x84, "B",   "" },
/* djnz */  { 0xd5, "..",  "DR" }, { 0xd8, "n.",  "nR" },
/* inc */   { 0x06, "i",   "i" },  { 0x04, "A",   "" },  { 0x05, ".",   "D" },
            { 0xa3, "D",   "" },   { 0x08, "n",   "n" },
/* jb */    { 0x20, "..",  "BR" },
/* jbc */   { 0x10, "..",  "BR" },
/* jc */    { 0x40, ".",   "R" },
/* jmp */   { 0x73, ">",   "" },
/* jnb */   { 0x30, "..",  "BR" },
/* jnc */   { 0x50, ".",   "R" },
/* jnz */   { 0x70, ".",   "R" },
/* jz */    { 0x60, ".",   "R" },
/* lcall */ { 0x12, ".",   "L" },
/* ljmp */  { 0x02, ".",   "L" },
/* mov */   { 0x76, "i#",  "ib" }, { 0xf6, "iA",  "i" }, { 0xa6, "i.",  "iD" },
            { 0x74, "A#",  "b" },  { 0xe6, "Ai",  "i" }, { 0xe5, "A.",  "D" },
            { 0xe8, "An",  "n" },  { 0x92, ".C",  "B" }, { 0xa2, "C.",  "B" },
            { 0x75, ".#",  "Db" }, { 0x86, ".i",  "iD" },{ 0xf5, ".A",  "D" },
            { 0x85, "..",  "xDD" },{ 0x88, ".n",  "nD" },{ 0x90, "D#",  "w" },
            { 0x78, "n#",  "nb" }, { 0xf8, "nA",  "n" }, { 0xa8, "n.",  "nD" },
/* movc */  { 0x93, "A>",  "" },   { 0x83, "A$",  "" },
/* movx */  { 0xf0, "@A",  "" },   { 0xf2, "iA",  "i" }, { 0xe0, "A@",  "" },
            { 0xe2, "Ai",  "i" },
/* mul */   { 0xa4, "B",   "" },
/* nop */   { 0x00, "",    "" },
/* orl */   { 0x44, "A#",  "b" },  { 0x46, "Ai",  "i" }, { 0x45, "A.",  "D" },
            { 0x48, "An",  "n" },  { 0xa0, "C/",  "B" }, { 0x72, "C.",  "B" },
            { 0x43, ".#",  "Db" }, { 0x42, ".A",  "D" },
/* pop */   { 0xd0, ".",   "D" },
/* push */  { 0xc0, ".",   "D" },
/* ret */   { 0x22, "",    "" },
/* reti */  { 0x32, "",    "" },
/* rl */    { 0x23, "A",   "" },
/* rlc */   { 0x33, "A",   "" },
/* rr */    { 0x03, "A",   "" },
/* rrc */   { 0x13, "A",   "" },
/* setb */  { 0xd2, ".",   "B" },  { 0xd3, "C",   "" },
/* sjmp */  { 0x80, ".",   "R" },
/* subb */  { 0x94, "A#",  "b" },  { 0x96, "Ai",  "i" }, { 0x95, "A.",  "D" },
            { 0x98, "An",  "n" },
/* swap */  { 0xc4, "A",   "" },
/* xch */   { 0xc6, "Ai",  "i" },  { 0xc5, "A.",  "D" }, { 0xc8, "An",  "n" },
/* xchd */  { 0xd6, "Ai",  "i" },
/* xrl */   { 0x64, "A#",  "b" },  { 0x66, "Ai",  "i" }, { 0x65, "A.",  "D" },
            { 0x68, "An",  "n" },  { 0x63, ".#",  "Db" },{ 0x62, ".A",  "D" }
};

Code CodeTab[] = {
   { "acall", 1 }, { "add",   4 }, { "addc",  4 }, { "ajmp",  1 },
   { "anl",   8 }, { "cjne",  4 }, { "clr",   3 }, { "cpl",   3 },
   { "da",    1 }, { "dec",   4 }, { "div",   1 }, { "djnz",  2 },
   { "inc",   5 }, { "jb",    1 }, { "jbc",   1 }, { "jc",    1 },
   { "jmp",   1 }, { "jnb",   1 }, { "jnc",   1 }, { "jnz",   1 },
   { "jz",    1 }, { "lcall", 1 }, { "ljmp",  1 }, { "mov",  18 },
   { "movc",  2 }, { "movx",  4 }, { "mul",   1 }, { "nop",   1 },
   { "orl",   8 }, { "pop",   1 }, { "push",  1 }, { "ret",   1 },
   { "reti",  1 }, { "rl",    1 }, { "rlc",   1 }, { "rr",    1 },
   { "rrc",   1 }, { "setb",  2 }, { "sjmp",  1 }, { "subb",  4 },
   { "swap",  1 }, { "xch",   3 }, { "xchd",  1 }, { "xrl",   6 },
   { 0, 0 }
};

/* Argument buffer. */
#define A_MAX 4
char XBuf[A_MAX + 1]; Exp XExp[A_MAX];

void OpInit(void) {
   Code *OC; Mode *M; int I, J;
   for (OC = CodeTab, M = ModeTab; OC < CodeTab + ELEMENTS(CodeTab); OC++) {
      OC->Start = M; M += OC->Modes;
      if (M > ModeTab + ELEMENTS(ModeTab))
         fprintf(stderr, "Bad opcode initialization.\n"), exit(1);
   }
}

void ParseArgs(byte Mnem) {
   Lexical L = OldL; Code *CP; Mode *MP; int XReg;
   char *XP, *S; Exp *EP, E; byte Op; word W;
   XP = XBuf, EP = XExp;
   do {
      L = Scan();
      if (L == SEMI) {
         if (XP > XBuf) ERROR("Extra ','."); break;
      }
      switch (L) {
         case POUND: *XP = '#', Scan(), *EP++ = Parse(2); break;
         case DIV: *XP = '/', Scan(), *EP++ = Parse(2); break;
         case AT:
            L = Scan();
            switch (L) {
               case REGISTER: switch ((Register)Value) {
                  case ACC:
                     if (Scan() != PLUS)
                        ERROR("Cannot use @A."), *XP = ' ';
                     else if (Scan() != REGISTER)
                        ERROR("Missing DPTR or PC after @A+"), *XP = ' ';
                     else {
                        switch (Value) {
                           case ACC:ERROR("Cannot use @A+A"), *XP = ' '; break;
                           case AB:ERROR("Cannot use @A+AB."); *XP = ' '; break;
                           case CY:ERROR("Cannot use @A+C."); *XP = ' '; break;
                           case DPTR: *XP = '>'; break;
                           case PC: *XP = '$'; break;
                           default:ERROR("Cannot use @A+Rn"); *XP = ' '; break;
                        }
                        Scan();
                     }
                  break;
                  case AB: ERROR("Cannot use @AB."); *XP = ' '; break;
                  case CY: ERROR("Cannot use @C."); *XP = ' '; break;
                  case DPTR: *XP = '@'; Scan(); break;
                  case PC: ERROR("Cannot use @PC."); *XP = ' '; break;
                  default:
                     Value -= (short)R0;
                     if (Value != 0 && Value != 1) {
                        ERROR("Register in @Rn out of range."); *XP = ' ';
                     } else *XP = 'i', XReg = Value;
                     Scan();
                  break;
               }
               break;
               default:
                  ERROR("Register must appear after @."); *XP = ' ';
               break;
            }
         break;
         case REGISTER: switch ((Register)Value) {
            case ACC: *XP = 'A'; break;
            case AB: *XP = 'B'; break;
            case CY: *XP = 'C'; break;
            case DPTR: *XP = 'D'; break;
            case PC:
               ERROR("Cannot use PC as a register.");
               *XP = '.', *EP++ = MakeExp(AddrX, SegP, (word)LOC);
            break;
            default: *XP = 'n', XReg = Value - (short)R0; break;
         }
            Scan();
         break;
         default: *XP = '.', *EP++ = Parse(2); break;
      }
      L = OldL;
      if (XP++ >= XBuf + A_MAX) {
         ERROR("Too many arguments specified."); return;
         while (L != SEMI && L != 0) L = Scan();
         break;
      }
   } while (L == COMMA);
   if (!Active) return;
   *XP = 0, *EP = 0;
   CP = CodeTab + Mnem;
   for (MP = CP->Start; MP < CP->Start + CP->Modes; MP++)
      if (strcmp(XBuf, MP->X) == 0) break;
   if (MP >= CP->Start + CP->Modes) {
      ERROR("Invalid addressing mode: %s.", CP->Name); return;
   }
   Op = MP->OpCode;
   S = MP->Y, EP = XExp;
   switch (*S) {
      case 'n': S++; Op |= XReg&7; break;/* Register R0/R1/R2/R3/R4/R5/R6/R7 */
      case 'i': S++; Op |= XReg&1; break;/* Register Pointer @R0/@R1 */
      case 'x': E = EP[0], EP[0] = EP[1], EP[1] = E; S++; break;
      case 'P': Reloc(Op, 'P', *EP++); return; /* P Paged Address */
   }
   PByte(Op);
   for (; *S != 0; S++) Reloc(0, *S, *EP++);
}
