1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* main.c */ |
4 | /* */ |
5 | /* Main program for the ca65 macroassembler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2013, 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 <time.h> |
40 | |
41 | /* common */ |
42 | #include "addrsize.h" |
43 | #include "chartype.h" |
44 | #include "cmdline.h" |
45 | #include "debugflag.h" |
46 | #include "mmodel.h" |
47 | #include "print.h" |
48 | #include "scopedefs.h" |
49 | #include "strbuf.h" |
50 | #include "target.h" |
51 | #include "tgttrans.h" |
52 | #include "version.h" |
53 | |
54 | /* ca65 */ |
55 | #include "abend.h" |
56 | #include "asserts.h" |
57 | #include "dbginfo.h" |
58 | #include "error.h" |
59 | #include "expr.h" |
60 | #include "feature.h" |
61 | #include "filetab.h" |
62 | #include "global.h" |
63 | #include "incpath.h" |
64 | #include "instr.h" |
65 | #include "istack.h" |
66 | #include "lineinfo.h" |
67 | #include "listing.h" |
68 | #include "macro.h" |
69 | #include "nexttok.h" |
70 | #include "objfile.h" |
71 | #include "options.h" |
72 | #include "pseudo.h" |
73 | #include "scanner.h" |
74 | #include "segment.h" |
75 | #include "sizeof.h" |
76 | #include "span.h" |
77 | #include "spool.h" |
78 | #include "symbol.h" |
79 | #include "symtab.h" |
80 | #include "ulabel.h" |
81 | |
82 | |
83 | |
84 | /*****************************************************************************/ |
85 | /* Code */ |
86 | /*****************************************************************************/ |
87 | |
88 | |
89 | |
90 | static void Usage (void) |
91 | /* Print usage information and exit */ |
92 | { |
93 | printf ("Usage: %s [options] file\n" |
94 | "Short options:\n" |
95 | " -D name[=value]\t\tDefine a symbol\n" |
96 | " -I dir\t\t\tSet an include directory search path\n" |
97 | " -U\t\t\t\tMark unresolved symbols as import\n" |
98 | " -V\t\t\t\tPrint the assembler version\n" |
99 | " -W n\t\t\t\tSet warning level n\n" |
100 | " -d\t\t\t\tDebug mode\n" |
101 | " -g\t\t\t\tAdd debug info to object file\n" |
102 | " -h\t\t\t\tHelp (this text)\n" |
103 | " -i\t\t\t\tIgnore case of symbols\n" |
104 | " -l name\t\t\tCreate a listing file if assembly was ok\n" |
105 | " -mm model\t\t\tSet the memory model\n" |
106 | " -o name\t\t\tName the output file\n" |
107 | " -s\t\t\t\tEnable smart mode\n" |
108 | " -t sys\t\t\tSet the target system\n" |
109 | " -v\t\t\t\tIncrease verbosity\n" |
110 | "\n" |
111 | "Long options:\n" |
112 | " --auto-import\t\t\tMark unresolved symbols as import\n" |
113 | " --bin-include-dir dir\t\tSet a search path for binary includes\n" |
114 | " --cpu type\t\t\tSet cpu type\n" |
115 | " --create-dep name\t\tCreate a make dependency file\n" |
116 | " --create-full-dep name\tCreate a full make dependency file\n" |
117 | " --debug\t\t\tDebug mode\n" |
118 | " --debug-info\t\t\tAdd debug info to object file\n" |
119 | " --feature name\t\tSet an emulation feature\n" |
120 | " --help\t\t\tHelp (this text)\n" |
121 | " --ignore-case\t\t\tIgnore case of symbols\n" |
122 | " --include-dir dir\t\tSet an include directory search path\n" |
123 | " --large-alignment\t\tDon't warn about large alignments\n" |
124 | " --listing name\t\tCreate a listing file if assembly was ok\n" |
125 | " --list-bytes n\t\tMaximum number of bytes per listing line\n" |
126 | " --memory-model model\t\tSet the memory model\n" |
127 | " --pagelength n\t\tSet the page length for the listing\n" |
128 | " --relax-checks\t\tRelax some checks (see docs)\n" |
129 | " --smart\t\t\tEnable smart mode\n" |
130 | " --target sys\t\t\tSet the target system\n" |
131 | " --verbose\t\t\tIncrease verbosity\n" |
132 | " --version\t\t\tPrint the assembler version\n" , |
133 | ProgName); |
134 | } |
135 | |
136 | |
137 | |
138 | static void SetOptions (void) |
139 | /* Set the option for the translator */ |
140 | { |
141 | StrBuf Buf = STATIC_STRBUF_INITIALIZER; |
142 | |
143 | /* Set the translator */ |
144 | SB_Printf (&Buf, "ca65 V%s" , GetVersionAsString ()); |
145 | OptTranslator (&Buf); |
146 | |
147 | /* Set date and time */ |
148 | OptDateTime ((unsigned long) time(0)); |
149 | |
150 | /* Release memory for the string */ |
151 | SB_Done (&Buf); |
152 | } |
153 | |
154 | |
155 | |
156 | static void NewSymbol (const char* SymName, long Val) |
157 | /* Define a symbol with a fixed numeric value in the current scope */ |
158 | { |
159 | ExprNode* Expr; |
160 | SymEntry* Sym; |
161 | |
162 | /* Convert the name to a string buffer */ |
163 | StrBuf SymBuf = STATIC_STRBUF_INITIALIZER; |
164 | SB_CopyStr (&SymBuf, SymName); |
165 | |
166 | /* Search for the symbol, allocate a new one if it doesn't exist */ |
167 | Sym = SymFind (CurrentScope, &SymBuf, SYM_ALLOC_NEW); |
168 | |
169 | /* Check if have already a symbol with this name */ |
170 | if (SymIsDef (Sym)) { |
171 | AbEnd ("'%s' is already defined" , SymName); |
172 | } |
173 | |
174 | /* Generate an expression for the symbol */ |
175 | Expr = GenLiteralExpr (Val); |
176 | |
177 | /* Mark the symbol as defined */ |
178 | SymDef (Sym, Expr, ADDR_SIZE_DEFAULT, SF_NONE); |
179 | |
180 | /* Free string buffer memory */ |
181 | SB_Done (&SymBuf); |
182 | } |
183 | |
184 | |
185 | |
186 | static void CBMSystem (const char* Sys) |
187 | /* Define a CBM system */ |
188 | { |
189 | NewSymbol ("__CBM__" , 1); |
190 | NewSymbol (Sys, 1); |
191 | } |
192 | |
193 | |
194 | |
195 | static void SetSys (const char* Sys) |
196 | /* Define a target system */ |
197 | { |
198 | switch (Target = FindTarget (Sys)) { |
199 | |
200 | case TGT_NONE: |
201 | break; |
202 | |
203 | case TGT_MODULE: |
204 | AbEnd ("Cannot use 'module' as a target for the assembler" ); |
205 | break; |
206 | |
207 | case TGT_ATARI2600: |
208 | NewSymbol ("__ATARI2600__" , 1); |
209 | break; |
210 | |
211 | case TGT_ATARI5200: |
212 | NewSymbol ("__ATARI5200__" , 1); |
213 | break; |
214 | |
215 | case TGT_ATARI: |
216 | NewSymbol ("__ATARI__" , 1); |
217 | break; |
218 | |
219 | case TGT_ATARIXL: |
220 | NewSymbol ("__ATARI__" , 1); |
221 | NewSymbol ("__ATARIXL__" , 1); |
222 | break; |
223 | |
224 | case TGT_C16: |
225 | CBMSystem ("__C16__" ); |
226 | break; |
227 | |
228 | case TGT_C64: |
229 | CBMSystem ("__C64__" ); |
230 | break; |
231 | |
232 | case TGT_C65: |
233 | CBMSystem ("__C65__" ); |
234 | break; |
235 | |
236 | case TGT_VIC20: |
237 | CBMSystem ("__VIC20__" ); |
238 | break; |
239 | |
240 | case TGT_C128: |
241 | CBMSystem ("__C128__" ); |
242 | break; |
243 | |
244 | case TGT_PLUS4: |
245 | CBMSystem ("__C16__" ); |
246 | NewSymbol ("__PLUS4__" , 1); |
247 | break; |
248 | |
249 | case TGT_CBM510: |
250 | CBMSystem ("__CBM510__" ); |
251 | break; |
252 | |
253 | case TGT_CBM610: |
254 | CBMSystem ("__CBM610__" ); |
255 | break; |
256 | |
257 | case TGT_PET: |
258 | CBMSystem ("__PET__" ); |
259 | break; |
260 | |
261 | case TGT_BBC: |
262 | NewSymbol ("__BBC__" , 1); |
263 | break; |
264 | |
265 | case TGT_APPLE2: |
266 | NewSymbol ("__APPLE2__" , 1); |
267 | break; |
268 | |
269 | case TGT_APPLE2ENH: |
270 | NewSymbol ("__APPLE2__" , 1); |
271 | NewSymbol ("__APPLE2ENH__" , 1); |
272 | break; |
273 | |
274 | case TGT_GAMATE: |
275 | NewSymbol ("__GAMATE__" , 1); |
276 | break; |
277 | |
278 | case TGT_GEOS_CBM: |
279 | /* Do not handle as a CBM system */ |
280 | NewSymbol ("__GEOS__" , 1); |
281 | NewSymbol ("__GEOS_CBM__" , 1); |
282 | break; |
283 | |
284 | case TGT_CREATIVISION: |
285 | NewSymbol ("__CREATIVISION__" , 1); |
286 | break; |
287 | |
288 | case TGT_GEOS_APPLE: |
289 | NewSymbol ("__GEOS__" , 1); |
290 | NewSymbol ("__GEOS_APPLE__" , 1); |
291 | break; |
292 | |
293 | case TGT_LUNIX: |
294 | NewSymbol ("__LUNIX__" , 1); |
295 | break; |
296 | |
297 | case TGT_ATMOS: |
298 | NewSymbol ("__ATMOS__" , 1); |
299 | break; |
300 | |
301 | case TGT_TELESTRAT: |
302 | NewSymbol ("__TELESTRAT__" , 1); |
303 | break; |
304 | |
305 | case TGT_NES: |
306 | NewSymbol ("__NES__" , 1); |
307 | break; |
308 | |
309 | case TGT_SUPERVISION: |
310 | NewSymbol ("__SUPERVISION__" , 1); |
311 | break; |
312 | |
313 | case TGT_LYNX: |
314 | NewSymbol ("__LYNX__" , 1); |
315 | break; |
316 | |
317 | case TGT_SIM6502: |
318 | NewSymbol ("__SIM6502__" , 1); |
319 | break; |
320 | |
321 | case TGT_SIM65C02: |
322 | NewSymbol ("__SIM65C02__" , 1); |
323 | break; |
324 | |
325 | case TGT_OSIC1P: |
326 | NewSymbol ("__OSIC1P__" , 1); |
327 | break; |
328 | |
329 | case TGT_PCENGINE: |
330 | NewSymbol ("__PCE__" , 1); |
331 | break; |
332 | |
333 | default: |
334 | AbEnd ("Invalid target name: '%s'" , Sys); |
335 | |
336 | } |
337 | |
338 | /* Initialize the translation tables for the target system */ |
339 | TgtTranslateInit (); |
340 | } |
341 | |
342 | |
343 | |
344 | static void FileNameOption (const char* Opt, const char* Arg, StrBuf* Name) |
345 | /* Handle an option that remembers a file name for later */ |
346 | { |
347 | /* Cannot have the option twice */ |
348 | if (SB_NotEmpty (Name)) { |
349 | AbEnd ("Cannot use option '%s' twice" , Opt); |
350 | } |
351 | /* Remember the file name for later */ |
352 | SB_CopyStr (Name, Arg); |
353 | SB_Terminate (Name); |
354 | } |
355 | |
356 | |
357 | |
358 | static void DefineSymbol (const char* Def) |
359 | /* Define a symbol from the command line */ |
360 | { |
361 | const char* P; |
362 | long Val; |
363 | StrBuf SymName = AUTO_STRBUF_INITIALIZER; |
364 | |
365 | |
366 | /* The symbol must start with a character or underline */ |
367 | if (!IsIdStart (Def [0])) { |
368 | InvDef (Def); |
369 | } |
370 | P = Def; |
371 | |
372 | /* Copy the symbol, checking the rest */ |
373 | while (IsIdChar (*P)) { |
374 | SB_AppendChar (&SymName, *P++); |
375 | } |
376 | SB_Terminate (&SymName); |
377 | |
378 | /* Do we have a value given? */ |
379 | if (*P != '=') { |
380 | if (*P != '\0') { |
381 | InvDef (Def); |
382 | } |
383 | Val = 0; |
384 | } else { |
385 | /* We have a value */ |
386 | ++P; |
387 | if (*P == '$') { |
388 | ++P; |
389 | if (sscanf (P, "%lx" , &Val) != 1) { |
390 | InvDef (Def); |
391 | } |
392 | } else { |
393 | if (sscanf (P, "%li" , &Val) != 1) { |
394 | InvDef (Def); |
395 | } |
396 | } |
397 | } |
398 | |
399 | /* Define the new symbol */ |
400 | NewSymbol (SB_GetConstBuf (&SymName), Val); |
401 | |
402 | /* Release string memory */ |
403 | SB_Done (&SymName); |
404 | } |
405 | |
406 | |
407 | |
408 | static void OptAutoImport (const char* Opt attribute ((unused)), |
409 | const char* Arg attribute ((unused))) |
410 | /* Mark unresolved symbols as imported */ |
411 | { |
412 | AutoImport = 1; |
413 | } |
414 | |
415 | |
416 | |
417 | static void OptBinIncludeDir (const char* Opt attribute ((unused)), const char* Arg) |
418 | /* Add an include search path for binaries */ |
419 | { |
420 | AddSearchPath (BinSearchPath, Arg); |
421 | } |
422 | |
423 | |
424 | |
425 | static void OptCPU (const char* Opt attribute ((unused)), const char* Arg) |
426 | /* Handle the --cpu option */ |
427 | { |
428 | cpu_t CPU = FindCPU (Arg); |
429 | if (CPU == CPU_UNKNOWN) { |
430 | AbEnd ("Invalid CPU: '%s'" , Arg); |
431 | } else { |
432 | SetCPU (CPU); |
433 | } |
434 | } |
435 | |
436 | |
437 | |
438 | static void OptCreateDep (const char* Opt, const char* Arg) |
439 | /* Handle the --create-dep option */ |
440 | { |
441 | FileNameOption (Opt, Arg, &DepName); |
442 | } |
443 | |
444 | |
445 | |
446 | static void OptCreateFullDep (const char* Opt attribute ((unused)), |
447 | const char* Arg) |
448 | /* Handle the --create-full-dep option */ |
449 | { |
450 | FileNameOption (Opt, Arg, &FullDepName); |
451 | } |
452 | |
453 | |
454 | |
455 | static void OptDebug (const char* Opt attribute ((unused)), |
456 | const char* Arg attribute ((unused))) |
457 | /* Compiler debug mode */ |
458 | { |
459 | ++Debug; |
460 | } |
461 | |
462 | |
463 | |
464 | static void OptDebugInfo (const char* Opt attribute ((unused)), |
465 | const char* Arg attribute ((unused))) |
466 | /* Add debug info to the object file */ |
467 | { |
468 | DbgSyms = 1; |
469 | } |
470 | |
471 | |
472 | |
473 | static void OptFeature (const char* Opt attribute ((unused)), const char* Arg) |
474 | /* Set an emulation feature */ |
475 | { |
476 | /* Make a string buffer from Arg */ |
477 | StrBuf Feature; |
478 | |
479 | /* Set the feature, check for errors */ |
480 | if (SetFeature (SB_InitFromString (&Feature, Arg)) == FEAT_UNKNOWN) { |
481 | AbEnd ("Illegal emulation feature: '%s'" , Arg); |
482 | } |
483 | } |
484 | |
485 | |
486 | |
487 | static void OptHelp (const char* Opt attribute ((unused)), |
488 | const char* Arg attribute ((unused))) |
489 | /* Print usage information and exit */ |
490 | { |
491 | Usage (); |
492 | exit (EXIT_SUCCESS); |
493 | } |
494 | |
495 | |
496 | |
497 | static void OptIgnoreCase (const char* Opt attribute ((unused)), |
498 | const char* Arg attribute ((unused))) |
499 | /* Ignore case on symbols */ |
500 | { |
501 | IgnoreCase = 1; |
502 | } |
503 | |
504 | |
505 | |
506 | static void OptIncludeDir (const char* Opt attribute ((unused)), const char* Arg) |
507 | /* Add an include search path */ |
508 | { |
509 | AddSearchPath (IncSearchPath, Arg); |
510 | } |
511 | |
512 | |
513 | |
514 | static void OptLargeAlignment (const char* Opt attribute ((unused)), |
515 | const char* Arg attribute ((unused))) |
516 | /* Don't warn about large alignments */ |
517 | { |
518 | LargeAlignment = 1; |
519 | } |
520 | |
521 | |
522 | |
523 | static void OptListBytes (const char* Opt, const char* Arg) |
524 | /* Set the maximum number of bytes per listing line */ |
525 | { |
526 | unsigned Num; |
527 | char Check; |
528 | |
529 | /* Convert the argument to a number */ |
530 | if (sscanf (Arg, "%u%c" , &Num, &Check) != 1) { |
531 | InvArg (Opt, Arg); |
532 | } |
533 | |
534 | /* Check the bounds */ |
535 | if (Num != 0 && (Num < MIN_LIST_BYTES || Num > MAX_LIST_BYTES)) { |
536 | AbEnd ("Argument for option '%s' is out of range" , Opt); |
537 | } |
538 | |
539 | /* Use the value */ |
540 | SetListBytes (Num); |
541 | } |
542 | |
543 | |
544 | |
545 | static void OptListing (const char* Opt, const char* Arg) |
546 | /* Create a listing file */ |
547 | { |
548 | /* Since the meaning of -l and --listing has changed, print an error if |
549 | ** the filename is empty or begins with the option char. |
550 | */ |
551 | if (Arg == 0 || *Arg == '\0' || *Arg == '-') { |
552 | Fatal ("The meaning of '%s' has changed. It does now " |
553 | "expect a file name as argument." , Opt); |
554 | } |
555 | |
556 | /* Get the file name */ |
557 | FileNameOption (Opt, Arg, &ListingName); |
558 | } |
559 | |
560 | |
561 | |
562 | static void OptMemoryModel (const char* Opt, const char* Arg) |
563 | /* Set the memory model */ |
564 | { |
565 | mmodel_t M; |
566 | |
567 | /* Check the current memory model */ |
568 | if (MemoryModel != MMODEL_UNKNOWN) { |
569 | AbEnd ("Cannot use option '%s' twice" , Opt); |
570 | } |
571 | |
572 | /* Translate the memory model name and check it */ |
573 | M = FindMemoryModel (Arg); |
574 | if (M == MMODEL_UNKNOWN) { |
575 | AbEnd ("Unknown memory model: %s" , Arg); |
576 | } else if (M == MMODEL_HUGE) { |
577 | AbEnd ("Unsupported memory model: %s" , Arg); |
578 | } |
579 | |
580 | /* Set the memory model */ |
581 | SetMemoryModel (M); |
582 | } |
583 | |
584 | |
585 | |
586 | static void OptPageLength (const char* Opt attribute ((unused)), const char* Arg) |
587 | /* Handle the --pagelength option */ |
588 | { |
589 | int Len = atoi (Arg); |
590 | if (Len != -1 && (Len < MIN_PAGE_LEN || Len > MAX_PAGE_LEN)) { |
591 | AbEnd ("Invalid page length: %d" , Len); |
592 | } |
593 | PageLength = Len; |
594 | } |
595 | |
596 | |
597 | |
598 | static void OptRelaxChecks (const char* Opt attribute ((unused)), |
599 | const char* Arg attribute ((unused))) |
600 | /* Handle the --relax-checks options */ |
601 | { |
602 | RelaxChecks = 1; |
603 | } |
604 | |
605 | |
606 | |
607 | static void OptSmart (const char* Opt attribute ((unused)), |
608 | const char* Arg attribute ((unused))) |
609 | /* Handle the -s/--smart options */ |
610 | { |
611 | SmartMode = 1; |
612 | } |
613 | |
614 | |
615 | |
616 | static void OptTarget (const char* Opt attribute ((unused)), const char* Arg) |
617 | /* Set the target system */ |
618 | { |
619 | SetSys (Arg); |
620 | } |
621 | |
622 | |
623 | |
624 | static void OptVerbose (const char* Opt attribute ((unused)), |
625 | const char* Arg attribute ((unused))) |
626 | /* Increase verbosity */ |
627 | { |
628 | ++Verbosity; |
629 | } |
630 | |
631 | |
632 | |
633 | static void OptVersion (const char* Opt attribute ((unused)), |
634 | const char* Arg attribute ((unused))) |
635 | /* Print the assembler version */ |
636 | { |
637 | fprintf (stderr, "%s V%s\n" , ProgName, GetVersionAsString ()); |
638 | exit(EXIT_SUCCESS); |
639 | } |
640 | |
641 | |
642 | |
643 | static void DoPCAssign (void) |
644 | /* Start absolute code */ |
645 | { |
646 | long PC = ConstExpression (); |
647 | if (PC < 0 || PC > 0xFFFFFF) { |
648 | Error ("Range error" ); |
649 | } else { |
650 | EnterAbsoluteMode (PC); |
651 | } |
652 | } |
653 | |
654 | |
655 | |
656 | static void OneLine (void) |
657 | /* Assemble one line */ |
658 | { |
659 | Segment* Seg = 0; |
660 | unsigned long PC = 0; |
661 | SymEntry* Sym = 0; |
662 | Macro* Mac = 0; |
663 | int Instr = -1; |
664 | |
665 | /* Initialize the new listing line if we are actually reading from file |
666 | ** and not from internally pushed input. |
667 | */ |
668 | if (!HavePushedInput ()) { |
669 | InitListingLine (); |
670 | } |
671 | |
672 | /* Single colon means unnamed label */ |
673 | if (CurTok.Tok == TOK_COLON) { |
674 | ULabDef (); |
675 | NextTok (); |
676 | } |
677 | |
678 | /* If the first token on the line is an identifier, check for a macro or |
679 | ** an instruction. |
680 | */ |
681 | if (CurTok.Tok == TOK_IDENT) { |
682 | if (UbiquitousIdents) { |
683 | /* Macros CAN be instructions, so check for them first */ |
684 | Mac = FindMacro (&CurTok.SVal); |
685 | if (Mac == 0) { |
686 | Instr = FindInstruction (&CurTok.SVal); |
687 | } |
688 | } else { |
689 | /* Macros and symbols may NOT use the names of instructions */ |
690 | Instr = FindInstruction (&CurTok.SVal); |
691 | if (Instr < 0) { |
692 | Mac = FindMacro (&CurTok.SVal); |
693 | } |
694 | } |
695 | } |
696 | |
697 | /* Handle an identifier. This may be a cheap local symbol, or a fully |
698 | ** scoped identifier which may start with a namespace token (for global |
699 | ** namespace) |
700 | */ |
701 | if (CurTok.Tok == TOK_LOCAL_IDENT || |
702 | CurTok.Tok == TOK_NAMESPACE || |
703 | (CurTok.Tok == TOK_IDENT && Instr < 0 && Mac == 0)) { |
704 | |
705 | /* Did we have whitespace before the ident? */ |
706 | int HadWS = CurTok.WS; |
707 | |
708 | /* Generate the symbol table entry, then skip the name */ |
709 | Sym = ParseAnySymName (SYM_ALLOC_NEW); |
710 | |
711 | /* If a colon follows, this is a label definition. If there |
712 | ** is no colon, it's an assignment. |
713 | */ |
714 | if (CurTok.Tok == TOK_EQ || CurTok.Tok == TOK_ASSIGN) { |
715 | |
716 | /* Determine the symbol flags from the assignment token */ |
717 | unsigned Flags = (CurTok.Tok == TOK_ASSIGN)? SF_LABEL : SF_NONE; |
718 | |
719 | /* Skip the '=' */ |
720 | NextTok (); |
721 | |
722 | /* Define the symbol with the expression following the '=' */ |
723 | SymDef (Sym, Expression(), ADDR_SIZE_DEFAULT, Flags); |
724 | |
725 | /* Don't allow anything after a symbol definition */ |
726 | ConsumeSep (); |
727 | return; |
728 | |
729 | } else if (CurTok.Tok == TOK_SET) { |
730 | |
731 | ExprNode* Expr; |
732 | |
733 | /* .SET defines variables (= redefinable symbols) */ |
734 | NextTok (); |
735 | |
736 | /* Read the assignment expression, which must be constant */ |
737 | Expr = GenLiteralExpr (ConstExpression ()); |
738 | |
739 | /* Define the symbol with the constant expression following |
740 | ** the '=' |
741 | */ |
742 | SymDef (Sym, Expr, ADDR_SIZE_DEFAULT, SF_VAR); |
743 | |
744 | /* Don't allow anything after a symbol definition */ |
745 | ConsumeSep (); |
746 | return; |
747 | |
748 | } else { |
749 | |
750 | /* A label. Remember the current segment, so we can later |
751 | ** determine the size of the data stored under the label. |
752 | */ |
753 | Seg = ActiveSeg; |
754 | PC = GetPC (); |
755 | |
756 | /* Define the label */ |
757 | SymDef (Sym, GenCurrentPC (), ADDR_SIZE_DEFAULT, SF_LABEL); |
758 | |
759 | /* Skip the colon. If NoColonLabels is enabled, allow labels |
760 | ** without a colon if there is no whitespace before the |
761 | ** identifier. |
762 | */ |
763 | if (CurTok.Tok != TOK_COLON) { |
764 | if (HadWS || !NoColonLabels) { |
765 | Error ("':' expected" ); |
766 | /* Try some smart error recovery */ |
767 | if (CurTok.Tok == TOK_NAMESPACE) { |
768 | NextTok (); |
769 | } |
770 | } |
771 | } else { |
772 | /* Skip the colon */ |
773 | NextTok (); |
774 | } |
775 | |
776 | /* If we come here, a new identifier may be waiting, which may |
777 | ** be a macro or instruction. |
778 | */ |
779 | if (CurTok.Tok == TOK_IDENT) { |
780 | if (UbiquitousIdents) { |
781 | /* Macros CAN be instructions, so check for them first */ |
782 | Mac = FindMacro (&CurTok.SVal); |
783 | if (Mac == 0) { |
784 | Instr = FindInstruction (&CurTok.SVal); |
785 | } |
786 | } else { |
787 | /* Macros and symbols may NOT use the names of instructions */ |
788 | Instr = FindInstruction (&CurTok.SVal); |
789 | if (Instr < 0) { |
790 | Mac = FindMacro (&CurTok.SVal); |
791 | } |
792 | } |
793 | } |
794 | } |
795 | } |
796 | |
797 | /* We've handled a possible label, now handle the remainder of the line */ |
798 | if (CurTok.Tok >= TOK_FIRSTPSEUDO && CurTok.Tok <= TOK_LASTPSEUDO) { |
799 | /* A control command */ |
800 | HandlePseudo (); |
801 | } else if (Mac != 0) { |
802 | /* A macro expansion */ |
803 | MacExpandStart (Mac); |
804 | } else if (Instr >= 0) { |
805 | /* A mnemonic - assemble one instruction */ |
806 | HandleInstruction (Instr); |
807 | } else if (PCAssignment && (CurTok.Tok == TOK_STAR || CurTok.Tok == TOK_PC)) { |
808 | NextTok (); |
809 | if (CurTok.Tok != TOK_EQ) { |
810 | Error ("'=' expected" ); |
811 | SkipUntilSep (); |
812 | } else { |
813 | /* Skip the equal sign */ |
814 | NextTok (); |
815 | /* Enter absolute mode */ |
816 | DoPCAssign (); |
817 | } |
818 | } |
819 | |
820 | /* If we have defined a label, remember its size. Sym is also set by |
821 | ** a symbol assignment, but in this case Done is false, so we don't |
822 | ** come here. |
823 | */ |
824 | if (Sym) { |
825 | unsigned long Size; |
826 | if (Seg == ActiveSeg) { |
827 | /* Same segment */ |
828 | Size = GetPC () - PC; |
829 | } else { |
830 | /* The line has switched the segment */ |
831 | Size = 0; |
832 | } |
833 | DefSizeOfSymbol (Sym, Size); |
834 | } |
835 | |
836 | /* Line separator must come here */ |
837 | ConsumeSep (); |
838 | } |
839 | |
840 | |
841 | |
842 | static void Assemble (void) |
843 | /* Start the ball rolling ... */ |
844 | { |
845 | /* Prime the pump */ |
846 | NextTok (); |
847 | |
848 | /* Assemble lines until end of file */ |
849 | while (CurTok.Tok != TOK_EOF) { |
850 | OneLine (); |
851 | } |
852 | } |
853 | |
854 | |
855 | |
856 | static void CreateObjFile (void) |
857 | /* Create the object file */ |
858 | { |
859 | /* Open the object, write the header */ |
860 | ObjOpen (); |
861 | |
862 | /* Write the object file options */ |
863 | WriteOptions (); |
864 | |
865 | /* Write the list of input files */ |
866 | WriteFiles (); |
867 | |
868 | /* Write the segment data to the file */ |
869 | WriteSegments (); |
870 | |
871 | /* Write the import list */ |
872 | WriteImports (); |
873 | |
874 | /* Write the export list */ |
875 | WriteExports (); |
876 | |
877 | /* Write debug symbols if requested */ |
878 | WriteDbgSyms (); |
879 | |
880 | /* Write the scopes if requested */ |
881 | WriteScopes (); |
882 | |
883 | /* Write line infos if requested */ |
884 | WriteLineInfos (); |
885 | |
886 | /* Write the string pool */ |
887 | WriteStrPool (); |
888 | |
889 | /* Write the assertions */ |
890 | WriteAssertions (); |
891 | |
892 | /* Write the spans */ |
893 | WriteSpans (); |
894 | |
895 | /* Write an updated header and close the file */ |
896 | ObjClose (); |
897 | } |
898 | |
899 | |
900 | |
901 | int main (int argc, char* argv []) |
902 | /* Assembler main program */ |
903 | { |
904 | /* Program long options */ |
905 | static const LongOpt OptTab[] = { |
906 | { "--auto-import" , 0, OptAutoImport }, |
907 | { "--bin-include-dir" , 1, OptBinIncludeDir }, |
908 | { "--cpu" , 1, OptCPU }, |
909 | { "--create-dep" , 1, OptCreateDep }, |
910 | { "--create-full-dep" , 1, OptCreateFullDep }, |
911 | { "--debug" , 0, OptDebug }, |
912 | { "--debug-info" , 0, OptDebugInfo }, |
913 | { "--feature" , 1, OptFeature }, |
914 | { "--help" , 0, OptHelp }, |
915 | { "--ignore-case" , 0, OptIgnoreCase }, |
916 | { "--include-dir" , 1, OptIncludeDir }, |
917 | { "--large-alignment" , 0, OptLargeAlignment }, |
918 | { "--list-bytes" , 1, OptListBytes }, |
919 | { "--listing" , 1, OptListing }, |
920 | { "--memory-model" , 1, OptMemoryModel }, |
921 | { "--pagelength" , 1, OptPageLength }, |
922 | { "--relax-checks" , 0, OptRelaxChecks }, |
923 | { "--smart" , 0, OptSmart }, |
924 | { "--target" , 1, OptTarget }, |
925 | { "--verbose" , 0, OptVerbose }, |
926 | { "--version" , 0, OptVersion }, |
927 | }; |
928 | |
929 | /* Name of the global name space */ |
930 | static const StrBuf GlobalNameSpace = STATIC_STRBUF_INITIALIZER; |
931 | |
932 | unsigned I; |
933 | |
934 | /* Initialize the cmdline module */ |
935 | InitCmdLine (&argc, &argv, "ca65" ); |
936 | |
937 | /* Initialize the string pool */ |
938 | InitStrPool (); |
939 | |
940 | /* Initialize the include search paths */ |
941 | InitIncludePaths (); |
942 | |
943 | /* Create the predefined segments */ |
944 | SegInit (); |
945 | |
946 | /* Enter the base lexical level. We must do that here, since we may |
947 | ** define symbols using -D. |
948 | */ |
949 | SymEnterLevel (&GlobalNameSpace, SCOPE_FILE, ADDR_SIZE_DEFAULT, 0); |
950 | |
951 | /* Initialize the line infos. Must be done here, since we need line infos |
952 | ** for symbol definitions. |
953 | */ |
954 | InitLineInfo (); |
955 | |
956 | /* Check the parameters */ |
957 | I = 1; |
958 | while (I < ArgCount) { |
959 | |
960 | /* Get the argument */ |
961 | const char* Arg = ArgVec [I]; |
962 | |
963 | /* Check for an option */ |
964 | if (Arg[0] == '-') { |
965 | switch (Arg[1]) { |
966 | |
967 | case '-': |
968 | LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0])); |
969 | break; |
970 | |
971 | case 'd': |
972 | OptDebug (Arg, 0); |
973 | break; |
974 | |
975 | case 'g': |
976 | OptDebugInfo (Arg, 0); |
977 | break; |
978 | |
979 | case 'h': |
980 | OptHelp (Arg, 0); |
981 | break; |
982 | |
983 | case 'i': |
984 | OptIgnoreCase (Arg, 0); |
985 | break; |
986 | |
987 | case 'l': |
988 | OptListing (Arg, GetArg (&I, 2)); |
989 | break; |
990 | |
991 | case 'm': |
992 | if (Arg[2] == 'm') { |
993 | OptMemoryModel (Arg, GetArg (&I, 3)); |
994 | } else { |
995 | UnknownOption (Arg); |
996 | } |
997 | break; |
998 | |
999 | case 'o': |
1000 | OutFile = GetArg (&I, 2); |
1001 | break; |
1002 | |
1003 | case 's': |
1004 | OptSmart (Arg, 0); |
1005 | break; |
1006 | |
1007 | case 't': |
1008 | OptTarget (Arg, GetArg (&I, 2)); |
1009 | break; |
1010 | |
1011 | case 'v': |
1012 | OptVerbose (Arg, 0); |
1013 | break; |
1014 | |
1015 | case 'D': |
1016 | DefineSymbol (GetArg (&I, 2)); |
1017 | break; |
1018 | |
1019 | case 'I': |
1020 | OptIncludeDir (Arg, GetArg (&I, 2)); |
1021 | break; |
1022 | |
1023 | case 'U': |
1024 | OptAutoImport (Arg, 0); |
1025 | break; |
1026 | |
1027 | case 'V': |
1028 | OptVersion (Arg, 0); |
1029 | break; |
1030 | |
1031 | case 'W': |
1032 | WarnLevel = atoi (GetArg (&I, 2)); |
1033 | break; |
1034 | |
1035 | default: |
1036 | UnknownOption (Arg); |
1037 | break; |
1038 | |
1039 | } |
1040 | } else { |
1041 | /* Filename. Check if we already had one */ |
1042 | if (InFile) { |
1043 | fprintf (stderr, "%s: Don't know what to do with '%s'\n" , |
1044 | ProgName, Arg); |
1045 | exit (EXIT_FAILURE); |
1046 | } else { |
1047 | InFile = Arg; |
1048 | } |
1049 | } |
1050 | |
1051 | /* Next argument */ |
1052 | ++I; |
1053 | } |
1054 | |
1055 | /* Do we have an input file? */ |
1056 | if (InFile == 0) { |
1057 | fprintf (stderr, "%s: No input files\n" , ProgName); |
1058 | exit (EXIT_FAILURE); |
1059 | } |
1060 | |
1061 | /* Add the default include search paths. */ |
1062 | FinishIncludePaths (); |
1063 | |
1064 | /* If no CPU given, use the default CPU for the target */ |
1065 | if (GetCPU () == CPU_UNKNOWN) { |
1066 | if (Target != TGT_UNKNOWN) { |
1067 | SetCPU (GetTargetProperties (Target)->DefaultCPU); |
1068 | } else { |
1069 | SetCPU (CPU_6502); |
1070 | } |
1071 | } |
1072 | |
1073 | /* If no memory model was given, use the default */ |
1074 | if (MemoryModel == MMODEL_UNKNOWN) { |
1075 | SetMemoryModel (MMODEL_NEAR); |
1076 | } |
1077 | |
1078 | /* Set the default segment sizes according to the memory model */ |
1079 | SetSegmentSizes (); |
1080 | |
1081 | /* Initialize the scanner, open the input file */ |
1082 | InitScanner (InFile); |
1083 | |
1084 | /* Define the default options */ |
1085 | SetOptions (); |
1086 | |
1087 | /* Assemble the input */ |
1088 | Assemble (); |
1089 | |
1090 | /* If we didn't have any errors, check the pseudo insn stacks */ |
1091 | if (ErrorCount == 0) { |
1092 | CheckPseudo (); |
1093 | } |
1094 | |
1095 | /* If we didn't have any errors, check and cleanup the unnamed labels */ |
1096 | if (ErrorCount == 0) { |
1097 | ULabDone (); |
1098 | } |
1099 | |
1100 | /* If we didn't have any errors, check the symbol table */ |
1101 | if (ErrorCount == 0) { |
1102 | SymCheck (); |
1103 | } |
1104 | |
1105 | /* If we didn't have any errors, check the hll debug symbols */ |
1106 | if (ErrorCount == 0) { |
1107 | DbgInfoCheck (); |
1108 | } |
1109 | |
1110 | /* If we didn't have any errors, close the file scope lexical level */ |
1111 | if (ErrorCount == 0) { |
1112 | SymLeaveLevel (); |
1113 | } |
1114 | |
1115 | /* If we didn't have any errors, check and resolve the segment data */ |
1116 | if (ErrorCount == 0) { |
1117 | SegDone (); |
1118 | } |
1119 | |
1120 | /* If we didn't have any errors, check the assertions */ |
1121 | if (ErrorCount == 0) { |
1122 | CheckAssertions (); |
1123 | } |
1124 | |
1125 | /* Dump the data */ |
1126 | if (Verbosity >= 2) { |
1127 | SymDump (stdout); |
1128 | SegDump (); |
1129 | } |
1130 | |
1131 | /* If we didn't have an errors, finish off the line infos */ |
1132 | DoneLineInfo (); |
1133 | |
1134 | /* If we didn't have any errors, create the object, listing and |
1135 | ** dependency files |
1136 | */ |
1137 | if (ErrorCount == 0) { |
1138 | CreateObjFile (); |
1139 | if (SB_GetLen (&ListingName) > 0) { |
1140 | CreateListing (); |
1141 | } |
1142 | CreateDependencies (); |
1143 | } |
1144 | |
1145 | /* Close the input file */ |
1146 | DoneScanner (); |
1147 | |
1148 | /* Return an apropriate exit code */ |
1149 | return (ErrorCount == 0)? EXIT_SUCCESS : EXIT_FAILURE; |
1150 | } |
1151 | |