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