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