/* codeol50.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator OKI OLMS-50-Familie                                         */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>
#include <ctype.h>

#include "bpemu.h"
#include "strutil.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmallg.h"
#include "asmpars.h"
#include "asmitree.h"
#include "assume.h"
#include "codepseudo.h"
#include "fourpseudo.h"
#include "codevars.h"
#include "headids.h"
#include "errmsg.h"

#include "codeol50.h"

typedef enum
{
  ModACC = 0,
  ModAP = 1,
  ModAX = 2,
  ModImm = 3,
  ModNone = 0x7f
} tAdrMode;

typedef struct
{
  Word ACCCode, ImmCode;
  Word CPUMask;
} tAriOrder;
static tAriOrder *AriOrders;

typedef struct
{
  Word Code, CPUMask;
} tAPOrder;
static tAPOrder *APOrders;

typedef struct
{
  Word APCode, AXCode, CPUMask;
} tAPAXOrder;
static tAPAXOrder *APAXOrders;

typedef struct
{
  Word Code, CPUMask;
} tFixedOrder;
static tFixedOrder *FixedOrders;

typedef struct
{
  Word Code, CPUMask;
} tImmOrder;
static tImmOrder *ImmOrders;

typedef struct
{
  Word Code, PlusCPUMask, MinusCPUMask;
} tRelOrder;
static tRelOrder *RelOrders;

typedef struct
{
  Word Code, CPUMask;
} tDSPOrder;
static tDSPOrder *DSPOrders;

typedef struct
{
  Word Code;
  Word Shift;
  Word CPUMask;
} tONOFFOrder;
static tONOFFOrder *ONOFFOrders;

typedef struct
{
  Word APCode, ImmCode;
  Boolean AllowAP;
  Word CPUMask;
} tCtrlOrder;
static tCtrlOrder *CtrlOrders;

typedef struct
{
  Word Code;
  Word CPUMask;
} tMemOrder;
static tMemOrder *MemOrders;

#define MModACC (1 << ModACC)
#define MModAP (1 << ModAP)
#define MModAX (1 << ModAX)
#define MModImm (1 << ModImm)

#define M_5054 (1 << 0)
#define M_5055 (1 << 1)
#define M_5056 (1 << 2)
#define M_6051 (1 << 3)
#define M_6052 (1 << 4)

static CPUVar CPU5054, CPU5055, CPU5056, CPU6051, CPU6052;
static tAdrMode AdrMode;
static Word AdrVal;
static LongInt PRegAssume;
static IntType CodeIntType, DataIntType;

/*-------------------------------------------------------------------------*/

static Boolean DecodeAdr(const tStrComp *pArg, Word Mask)
{
  Boolean OK;
  tSymbolFlags Flags;

  AdrMode = ModNone;

  if (!as_strcasecmp(pArg->str.p_str, "ACC"))
  {
    AdrMode = ModACC;
    goto AdrFound;
  }

  if (0[pArg->str.p_str] == '#')
  {
    AdrVal = EvalStrIntExpressionOffs(pArg, 1, Int4, &OK);
    if (OK)
    {
      AdrVal = (AdrVal & 15) << 4;
      AdrMode = ModImm;
    }
    goto AdrFound;
  }

  AdrVal = EvalStrIntExpressionWithFlags(pArg, DataIntType, &OK, &Flags);
  if (OK)
  {
    if (mFirstPassUnknown(Flags))
      AdrVal &= 15;

    if (AdrVal < 16)
      AdrMode = ModAP;
    else if (((AdrVal >> 4) & 15) == PRegAssume)
    {
      AdrVal = (AdrVal & 15) | 0x100;
      AdrMode = ModAP;
    }
    else if (Mask & MModAX)
      AdrMode = ModAX;
    else
      WrError(ErrNum_AddrOnDifferentPage);
  }

AdrFound:

  if ((AdrMode != ModNone) && (!(Mask & (1 << AdrMode))))
  {
    WrError(ErrNum_InvAddrMode);
    AdrMode = ModNone; AdrCnt = 0;
  }
  return (AdrMode != ModNone);
}

static Boolean CheckCPU(Byte Mask)
{
  if (MomCPU == CPU5054)
    Mask &= M_5054;
  else if (MomCPU == CPU5055)
    Mask &= M_5055;
  else if (MomCPU == CPU5056)
    Mask &= M_5056;
  else if (MomCPU == CPU6051)
    Mask &= M_6051;
  else if (MomCPU == CPU6052)
    Mask &= M_6052;
  return !!Mask;
}

static int ImmPtr(const char *pArg)
{
  return !!(*pArg == '#');
}

/*-------------------------------------------------------------------------*/

static void DecodeAri(Word Index)
{
  const tAriOrder *pOrder = AriOrders + Index;

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    if (DecodeAdr(&ArgStr[2], MModAP))
    {
      WAsmCode[0] = AdrVal;
      DecodeAdr(&ArgStr[1], MModACC | MModImm);
      switch (AdrMode)
      {
        case ModACC:
          WAsmCode[0] |= pOrder->ACCCode;
          CodeLen = 1;
          break;
        case ModImm:
          WAsmCode[0] |= pOrder->ImmCode | AdrVal;
          CodeLen = 1;
          break;
        default:
          break;
      }
    }
  }
}

static void DecodeADJUST(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(M_5054 | M_5055, CPU5054) >= 0))
  {
    if (DecodeAdr(&ArgStr[2], MModAP))
    {
      Boolean OK;
      Word N = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);

      if (OK)
      {
        N = ((~N) + 1) & 15;
        WAsmCode[0] = 0x3000 | AdrVal | (N << 4);
        CodeLen = 1;
      }
    }
  }
}

static void DecodeAP(Word Index)
{
  const tAPOrder *pOrder = APOrders + Index;

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    if (DecodeAdr(&ArgStr[1], MModAP))
    {
      WAsmCode[0] = pOrder->Code | AdrVal;
      CodeLen = 1;
    }
  }
}

static void DecodeFixed(Word Index)
{
  const tFixedOrder *pOrder = FixedOrders + Index;

  if (ChkArgCnt(0, 0)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    WAsmCode[0] = pOrder->Code;
    CodeLen = 1;
  }
}

static void DecodeMOV(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(M_5054 | M_5055 | M_5056 | M_6051 | M_6052, CPU5054) >= 0))
  {
    DecodeAdr(&ArgStr[2], MModACC | MModAP | MModAX);
    switch (AdrMode)
    {
      case ModACC:
        DecodeAdr(&ArgStr[1], MModAP | MModAX);
        switch (AdrMode)
        {
          case ModAP:
          case ModAX:
            WAsmCode[0] = 0x3e00 | AdrVal;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case ModAP:
        WAsmCode[0] = AdrVal;
        DecodeAdr(&ArgStr[1], MModACC | MModImm);
        switch (AdrMode)
        {
          case ModACC:
            WAsmCode[0] |= 0x3c00;
            CodeLen = 1;
            break;
          case ModImm:
            WAsmCode[0] |= 0x1c00 | AdrVal;
            CodeLen = 1;
            break;
          default:
            break;
        }
        break;
      case ModAX:
        WAsmCode[0] = AdrVal;
        if (DecodeAdr(&ArgStr[1], MModACC))
        {
          WAsmCode[0] |= 0x3c00;
          CodeLen = 1;
        }
        break;
      default:
        break;
    }
  }
}

static void DecodeAPAX(Word Index)
{
  const tAPAXOrder *pOrder = APAXOrders + Index;

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    DecodeAdr(&ArgStr[1], MModAP | MModAX);
    switch (AdrMode)
    {
      case ModAP:
        WAsmCode[0] = pOrder->APCode | AdrVal;
        CodeLen = 1;
        break;
      case ModAX:
        WAsmCode[0] = pOrder->AXCode | AdrVal;
        CodeLen = 1;
        break;
      default:
        break;
    }
  }
}

static void DecodeJMPIO(Word Code)
{
  if (ChkArgCnt(1, 1));
  else if (ChkExactCPUMask(M_5054 | M_5055 | M_5056 | M_6051 | M_6052, CPU5054) < 0);
  else if (*ArgStr[1].str.p_str != '@') WrError(ErrNum_InvAddrMode);
  {
    tStrComp Arg;

    StrCompRefRight(&Arg, &ArgStr[1], 1);
    if (DecodeAdr(&Arg, MModAP))
    {
      WAsmCode[0] = Code | AdrVal;
      CodeLen = 1;
    }
  }
}

static void DecodeJMP(Word Code)
{
  UNUSED(Code);

  if (!ChkArgCnt(1, 1));
  else if (ChkExactCPUMask(M_5054 | M_5055 | M_5056 | M_6051 | M_6052, CPU5054) < 0);
  else if (*ArgStr[1].str.p_str == '@')
    DecodeJMPIO(0x00d0);
  else
  {
    Boolean OK;
    tSymbolFlags Flags;

    WAsmCode[0] = EvalStrIntExpressionWithFlags(&ArgStr[1], CodeIntType, &OK, &Flags);
    if (OK)
    {
      if (mFirstPassUnknown(Flags) && (WAsmCode[0] >= SegLimits[SegCode]))
        WAsmCode[0] = SegLimits[SegCode];
      if (ChkRange(WAsmCode[0], 0, SegLimits[SegCode]))
      {
        WAsmCode[0] |= 0x2000;
        CodeLen = 1;
      }
    }
  }
}

static void DecodeCALL(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(M_6051 | M_6052, CPU5054) >= 0))
  {
    Boolean OK;

    WAsmCode[0] = EvalStrIntExpression(&ArgStr[1], (MomCPU == CPU6052) ? UInt11 : UInt10, &OK);
    if (OK)
    {
      WAsmCode[0] |= (MomCPU == CPU6052) ? 0x2800 : 0x2c00;
      CodeLen = 1;
    }
  }
}

static void DecodeMSA(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(M_6051, CPU5054) >= 0))
  {
    Boolean OK;

    WAsmCode[0] = EvalStrIntExpression(&ArgStr[1], UInt9, &OK);
    if (OK)
    {
      WAsmCode[0] |= 0x2a00;
      CodeLen = 1;
    }
  }
}

static void DecodeRel(Word Index)
{
  const tRelOrder *pOrder = RelOrders + Index;
  Boolean AllowMinus = CheckCPU(pOrder->MinusCPUMask),
       AllowPlus = CheckCPU(pOrder->PlusCPUMask);

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(pOrder->MinusCPUMask | pOrder->PlusCPUMask, CPU5054) >= 0))
  {
    Boolean OK;
    tSymbolFlags Flags;
    Integer Distance = EvalStrIntExpressionWithFlags(&ArgStr[1], CodeIntType, &OK, &Flags) - (EProgCounter() + 1);
    Integer MinDist = AllowMinus ? -32 : 0,
            MaxDist = AllowPlus ? 31 : -1;

    if (((Distance < MinDist) || (Distance > MaxDist)) && !mSymbolQuestionable(Flags)) WrError(ErrNum_JmpDistTooBig);
    else
    {
      if (Distance >= 0)
        WAsmCode[0] = 0;
      else
      {
        WAsmCode[0] = 0x0100;
        Distance = -1 - Distance;
      }
      WAsmCode[0] |= pOrder->Code | (Distance & 0x1f);
      CodeLen = 1;
    }
  }
}

static void DecodeCtrl(Word Index)
{
  const tCtrlOrder *pOrder = CtrlOrders + Index;

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    DecodeAdr(&ArgStr[1], MModImm | MModAP);
    switch (AdrMode)
    {
      case ModImm:
        WAsmCode[0] = pOrder->ImmCode | (AdrVal >> 4);
        CodeLen = 1;
        break;
      case ModAP:
        if (!pOrder->AllowAP && (AdrVal & 0x100)) WrError(ErrNum_InvAddrMode);
        else
        {
          WAsmCode[0] = pOrder->APCode | AdrVal;
          CodeLen = 1;
        }
        break;
      default:
        break;
    }
  }
}

static void DecodeDSP(Word Index)
{
  const tDSPOrder *pOrder = DSPOrders + Index;

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0)
   && DecodeAdr(&ArgStr[2], MModAP))
  {
    Boolean OK;
    Word Digit;

    Digit = EvalStrIntExpressionOffs(&ArgStr[1], ImmPtr(ArgStr[1].str.p_str), UInt4, &OK);
    if (OK)
    {
      WAsmCode[0] = pOrder->Code | AdrVal | (Digit << 4);
      CodeLen = 1;
    }
  }
}

static void DecodeINTENDSAB(Word Code)
{
  if (ChkArgCnt(0, 0)
   && (ChkExactCPUMask(M_5054 | M_5055 | M_6051, CPU5054) >= 0))
  {
    WAsmCode[0] = 0x04b0 | (Code & 0x0f);
    WAsmCode[1] = 0x0440 | ((Code >> 4) & 0x0f);
    CodeLen = 2;
  }
}

static void DecodeONOFF(Word Index)
{
  const tONOFFOrder *pOrder = ONOFFOrders + Index;
  Boolean IsON;

  if (!ChkArgCnt(1, 1));
  else if (ChkExactCPUMask(pOrder->CPUMask, CPU5054) < 0);
  else if (CheckONOFFArg(&ArgStr[1], &IsON))
  {
    WAsmCode[0] = pOrder->Code | ((IsON ? 3 : 2) << pOrder->Shift);
    CodeLen = 1;
  }
}

static void DecodeImm(Word Index)
{
  const tImmOrder *pOrder = ImmOrders + Index;

  if (ChkArgCnt(1, 1)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    Boolean OK;
    Word Freq = EvalStrIntExpressionOffs(&ArgStr[1], ImmPtr(ArgStr[1].str.p_str), UInt4, &OK);

    if (OK)
    {
      WAsmCode[0] = pOrder->Code | Freq;
      CodeLen = 1;
    }
  }
}

static void DecodeBUZZER(Word Code)
{
  int ReqArgCnt = (MomCPU == CPU5055) ? 1 : 2;

  UNUSED(Code);

  if (ChkArgCnt(ReqArgCnt, ReqArgCnt)
   && (ChkExactCPUMask(M_5054 | M_5055, CPU5054) >= 0))
  {
    Boolean OK = True, OK2;
    Word Freq, Sound;

    Freq =  (ReqArgCnt >= 2) ? EvalStrIntExpressionOffs(&ArgStr[1], ImmPtr(ArgStr[1].str.p_str), UInt2, &OK) : 2;
    Sound = EvalStrIntExpressionOffs(&ArgStr[ArgCnt], ImmPtr(ArgStr[ArgCnt].str.p_str), UInt2, &OK2);
    if (OK && OK2)
    {
      WAsmCode[0] = 0x04c0 | Freq | (Sound << 2);
      CodeLen = 1;
    }
  }
}

static void DecodeINP(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(M_5056, CPU5054) >= 0))
  {
    Boolean OK;
    Word Port = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);

    if (OK && DecodeAdr(&ArgStr[2], MModAP))
    {
      WAsmCode[0] = 0x3400 | AdrVal | (Port << 4);
      CodeLen = 1;
    }
  }
}

static void DecodeIN(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(M_6052, CPU5054) >= 0))
  {
    Boolean OK;
    Word Port = EvalStrIntExpression(&ArgStr[1], UInt4, &OK);

    if (OK && DecodeAdr(&ArgStr[2], MModAP))
    {
      WAsmCode[0] = 0x0400 | AdrVal | (Port << 4);
      CodeLen = 1;
    }
  }
}

static void DecodeOUT(Word Code)
{
  UNUSED(Code);

  if (ChkArgCnt(2, 2)
   && (ChkExactCPUMask(M_5056 | M_6052, CPU5054) >= 0))
  {
    Boolean OK;
    Word Port = EvalStrIntExpression(&ArgStr[2], (MomCPU == CPU6052) ? UInt5 : UInt4, &OK);

    if (OK)
    {
      if (MomCPU == CPU6052)
        Port = (Port & 15) | ((Port & 16) << 1);
      DecodeAdr(&ArgStr[1], MModAP | MModImm);
      switch (AdrMode)
      {
        case ModAP:
          WAsmCode[0] = ((MomCPU == CPU6052) ? 0x0800 : 0x3600) | AdrVal | (Port << 4);
          CodeLen = 1;
          break;
        case ModImm:
          WAsmCode[0] = ((MomCPU == CPU6052) ? 0x0c00 : 0x0400) | (AdrVal >> 4) | (Port << 4);
          CodeLen = 1;
          break;
        default:
          break;
      }
    }
  }
}

static void DecodeMem(Word Index)
{
  const tMemOrder *pOrder = MemOrders + Index;

  if (ChkArgCnt(0, 3)
   && (ChkExactCPUMask(pOrder->CPUMask, CPU5054) >= 0))
  {
    WAsmCode[0] = pOrder->Code;
    if (ArgCnt < 1)
      CodeLen = 1;
    else
    {
      if (!strcmp(ArgStr[1].str.p_str, "-"))
        WAsmCode[0] |= 0x0010;
      else if (strcmp(ArgStr[1].str.p_str, "+"))
      {
        WrError(ErrNum_InvAddrMode);
        return;
      }
      if (ArgCnt < 2)
      {
        WAsmCode[0] |= 0x0020;
        CodeLen = 1;
      }
      else
      {
        if (!as_strcasecmp(ArgStr[2].str.p_str, "Z"))
          WAsmCode[0] |= 0x0040;
        else if (!as_strcasecmp(ArgStr[2].str.p_str, "N"))
          WAsmCode[0] |= 0x0080;
        else if (!as_strcasecmp(ArgStr[2].str.p_str, "L"))
          WAsmCode[0] |= 0x0200;
        else
        {
          WrError(ErrNum_InvAddrMode);
          return;
        }
        if (ArgCnt < 3)
          CodeLen = 1;
        else if (as_strcasecmp(ArgStr[3].str.p_str, "L"))
        {
          WrError(ErrNum_InvAddrMode);
          return;
        }
        else
        {
          WAsmCode[0] |= 0x0200;
          CodeLen = 1;
        }
      }
    }
  }
}

/*-------------------------------------------------------------------------*/

static void DecodeDATA_OLMS50(Word Code)
{
  UNUSED(Code);

  DecodeDATA(Int14, Int4);
}

static void DecodeSFR(Word Code)
{
  UNUSED(Code);

  code_equate_segment(SegData);
}

/*-------------------------------------------------------------------------*/

static void AddAri(const char *pName, Word ACCCode, Word ImmCode, Word CPUMask)
{
  order_array_rsv_end(AriOrders, tAriOrder);
  AriOrders[InstrZ].ACCCode = ACCCode;
  AriOrders[InstrZ].ImmCode = ImmCode;
  AriOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeAri);
}

static void AddAP(const char *pName, Word Code, Word CPUMask)
{
  order_array_rsv_end(APOrders, tAPOrder);
  APOrders[InstrZ].Code = Code;
  APOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeAP);
}

static void AddImm(const char *pName, Word Code, Word CPUMask)
{
  order_array_rsv_end(ImmOrders, tImmOrder);
  ImmOrders[InstrZ].Code = Code;
  ImmOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeImm);
}

static void AddAPAX(const char *pName, Word APCode, Word AXCode, Word CPUMask)
{
  order_array_rsv_end(APAXOrders, tAPAXOrder);
  APAXOrders[InstrZ].APCode = APCode;
  APAXOrders[InstrZ].AXCode = AXCode;
  APAXOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeAPAX);
}

static void AddFixed(const char *pName, Word Code, Word CPUMask)
{
  order_array_rsv_end(FixedOrders, tFixedOrder);
  FixedOrders[InstrZ].Code = Code;
  FixedOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeFixed);
}

static void AddRel(const char *pName, Word Code, Word PlusCPUMask, Word MinusCPUMask)
{
  order_array_rsv_end(RelOrders, tRelOrder);
  RelOrders[InstrZ].Code = Code;
  RelOrders[InstrZ].PlusCPUMask = PlusCPUMask;
  RelOrders[InstrZ].MinusCPUMask = MinusCPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeRel);
}

static void AddCtrl(const char *pName, Word APCode, Word ImmCode, Boolean AllowAP, Word CPUMask)
{
  order_array_rsv_end(CtrlOrders, tCtrlOrder);
  CtrlOrders[InstrZ].APCode = APCode;
  CtrlOrders[InstrZ].ImmCode = ImmCode;
  CtrlOrders[InstrZ].AllowAP = AllowAP;
  CtrlOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeCtrl);
}

static void AddDSP(const char *pName, Word Code, Word CPUMask)
{
  order_array_rsv_end(DSPOrders, tDSPOrder);
  DSPOrders[InstrZ].Code = Code;
  DSPOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeDSP);
}

static void AddONOFFInstr(const char *pName, Word Code, Word Shift, Word CPUMask)
{
  order_array_rsv_end(ONOFFOrders, tONOFFOrder);
  ONOFFOrders[InstrZ].Code = Code;
  ONOFFOrders[InstrZ].CPUMask = CPUMask;
  ONOFFOrders[InstrZ].Shift = Shift;
  AddInstTable(InstTable, pName, InstrZ++, DecodeONOFF);
}

static void AddMem(const char *pName, Word Code, Word CPUMask)
{
  order_array_rsv_end(MemOrders, tMemOrder);
  MemOrders[InstrZ].Code = Code;
  MemOrders[InstrZ].CPUMask = CPUMask;
  AddInstTable(InstTable, pName, InstrZ++, DecodeMem);
}

static void InitFields(void)
{
  int N;
  char Op[20];

  InstTable = CreateInstTable(201);
  SetDynamicInstTable(InstTable);

  add_null_pseudo(InstTable);

  InstrZ = 0;
  AddAri("ADD", 0x0040, 0x1800, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("SUB", 0x0240, 0x1a00, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("CMP", 0x02e0, 0x1600, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("XOR", 0x0070, 0x1e00, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("BIT", 0x00e0, 0x1400, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("BIS", 0x0060, 0x1000, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAri("BIC", 0x0260, 0x1200, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);

  InstrZ = 0;
  if (CheckCPU(M_6052))
  {
    AddRel("BEQ", 0x3a40, M_6052, M_6052);
    AddRel("BZE", 0x3a40, M_6052, M_6052);
    AddRel("BNE", 0x3ac0, M_6052, M_6052);
    AddRel("BNZ", 0x3ac0, M_6052, M_6052);
    AddRel("BCS", 0x3a00, M_6052, M_6052);
    AddRel("BCC", 0x3a80, M_6052, M_6052);
    AddRel("BGT", 0x3a20, M_6052, M_6052);
    AddRel("BLE", 0x3aa0, M_6052, M_6052);
    AddRel("BGE", 0x3a60, M_6052, M_6052);
    AddRel("BLT", 0x3ae0, M_6052, M_6052);
  }
  else
  {
    AddRel("BEQ", 0x0640, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BZE", 0x0640, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BNE", 0x06c0, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BNZ", 0x06c0, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BCS", (MomCPU == CPU5056) ? 0x0620 : 0x0600, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BLT", (MomCPU == CPU6051) ? 0x06e0 : (MomCPU == CPU5056 ? 0x0620 : 0x0600), M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BCC", (MomCPU == CPU5056) ? 0x06a0 : 0x0680, M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BGE", (MomCPU == CPU6051) ? 0x0660 : (MomCPU == CPU5056 ? 0x06a0 : 0x0680), M_5054 | M_5055 | M_5056 | M_6051, M_6051);
    AddRel("BGT", (MomCPU == CPU5056) ? 0x06e0 : 0x0620, M_5056 | M_6051, M_6051);
    AddRel("BLE", (MomCPU == CPU5056) ? 0x0660 : 0x06a0, M_5056 | M_6051, M_6051);
  }

  InstrZ = 0;
  AddCtrl("MATRIX", 0x3620, 0x0420, True,  M_5054 | M_5055 | M_6051);
  AddCtrl("FORMAT", 0x3630, 0x0430, True,  M_5054 | M_5055 | M_6051);
  AddCtrl("PAGE",   0x3650, 0x0450, False, M_5054 | M_5055 | M_6051);
  AddCtrl("ADRS",   0x3660, 0x0460, True,  M_5055 | M_6051);

  InstrZ = 0;
  AddDSP("DSP",   0x0800,  M_5054 | M_5055 | M_5056 | M_6051);
  AddDSP("DSPH",  0x0a00,  M_5055 | M_6051);
  AddDSP("DSPF",  0x0c00,  M_5054 | M_5055 | M_5056 | M_6051);
  AddDSP("DSPFH", 0x0e00,  M_5055 | M_6051);

  InstrZ = 0;
  AddONOFFInstr("LAMP",   0x0410, 0, M_5054 | M_5055 | M_6051);
  AddONOFFInstr("BACKUP", 0x0410, 2, M_5054 | M_5055 | M_6051);
  AddONOFFInstr("XTCP",   0x0480, 0, M_5055 | M_6051);

  InstrZ = 0;
  if (CheckCPU(M_5054 | M_5055 | M_5056))
  {
    AddAP("INC"     , 0x1810, M_5054 | M_5055 | M_5056);
    AddAP("DEC"     , 0x1a10, M_5054 | M_5055 | M_5056);
  }
  AddAP("ASR"     , 0x0030, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAP("ASL"     , 0x0230, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddAP("ROR"     , 0x0020, M_6051 | M_6052);
  AddAP("ROL"     , 0x0220, M_6051 | M_6052);
  AddAP("SWITCH"  , 0x3410, M_5054 | M_5055 | M_6051);
  AddAP("KSWITCH" , 0x3420, M_5054 | M_5055 | M_6051);
  AddAP("INTMODE" , 0x3440, M_5054 | M_5055 | M_6051);
  AddAP("RATE"    , 0x3490, M_5054 | M_5055 | M_6051);
  AddAP("ADC"     , 0x0050, M_5056 | M_6051 | M_6052);
  AddAP("SBC"     , 0x0250, M_5056 | M_6051 | M_6052);
  AddAP("STATUS"  , 0x34a0, M_6051);
  AddAP("FLAGIN"  , 0x34e0, M_6051);
  AddAP("S1RATE"  , 0x34c0, M_6051);
  AddAP("S2RATE"  , 0x34d0, M_6051);
  for (N = 0; N < 16; N++)
  {
    as_snprintf(Op, sizeof(Op), "ADC%d", N);
    AddAP(Op, 0x3000 | (N << 4), M_6051);
    as_snprintf(Op, sizeof(Op), "SBC%d", N);
    AddAP(Op, 0x3200 | (N << 4), M_6051);
  }

  InstrZ = 0;
  AddAPAX("CHG", 0x3800, 0x3800, M_5056 | M_6052);
  if (CheckCPU(M_6051))
  {
    AddAPAX("INC" , 0x3800, 0x3800, M_6051);
    AddAPAX("DEC" , 0x3a00, 0x3a00, M_6051);
  }

  InstrZ = 0;
  AddFixed("CLZ"     , 0x00a0, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("CLC"     , 0x0090, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("CLG"     , 0x0080, M_6051 | M_6052);
  AddFixed("CLA"     , 0x00b0, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("SEZ"     , 0x02a0, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("SEC"     , 0x0290, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("SEG"     , 0x0280, M_6051 | M_6052);
  AddFixed("SEA"     , 0x02b0, M_5054 | M_5055 | M_5056 | M_6052);
  AddFixed("BSO"     , 0x04c0, M_5054 | M_5055);
  AddFixed("HALT"    , (MomCPU == CPU6052) ? 0x0e10 : 0x0400, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("RSTRATE" , 0x0488, M_5054 | M_5055 | M_6051);
  AddFixed("NOP"     , 0x0000, M_5054 | M_5055 | M_5056 | M_6051 | M_6052);
  AddFixed("RET"     , 0x00c0, M_6051 | M_6052);
  AddFixed("RTI"     , 0x02c0, M_6051 | M_6052);
  AddFixed("MSO"     , 0x04a0, M_6051);
  AddFixed("ACTIVATE", 0x0490, M_6051);
  AddFixed("KENAB"   , 0x04e8, M_6051);
  AddFixed("KDSAB"   , 0x04e4, M_6051);
  AddFixed("STOP"    , 0x0e00, M_6052);
  AddFixed("ACT"     , 0x0e20, M_6052);
  AddFixed("EI"      , 0x0e68, M_6052);
  AddFixed("DI"      , 0x0e64, M_6052);
  AddFixed("ET"      , 0x0e62, M_6052);
  AddFixed("DT"      , 0x0e61, M_6052);
  AddFixed("EC"      , 0x0e78, M_6052);
  AddFixed("DC"      , 0x0e74, M_6052);
  AddFixed("OM"      , 0x0e72, M_6052);
  AddFixed("IM"      , 0x0e71, M_6052);
  AddFixed("RST"     , 0x0e90, M_6052);

  InstrZ = 0;
  AddImm("FREQ",  0x04d0, M_5055);
  AddImm("PITCH", 0x04c0, M_6051);

  InstrZ = 0;
  AddMem("RDAR",  0x3000, M_6052);
  AddMem("MVAR",  0x3400, M_6052);

  AddInstTable(InstTable, "ADJUST", 0, DecodeADJUST);
  AddInstTable(InstTable, "MOV", 0, DecodeMOV);
  AddInstTable(InstTable, "JMP", 0, DecodeJMP);
  AddInstTable(InstTable, "CALL", 0, DecodeCALL);
  AddInstTable(InstTable, "MSA", 0, DecodeMSA);
  AddInstTable(InstTable, "JMPIO", 0x02d0, DecodeJMPIO);

  AddInstTable(InstTable, "RES", 0, DecodeRES);
  AddInstTable(InstTable, "DATA", 0, DecodeDATA_OLMS50);
  AddInstTable(InstTable, "SFR", 0, DecodeSFR);
  AddInstTable(InstTable, "INTENAB", 0x0028, DecodeINTENDSAB);
  AddInstTable(InstTable, "INTDSAB", 0x0014, DecodeINTENDSAB);
  AddInstTable(InstTable, "INP", 0, DecodeINP);
  AddInstTable(InstTable, "IN" , 0, DecodeIN);
  AddInstTable(InstTable, "OUT", 0, DecodeOUT);

  AddInstTable(InstTable, "BUZZER", 0, DecodeBUZZER);
}

static void DeinitFields(void)
{
  DestroyInstTable(InstTable);
  order_array_free(AriOrders);
  order_array_free(APOrders);
  order_array_free(APAXOrders);
  order_array_free(FixedOrders);
  order_array_free(RelOrders);
  order_array_free(CtrlOrders);
  order_array_free(DSPOrders);
  order_array_free(ONOFFOrders);
  order_array_free(ImmOrders);
  order_array_free(MemOrders);
}

/*-------------------------------------------------------------------------*/

static void  MakeCode_OLMS50(void)
{
  if (!LookupInstTable(InstTable, OpPart.str.p_str))
    WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);
}


static Boolean IsDef_OLMS50(void)
{
  return False;
}

static void SwitchFrom_OLMS50(void)
{
  DeinitFields();
}

static void SwitchTo_OLMS50(void)
{
  static as_assume_rec_t ASSUMEs[] =
{
  { "P", &PRegAssume, 0, 0x3, 0x8, NULL }
};

  const TFamilyDescr *pDescr;

  pDescr = FindFamilyByName("OLMS-50");

  TurnWords = False;
  SetIntConstMode(eIntConstModeIntel);
  SwitchIsOccupied = True;
  PageIsOccupied = True;

  PCSymbol = "$";
  HeaderID = pDescr->Id;
  NOPCode = 0x00;
  DivideChars = ",";
  HasAttrs = False;

  ValidSegs = (1 << SegCode) | (1 << SegData);
  Grans[SegCode] = 2; ListGrans[SegCode] = 2; SegInits[SegCode] = 0;
  Grans[SegData] = 1; ListGrans[SegData] = 1; SegInits[SegCode] = 0;
  if (MomCPU == CPU5054)
  {
    CodeIntType = UInt10;
    DataIntType = UInt6;
    SegLimits[SegData] = 61;
    SegLimits[SegCode] = 1023;
  }
  else if (MomCPU == CPU5055)
  {
    CodeIntType = UInt11;
    DataIntType = UInt7;
    SegLimits[SegData] = 95;
    SegLimits[SegCode] = 1791;
  }
  else if (MomCPU == CPU5056)
  {
    CodeIntType = UInt11;
    DataIntType = UInt7;
    SegLimits[SegData] = 89;
    SegLimits[SegCode] = 1791;
  }
  else if (MomCPU == CPU6051)
  {
    CodeIntType = UInt12;
    DataIntType = UInt7;
    SegLimits[SegData] = 119;
    SegLimits[SegCode] = 2559;
  }
  else if (MomCPU == CPU6052)
  {
    CodeIntType = UInt11;
    DataIntType = UInt10;
    SegLimits[SegData] = 639;
    SegLimits[SegCode] = 2047;
  }

  MakeCode = MakeCode_OLMS50;
  IsDef = IsDef_OLMS50;
  SwitchFrom = SwitchFrom_OLMS50; InitFields();

  assume_set(ASSUMEs, as_array_size(ASSUMEs));
}

void codeolms50_init(void)
{
  CPU5054 = AddCPU("MSM5054" , SwitchTo_OLMS50);
  CPU5055 = AddCPU("MSM5055" , SwitchTo_OLMS50);
  CPU5056 = AddCPU("MSM5056" , SwitchTo_OLMS50);
  CPU6051 = AddCPU("MSM6051" , SwitchTo_OLMS50);
  CPU6052 = AddCPU("MSM6052" , SwitchTo_OLMS50);
}
