Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  1. /* errmsg.h */
  2. /*****************************************************************************/
  3. /* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
  4. /*                                                                           */
  5. /* Cross Assembler                                                           */
  6. /*                                                                           */
  7. /* Error message definition & associated checking                            */
  8. /*                                                                           */
  9. /*****************************************************************************/
  10.  
  11. #include <string.h>
  12. #include <stdarg.h>
  13. #include "strutil.h"
  14.  
  15. #include "datatypes.h"
  16. #include "asmdef.h"
  17. #include "asmpars.h"
  18. #include "strutil.h"
  19. #include "asmsub.h"
  20. #include "nlmessages.h"
  21. #include "cpulist.h"
  22. #include "as.rsc"
  23. #include "errmsg.h"
  24.  
  25. static tErrorNum GetDefaultCPUErrorNum(tErrorNum ThisNum)
  26. {
  27.   return ThisNum ? ThisNum : ErrNum_InstructionNotSupported;
  28. }
  29.  
  30. /*!------------------------------------------------------------------------
  31.  * \fn     report_error_range(LargeInt value, LargeInt ref, char comp_op, tErrorNum error_num, const tStrComp *p_comp)
  32.  * \brief  core routine to emit range error message
  33.  * \param  value offending value
  34.  * \param  ref reference value was checked against
  35.  * \param  comp_op comparison op that failed
  36.  * \param  error_num error message to emit
  37.  * \param  p_comp source argument (may be NULL)
  38.  * \return constant False
  39.  * ------------------------------------------------------------------------ */
  40.  
  41. static Boolean report_error_range(LargeInt value, LargeInt ref, char comp_op, tErrorNum error_num, const tStrComp *p_comp)
  42. {
  43.   char s[100];
  44.  
  45.   as_snprintf(s, sizeof(s), "%llld %c %llld", value, comp_op, ref);
  46.   if (p_comp)
  47.     WrXErrorPos(error_num, s, &p_comp->Pos);
  48.   else
  49.     WrXError(error_num, s);
  50.   return False;
  51. }
  52.  
  53. /*!------------------------------------------------------------------------
  54.  * \fn     ChkRangePos(LargeInt Value, LargeInt Min, LargeInt Max, const tStrComp *p_comp)
  55.  * \brief  check whether integer is in range and issue error if not
  56.  * \param  Value value to check
  57.  * \param  Min minimum of range
  58.  * \param  Max maximum of range
  59.  * \param  p_comp corresponding source argument (may be NULL)
  60.  * \return TRUE if in-range and no error
  61.  * ------------------------------------------------------------------------ */
  62.  
  63. Boolean ChkRangePos(LargeInt Value, LargeInt Min, LargeInt Max, const tStrComp *p_comp)
  64. {
  65.   if (Value < Min)
  66.     return report_error_range(Value, Min, '<', ErrNum_UnderRange, p_comp);
  67.   else if (Value > Max)
  68.     return report_error_range(Value, Max, '>', ErrNum_OverRange, p_comp);
  69.   else
  70.     return True;
  71. }
  72.  
  73. /*!------------------------------------------------------------------------
  74.  * \fn     ChkRangeWarnPos(LargeInt Value, LargeInt Min, LargeInt Max, const tStrComp *p_comp)
  75.  * \brief  check whether integer is in range and issue error if not
  76.  * \param  Value value to check
  77.  * \param  Min minimum of range
  78.  * \param  Max maximum of range
  79.  * \param  p_comp corresponding source argument (may be NULL)
  80.  * \return TRUE if in-range and no error
  81.  * ------------------------------------------------------------------------ */
  82.  
  83. Boolean ChkRangeWarnPos(LargeInt Value, LargeInt Min, LargeInt Max, const tStrComp *p_comp)
  84. {
  85.   if (Value < Min)
  86.     return report_error_range(Value, Min, '<', ErrNum_WUnderRange, p_comp);
  87.   else if (Value > Max)
  88.     return report_error_range(Value, Max, '>', ErrNum_WOverRange, p_comp);
  89.   else
  90.     return True;
  91. }
  92.  
  93. /*!------------------------------------------------------------------------
  94.  * \fn     ChkArgCntExtPos(int ThisCnt, int MinCnt, int MaxCnt, const struct sLineComp *pComp)
  95.  * \brief  check whether argument count is within given range and issue error if not
  96.  * \param  ThisCnt count to check
  97.  * \param  MinCnt minimum allowed count
  98.  * \param  MaxCnt maximum allowed count
  99.  * \param  pComp position in source line (optional)
  100.  * \return TRUE if in-range and no error
  101.  * ------------------------------------------------------------------------ */
  102.  
  103. Boolean ChkArgCntExtPos(int ThisCnt, int MinCnt, int MaxCnt, const struct sLineComp *pComp)
  104. {
  105.   if ((ThisCnt < MinCnt) || (ThisCnt > MaxCnt))
  106.   {
  107.     char Str[100];
  108.  
  109.     if (MinCnt != MaxCnt)
  110.       as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgArgCntFromTo), MinCnt, MaxCnt, ThisCnt);
  111.     else switch (MinCnt)
  112.     {
  113.       case 0:
  114.         as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgArgCntZero), ThisCnt);
  115.         break;
  116.       case 1:
  117.         as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgArgCntOne), ThisCnt);
  118.         break;
  119.       default:
  120.         as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgArgCntMulti), MinCnt, ThisCnt);
  121.     }
  122.     if (pComp)
  123.       WrXErrorPos(ErrNum_WrongArgCnt, Str, pComp);
  124.     else
  125.       WrXError(ErrNum_WrongArgCnt, Str);
  126.     return False;
  127.   }
  128.   else
  129.     return True;
  130. }
  131.  
  132. /*!------------------------------------------------------------------------
  133.  * \fn     ChkArgCntExtEitherOr(int ThisCnt, int EitherCnt, int OrCnt)
  134.  * \brief  check whether argument count is according to given values and issue error if not
  135.  * \param  ThisCnt count to check
  136.  * \param  EitherCnt allowed count
  137.  * \param  OrCnt other allowed count
  138.  * \return TRUE if count OK and no error
  139.  * ------------------------------------------------------------------------ */
  140.  
  141. Boolean ChkArgCntExtEitherOr(int ThisCnt, int EitherCnt, int OrCnt)
  142. {
  143.   if ((ThisCnt != EitherCnt) && (ThisCnt != OrCnt))
  144.   {
  145.     char Str[100];
  146.  
  147.     as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgArgCntEitherOr), EitherCnt, OrCnt, ThisCnt);
  148.     WrXError(ErrNum_WrongArgCnt, Str);
  149.     return False;
  150.   }
  151.   else
  152.     return True;
  153. }
  154.  
  155. /*!------------------------------------------------------------------------
  156.  * \fn     ChkMinCPUExt(CPUVar MinCPU, tErrorNum ErrorNum)
  157.  * \brief  check whether currently selected CPU is at least given one and issue error if not
  158.  * \param  MinCPU minimum required CPU
  159.  * \param  ErrorNum error to issue if not OK (0 = default message)
  160.  * \return TRUE if currently selected CPU is OK and no error
  161.  * ------------------------------------------------------------------------ */
  162.  
  163. extern Boolean ChkMinCPUExt(CPUVar MinCPU, tErrorNum ErrorNum)
  164. {
  165.   if (MomCPU < MinCPU)
  166.   {
  167.     const tCPUDef *pCPUDef;
  168.     ErrorNum = GetDefaultCPUErrorNum(ErrorNum);
  169.  
  170.     pCPUDef = LookupCPUDefByVar(MinCPU);
  171.     if (pCPUDef)
  172.     {
  173.       char Str[100];
  174.  
  175.       as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgMinCPUSupported), pCPUDef->Name);
  176.       WrXError(ErrorNum, Str);
  177.     }
  178.     else
  179.       WrError(ErrorNum);
  180.     return False;
  181.   }
  182.   return True;
  183. }
  184.  
  185. /*!------------------------------------------------------------------------
  186.  * \fn     AChkMinCPUExtPos(CPUVar MinCPU, tErrorNum ErrorNum, const struct sStrComp *pComp)
  187.  * \brief  check for minimum CPU of this addressing mode
  188.  * \param  MinCPU min. CPU required
  189.  * \param  ErrorNum error message to print
  190.  * \param  pComp argument to complain about
  191.  * \return True if CPU is OK
  192.  * ------------------------------------------------------------------------ */
  193.  
  194. Boolean AChkMinCPUExtPos(CPUVar MinCPU, tErrorNum ErrorNum, const struct sStrComp *pComp)
  195. {
  196.   if (MomCPU < MinCPU)
  197.   {
  198.     WrStrErrorPos(ErrorNum, pComp);
  199.     return False;
  200.   }
  201.   return True;
  202. }
  203.  
  204. /*!------------------------------------------------------------------------
  205.  * \fn     ChkMaxCPUExt(CPUVar MaxCPU, tErrorNum ErrorNum)
  206.  * \brief  check whether currently selected CPU is at most given one and issue error if not
  207.  * \param  MaxCPU maximum required CPU
  208.  * \param  ErrorNum error to issue if not OK (0 = default message)
  209.  * \return TRUE if currently selected CPU is OK and no error
  210.  * ------------------------------------------------------------------------ */
  211.  
  212. extern Boolean ChkMaxCPUExt(CPUVar MaxCPU, tErrorNum ErrorNum)
  213. {
  214.   if (MomCPU > MaxCPU)
  215.   {
  216.     const tCPUDef *pCPUDef;
  217.     ErrorNum = GetDefaultCPUErrorNum(ErrorNum);
  218.  
  219.     pCPUDef = LookupCPUDefByVar(MaxCPU);
  220.     if (pCPUDef)
  221.     {
  222.       char Str[100];
  223.  
  224.       as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgMaxCPUSupported), pCPUDef->Name);
  225.       WrXError(ErrorNum, Str);
  226.     }
  227.     else
  228.       WrError(ErrorNum);
  229.     return False;
  230.   }
  231.   return True;
  232. }
  233.  
  234. /*!------------------------------------------------------------------------
  235.  * \fn     ChkRangeCPUExt(CPUVar MinCPU, CPUVar MaxCPU, tErrorNum ErrorNum)
  236.  * \brief  check whether currently selected CPU is within given range
  237.  * \param  MinCPU minimum required CPU
  238.  * \param  MaxCPU maximum required CPU
  239.  * \param  ErrorNum error to issue if not OK (0 = default message)
  240.  * \return TRUE if currently selected CPU is OK and no error
  241.  * ------------------------------------------------------------------------ */
  242.  
  243. extern Boolean ChkRangeCPUExt(CPUVar MinCPU, CPUVar MaxCPU, tErrorNum ErrorNum)
  244. {
  245.   if ((MomCPU < MinCPU) || (MomCPU > MaxCPU))
  246.   {
  247.     const tCPUDef *pCPUDefMin, *pCPUDefMax;
  248.     ErrorNum = GetDefaultCPUErrorNum(ErrorNum);
  249.  
  250.     pCPUDefMin = LookupCPUDefByVar(MinCPU);
  251.     pCPUDefMax = LookupCPUDefByVar(MaxCPU);
  252.     if (pCPUDefMin && pCPUDefMax)
  253.     {
  254.       char Str[100];
  255.  
  256.       as_snprintf(Str, sizeof(Str), getmessage(Num_ErrMsgRangeCPUSupported), pCPUDefMin->Name, pCPUDefMax->Name);
  257.       WrXError(ErrorNum, Str);
  258.     }
  259.     else
  260.       WrError(ErrorNum);
  261.     return False;
  262.   }
  263.   return True;
  264. }
  265.  
  266. /*!------------------------------------------------------------------------
  267.  * \fn     ChkMinCPUExt(CPUVar MatchCPU, tErrorNum ErrorNum)
  268.  * \brief  check whether currently selected CPU is given one and issue error if not
  269.  * \param  MatchCPU required CPU
  270.  * \param  ErrorNum error to issue if not OK (0 = default message)
  271.  * \return TRUE if currently selected CPU is OK and no error
  272.  * ------------------------------------------------------------------------ */
  273.  
  274. extern Boolean ChkExactCPUExt(CPUVar MatchCPU, tErrorNum ErrorNum)
  275. {
  276.   if (MomCPU != MatchCPU)
  277.   {
  278.     const tCPUDef *pCPUDef;
  279.     ErrorNum = GetDefaultCPUErrorNum(ErrorNum);
  280.  
  281.     pCPUDef = LookupCPUDefByVar(MatchCPU);
  282.     if (pCPUDef)
  283.     {
  284.       char Str[100];
  285.  
  286.       as_snprintf(Str, sizeof(Str), "%s%s%s", getmessage(Num_ErrMsgOnlyCPUSupported1), pCPUDef->Name, getmessage(Num_ErrMsgOnlyCPUSupported2));
  287.       WrXError(ErrorNum, Str);
  288.     }
  289.     else
  290.       WrError(ErrorNum);
  291.     return False;
  292.   }
  293.   return True;
  294. }
  295.  
  296. /*!------------------------------------------------------------------------
  297.  * \fn     ChkExcludeCPUExt(CPUVar MatchCPU, tErrorNum ErrorNum)
  298.  * \brief  check whether currently selected CPU is NOT given one and issue error if not
  299.  * \param  MatchCPU disallowed CPU
  300.  * \param  ErrorNum error to issue if not OK (0 = default message)
  301.  * \return TRUE if currently selected CPU is OK and no error
  302.  * ------------------------------------------------------------------------ */
  303.  
  304. typedef struct
  305. {
  306.   const tCPUDef *pExcludeCPUDef;
  307.   const tCPUDef *pLastCPUDef;
  308.   String Str;
  309.   Boolean First;
  310.   Word ExcludeMask;
  311.   CPUVar ExcludeCPUFirst;
  312. } tExcludeContext;
  313.  
  314. static void IterateExclude(const tCPUDef *pThisCPUDef, void *pUser)
  315. {
  316.   tExcludeContext *pContext = (tExcludeContext*)pUser;
  317.  
  318.   /* ignore other families or aliases */
  319.  
  320.   if (pThisCPUDef)
  321.   {
  322.     if ((pThisCPUDef->SwitchProc != pContext->pExcludeCPUDef->SwitchProc)
  323.      || ((1 << (pThisCPUDef->Number - pContext->ExcludeCPUFirst)) & pContext->ExcludeMask)
  324.      || (pThisCPUDef->Number != pThisCPUDef->Orig))
  325.       return;
  326.   }
  327.  
  328.   if (pContext->pLastCPUDef)
  329.   {
  330.     if (!pContext->First)
  331.       strmaxcat(pContext->Str, pThisCPUDef ? ", " : getmessage(Num_ErrMsgOnlyCPUSupportedOr), sizeof(pContext->Str));
  332.     strmaxcat(pContext->Str, pContext->pLastCPUDef->Name, sizeof(pContext->Str));
  333.     pContext->First = False;
  334.   }
  335.   pContext->pLastCPUDef = pThisCPUDef;
  336. }
  337.  
  338. extern Boolean ChkExcludeCPUExt(CPUVar MatchCPU, tErrorNum ErrorNum)
  339. {
  340.   tExcludeContext Context;
  341.  
  342.   if (MomCPU != MatchCPU)
  343.     return True;
  344.  
  345.   Context.pExcludeCPUDef = LookupCPUDefByVar(MatchCPU);
  346.  
  347.   if (Context.pExcludeCPUDef)
  348.   {
  349.     *Context.Str = '\0';
  350.     Context.First = True;
  351.     Context.pLastCPUDef = NULL;
  352.     Context.ExcludeMask = 1;
  353.     Context.ExcludeCPUFirst = MatchCPU;
  354.     strmaxcat(Context.Str, getmessage(Num_ErrMsgOnlyCPUSupported1), sizeof(Context.Str));
  355.     IterateCPUList(IterateExclude, &Context);
  356.     IterateExclude(NULL, &Context);
  357.     WrXError(GetDefaultCPUErrorNum(ErrorNum), Context.Str);
  358.   }
  359.   else
  360.     WrError(GetDefaultCPUErrorNum(ErrorNum));
  361.   return False;
  362. }
  363.  
  364. /*!------------------------------------------------------------------------
  365.  * \fn     ChkExcludeCPUList(int ErrorNum, ...)
  366.  * \brief  check whether currently selected CPU is one of the given ones and issue error if it is
  367.  * \param  ErrorNum error to issue if not OK (0 = default message)
  368.  * \param  ... List of CPUs terminated by CPUNone
  369.  * \return Index (-1...-n) of matching CPU or 0 if current CPU does not match any
  370.  * ------------------------------------------------------------------------ */
  371.  
  372. int ChkExcludeCPUList(int ErrorNum, ...)
  373. {
  374.   va_list ap;
  375.   int Index = -1, FoundIndex = 0;
  376.   CPUVar ThisCPU;
  377.  
  378.   va_start(ap, ErrorNum);
  379.   while (True)
  380.   {
  381.     ThisCPU = va_arg(ap, CPUVar);
  382.     if (ThisCPU == CPUNone)
  383.       break;
  384.     if (MomCPU == ThisCPU)
  385.     {
  386.       FoundIndex = Index;
  387.       break;
  388.     }
  389.   }
  390.   va_end(ap);
  391.  
  392.   if (FoundIndex < 0)
  393.   {
  394.     tExcludeContext Context;
  395.  
  396.     *Context.Str = '\0';
  397.     Context.First = True;
  398.     Context.pExcludeCPUDef =
  399.     Context.pLastCPUDef = NULL;
  400.     strmaxcat(Context.Str, getmessage(Num_ErrMsgOnlyCPUSupported1), sizeof(Context.Str));
  401.  
  402.     /* convert vararg list to bitmap */
  403.  
  404.     Context.ExcludeMask = 0;
  405.     Context.ExcludeCPUFirst = CPUNone;
  406.     va_start(ap, ErrorNum);
  407.     while (TRUE)
  408.     {
  409.       ThisCPU = va_arg(ap, CPUVar);
  410.       if (ThisCPU == CPUNone)
  411.         break;
  412.       if (!Context.pExcludeCPUDef)
  413.         Context.pExcludeCPUDef = LookupCPUDefByVar(ThisCPU);
  414.       if (Context.ExcludeCPUFirst == CPUNone)
  415.       {
  416.         Context.ExcludeCPUFirst = ThisCPU;
  417.         Context.ExcludeMask = 1;
  418.       }
  419.       else if (ThisCPU > Context.ExcludeCPUFirst)
  420.         Context.ExcludeMask |= 1 << (ThisCPU - Context.ExcludeCPUFirst);
  421.       else if (ThisCPU < Context.ExcludeCPUFirst)
  422.       {
  423.         Context.ExcludeMask <<= Context.ExcludeCPUFirst - ThisCPU;
  424.         Context.ExcludeMask |= 1;
  425.         Context.ExcludeCPUFirst = ThisCPU;
  426.       }
  427.     }
  428.     va_end(ap);
  429.     IterateCPUList(IterateExclude, &Context);
  430.     IterateExclude(NULL, &Context);
  431.     WrXError(GetDefaultCPUErrorNum((tErrorNum)ErrorNum), Context.Str);
  432.   }
  433.  
  434.   return FoundIndex;
  435. }
  436.  
  437. /*!------------------------------------------------------------------------
  438.  * \fn     ChkExactCPUList(int ErrorNum)
  439.  * \brief  check whether currently selected CPU is one of the given ones and issue error if not
  440.  * \param  ErrorNum error to issue if not OK (0 = default message)
  441.  * \param  ... List of CPUs terminated by CPUNone
  442.  * \return Index (0...) of matching CPU or -1 if current CPU does not match
  443.  * ------------------------------------------------------------------------ */
  444.  
  445. extern int ChkExactCPUList(int ErrorNum, ...)
  446. {
  447.   va_list ap;
  448.   String Str;
  449.   CPUVar ThisCPU, NextCPU;
  450.   const tCPUDef *pCPUDef;
  451.   Boolean First = True;
  452.   int FoundIndex = 0;
  453.  
  454.   va_start(ap, ErrorNum);
  455.   while (True)
  456.   {
  457.     ThisCPU = va_arg(ap, CPUVar);
  458.     if ((ThisCPU == CPUNone) || (MomCPU == ThisCPU))
  459.       break;
  460.     FoundIndex++;
  461.   }
  462.   va_end(ap);
  463.   if (ThisCPU != CPUNone)
  464.     return FoundIndex;
  465.  
  466.   va_start(ap, ErrorNum);
  467.   *Str = '\0';
  468.   strmaxcat(Str, getmessage(Num_ErrMsgOnlyCPUSupported1), sizeof(Str));
  469.   ThisCPU = CPUNone;
  470.   while (True)
  471.   {
  472.     NextCPU = va_arg(ap, CPUVar);
  473.     pCPUDef = (ThisCPU != CPUNone) ? LookupCPUDefByVar(ThisCPU) : NULL;
  474.     if (pCPUDef)
  475.     {
  476.       if (!First)
  477.         strmaxcat(Str, (NextCPU == CPUNone) ? getmessage(Num_ErrMsgOnlyCPUSupportedOr) : ", ", sizeof(Str));
  478.       strmaxcat(Str, pCPUDef->Name, sizeof(Str));
  479.       First = False;
  480.     }
  481.     if (NextCPU == CPUNone)
  482.       break;
  483.     ThisCPU = NextCPU;
  484.   }
  485.   va_end(ap);
  486.   strmaxcat(Str, getmessage(Num_ErrMsgOnlyCPUSupported2), sizeof(Str));
  487.   WrXError(GetDefaultCPUErrorNum((tErrorNum)ErrorNum), Str);
  488.   return -1;
  489. }
  490.  
  491. /*!------------------------------------------------------------------------
  492.  * \fn     ChkExactCPUMaskExt(Word CPUMask, CPUVar FirstCPU, tErrorNum ErrorNum)
  493.  * \brief  check whether currently selected CPU is one of the given ones and issue error if not
  494.  * \param  CPUMask bit mask of allowed CPUs
  495.  * \param  CPUVar CPU corresponding to bit 0 in mask
  496.  * \param  ErrorNum error to issue if not OK (0 = default message)
  497.  * \param  ... List of CPUs terminated by CPUNone
  498.  * \return Index (0...) of matching CPU or -1 if current CPU does not match
  499.  * ------------------------------------------------------------------------ */
  500.  
  501. int ChkExactCPUMaskExt(Word CPUMask, CPUVar FirstCPU, tErrorNum ErrorNum)
  502. {
  503.   int Bit = MomCPU - FirstCPU;
  504.   String Str;
  505.   const tCPUDef *pCPUDef;
  506.   Boolean First = True;
  507.   CPUVar ThisCPU;
  508.  
  509.   if (CPUMask & (1 << Bit))
  510.     return Bit;
  511.  
  512.   *Str = '\0';
  513.   strmaxcat(Str, getmessage(Num_ErrMsgOnlyCPUSupported1), sizeof(Str));
  514.   for (Bit = 0, ThisCPU = FirstCPU; Bit < 16; Bit++, ThisCPU++)
  515.   {
  516.     if (!(CPUMask & (1 << Bit)))
  517.       continue;
  518.     CPUMask &= ~(1 << Bit);
  519.     pCPUDef = LookupCPUDefByVar(ThisCPU);
  520.     if (pCPUDef)
  521.     {
  522.       if (!First)
  523.         strmaxcat(Str, CPUMask ? ", " : getmessage(Num_ErrMsgOnlyCPUSupportedOr), sizeof(Str));
  524.       strmaxcat(Str, pCPUDef->Name, sizeof(Str));
  525.       First = False;
  526.     }
  527.   }
  528.   strmaxcat(Str, getmessage(Num_ErrMsgOnlyCPUSupported2), sizeof(Str));
  529.   WrXError(ErrorNum ? ErrorNum : ErrNum_InstructionNotSupported, Str);
  530.   return -1;
  531. }
  532.  
  533. /*!------------------------------------------------------------------------
  534.  * \fn     ChkSamePage(LargeWord Addr1, LargeWord Addr2, unsigned PageBits)
  535.  * \brief  check whether two addresses are of same page
  536.  * \param  CurrAddr, DestAddr addresses to check
  537.  * \param  PageBits page size in bits
  538.  * \param  DestFlags symbol flags of DestAddr
  539.  * \return TRUE if OK
  540.  * ------------------------------------------------------------------------ */
  541.  
  542. Boolean ChkSamePage(LargeWord CurrAddr, LargeWord DestAddr, unsigned PageBits, tSymbolFlags DestFlags)
  543. {
  544.   LargeWord Mask = ~((1ul << PageBits) - 1);
  545.   Boolean Result = ((CurrAddr & Mask) == (DestAddr & Mask))
  546.                 || mFirstPassUnknownOrQuestionable(DestFlags);
  547.   if (!Result)
  548.     WrError(ErrNum_JmpTargOnDiffPage);
  549.   return Result;
  550. }
  551.