1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* pseudo.c */ |
4 | /* */ |
5 | /* Pseudo instructions for the ca65 macroassembler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | #include <ctype.h> |
40 | #include <errno.h> |
41 | |
42 | /* common */ |
43 | #include "alignment.h" |
44 | #include "assertion.h" |
45 | #include "bitops.h" |
46 | #include "cddefs.h" |
47 | #include "coll.h" |
48 | #include "filestat.h" |
49 | #include "gentype.h" |
50 | #include "intstack.h" |
51 | #include "scopedefs.h" |
52 | #include "symdefs.h" |
53 | #include "tgttrans.h" |
54 | #include "xmalloc.h" |
55 | |
56 | /* ca65 */ |
57 | #include "anonname.h" |
58 | #include "asserts.h" |
59 | #include "condasm.h" |
60 | #include "dbginfo.h" |
61 | #include "enum.h" |
62 | #include "error.h" |
63 | #include "expr.h" |
64 | #include "feature.h" |
65 | #include "filetab.h" |
66 | #include "global.h" |
67 | #include "incpath.h" |
68 | #include "instr.h" |
69 | #include "listing.h" |
70 | #include "macro.h" |
71 | #include "nexttok.h" |
72 | #include "objcode.h" |
73 | #include "options.h" |
74 | #include "pseudo.h" |
75 | #include "repeat.h" |
76 | #include "segment.h" |
77 | #include "sizeof.h" |
78 | #include "span.h" |
79 | #include "spool.h" |
80 | #include "struct.h" |
81 | #include "symbol.h" |
82 | #include "symtab.h" |
83 | |
84 | |
85 | |
86 | /*****************************************************************************/ |
87 | /* Data */ |
88 | /*****************************************************************************/ |
89 | |
90 | |
91 | |
92 | /* Keyword we're about to handle */ |
93 | static StrBuf Keyword = STATIC_STRBUF_INITIALIZER; |
94 | |
95 | /* CPU stack */ |
96 | static IntStack CPUStack = STATIC_INTSTACK_INITIALIZER; |
97 | |
98 | /* Segment stack */ |
99 | #define MAX_PUSHED_SEGMENTS 16 |
100 | static Collection SegStack = STATIC_COLLECTION_INITIALIZER; |
101 | |
102 | |
103 | |
104 | /*****************************************************************************/ |
105 | /* Forwards */ |
106 | /*****************************************************************************/ |
107 | |
108 | |
109 | |
110 | static void DoUnexpected (void); |
111 | /* Got an unexpected keyword */ |
112 | |
113 | static void DoInvalid (void); |
114 | /* Handle a token that is invalid here, since it should have been handled on |
115 | ** a much lower level of the expression hierarchy. Getting this sort of token |
116 | ** means that the lower level code has bugs. |
117 | ** This function differs to DoUnexpected in that the latter may be triggered |
118 | ** by the user by using keywords in the wrong location. DoUnexpected is not |
119 | ** an error in the assembler itself, while DoInvalid is. |
120 | */ |
121 | |
122 | |
123 | |
124 | /*****************************************************************************/ |
125 | /* Helper functions */ |
126 | /*****************************************************************************/ |
127 | |
128 | |
129 | |
130 | static unsigned char OptionalAddrSize (void) |
131 | /* If a colon follows, parse an optional address size spec and return it. |
132 | ** Otherwise return ADDR_SIZE_DEFAULT. |
133 | */ |
134 | { |
135 | unsigned AddrSize = ADDR_SIZE_DEFAULT; |
136 | if (CurTok.Tok == TOK_COLON) { |
137 | NextTok (); |
138 | AddrSize = ParseAddrSize (); |
139 | if (!ValidAddrSizeForCPU (AddrSize)) { |
140 | /* Print an error and reset to default */ |
141 | Error ("Invalid address size specification for current CPU" ); |
142 | AddrSize = ADDR_SIZE_DEFAULT; |
143 | } |
144 | NextTok (); |
145 | } |
146 | return AddrSize; |
147 | } |
148 | |
149 | |
150 | |
151 | static void SetBoolOption (unsigned char* Flag) |
152 | /* Read a on/off/+/- option and set flag accordingly */ |
153 | { |
154 | static const char* const Keys[] = { |
155 | "OFF" , |
156 | "ON" , |
157 | }; |
158 | |
159 | if (CurTok.Tok == TOK_PLUS) { |
160 | *Flag = 1; |
161 | NextTok (); |
162 | } else if (CurTok.Tok == TOK_MINUS) { |
163 | *Flag = 0; |
164 | NextTok (); |
165 | } else if (CurTok.Tok == TOK_IDENT) { |
166 | /* Map the keyword to a number */ |
167 | switch (GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0]))) { |
168 | case 0: *Flag = 0; NextTok (); break; |
169 | case 1: *Flag = 1; NextTok (); break; |
170 | default: ErrorSkip ("'on' or 'off' expected" ); break; |
171 | } |
172 | } else if (TokIsSep (CurTok.Tok)) { |
173 | /* Without anything assume switch on */ |
174 | *Flag = 1; |
175 | } else { |
176 | ErrorSkip ("'on' or 'off' expected" ); |
177 | } |
178 | } |
179 | |
180 | |
181 | |
182 | static void ExportWithAssign (SymEntry* Sym, unsigned char AddrSize, unsigned Flags) |
183 | /* Allow to assign the value of an export in an .export statement */ |
184 | { |
185 | /* The name and optional address size spec may be followed by an assignment |
186 | ** or equal token. |
187 | */ |
188 | if (CurTok.Tok == TOK_ASSIGN || CurTok.Tok == TOK_EQ) { |
189 | |
190 | /* Assignment means the symbol is a label */ |
191 | if (CurTok.Tok == TOK_ASSIGN) { |
192 | Flags |= SF_LABEL; |
193 | } |
194 | |
195 | /* Skip the assignment token */ |
196 | NextTok (); |
197 | |
198 | /* Define the symbol with the expression following the '=' */ |
199 | SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags); |
200 | |
201 | } |
202 | |
203 | /* Now export the symbol */ |
204 | SymExport (Sym, AddrSize, Flags); |
205 | } |
206 | |
207 | |
208 | |
209 | static void ExportImport (void (*Func) (SymEntry*, unsigned char, unsigned), |
210 | unsigned char DefAddrSize, unsigned Flags) |
211 | /* Export or import symbols */ |
212 | { |
213 | SymEntry* Sym; |
214 | unsigned char AddrSize; |
215 | |
216 | while (1) { |
217 | |
218 | /* We need an identifier here */ |
219 | if (CurTok.Tok != TOK_IDENT) { |
220 | ErrorSkip ("Identifier expected" ); |
221 | return; |
222 | } |
223 | |
224 | /* Find the symbol table entry, allocate a new one if necessary */ |
225 | Sym = SymFind (CurrentScope, &CurTok.SVal, SYM_ALLOC_NEW); |
226 | |
227 | /* Skip the name */ |
228 | NextTok (); |
229 | |
230 | /* Get an optional address size */ |
231 | AddrSize = OptionalAddrSize (); |
232 | if (AddrSize == ADDR_SIZE_DEFAULT) { |
233 | AddrSize = DefAddrSize; |
234 | } |
235 | |
236 | /* Call the actual import/export function */ |
237 | Func (Sym, AddrSize, Flags); |
238 | |
239 | /* More symbols? */ |
240 | if (CurTok.Tok == TOK_COMMA) { |
241 | NextTok (); |
242 | } else { |
243 | break; |
244 | } |
245 | } |
246 | } |
247 | |
248 | |
249 | |
250 | static long IntArg (long Min, long Max) |
251 | /* Read an integer argument and check a range. Accept the token "unlimited" |
252 | ** and return -1 in this case. |
253 | */ |
254 | { |
255 | if (CurTok.Tok == TOK_IDENT && SB_CompareStr (&CurTok.SVal, "unlimited" ) == 0) { |
256 | NextTok (); |
257 | return -1; |
258 | } else { |
259 | long Val = ConstExpression (); |
260 | if (Val < Min || Val > Max) { |
261 | Error ("Range error" ); |
262 | Val = Min; |
263 | } |
264 | return Val; |
265 | } |
266 | } |
267 | |
268 | |
269 | |
270 | static void ConDes (const StrBuf* Name, unsigned Type) |
271 | /* Parse remaining line for constructor/destructor of the remaining type */ |
272 | { |
273 | long Prio; |
274 | |
275 | |
276 | /* Find the symbol table entry, allocate a new one if necessary */ |
277 | SymEntry* Sym = SymFind (CurrentScope, Name, SYM_ALLOC_NEW); |
278 | |
279 | /* Optional constructor priority */ |
280 | if (CurTok.Tok == TOK_COMMA) { |
281 | /* Priority value follows */ |
282 | NextTok (); |
283 | Prio = ConstExpression (); |
284 | if (Prio < CD_PRIO_MIN || Prio > CD_PRIO_MAX) { |
285 | /* Value out of range */ |
286 | Error ("Range error" ); |
287 | return; |
288 | } |
289 | } else { |
290 | /* Use the default priority value */ |
291 | Prio = CD_PRIO_DEF; |
292 | } |
293 | |
294 | /* Define the symbol */ |
295 | SymConDes (Sym, ADDR_SIZE_DEFAULT, Type, (unsigned) Prio); |
296 | } |
297 | |
298 | |
299 | |
300 | static StrBuf* GenArrayType (StrBuf* Type, unsigned SpanSize, |
301 | const char* ElementType, |
302 | unsigned ElementTypeLen) |
303 | /* Create an array (or single data) of the given type. SpanSize is the size |
304 | ** of the span, ElementType is a string that encodes the element data type. |
305 | ** The function returns Type. |
306 | */ |
307 | { |
308 | /* Get the size of the element type */ |
309 | unsigned ElementSize = GT_GET_SIZE (ElementType[0]); |
310 | |
311 | /* Get the number of array elements */ |
312 | unsigned ElementCount = SpanSize / ElementSize; |
313 | |
314 | /* The span size must be divideable by the element size */ |
315 | CHECK ((SpanSize % ElementSize) == 0); |
316 | |
317 | /* Encode the array */ |
318 | GT_AddArray (Type, ElementCount); |
319 | SB_AppendBuf (Type, ElementType, ElementTypeLen); |
320 | |
321 | /* Return the pointer to the created array type */ |
322 | return Type; |
323 | } |
324 | |
325 | |
326 | |
327 | /*****************************************************************************/ |
328 | /* Handler functions */ |
329 | /*****************************************************************************/ |
330 | |
331 | |
332 | |
333 | static void DoA16 (void) |
334 | /* Switch the accu to 16 bit mode (assembler only) */ |
335 | { |
336 | if (GetCPU() != CPU_65816) { |
337 | Error ("Command is only valid in 65816 mode" ); |
338 | } else { |
339 | /* Immidiate mode has two extension bytes */ |
340 | ExtBytes [AM65I_IMM_ACCU] = 2; |
341 | } |
342 | } |
343 | |
344 | |
345 | |
346 | static void DoA8 (void) |
347 | /* Switch the accu to 8 bit mode (assembler only) */ |
348 | { |
349 | if (GetCPU() != CPU_65816) { |
350 | Error ("Command is only valid in 65816 mode" ); |
351 | } else { |
352 | /* Immidiate mode has one extension byte */ |
353 | ExtBytes [AM65I_IMM_ACCU] = 1; |
354 | } |
355 | } |
356 | |
357 | |
358 | |
359 | static void DoAddr (void) |
360 | /* Define addresses */ |
361 | { |
362 | /* Element type for the generated array */ |
363 | static const char EType[2] = { GT_PTR, GT_VOID }; |
364 | |
365 | /* Record type information */ |
366 | Span* S = OpenSpan (); |
367 | StrBuf Type = STATIC_STRBUF_INITIALIZER; |
368 | |
369 | /* Parse arguments */ |
370 | while (1) { |
371 | ExprNode* Expr = Expression (); |
372 | if (GetCPU () == CPU_65816 || ForceRange) { |
373 | /* Do a range check */ |
374 | Expr = GenWordExpr (Expr); |
375 | } |
376 | EmitWord (Expr); |
377 | if (CurTok.Tok != TOK_COMMA) { |
378 | break; |
379 | } else { |
380 | NextTok (); |
381 | } |
382 | } |
383 | |
384 | /* Close the span, then add type information to it */ |
385 | S = CloseSpan (S); |
386 | SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType))); |
387 | |
388 | /* Free the strings */ |
389 | SB_Done (&Type); |
390 | } |
391 | |
392 | |
393 | |
394 | static void DoAlign (void) |
395 | /* Align the PC to some boundary */ |
396 | { |
397 | long FillVal; |
398 | long Alignment; |
399 | |
400 | /* Read the alignment value */ |
401 | Alignment = ConstExpression (); |
402 | if (Alignment <= 0 || (unsigned long) Alignment > MAX_ALIGNMENT) { |
403 | ErrorSkip ("Range error" ); |
404 | return; |
405 | } |
406 | |
407 | /* Optional value follows */ |
408 | if (CurTok.Tok == TOK_COMMA) { |
409 | NextTok (); |
410 | FillVal = ConstExpression (); |
411 | /* We need a byte value here */ |
412 | if (!IsByteRange (FillVal)) { |
413 | ErrorSkip ("Range error" ); |
414 | return; |
415 | } |
416 | } else { |
417 | FillVal = -1; |
418 | } |
419 | |
420 | /* Generate the alignment */ |
421 | SegAlign (Alignment, (int) FillVal); |
422 | } |
423 | |
424 | |
425 | |
426 | static void DoASCIIZ (void) |
427 | /* Define text with a zero terminator */ |
428 | { |
429 | while (1) { |
430 | /* Must have a string constant */ |
431 | if (CurTok.Tok != TOK_STRCON) { |
432 | ErrorSkip ("String constant expected" ); |
433 | return; |
434 | } |
435 | |
436 | /* Translate into target charset and emit */ |
437 | TgtTranslateStrBuf (&CurTok.SVal); |
438 | EmitStrBuf (&CurTok.SVal); |
439 | NextTok (); |
440 | if (CurTok.Tok == TOK_COMMA) { |
441 | NextTok (); |
442 | } else { |
443 | break; |
444 | } |
445 | } |
446 | Emit0 (0); |
447 | } |
448 | |
449 | |
450 | |
451 | static void DoAssert (void) |
452 | /* Add an assertion */ |
453 | { |
454 | static const char* const ActionTab [] = { |
455 | "WARN" , "WARNING" , |
456 | "ERROR" , |
457 | "LDWARN" , "LDWARNING" , |
458 | "LDERROR" , |
459 | }; |
460 | |
461 | AssertAction Action; |
462 | unsigned Msg; |
463 | |
464 | /* First we have the expression that has to evaluated */ |
465 | ExprNode* Expr = Expression (); |
466 | ConsumeComma (); |
467 | |
468 | /* Action follows */ |
469 | if (CurTok.Tok != TOK_IDENT) { |
470 | ErrorSkip ("Identifier expected" ); |
471 | return; |
472 | } |
473 | switch (GetSubKey (ActionTab, sizeof (ActionTab) / sizeof (ActionTab[0]))) { |
474 | |
475 | case 0: |
476 | case 1: |
477 | /* Warning */ |
478 | Action = ASSERT_ACT_WARN; |
479 | break; |
480 | |
481 | case 2: |
482 | /* Error */ |
483 | Action = ASSERT_ACT_ERROR; |
484 | break; |
485 | |
486 | case 3: |
487 | case 4: |
488 | /* Linker warning */ |
489 | Action = ASSERT_ACT_LDWARN; |
490 | break; |
491 | |
492 | case 5: |
493 | /* Linker error */ |
494 | Action = ASSERT_ACT_LDERROR; |
495 | break; |
496 | |
497 | default: |
498 | Error ("Illegal assert action specifier" ); |
499 | /* Use lderror - there won't be an .o file anyway */ |
500 | Action = ASSERT_ACT_LDERROR; |
501 | break; |
502 | |
503 | } |
504 | NextTok (); |
505 | |
506 | /* We can have an optional message. If no message is present, use |
507 | ** "Assertion failed". |
508 | */ |
509 | if (CurTok.Tok == TOK_COMMA) { |
510 | |
511 | /* Skip the comma */ |
512 | NextTok (); |
513 | |
514 | /* Read the message */ |
515 | if (CurTok.Tok != TOK_STRCON) { |
516 | ErrorSkip ("String constant expected" ); |
517 | return; |
518 | } |
519 | |
520 | /* Translate the message into a string id. We can then skip the input |
521 | ** string. |
522 | */ |
523 | Msg = GetStrBufId (&CurTok.SVal); |
524 | NextTok (); |
525 | |
526 | } else { |
527 | |
528 | /* Use "Assertion failed" */ |
529 | Msg = GetStringId ("Assertion failed" ); |
530 | |
531 | } |
532 | |
533 | /* Remember the assertion */ |
534 | AddAssertion (Expr, (AssertAction) Action, Msg); |
535 | } |
536 | |
537 | |
538 | |
539 | static void DoAutoImport (void) |
540 | /* Mark unresolved symbols as imported */ |
541 | { |
542 | SetBoolOption (&AutoImport); |
543 | } |
544 | |
545 | |
546 | static void DoBankBytes (void) |
547 | /* Define bytes, extracting the bank byte from each expression in the list */ |
548 | { |
549 | while (1) { |
550 | EmitByte (FuncBankByte ()); |
551 | if (CurTok.Tok != TOK_COMMA) { |
552 | break; |
553 | } else { |
554 | NextTok (); |
555 | } |
556 | } |
557 | } |
558 | |
559 | |
560 | |
561 | static void DoBss (void) |
562 | /* Switch to the BSS segment */ |
563 | { |
564 | UseSeg (&BssSegDef); |
565 | } |
566 | |
567 | |
568 | |
569 | static void DoByte (void) |
570 | /* Define bytes */ |
571 | { |
572 | /* Element type for the generated array */ |
573 | static const char EType[1] = { GT_BYTE }; |
574 | |
575 | /* Record type information */ |
576 | Span* S = OpenSpan (); |
577 | StrBuf Type = AUTO_STRBUF_INITIALIZER; |
578 | |
579 | /* Parse arguments */ |
580 | while (1) { |
581 | if (CurTok.Tok == TOK_STRCON) { |
582 | /* A string, translate into target charset and emit */ |
583 | TgtTranslateStrBuf (&CurTok.SVal); |
584 | EmitStrBuf (&CurTok.SVal); |
585 | NextTok (); |
586 | } else { |
587 | EmitByte (BoundedExpr (Expression, 1)); |
588 | } |
589 | if (CurTok.Tok != TOK_COMMA) { |
590 | break; |
591 | } else { |
592 | NextTok (); |
593 | /* Do smart handling of dangling comma */ |
594 | if (CurTok.Tok == TOK_SEP) { |
595 | Error ("Unexpected end of line" ); |
596 | break; |
597 | } |
598 | } |
599 | } |
600 | |
601 | /* Close the span, then add type information to it. |
602 | ** Note: empty string operands emit nothing; |
603 | ** so, add a type only if there's a span. |
604 | */ |
605 | S = CloseSpan (S); |
606 | if (S != 0) { |
607 | SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType))); |
608 | } |
609 | |
610 | /* Free the type string */ |
611 | SB_Done (&Type); |
612 | } |
613 | |
614 | |
615 | |
616 | static void DoCase (void) |
617 | /* Switch the IgnoreCase option */ |
618 | { |
619 | SetBoolOption (&IgnoreCase); |
620 | IgnoreCase = !IgnoreCase; |
621 | } |
622 | |
623 | |
624 | |
625 | static void DoCharMap (void) |
626 | /* Allow custom character mappings */ |
627 | { |
628 | long Index; |
629 | long Code; |
630 | |
631 | /* Read the index as numerical value */ |
632 | Index = ConstExpression (); |
633 | if (Index < 0 || Index > 255) { |
634 | /* Value out of range */ |
635 | ErrorSkip ("Index range error" ); |
636 | return; |
637 | } |
638 | |
639 | /* Comma follows */ |
640 | ConsumeComma (); |
641 | |
642 | /* Read the character code */ |
643 | Code = ConstExpression (); |
644 | if (Code < 0 || Code > 255) { |
645 | /* Value out of range */ |
646 | ErrorSkip ("Code range error" ); |
647 | return; |
648 | } |
649 | |
650 | /* Set the character translation */ |
651 | TgtTranslateSet ((unsigned) Index, (unsigned char) Code); |
652 | } |
653 | |
654 | |
655 | |
656 | static void DoCode (void) |
657 | /* Switch to the code segment */ |
658 | { |
659 | UseSeg (&CodeSegDef); |
660 | } |
661 | |
662 | |
663 | |
664 | static void DoConDes (void) |
665 | /* Export a symbol as constructor/destructor */ |
666 | { |
667 | static const char* const Keys[] = { |
668 | "CONSTRUCTOR" , |
669 | "DESTRUCTOR" , |
670 | "INTERRUPTOR" , |
671 | }; |
672 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
673 | long Type; |
674 | |
675 | /* Symbol name follows */ |
676 | if (CurTok.Tok != TOK_IDENT) { |
677 | ErrorSkip ("Identifier expected" ); |
678 | return; |
679 | } |
680 | SB_Copy (&Name, &CurTok.SVal); |
681 | NextTok (); |
682 | |
683 | /* Type follows. May be encoded as identifier or numerical */ |
684 | ConsumeComma (); |
685 | if (CurTok.Tok == TOK_IDENT) { |
686 | |
687 | /* Map the following keyword to a number, then skip it */ |
688 | Type = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); |
689 | NextTok (); |
690 | |
691 | /* Check if we got a valid keyword */ |
692 | if (Type < 0) { |
693 | ErrorSkip ("Syntax error" ); |
694 | goto ExitPoint; |
695 | } |
696 | |
697 | } else { |
698 | |
699 | /* Read the type as numerical value */ |
700 | Type = ConstExpression (); |
701 | if (Type < CD_TYPE_MIN || Type > CD_TYPE_MAX) { |
702 | /* Value out of range */ |
703 | ErrorSkip ("Range error" ); |
704 | goto ExitPoint; |
705 | } |
706 | |
707 | } |
708 | |
709 | /* Parse the remainder of the line and export the symbol */ |
710 | ConDes (&Name, (unsigned) Type); |
711 | |
712 | ExitPoint: |
713 | /* Free string memory */ |
714 | SB_Done (&Name); |
715 | } |
716 | |
717 | |
718 | |
719 | static void DoConstructor (void) |
720 | /* Export a symbol as constructor */ |
721 | { |
722 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
723 | |
724 | /* Symbol name follows */ |
725 | if (CurTok.Tok != TOK_IDENT) { |
726 | ErrorSkip ("Identifier expected" ); |
727 | return; |
728 | } |
729 | SB_Copy (&Name, &CurTok.SVal); |
730 | NextTok (); |
731 | |
732 | /* Parse the remainder of the line and export the symbol */ |
733 | ConDes (&Name, CD_TYPE_CON); |
734 | |
735 | /* Free string memory */ |
736 | SB_Done (&Name); |
737 | } |
738 | |
739 | |
740 | |
741 | static void DoData (void) |
742 | /* Switch to the data segment */ |
743 | { |
744 | UseSeg (&DataSegDef); |
745 | } |
746 | |
747 | |
748 | |
749 | static void DoDbg (void) |
750 | /* Add debug information from high level code */ |
751 | { |
752 | static const char* const Keys[] = { |
753 | "FILE" , |
754 | "FUNC" , |
755 | "LINE" , |
756 | "SYM" , |
757 | }; |
758 | int Key; |
759 | |
760 | |
761 | /* We expect a subkey */ |
762 | if (CurTok.Tok != TOK_IDENT) { |
763 | ErrorSkip ("Identifier expected" ); |
764 | return; |
765 | } |
766 | |
767 | /* Map the following keyword to a number */ |
768 | Key = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); |
769 | |
770 | /* Skip the subkey */ |
771 | NextTok (); |
772 | |
773 | /* Check the key and dispatch to a handler */ |
774 | switch (Key) { |
775 | case 0: DbgInfoFile (); break; |
776 | case 1: DbgInfoFunc (); break; |
777 | case 2: DbgInfoLine (); break; |
778 | case 3: DbgInfoSym (); break; |
779 | default: ErrorSkip ("Syntax error" ); break; |
780 | } |
781 | } |
782 | |
783 | |
784 | |
785 | static void DoDByt (void) |
786 | /* Output double bytes */ |
787 | { |
788 | /* Element type for the generated array */ |
789 | static const char EType[1] = { GT_DBYTE }; |
790 | |
791 | /* Record type information */ |
792 | Span* S = OpenSpan (); |
793 | StrBuf Type = STATIC_STRBUF_INITIALIZER; |
794 | |
795 | /* Parse arguments */ |
796 | while (1) { |
797 | EmitWord (GenSwapExpr (BoundedExpr (Expression, 2))); |
798 | if (CurTok.Tok != TOK_COMMA) { |
799 | break; |
800 | } else { |
801 | NextTok (); |
802 | } |
803 | } |
804 | |
805 | /* Close the span, then add type information to it */ |
806 | S = CloseSpan (S); |
807 | SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType))); |
808 | |
809 | /* Free the type string */ |
810 | SB_Done (&Type); |
811 | } |
812 | |
813 | |
814 | |
815 | static void DoDebugInfo (void) |
816 | /* Switch debug info on or off */ |
817 | { |
818 | SetBoolOption (&DbgSyms); |
819 | } |
820 | |
821 | |
822 | |
823 | static void DoDefine (void) |
824 | /* Define a one line macro */ |
825 | { |
826 | MacDef (MAC_STYLE_DEFINE); |
827 | } |
828 | |
829 | |
830 | |
831 | static void DoDelMac (void) |
832 | /* Delete a classic macro */ |
833 | { |
834 | /* We expect an identifier */ |
835 | if (CurTok.Tok != TOK_IDENT) { |
836 | ErrorSkip ("Identifier expected" ); |
837 | } else { |
838 | MacUndef (&CurTok.SVal, MAC_STYLE_CLASSIC); |
839 | NextTok (); |
840 | } |
841 | } |
842 | |
843 | |
844 | |
845 | static void DoDestructor (void) |
846 | /* Export a symbol as destructor */ |
847 | { |
848 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
849 | |
850 | /* Symbol name follows */ |
851 | if (CurTok.Tok != TOK_IDENT) { |
852 | ErrorSkip ("Identifier expected" ); |
853 | return; |
854 | } |
855 | SB_Copy (&Name, &CurTok.SVal); |
856 | NextTok (); |
857 | |
858 | /* Parse the remainder of the line and export the symbol */ |
859 | ConDes (&Name, CD_TYPE_DES); |
860 | |
861 | /* Free string memory */ |
862 | SB_Done (&Name); |
863 | } |
864 | |
865 | |
866 | |
867 | static void DoDWord (void) |
868 | /* Define dwords */ |
869 | { |
870 | while (1) { |
871 | EmitDWord (BoundedExpr (Expression, 4)); |
872 | if (CurTok.Tok != TOK_COMMA) { |
873 | break; |
874 | } else { |
875 | NextTok (); |
876 | } |
877 | } |
878 | } |
879 | |
880 | |
881 | |
882 | static void DoEnd (void) |
883 | /* End of assembly */ |
884 | { |
885 | ForcedEnd = 1; |
886 | NextTok (); |
887 | } |
888 | |
889 | |
890 | |
891 | static void DoEndProc (void) |
892 | /* Leave a lexical level */ |
893 | { |
894 | if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label == 0) { |
895 | /* No local scope */ |
896 | ErrorSkip ("No open .PROC" ); |
897 | } else { |
898 | SymLeaveLevel (); |
899 | } |
900 | } |
901 | |
902 | |
903 | |
904 | static void DoEndScope (void) |
905 | /* Leave a lexical level */ |
906 | { |
907 | if (CurrentScope->Type != SCOPE_SCOPE || CurrentScope->Label != 0) { |
908 | /* No local scope */ |
909 | ErrorSkip ("No open .SCOPE" ); |
910 | } else { |
911 | SymLeaveLevel (); |
912 | } |
913 | } |
914 | |
915 | |
916 | |
917 | static void DoError (void) |
918 | /* User error */ |
919 | { |
920 | if (CurTok.Tok != TOK_STRCON) { |
921 | ErrorSkip ("String constant expected" ); |
922 | } else { |
923 | Error ("User error: %m%p" , &CurTok.SVal); |
924 | SkipUntilSep (); |
925 | } |
926 | } |
927 | |
928 | |
929 | |
930 | static void DoExitMacro (void) |
931 | /* Exit a macro expansion */ |
932 | { |
933 | if (!InMacExpansion ()) { |
934 | /* We aren't expanding a macro currently */ |
935 | DoUnexpected (); |
936 | } else { |
937 | MacAbort (); |
938 | } |
939 | } |
940 | |
941 | |
942 | |
943 | static void DoExport (void) |
944 | /* Export a symbol */ |
945 | { |
946 | ExportImport (ExportWithAssign, ADDR_SIZE_DEFAULT, SF_NONE); |
947 | } |
948 | |
949 | |
950 | |
951 | static void DoExportZP (void) |
952 | /* Export a zeropage symbol */ |
953 | { |
954 | ExportImport (ExportWithAssign, ADDR_SIZE_ZP, SF_NONE); |
955 | } |
956 | |
957 | |
958 | |
959 | static void DoFarAddr (void) |
960 | /* Define far addresses (24 bit) */ |
961 | { |
962 | /* Element type for the generated array */ |
963 | static const char EType[2] = { GT_FAR_PTR, GT_VOID }; |
964 | |
965 | /* Record type information */ |
966 | Span* S = OpenSpan (); |
967 | StrBuf Type = STATIC_STRBUF_INITIALIZER; |
968 | |
969 | /* Parse arguments */ |
970 | while (1) { |
971 | EmitFarAddr (BoundedExpr (Expression, 3)); |
972 | if (CurTok.Tok != TOK_COMMA) { |
973 | break; |
974 | } else { |
975 | NextTok (); |
976 | } |
977 | } |
978 | |
979 | /* Close the span, then add type information to it */ |
980 | S = CloseSpan (S); |
981 | SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType))); |
982 | |
983 | /* Free the type string */ |
984 | SB_Done (&Type); |
985 | } |
986 | |
987 | |
988 | |
989 | static void DoFatal (void) |
990 | /* Fatal user error */ |
991 | { |
992 | if (CurTok.Tok != TOK_STRCON) { |
993 | ErrorSkip ("String constant expected" ); |
994 | } else { |
995 | Fatal ("User error: %m%p" , &CurTok.SVal); |
996 | SkipUntilSep (); |
997 | } |
998 | } |
999 | |
1000 | |
1001 | |
1002 | static void DoFeature (void) |
1003 | /* Switch the Feature option */ |
1004 | { |
1005 | /* Allow a list of comma separated keywords */ |
1006 | while (1) { |
1007 | |
1008 | /* We expect an identifier */ |
1009 | if (CurTok.Tok != TOK_IDENT) { |
1010 | ErrorSkip ("Identifier expected" ); |
1011 | return; |
1012 | } |
1013 | |
1014 | /* Make the string attribute lower case */ |
1015 | LocaseSVal (); |
1016 | |
1017 | /* Set the feature and check for errors */ |
1018 | if (SetFeature (&CurTok.SVal) == FEAT_UNKNOWN) { |
1019 | /* Not found */ |
1020 | ErrorSkip ("Invalid feature: '%m%p'" , &CurTok.SVal); |
1021 | return; |
1022 | } else { |
1023 | /* Skip the keyword */ |
1024 | NextTok (); |
1025 | } |
1026 | |
1027 | /* Allow more than one keyword */ |
1028 | if (CurTok.Tok == TOK_COMMA) { |
1029 | NextTok (); |
1030 | } else { |
1031 | break; |
1032 | } |
1033 | } |
1034 | } |
1035 | |
1036 | |
1037 | |
1038 | static void DoFileOpt (void) |
1039 | /* Insert a file option */ |
1040 | { |
1041 | long OptNum; |
1042 | |
1043 | /* The option type may be given as a keyword or as a number. */ |
1044 | if (CurTok.Tok == TOK_IDENT) { |
1045 | |
1046 | /* Option given as keyword */ |
1047 | static const char* const Keys [] = { |
1048 | "AUTHOR" , "COMMENT" , "COMPILER" |
1049 | }; |
1050 | |
1051 | /* Map the option to a number */ |
1052 | OptNum = GetSubKey (Keys, sizeof (Keys) / sizeof (Keys [0])); |
1053 | if (OptNum < 0) { |
1054 | /* Not found */ |
1055 | ErrorSkip ("File option keyword expected" ); |
1056 | return; |
1057 | } |
1058 | |
1059 | /* Skip the keyword */ |
1060 | NextTok (); |
1061 | |
1062 | /* Must be followed by a comma */ |
1063 | ConsumeComma (); |
1064 | |
1065 | /* We accept only string options for now */ |
1066 | if (CurTok.Tok != TOK_STRCON) { |
1067 | ErrorSkip ("String constant expected" ); |
1068 | return; |
1069 | } |
1070 | |
1071 | /* Insert the option */ |
1072 | switch (OptNum) { |
1073 | |
1074 | case 0: |
1075 | /* Author */ |
1076 | OptAuthor (&CurTok.SVal); |
1077 | break; |
1078 | |
1079 | case 1: |
1080 | /* Comment */ |
1081 | OptComment (&CurTok.SVal); |
1082 | break; |
1083 | |
1084 | case 2: |
1085 | /* Compiler */ |
1086 | OptCompiler (&CurTok.SVal); |
1087 | break; |
1088 | |
1089 | default: |
1090 | Internal ("Invalid OptNum: %ld" , OptNum); |
1091 | |
1092 | } |
1093 | |
1094 | /* Done */ |
1095 | NextTok (); |
1096 | |
1097 | } else { |
1098 | |
1099 | /* Option given as number */ |
1100 | OptNum = ConstExpression (); |
1101 | if (!IsByteRange (OptNum)) { |
1102 | ErrorSkip ("Range error" ); |
1103 | return; |
1104 | } |
1105 | |
1106 | /* Must be followed by a comma */ |
1107 | ConsumeComma (); |
1108 | |
1109 | /* We accept only string options for now */ |
1110 | if (CurTok.Tok != TOK_STRCON) { |
1111 | ErrorSkip ("String constant expected" ); |
1112 | return; |
1113 | } |
1114 | |
1115 | /* Insert the option */ |
1116 | OptStr ((unsigned char) OptNum, &CurTok.SVal); |
1117 | |
1118 | /* Done */ |
1119 | NextTok (); |
1120 | } |
1121 | } |
1122 | |
1123 | |
1124 | |
1125 | static void DoForceImport (void) |
1126 | /* Do a forced import on a symbol */ |
1127 | { |
1128 | ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_FORCED); |
1129 | } |
1130 | |
1131 | |
1132 | |
1133 | static void DoGlobal (void) |
1134 | /* Declare a global symbol */ |
1135 | { |
1136 | ExportImport (SymGlobal, ADDR_SIZE_DEFAULT, SF_NONE); |
1137 | } |
1138 | |
1139 | |
1140 | |
1141 | static void DoGlobalZP (void) |
1142 | /* Declare a global zeropage symbol */ |
1143 | { |
1144 | ExportImport (SymGlobal, ADDR_SIZE_ZP, SF_NONE); |
1145 | } |
1146 | |
1147 | |
1148 | static void DoHiBytes (void) |
1149 | /* Define bytes, extracting the hi byte from each expression in the list */ |
1150 | { |
1151 | while (1) { |
1152 | EmitByte (FuncHiByte ()); |
1153 | if (CurTok.Tok != TOK_COMMA) { |
1154 | break; |
1155 | } else { |
1156 | NextTok (); |
1157 | } |
1158 | } |
1159 | } |
1160 | |
1161 | |
1162 | |
1163 | static void DoI16 (void) |
1164 | /* Switch the index registers to 16 bit mode (assembler only) */ |
1165 | { |
1166 | if (GetCPU() != CPU_65816) { |
1167 | Error ("Command is only valid in 65816 mode" ); |
1168 | } else { |
1169 | /* Immidiate mode has two extension bytes */ |
1170 | ExtBytes [AM65I_IMM_INDEX] = 2; |
1171 | } |
1172 | } |
1173 | |
1174 | |
1175 | |
1176 | static void DoI8 (void) |
1177 | /* Switch the index registers to 16 bit mode (assembler only) */ |
1178 | { |
1179 | if (GetCPU() != CPU_65816) { |
1180 | Error ("Command is only valid in 65816 mode" ); |
1181 | } else { |
1182 | /* Immidiate mode has one extension byte */ |
1183 | ExtBytes [AM65I_IMM_INDEX] = 1; |
1184 | } |
1185 | } |
1186 | |
1187 | |
1188 | |
1189 | static void DoImport (void) |
1190 | /* Import a symbol */ |
1191 | { |
1192 | ExportImport (SymImport, ADDR_SIZE_DEFAULT, SF_NONE); |
1193 | } |
1194 | |
1195 | |
1196 | |
1197 | static void DoImportZP (void) |
1198 | /* Import a zero page symbol */ |
1199 | { |
1200 | ExportImport (SymImport, ADDR_SIZE_ZP, SF_NONE); |
1201 | } |
1202 | |
1203 | |
1204 | |
1205 | static void DoIncBin (void) |
1206 | /* Include a binary file */ |
1207 | { |
1208 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
1209 | struct stat StatBuf; |
1210 | long Start = 0L; |
1211 | long Count = -1L; |
1212 | long Size; |
1213 | FILE* F; |
1214 | |
1215 | /* Name must follow */ |
1216 | if (CurTok.Tok != TOK_STRCON) { |
1217 | ErrorSkip ("String constant expected" ); |
1218 | return; |
1219 | } |
1220 | SB_Copy (&Name, &CurTok.SVal); |
1221 | SB_Terminate (&Name); |
1222 | NextTok (); |
1223 | |
1224 | /* A starting offset may follow */ |
1225 | if (CurTok.Tok == TOK_COMMA) { |
1226 | NextTok (); |
1227 | Start = ConstExpression (); |
1228 | |
1229 | /* And a length may follow */ |
1230 | if (CurTok.Tok == TOK_COMMA) { |
1231 | NextTok (); |
1232 | Count = ConstExpression (); |
1233 | } |
1234 | |
1235 | } |
1236 | |
1237 | /* Try to open the file */ |
1238 | F = fopen (SB_GetConstBuf (&Name), "rb" ); |
1239 | if (F == 0) { |
1240 | |
1241 | /* Search for the file in the binary include directory */ |
1242 | char* PathName = SearchFile (BinSearchPath, SB_GetConstBuf (&Name)); |
1243 | if (PathName == 0 || (F = fopen (PathName, "rb" )) == 0) { |
1244 | /* Not found or cannot open, print an error and bail out */ |
1245 | ErrorSkip ("Cannot open include file '%m%p': %s" , &Name, strerror (errno)); |
1246 | xfree (PathName); |
1247 | goto ExitPoint; |
1248 | } |
1249 | |
1250 | /* Remember the new file name */ |
1251 | SB_CopyStr (&Name, PathName); |
1252 | |
1253 | /* Free the allocated memory */ |
1254 | xfree (PathName); |
1255 | } |
1256 | |
1257 | /* Get the size of the file */ |
1258 | fseek (F, 0, SEEK_END); |
1259 | Size = ftell (F); |
1260 | |
1261 | /* Stat the file and remember the values. There's a race condition here, |
1262 | ** since we cannot use fileno() (non-standard identifier in standard |
1263 | ** header file), and therefore not fstat. When using stat with the |
1264 | ** file name, there's a risk that the file was deleted and recreated |
1265 | ** while it was open. Since mtime and size are only used to check |
1266 | ** if a file has changed in the debugger, we will ignore this problem |
1267 | ** here. |
1268 | */ |
1269 | SB_Terminate (&Name); |
1270 | if (FileStat (SB_GetConstBuf (&Name), &StatBuf) != 0) { |
1271 | Fatal ("Cannot stat input file '%m%p': %s" , &Name, strerror (errno)); |
1272 | } |
1273 | |
1274 | /* Add the file to the input file table */ |
1275 | AddFile (&Name, FT_BINARY, Size, (unsigned long) StatBuf.st_mtime); |
1276 | |
1277 | /* If a count was not given, calculate it now */ |
1278 | if (Count < 0) { |
1279 | Count = Size - Start; |
1280 | if (Count < 0) { |
1281 | /* Nothing to read - flag this as a range error */ |
1282 | ErrorSkip ("Range error" ); |
1283 | goto Done; |
1284 | } |
1285 | } else { |
1286 | /* Count was given, check if it is valid */ |
1287 | if (Start + Count > Size) { |
1288 | ErrorSkip ("Range error" ); |
1289 | goto Done; |
1290 | } |
1291 | } |
1292 | |
1293 | /* Seek to the start position */ |
1294 | fseek (F, Start, SEEK_SET); |
1295 | |
1296 | /* Read chunks and insert them into the output */ |
1297 | while (Count > 0) { |
1298 | |
1299 | unsigned char Buf [1024]; |
1300 | |
1301 | /* Calculate the number of bytes to read */ |
1302 | size_t BytesToRead = (Count > (long)sizeof(Buf))? sizeof(Buf) : (size_t) Count; |
1303 | |
1304 | /* Read chunk */ |
1305 | size_t BytesRead = fread (Buf, 1, BytesToRead, F); |
1306 | if (BytesToRead != BytesRead) { |
1307 | /* Some sort of error */ |
1308 | ErrorSkip ("Cannot read from include file '%m%p': %s" , |
1309 | &Name, strerror (errno)); |
1310 | break; |
1311 | } |
1312 | |
1313 | /* Insert it into the output */ |
1314 | EmitData (Buf, BytesRead); |
1315 | |
1316 | /* Keep the counters current */ |
1317 | Count -= BytesRead; |
1318 | } |
1319 | |
1320 | Done: |
1321 | /* Close the file, ignore errors since it's r/o */ |
1322 | (void) fclose (F); |
1323 | |
1324 | ExitPoint: |
1325 | /* Free string memory */ |
1326 | SB_Done (&Name); |
1327 | } |
1328 | |
1329 | |
1330 | |
1331 | static void DoInclude (void) |
1332 | /* Include another file */ |
1333 | { |
1334 | /* Name must follow */ |
1335 | if (CurTok.Tok != TOK_STRCON) { |
1336 | ErrorSkip ("String constant expected" ); |
1337 | } else { |
1338 | SB_Terminate (&CurTok.SVal); |
1339 | if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) { |
1340 | /* Error opening the file, skip remainder of line */ |
1341 | SkipUntilSep (); |
1342 | } |
1343 | } |
1344 | } |
1345 | |
1346 | |
1347 | |
1348 | static void DoInterruptor (void) |
1349 | /* Export a symbol as interruptor */ |
1350 | { |
1351 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
1352 | |
1353 | /* Symbol name follows */ |
1354 | if (CurTok.Tok != TOK_IDENT) { |
1355 | ErrorSkip ("Identifier expected" ); |
1356 | return; |
1357 | } |
1358 | SB_Copy (&Name, &CurTok.SVal); |
1359 | NextTok (); |
1360 | |
1361 | /* Parse the remainder of the line and export the symbol */ |
1362 | ConDes (&Name, CD_TYPE_INT); |
1363 | |
1364 | /* Free string memory */ |
1365 | SB_Done (&Name); |
1366 | } |
1367 | |
1368 | |
1369 | |
1370 | static void DoInvalid (void) |
1371 | /* Handle a token that is invalid here, since it should have been handled on |
1372 | ** a much lower level of the expression hierarchy. Getting this sort of token |
1373 | ** means that the lower level code has bugs. |
1374 | ** This function differs to DoUnexpected in that the latter may be triggered |
1375 | ** by the user by using keywords in the wrong location. DoUnexpected is not |
1376 | ** an error in the assembler itself, while DoInvalid is. |
1377 | */ |
1378 | { |
1379 | Internal ("Unexpected token: %m%p" , &Keyword); |
1380 | } |
1381 | |
1382 | |
1383 | |
1384 | static void DoLineCont (void) |
1385 | /* Switch the use of line continuations */ |
1386 | { |
1387 | SetBoolOption (&LineCont); |
1388 | } |
1389 | |
1390 | |
1391 | |
1392 | static void DoList (void) |
1393 | /* Enable/disable the listing */ |
1394 | { |
1395 | /* Get the setting */ |
1396 | unsigned char List = 0; |
1397 | SetBoolOption (&List); |
1398 | |
1399 | /* Manage the counter */ |
1400 | if (List) { |
1401 | EnableListing (); |
1402 | } else { |
1403 | DisableListing (); |
1404 | } |
1405 | } |
1406 | |
1407 | |
1408 | |
1409 | static void DoLoBytes (void) |
1410 | /* Define bytes, extracting the lo byte from each expression in the list */ |
1411 | { |
1412 | while (1) { |
1413 | EmitByte (FuncLoByte ()); |
1414 | if (CurTok.Tok != TOK_COMMA) { |
1415 | break; |
1416 | } else { |
1417 | NextTok (); |
1418 | } |
1419 | } |
1420 | } |
1421 | |
1422 | |
1423 | static void DoListBytes (void) |
1424 | /* Set maximum number of bytes to list for one line */ |
1425 | { |
1426 | SetListBytes (IntArg (MIN_LIST_BYTES, MAX_LIST_BYTES)); |
1427 | } |
1428 | |
1429 | |
1430 | |
1431 | static void DoLocalChar (void) |
1432 | /* Define the character that starts local labels */ |
1433 | { |
1434 | if (CurTok.Tok != TOK_CHARCON) { |
1435 | ErrorSkip ("Character constant expected" ); |
1436 | } else { |
1437 | if (CurTok.IVal != '@' && CurTok.IVal != '?') { |
1438 | Error ("Invalid start character for locals" ); |
1439 | } else { |
1440 | LocalStart = (char) CurTok.IVal; |
1441 | } |
1442 | NextTok (); |
1443 | } |
1444 | } |
1445 | |
1446 | |
1447 | |
1448 | static void DoMacPack (void) |
1449 | /* Insert a macro package */ |
1450 | { |
1451 | /* We expect an identifier */ |
1452 | if (CurTok.Tok != TOK_IDENT) { |
1453 | ErrorSkip ("Identifier expected" ); |
1454 | } else { |
1455 | SB_AppendStr (&CurTok.SVal, ".mac" ); |
1456 | SB_Terminate (&CurTok.SVal); |
1457 | if (NewInputFile (SB_GetConstBuf (&CurTok.SVal)) == 0) { |
1458 | /* Error opening the file, skip remainder of line */ |
1459 | SkipUntilSep (); |
1460 | } |
1461 | } |
1462 | } |
1463 | |
1464 | |
1465 | |
1466 | static void DoMacro (void) |
1467 | /* Start a macro definition */ |
1468 | { |
1469 | MacDef (MAC_STYLE_CLASSIC); |
1470 | } |
1471 | |
1472 | |
1473 | |
1474 | static void DoNull (void) |
1475 | /* Switch to the NULL segment */ |
1476 | { |
1477 | UseSeg (&NullSegDef); |
1478 | } |
1479 | |
1480 | |
1481 | |
1482 | static void DoOrg (void) |
1483 | /* Start absolute code */ |
1484 | { |
1485 | long PC = ConstExpression (); |
1486 | if (PC < 0 || PC > 0xFFFFFF) { |
1487 | Error ("Range error" ); |
1488 | return; |
1489 | } |
1490 | EnterAbsoluteMode (PC); |
1491 | } |
1492 | |
1493 | |
1494 | |
1495 | static void DoOut (void) |
1496 | /* Output a string */ |
1497 | { |
1498 | if (CurTok.Tok != TOK_STRCON) { |
1499 | ErrorSkip ("String constant expected" ); |
1500 | } else { |
1501 | /* Output the string and be sure to flush the output to keep it in |
1502 | ** sync with any error messages if the output is redirected to a file. |
1503 | */ |
1504 | printf ("%.*s\n" , |
1505 | (int) SB_GetLen (&CurTok.SVal), |
1506 | SB_GetConstBuf (&CurTok.SVal)); |
1507 | fflush (stdout); |
1508 | NextTok (); |
1509 | } |
1510 | } |
1511 | |
1512 | |
1513 | |
1514 | static void DoP02 (void) |
1515 | /* Switch to 6502 CPU */ |
1516 | { |
1517 | SetCPU (CPU_6502); |
1518 | } |
1519 | |
1520 | |
1521 | |
1522 | static void DoPC02 (void) |
1523 | /* Switch to 65C02 CPU */ |
1524 | { |
1525 | SetCPU (CPU_65C02); |
1526 | } |
1527 | |
1528 | |
1529 | |
1530 | static void DoP816 (void) |
1531 | /* Switch to 65816 CPU */ |
1532 | { |
1533 | SetCPU (CPU_65816); |
1534 | } |
1535 | |
1536 | |
1537 | |
1538 | static void DoP4510 (void) |
1539 | /* Switch to 4510 CPU */ |
1540 | { |
1541 | SetCPU (CPU_4510); |
1542 | } |
1543 | |
1544 | |
1545 | |
1546 | static void DoPageLength (void) |
1547 | /* Set the page length for the listing */ |
1548 | { |
1549 | PageLength = IntArg (MIN_PAGE_LEN, MAX_PAGE_LEN); |
1550 | } |
1551 | |
1552 | |
1553 | |
1554 | static void DoPopCPU (void) |
1555 | /* Pop an old CPU setting from the CPU stack */ |
1556 | { |
1557 | /* Must have a CPU on the stack */ |
1558 | if (IS_IsEmpty (&CPUStack)) { |
1559 | ErrorSkip ("CPU stack is empty" ); |
1560 | return; |
1561 | } |
1562 | |
1563 | /* Set the CPU to the value popped from stack */ |
1564 | SetCPU (IS_Pop (&CPUStack)); |
1565 | } |
1566 | |
1567 | |
1568 | |
1569 | static void DoPopSeg (void) |
1570 | /* Pop an old segment from the segment stack */ |
1571 | { |
1572 | SegDef* Def; |
1573 | |
1574 | /* Must have a segment on the stack */ |
1575 | if (CollCount (&SegStack) == 0) { |
1576 | ErrorSkip ("Segment stack is empty" ); |
1577 | return; |
1578 | } |
1579 | |
1580 | /* Pop the last element */ |
1581 | Def = CollPop (&SegStack); |
1582 | |
1583 | /* Restore this segment */ |
1584 | UseSeg (Def); |
1585 | |
1586 | /* Delete the segment definition */ |
1587 | FreeSegDef (Def); |
1588 | } |
1589 | |
1590 | |
1591 | |
1592 | static void DoProc (void) |
1593 | /* Start a new lexical scope */ |
1594 | { |
1595 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
1596 | unsigned char AddrSize; |
1597 | SymEntry* Sym = 0; |
1598 | |
1599 | |
1600 | if (CurTok.Tok == TOK_IDENT) { |
1601 | |
1602 | /* The new scope has a name. Remember it. */ |
1603 | SB_Copy (&Name, &CurTok.SVal); |
1604 | |
1605 | /* Search for the symbol, generate a new one if needed */ |
1606 | Sym = SymFind (CurrentScope, &Name, SYM_ALLOC_NEW); |
1607 | |
1608 | /* Skip the scope name */ |
1609 | NextTok (); |
1610 | |
1611 | /* Read an optional address size specifier */ |
1612 | AddrSize = OptionalAddrSize (); |
1613 | |
1614 | /* Mark the symbol as defined */ |
1615 | SymDef (Sym, GenCurrentPC (), AddrSize, SF_LABEL); |
1616 | |
1617 | } else { |
1618 | |
1619 | /* A .PROC statement without a name */ |
1620 | Warning (1, "Unnamed .PROCs are deprecated, please use .SCOPE" ); |
1621 | AnonName (&Name, "PROC" ); |
1622 | AddrSize = ADDR_SIZE_DEFAULT; |
1623 | |
1624 | } |
1625 | |
1626 | /* Enter a new scope */ |
1627 | SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, Sym); |
1628 | |
1629 | /* Free memory for Name */ |
1630 | SB_Done (&Name); |
1631 | } |
1632 | |
1633 | |
1634 | |
1635 | static void DoPSC02 (void) |
1636 | /* Switch to 65SC02 CPU */ |
1637 | { |
1638 | SetCPU (CPU_65SC02); |
1639 | } |
1640 | |
1641 | |
1642 | |
1643 | static void DoPushCPU (void) |
1644 | /* Push the current CPU setting onto the CPU stack */ |
1645 | { |
1646 | /* Can only push a limited size of segments */ |
1647 | if (IS_IsFull (&CPUStack)) { |
1648 | ErrorSkip ("CPU stack overflow" ); |
1649 | return; |
1650 | } |
1651 | |
1652 | /* Get the current segment and push it */ |
1653 | IS_Push (&CPUStack, GetCPU ()); |
1654 | } |
1655 | |
1656 | |
1657 | |
1658 | static void DoPushSeg (void) |
1659 | /* Push the current segment onto the segment stack */ |
1660 | { |
1661 | /* Can only push a limited size of segments */ |
1662 | if (CollCount (&SegStack) >= MAX_PUSHED_SEGMENTS) { |
1663 | ErrorSkip ("Segment stack overflow" ); |
1664 | return; |
1665 | } |
1666 | |
1667 | /* Get the current segment and push it */ |
1668 | CollAppend (&SegStack, DupSegDef (GetCurrentSegDef ())); |
1669 | } |
1670 | |
1671 | |
1672 | |
1673 | static void DoReloc (void) |
1674 | /* Enter relocatable mode */ |
1675 | { |
1676 | EnterRelocMode (); |
1677 | } |
1678 | |
1679 | |
1680 | |
1681 | static void DoRepeat (void) |
1682 | /* Repeat some instruction block */ |
1683 | { |
1684 | ParseRepeat (); |
1685 | } |
1686 | |
1687 | |
1688 | |
1689 | static void DoRes (void) |
1690 | /* Reserve some number of storage bytes */ |
1691 | { |
1692 | long Count; |
1693 | long Val; |
1694 | |
1695 | Count = ConstExpression (); |
1696 | if (Count > 0xFFFF || Count < 0) { |
1697 | ErrorSkip ("Range error" ); |
1698 | return; |
1699 | } |
1700 | if (CurTok.Tok == TOK_COMMA) { |
1701 | NextTok (); |
1702 | Val = ConstExpression (); |
1703 | /* We need a byte value here */ |
1704 | if (!IsByteRange (Val)) { |
1705 | ErrorSkip ("Range error" ); |
1706 | return; |
1707 | } |
1708 | |
1709 | /* Emit constant values */ |
1710 | while (Count--) { |
1711 | Emit0 ((unsigned char) Val); |
1712 | } |
1713 | |
1714 | } else { |
1715 | /* Emit fill fragments */ |
1716 | EmitFill (Count); |
1717 | } |
1718 | } |
1719 | |
1720 | |
1721 | |
1722 | static void DoROData (void) |
1723 | /* Switch to the r/o data segment */ |
1724 | { |
1725 | UseSeg (&RODataSegDef); |
1726 | } |
1727 | |
1728 | |
1729 | |
1730 | static void DoScope (void) |
1731 | /* Start a local scope */ |
1732 | { |
1733 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
1734 | unsigned char AddrSize; |
1735 | |
1736 | |
1737 | if (CurTok.Tok == TOK_IDENT) { |
1738 | |
1739 | /* The new scope has a name. Remember and skip it. */ |
1740 | SB_Copy (&Name, &CurTok.SVal); |
1741 | NextTok (); |
1742 | |
1743 | } else { |
1744 | |
1745 | /* An unnamed scope */ |
1746 | AnonName (&Name, "SCOPE" ); |
1747 | |
1748 | } |
1749 | |
1750 | /* Read an optional address size specifier */ |
1751 | AddrSize = OptionalAddrSize (); |
1752 | |
1753 | /* Enter the new scope */ |
1754 | SymEnterLevel (&Name, SCOPE_SCOPE, AddrSize, 0); |
1755 | |
1756 | /* Free memory for Name */ |
1757 | SB_Done (&Name); |
1758 | } |
1759 | |
1760 | |
1761 | |
1762 | static void DoSegment (void) |
1763 | /* Switch to another segment */ |
1764 | { |
1765 | StrBuf Name = STATIC_STRBUF_INITIALIZER; |
1766 | SegDef Def; |
1767 | |
1768 | if (CurTok.Tok != TOK_STRCON) { |
1769 | ErrorSkip ("String constant expected" ); |
1770 | } else { |
1771 | |
1772 | /* Save the name of the segment and skip it */ |
1773 | SB_Copy (&Name, &CurTok.SVal); |
1774 | NextTok (); |
1775 | |
1776 | /* Use the name for the segment definition */ |
1777 | SB_Terminate (&Name); |
1778 | Def.Name = SB_GetBuf (&Name); |
1779 | |
1780 | /* Check for an optional address size modifier */ |
1781 | Def.AddrSize = OptionalAddrSize (); |
1782 | |
1783 | /* Set the segment */ |
1784 | UseSeg (&Def); |
1785 | } |
1786 | |
1787 | /* Free memory for Name */ |
1788 | SB_Done (&Name); |
1789 | } |
1790 | |
1791 | |
1792 | |
1793 | static void DoSetCPU (void) |
1794 | /* Switch the CPU instruction set */ |
1795 | { |
1796 | /* We expect an identifier */ |
1797 | if (CurTok.Tok != TOK_STRCON) { |
1798 | ErrorSkip ("String constant expected" ); |
1799 | } else { |
1800 | cpu_t CPU; |
1801 | |
1802 | /* Try to find the CPU */ |
1803 | SB_Terminate (&CurTok.SVal); |
1804 | CPU = FindCPU (SB_GetConstBuf (&CurTok.SVal)); |
1805 | |
1806 | /* Switch to the new CPU */ |
1807 | SetCPU (CPU); |
1808 | |
1809 | /* Skip the identifier. If the CPU switch was successful, the scanner |
1810 | ** will treat the input now correctly for the new CPU. |
1811 | */ |
1812 | NextTok (); |
1813 | } |
1814 | } |
1815 | |
1816 | |
1817 | |
1818 | static void DoSmart (void) |
1819 | /* Smart mode on/off */ |
1820 | { |
1821 | SetBoolOption (&SmartMode); |
1822 | } |
1823 | |
1824 | |
1825 | |
1826 | static void DoTag (void) |
1827 | /* Allocate space for a struct */ |
1828 | { |
1829 | SymEntry* SizeSym; |
1830 | long Size; |
1831 | |
1832 | /* Read the struct name */ |
1833 | SymTable* Struct = ParseScopedSymTable (); |
1834 | |
1835 | /* Check the supposed struct */ |
1836 | if (Struct == 0) { |
1837 | ErrorSkip ("Unknown struct" ); |
1838 | return; |
1839 | } |
1840 | if (GetSymTabType (Struct) != SCOPE_STRUCT) { |
1841 | ErrorSkip ("Not a struct" ); |
1842 | return; |
1843 | } |
1844 | |
1845 | /* Get the symbol that defines the size of the struct */ |
1846 | SizeSym = GetSizeOfScope (Struct); |
1847 | |
1848 | /* Check if it does exist and if its value is known */ |
1849 | if (SizeSym == 0 || !SymIsConst (SizeSym, &Size)) { |
1850 | ErrorSkip ("Size of struct/union is unknown" ); |
1851 | return; |
1852 | } |
1853 | |
1854 | /* Optional multiplicator may follow */ |
1855 | if (CurTok.Tok == TOK_COMMA) { |
1856 | long Multiplicator; |
1857 | NextTok (); |
1858 | Multiplicator = ConstExpression (); |
1859 | /* Multiplicator must make sense */ |
1860 | if (Multiplicator <= 0) { |
1861 | ErrorSkip ("Range error" ); |
1862 | return; |
1863 | } |
1864 | Size *= Multiplicator; |
1865 | } |
1866 | |
1867 | /* Emit fill fragments */ |
1868 | EmitFill (Size); |
1869 | } |
1870 | |
1871 | |
1872 | |
1873 | static void DoUnDef (void) |
1874 | /* Undefine a define style macro */ |
1875 | { |
1876 | /* The function is called with the .UNDEF token in place, because we need |
1877 | ** to disable .define macro expansions before reading the next token. |
1878 | ** Otherwise the name of the macro would be expanded, so we would never |
1879 | ** see it. |
1880 | */ |
1881 | DisableDefineStyleMacros (); |
1882 | NextTok (); |
1883 | EnableDefineStyleMacros (); |
1884 | |
1885 | /* We expect an identifier */ |
1886 | if (CurTok.Tok != TOK_IDENT) { |
1887 | ErrorSkip ("Identifier expected" ); |
1888 | } else { |
1889 | MacUndef (&CurTok.SVal, MAC_STYLE_DEFINE); |
1890 | NextTok (); |
1891 | } |
1892 | } |
1893 | |
1894 | |
1895 | |
1896 | static void DoUnexpected (void) |
1897 | /* Got an unexpected keyword */ |
1898 | { |
1899 | Error ("Unexpected '%m%p'" , &Keyword); |
1900 | SkipUntilSep (); |
1901 | } |
1902 | |
1903 | |
1904 | |
1905 | static void DoWarning (void) |
1906 | /* User warning */ |
1907 | { |
1908 | if (CurTok.Tok != TOK_STRCON) { |
1909 | ErrorSkip ("String constant expected" ); |
1910 | } else { |
1911 | Warning (0, "User warning: %m%p" , &CurTok.SVal); |
1912 | SkipUntilSep (); |
1913 | } |
1914 | } |
1915 | |
1916 | |
1917 | |
1918 | static void DoWord (void) |
1919 | /* Define words */ |
1920 | { |
1921 | /* Element type for the generated array */ |
1922 | static const char EType[1] = { GT_WORD }; |
1923 | |
1924 | /* Record type information */ |
1925 | Span* S = OpenSpan (); |
1926 | StrBuf Type = STATIC_STRBUF_INITIALIZER; |
1927 | |
1928 | /* Parse arguments */ |
1929 | while (1) { |
1930 | EmitWord (BoundedExpr (Expression, 2)); |
1931 | if (CurTok.Tok != TOK_COMMA) { |
1932 | break; |
1933 | } else { |
1934 | NextTok (); |
1935 | } |
1936 | } |
1937 | |
1938 | /* Close the span, then add type information to it */ |
1939 | S = CloseSpan (S); |
1940 | SetSpanType (S, GenArrayType (&Type, GetSpanSize (S), EType, sizeof (EType))); |
1941 | |
1942 | /* Free the type string */ |
1943 | SB_Done (&Type); |
1944 | } |
1945 | |
1946 | |
1947 | |
1948 | static void DoZeropage (void) |
1949 | /* Switch to the zeropage segment */ |
1950 | { |
1951 | UseSeg (&ZeropageSegDef); |
1952 | } |
1953 | |
1954 | |
1955 | |
1956 | /*****************************************************************************/ |
1957 | /* Table data */ |
1958 | /*****************************************************************************/ |
1959 | |
1960 | |
1961 | |
1962 | /* Control commands flags */ |
1963 | enum { |
1964 | ccNone = 0x0000, /* No special flags */ |
1965 | ccKeepToken = 0x0001 /* Do not skip the current token */ |
1966 | }; |
1967 | |
1968 | /* Control command table */ |
1969 | typedef struct CtrlDesc CtrlDesc; |
1970 | struct CtrlDesc { |
1971 | unsigned Flags; /* Flags for this directive */ |
1972 | void (*Handler) (void); /* Command handler */ |
1973 | }; |
1974 | |
1975 | #define PSEUDO_COUNT (sizeof (CtrlCmdTab) / sizeof (CtrlCmdTab [0])) |
1976 | static CtrlDesc CtrlCmdTab [] = { |
1977 | { ccNone, DoA16 }, |
1978 | { ccNone, DoA8 }, |
1979 | { ccNone, DoAddr }, /* .ADDR */ |
1980 | { ccNone, DoUnexpected }, /* .ADDRSIZE */ |
1981 | { ccNone, DoAlign }, |
1982 | { ccNone, DoASCIIZ }, |
1983 | { ccNone, DoUnexpected }, /* .ASIZE */ |
1984 | { ccNone, DoAssert }, |
1985 | { ccNone, DoAutoImport }, |
1986 | { ccNone, DoUnexpected }, /* .BANK */ |
1987 | { ccNone, DoUnexpected }, /* .BANKBYTE */ |
1988 | { ccNone, DoBankBytes }, |
1989 | { ccNone, DoUnexpected }, /* .BLANK */ |
1990 | { ccNone, DoBss }, |
1991 | { ccNone, DoByte }, |
1992 | { ccNone, DoCase }, |
1993 | { ccNone, DoCharMap }, |
1994 | { ccNone, DoCode }, |
1995 | { ccNone, DoUnexpected, }, /* .CONCAT */ |
1996 | { ccNone, DoConDes }, |
1997 | { ccNone, DoUnexpected }, /* .CONST */ |
1998 | { ccNone, DoConstructor }, |
1999 | { ccNone, DoUnexpected }, /* .CPU */ |
2000 | { ccNone, DoData }, |
2001 | { ccNone, DoDbg, }, |
2002 | { ccNone, DoDByt }, |
2003 | { ccNone, DoDebugInfo }, |
2004 | { ccNone, DoDefine }, |
2005 | { ccNone, DoUnexpected }, /* .DEFINED */ |
2006 | { ccNone, DoUnexpected }, /* .DEFINEDMACRO */ |
2007 | { ccNone, DoDelMac }, |
2008 | { ccNone, DoDestructor }, |
2009 | { ccNone, DoDWord }, |
2010 | { ccKeepToken, DoConditionals }, /* .ELSE */ |
2011 | { ccKeepToken, DoConditionals }, /* .ELSEIF */ |
2012 | { ccKeepToken, DoEnd }, |
2013 | { ccNone, DoUnexpected }, /* .ENDENUM */ |
2014 | { ccKeepToken, DoConditionals }, /* .ENDIF */ |
2015 | { ccNone, DoUnexpected }, /* .ENDMACRO */ |
2016 | { ccNone, DoEndProc }, |
2017 | { ccNone, DoUnexpected }, /* .ENDREPEAT */ |
2018 | { ccNone, DoEndScope }, |
2019 | { ccNone, DoUnexpected }, /* .ENDSTRUCT */ |
2020 | { ccNone, DoUnexpected }, /* .ENDUNION */ |
2021 | { ccNone, DoEnum }, |
2022 | { ccNone, DoError }, |
2023 | { ccNone, DoExitMacro }, |
2024 | { ccNone, DoExport }, |
2025 | { ccNone, DoExportZP }, |
2026 | { ccNone, DoFarAddr }, |
2027 | { ccNone, DoFatal }, |
2028 | { ccNone, DoFeature }, |
2029 | { ccNone, DoFileOpt }, |
2030 | { ccNone, DoForceImport }, |
2031 | { ccNone, DoUnexpected }, /* .FORCEWORD */ |
2032 | { ccNone, DoGlobal }, |
2033 | { ccNone, DoGlobalZP }, |
2034 | { ccNone, DoUnexpected }, /* .HIBYTE */ |
2035 | { ccNone, DoHiBytes }, |
2036 | { ccNone, DoUnexpected }, /* .HIWORD */ |
2037 | { ccNone, DoI16 }, |
2038 | { ccNone, DoI8 }, |
2039 | { ccNone, DoUnexpected }, /* .IDENT */ |
2040 | { ccKeepToken, DoConditionals }, /* .IF */ |
2041 | { ccKeepToken, DoConditionals }, /* .IFBLANK */ |
2042 | { ccKeepToken, DoConditionals }, /* .IFCONST */ |
2043 | { ccKeepToken, DoConditionals }, /* .IFDEF */ |
2044 | { ccKeepToken, DoConditionals }, /* .IFNBLANK */ |
2045 | { ccKeepToken, DoConditionals }, /* .IFNCONST */ |
2046 | { ccKeepToken, DoConditionals }, /* .IFNDEF */ |
2047 | { ccKeepToken, DoConditionals }, /* .IFNREF */ |
2048 | { ccKeepToken, DoConditionals }, /* .IFP02 */ |
2049 | { ccKeepToken, DoConditionals }, /* .IFP4510 */ |
2050 | { ccKeepToken, DoConditionals }, /* .IFP816 */ |
2051 | { ccKeepToken, DoConditionals }, /* .IFPC02 */ |
2052 | { ccKeepToken, DoConditionals }, /* .IFPSC02 */ |
2053 | { ccKeepToken, DoConditionals }, /* .IFREF */ |
2054 | { ccNone, DoImport }, |
2055 | { ccNone, DoImportZP }, |
2056 | { ccNone, DoIncBin }, |
2057 | { ccNone, DoInclude }, |
2058 | { ccNone, DoInterruptor }, |
2059 | { ccNone, DoUnexpected }, /* .ISIZE */ |
2060 | { ccNone, DoUnexpected }, /* .ISMNEMONIC */ |
2061 | { ccNone, DoInvalid }, /* .LEFT */ |
2062 | { ccNone, DoLineCont }, |
2063 | { ccNone, DoList }, |
2064 | { ccNone, DoListBytes }, |
2065 | { ccNone, DoUnexpected }, /* .LOBYTE */ |
2066 | { ccNone, DoLoBytes }, |
2067 | { ccNone, DoUnexpected }, /* .LOCAL */ |
2068 | { ccNone, DoLocalChar }, |
2069 | { ccNone, DoUnexpected }, /* .LOWORD */ |
2070 | { ccNone, DoMacPack }, |
2071 | { ccNone, DoMacro }, |
2072 | { ccNone, DoUnexpected }, /* .MATCH */ |
2073 | { ccNone, DoUnexpected }, /* .MAX */ |
2074 | { ccNone, DoInvalid }, /* .MID */ |
2075 | { ccNone, DoUnexpected }, /* .MIN */ |
2076 | { ccNone, DoNull }, |
2077 | { ccNone, DoOrg }, |
2078 | { ccNone, DoOut }, |
2079 | { ccNone, DoP02 }, |
2080 | { ccNone, DoP4510 }, |
2081 | { ccNone, DoP816 }, |
2082 | { ccNone, DoPageLength }, |
2083 | { ccNone, DoUnexpected }, /* .PARAMCOUNT */ |
2084 | { ccNone, DoPC02 }, |
2085 | { ccNone, DoPopCPU }, |
2086 | { ccNone, DoPopSeg }, |
2087 | { ccNone, DoProc }, |
2088 | { ccNone, DoPSC02 }, |
2089 | { ccNone, DoPushCPU }, |
2090 | { ccNone, DoPushSeg }, |
2091 | { ccNone, DoUnexpected }, /* .REFERENCED */ |
2092 | { ccNone, DoReloc }, |
2093 | { ccNone, DoRepeat }, |
2094 | { ccNone, DoRes }, |
2095 | { ccNone, DoInvalid }, /* .RIGHT */ |
2096 | { ccNone, DoROData }, |
2097 | { ccNone, DoScope }, |
2098 | { ccNone, DoSegment }, |
2099 | { ccNone, DoUnexpected }, /* .SET */ |
2100 | { ccNone, DoSetCPU }, |
2101 | { ccNone, DoUnexpected }, /* .SIZEOF */ |
2102 | { ccNone, DoSmart }, |
2103 | { ccNone, DoUnexpected }, /* .SPRINTF */ |
2104 | { ccNone, DoUnexpected }, /* .STRAT */ |
2105 | { ccNone, DoUnexpected }, /* .STRING */ |
2106 | { ccNone, DoUnexpected }, /* .STRLEN */ |
2107 | { ccNone, DoStruct }, |
2108 | { ccNone, DoTag }, |
2109 | { ccNone, DoUnexpected }, /* .TCOUNT */ |
2110 | { ccNone, DoUnexpected }, /* .TIME */ |
2111 | { ccKeepToken, DoUnDef }, |
2112 | { ccNone, DoUnion }, |
2113 | { ccNone, DoUnexpected }, /* .VERSION */ |
2114 | { ccNone, DoWarning }, |
2115 | { ccNone, DoWord }, |
2116 | { ccNone, DoUnexpected }, /* .XMATCH */ |
2117 | { ccNone, DoZeropage }, |
2118 | }; |
2119 | |
2120 | |
2121 | |
2122 | /*****************************************************************************/ |
2123 | /* Code */ |
2124 | /*****************************************************************************/ |
2125 | |
2126 | |
2127 | |
2128 | void HandlePseudo (void) |
2129 | /* Handle a pseudo instruction */ |
2130 | { |
2131 | CtrlDesc* D; |
2132 | |
2133 | /* Calculate the index into the table */ |
2134 | unsigned Index = CurTok.Tok - TOK_FIRSTPSEUDO; |
2135 | |
2136 | /* Safety check */ |
2137 | if (PSEUDO_COUNT != (TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1)) { |
2138 | Internal ("Pseudo mismatch: PSEUDO_COUNT = %u, actual count = %u\n" , |
2139 | (unsigned) PSEUDO_COUNT, TOK_LASTPSEUDO - TOK_FIRSTPSEUDO + 1); |
2140 | } |
2141 | CHECK (Index < PSEUDO_COUNT); |
2142 | |
2143 | /* Get the pseudo intruction descriptor */ |
2144 | D = &CtrlCmdTab [Index]; |
2145 | |
2146 | /* Remember the instruction, then skip it if needed */ |
2147 | if ((D->Flags & ccKeepToken) == 0) { |
2148 | SB_Copy (&Keyword, &CurTok.SVal); |
2149 | NextTok (); |
2150 | } |
2151 | |
2152 | /* Call the handler */ |
2153 | D->Handler (); |
2154 | } |
2155 | |
2156 | |
2157 | |
2158 | void CheckPseudo (void) |
2159 | /* Check if the stacks are empty at end of assembly */ |
2160 | { |
2161 | if (CollCount (&SegStack) != 0) { |
2162 | Warning (1, "Segment stack is not empty" ); |
2163 | } |
2164 | if (!IS_IsEmpty (&CPUStack)) { |
2165 | Warning (1, "CPU stack is not empty" ); |
2166 | } |
2167 | } |
2168 | |