]> git.sur5r.net Git - cc65/blob - src/da65/main.c
Support for preprocessing info file via cpp or m4.
[cc65] / src / da65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*                  Main program for the da65 disassembler                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2014, 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 <limits.h>
41 #include <time.h>
42
43 /* common */
44 #include "abend.h"
45 #include "cmdline.h"
46 #include "cpu.h"
47 #include "fname.h"
48 #include "print.h"
49 #include "version.h"
50
51 /* da65 */
52 #include "attrtab.h"
53 #include "code.h"
54 #include "comments.h"
55 #include "data.h"
56 #include "error.h"
57 #include "global.h"
58 #include "infofile.h"
59 #include "labels.h"
60 #include "opctable.h"
61 #include "output.h"
62 #include "scanner.h"
63 #include "segment.h"
64
65
66
67 /*****************************************************************************/
68 /*                                   Code                                    */
69 /*****************************************************************************/
70
71
72
73 static void Usage (void)
74 /* Print usage information and exit */
75 {
76     printf ("Usage: %s [options] [inputfile]\n"
77             "Short options:\n"
78             "  -g\t\t\tAdd debug info to object file\n"
79             "  -h\t\t\tHelp (this text)\n"
80             "  -i name\t\tSpecify an info file\n"
81             "  -o name\t\tName the output file\n"
82             "  -v\t\t\tIncrease verbosity\n"
83             "  -F\t\t\tAdd formfeeds to the output\n"
84             "  -S addr\t\tSet the start/load address\n"
85             "  -s\t\t\tAccept line markers in the info file\n"
86             "  -V\t\t\tPrint the disassembler version\n"
87             "\n"
88             "Long options:\n"
89             "  --argument-column n\tSpecify argument start column\n"
90             "  --comment-column n\tSpecify comment start column\n"
91             "  --comments n\t\tSet the comment level for the output\n"
92             "  --cpu type\t\tSet cpu type\n"
93             "  --debug-info\t\tAdd debug info to object file\n"
94             "  --formfeeds\t\tAdd formfeeds to the output\n"
95             "  --help\t\tHelp (this text)\n"
96             "  --hexoffs\t\tUse hexadecimal label offsets\n"
97             "  --info name\t\tSpecify an info file\n"
98             "  --label-break n\tAdd newline if label exceeds length n\n"
99             "  --mnemonic-column n\tSpecify mnemonic start column\n"
100             "  --pagelength n\tSet the page length for the listing\n"
101             "  --start-addr addr\tSet the start/load address\n"
102             "  --sync-lines\t\tAccept line markers in the info file\n"
103             "  --text-column n\tSpecify text start column\n"
104             "  --verbose\t\tIncrease verbosity\n"
105             "  --version\t\tPrint the disassembler version\n",
106             ProgName);
107 }
108
109
110
111 static void RangeCheck (const char* Opt, unsigned long Val,
112                         unsigned long Min, unsigned long Max)
113 /* Do a range check for the given option and abort if there's a range
114 ** error.
115 */
116 {
117     if (Val < Min || Val > Max) {
118         Error ("Argument for %s outside valid range (%ld-%ld)", Opt, Min, Max);
119     }
120 }
121
122
123
124 static unsigned long CvtNumber (const char* Arg, const char* Number)
125 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
126 ** numbers.
127 */
128 {
129     unsigned long Val;
130     int           Converted;
131     char          BoundsCheck;
132
133     /* Convert */
134     if (*Number == '$') {
135         ++Number;
136         Converted = sscanf (Number, "%lx%c", &Val, &BoundsCheck);
137     } else {
138         Converted = sscanf (Number, "%li%c", (long*)&Val, &BoundsCheck);
139     }
140
141     /* Check if we do really have a number */
142     if (Converted != 1) {
143         Error ("Invalid number given in argument: %s\n", Arg);
144     }
145
146     /* Return the result */
147     return Val;
148 }
149
150
151
152 static void OptArgumentColumn (const char* Opt, const char* Arg)
153 /* Handle the --argument-column option */
154 {
155     /* Convert the argument to a number */
156     unsigned long Val = CvtNumber (Opt, Arg);
157
158     /* Check for a valid range */
159     RangeCheck (Opt, Val, MIN_ACOL, MAX_ACOL);
160
161     /* Use the value */
162     ACol = (unsigned char) Val;
163 }
164
165
166
167 static void OptBytesPerLine (const char* Opt, const char* Arg)
168 /* Handle the --bytes-per-line option */
169 {
170     /* Convert the argument to a number */
171     unsigned long Val = CvtNumber (Opt, Arg);
172
173     /* Check for a valid range */
174     RangeCheck (Opt, Val, MIN_BYTESPERLINE, MAX_BYTESPERLINE);
175
176     /* Use the value */
177     BytesPerLine = (unsigned char) Val;
178 }
179
180
181
182 static void OptCommentColumn (const char* Opt, const char* Arg)
183 /* Handle the --comment-column option */
184 {
185     /* Convert the argument to a number */
186     unsigned long Val = CvtNumber (Opt, Arg);
187
188     /* Check for a valid range */
189     RangeCheck (Opt, Val, MIN_CCOL, MAX_CCOL);
190
191     /* Use the value */
192     CCol = (unsigned char) Val;
193 }
194
195
196
197 static void OptComments (const char* Opt, const char* Arg)
198 /* Handle the --comments option */
199 {
200     /* Convert the argument to a number */
201     unsigned long Val = CvtNumber (Opt, Arg);
202
203     /* Check for a valid range */
204     RangeCheck (Opt, Val, MIN_COMMENTS, MAX_COMMENTS);
205
206     /* Use the value */
207     Comments = (unsigned char) Val;
208 }
209
210
211
212 static void OptCPU (const char* Opt attribute ((unused)), const char* Arg)
213 /* Handle the --cpu option */
214 {
215     /* Find the CPU from the given name */
216     CPU = FindCPU (Arg);
217     SetOpcTable (CPU);
218 }
219
220
221
222 static void OptDebugInfo (const char* Opt attribute ((unused)),
223                           const char* Arg attribute ((unused)))
224 /* Add debug info to the object file */
225 {
226     DebugInfo = 1;
227 }
228
229
230
231 static void OptFormFeeds (const char* Opt attribute ((unused)),
232                           const char* Arg attribute ((unused)))
233 /* Add form feeds to the output */
234 {
235     FormFeeds = 1;
236 }
237
238
239
240 static void OptHelp (const char* Opt attribute ((unused)),
241                      const char* Arg attribute ((unused)))
242 /* Print usage information and exit */
243 {
244     Usage ();
245     exit (EXIT_SUCCESS);
246 }
247
248
249
250 static void OptHexOffs (const char* Opt attribute ((unused)),
251                         const char* Arg attribute ((unused)))
252 /* Handle the --hexoffs option */
253 {
254     UseHexOffs = 1;
255 }
256
257
258
259 static void OptInfo (const char* Opt attribute ((unused)), const char* Arg)
260 /* Handle the --info option */
261 {
262     InfoSetName (Arg);
263 }
264
265
266
267 static void OptLabelBreak (const char* Opt, const char* Arg)
268 /* Handle the --label-break option */
269 {
270     /* Convert the argument to a number */
271     unsigned long Val = CvtNumber (Opt, Arg);
272
273     /* Check for a valid range */
274     RangeCheck (Opt, Val, MIN_LABELBREAK, MAX_LABELBREAK);
275
276     /* Use the value */
277     LBreak = (unsigned char) Val;
278 }
279
280
281
282 static void OptMnemonicColumn (const char* Opt, const char* Arg)
283 /* Handle the --mnemonic-column option */
284 {
285     /* Convert the argument to a number */
286     unsigned long Val = CvtNumber (Opt, Arg);
287
288     /* Check for a valid range */
289     RangeCheck (Opt, Val, MIN_MCOL, MAX_MCOL);
290
291     /* Use the value */
292     MCol = (unsigned char) Val;
293 }
294
295
296
297 static void OptPageLength (const char* Opt attribute ((unused)), const char* Arg)
298 /* Handle the --pagelength option */
299 {
300     int Len = atoi (Arg);
301     if (Len != 0) {
302         RangeCheck (Opt, Len, MIN_PAGE_LEN, MAX_PAGE_LEN);
303     }
304     PageLength = Len;
305 }
306
307
308
309 static void OptStartAddr (const char* Opt, const char* Arg)
310 /* Set the default start address */
311 {
312     StartAddr = CvtNumber (Opt, Arg);
313 }
314
315
316
317 static void OptSyncLines (const char* Opt attribute ((unused)),
318                           const char* Arg attribute ((unused)))
319 /* Handle the --sync-lines option */
320 {
321     InfoSyncLines = 1;
322 }
323
324
325
326 static void OptTextColumn (const char* Opt, const char* Arg)
327 /* Handle the --text-column option */
328 {
329     /* Convert the argument to a number */
330     unsigned long Val = CvtNumber (Opt, Arg);
331
332     /* Check for a valid range */
333     RangeCheck (Opt, Val, MIN_TCOL, MAX_TCOL);
334
335     /* Use the value */
336     TCol = (unsigned char) Val;
337 }
338
339
340
341 static void OptVerbose (const char* Opt attribute ((unused)),
342                         const char* Arg attribute ((unused)))
343 /* Increase verbosity */
344 {
345     ++Verbosity;
346 }
347
348
349
350 static void OptVersion (const char* Opt attribute ((unused)),
351                         const char* Arg attribute ((unused)))
352 /* Print the disassembler version */
353 {
354     fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ());
355     exit(EXIT_SUCCESS);
356 }
357
358
359
360 static void OneOpcode (unsigned RemainingBytes)
361 /* Disassemble one opcode */
362 {
363     unsigned I;
364     unsigned OldPC = PC;
365
366     /* Get the opcode from the current address */
367     unsigned char OPC = GetCodeByte (PC);
368
369     /* Get the opcode description for the opcode byte */
370     const OpcDesc* D = &OpcTable[OPC];
371
372     /* Get the output style for the current PC */
373     attr_t Style = GetStyleAttr (PC);
374
375     /* If a segment begins here, then name that segment.
376     ** Note that the segment is named even if its code is being skipped,
377     ** because some of its later code might not be skipped.
378     */
379     if (IsSegmentStart (PC)) {
380         StartSegment (GetSegmentStartName (PC), GetSegmentAddrSize (PC));
381     }
382
383     /* If we have a label at this address, output the label and an attached
384     ** comment, provided that we aren't in a skip area.
385     */
386     if (Style != atSkip && MustDefLabel (PC)) {
387         const char* Comment = GetComment (PC);
388         if (Comment) {
389             UserComment (Comment);
390         }
391         DefLabel (GetLabelName (PC));
392     }
393
394     /* Check...
395     **   - ...if we have enough bytes remaining for the code at this address.
396     **   - ...if the current instruction is valid for the given CPU.
397     **   - ...if there is no label somewhere between the instruction bytes.
398     **   - ...if there is no segment change between the instruction bytes.
399     ** If any one of those conditions is false, switch to data mode.
400     */
401     if (Style == atDefault) {
402         if (D->Size > RemainingBytes) {
403             Style = atIllegal;
404             MarkAddr (PC, Style);
405         } else if (D->Flags & flIllegal) {
406             Style = atIllegal;
407             MarkAddr (PC, Style);
408         } else {
409             for (I = PC + D->Size; --I > PC; ) {
410                 if (HaveLabel (I) || IsSegmentStart (I)) {
411                     Style = atIllegal;
412                     MarkAddr (PC, Style);
413                     break;
414                 }
415             }
416             for (I = 0; I < D->Size - 1u; ++I) {
417                 if (IsSegmentEnd (PC + I)) {
418                     Style = atIllegal;
419                     MarkAddr (PC, Style);
420                     break;
421                 }
422             }
423         }
424     }
425
426     /* Disassemble the line */
427     switch (Style) {
428
429         case atDefault:
430             D->Handler (D);
431             PC += D->Size;
432             break;
433
434         case atCode:
435             /* Beware: If we don't have enough bytes left to disassemble the
436             ** following insn, fall through to byte mode.
437             */
438             if (D->Size <= RemainingBytes) {
439                 /* Output labels within the next insn */
440                 for (I = 1; I < D->Size; ++I) {
441                     ForwardLabel (I);
442                 }
443                 /* Output the insn */
444                 D->Handler (D);
445                 PC += D->Size;
446                 break;
447             }
448             /* FALLTHROUGH */
449
450         case atByteTab:
451             ByteTable ();
452             break;
453
454         case atDByteTab:
455             DByteTable ();
456             break;
457
458         case atWordTab:
459             WordTable ();
460             break;
461
462         case atDWordTab:
463             DWordTable ();
464             break;
465
466         case atAddrTab:
467             AddrTable ();
468             break;
469
470         case atRtsTab:
471             RtsTable ();
472             break;
473
474         case atTextTab:
475             TextTable ();
476             break;
477
478         case atSkip:
479             ++PC;
480             break;
481
482         default:
483             DataByteLine (1);
484             ++PC;
485             break;
486     }
487
488     /* Change back to the default CODE segment if
489     ** a named segment stops at the current address.
490     */
491     for (I = PC - OldPC; I > 0; --I) {
492         if (IsSegmentEnd (PC - I)) {
493             EndSegment ();
494             break;
495         }
496     }
497 }
498
499
500
501 static void OnePass (void)
502 /* Make one pass through the code */
503 {
504     unsigned Count;
505
506     /* Disassemble until nothing left */
507     while ((Count = GetRemainingBytes()) > 0) {
508         OneOpcode (Count);
509     }
510 }
511
512
513
514 static void Disassemble (void)
515 /* Disassemble the code */
516 {
517     /* Pass 1 */
518     Pass = 1;
519     OnePass ();
520
521     Output ("---------------------------");
522     LineFeed ();
523
524     /* Pass 2 */
525     Pass = 2;
526     ResetCode ();
527     OutputSettings ();
528     DefOutOfRangeLabels ();
529     OnePass ();
530 }
531
532
533
534 int main (int argc, char* argv [])
535 /* Assembler main program */
536 {
537     /* Program long options */
538     static const LongOpt OptTab[] = {
539         { "--argument-column",  1,      OptArgumentColumn       },
540         { "--bytes-per-line",   1,      OptBytesPerLine         },
541         { "--comment-column",   1,      OptCommentColumn        },
542         { "--comments",         1,      OptComments             },
543         { "--cpu",              1,      OptCPU                  },
544         { "--debug-info",       0,      OptDebugInfo            },
545         { "--formfeeds",        0,      OptFormFeeds            },
546         { "--help",             0,      OptHelp                 },
547         { "--hexoffs",          0,      OptHexOffs              },
548         { "--info",             1,      OptInfo                 },
549         { "--label-break",      1,      OptLabelBreak           },
550         { "--mnemonic-column",  1,      OptMnemonicColumn       },
551         { "--pagelength",       1,      OptPageLength           },
552         { "--start-addr",       1,      OptStartAddr            },
553         { "--sync-lines",       0,      OptSyncLines            },
554         { "--text-column",      1,      OptTextColumn           },
555         { "--verbose",          0,      OptVerbose              },
556         { "--version",          0,      OptVersion              },
557     };
558
559     unsigned I;
560     time_t T;
561
562     /* Initialize the cmdline module */
563     InitCmdLine (&argc, &argv, "da65");
564
565     /* Check the parameters */
566     I = 1;
567     while (I < ArgCount) {
568
569         /* Get the argument */
570         const char* Arg = ArgVec[I];
571
572         /* Check for an option */
573         if (Arg [0] == '-') {
574             switch (Arg [1]) {
575
576                 case '-':
577                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
578                     break;
579
580                 case 'g':
581                     OptDebugInfo (Arg, 0);
582                     break;
583
584                 case 'h':
585                     OptHelp (Arg, 0);
586                     break;
587
588                 case 'i':
589                     OptInfo (Arg, GetArg (&I, 2));
590                     break;
591
592                 case 'o':
593                     OutFile = GetArg (&I, 2);
594                     break;
595
596                 case 'v':
597                     OptVerbose (Arg, 0);
598                     break;
599
600                 case 'S':
601                     OptStartAddr (Arg, GetArg (&I, 2));
602                     break;
603
604                 case 's':
605                     OptSyncLines (Arg, 0);
606                     break;
607
608                 case 'V':
609                     OptVersion (Arg, 0);
610                     break;
611
612                 default:
613                     UnknownOption (Arg);
614                     break;
615
616             }
617         } else {
618             /* Filename. Check if we already had one */
619             if (InFile) {
620                 fprintf (stderr, "%s: Don't know what to do with `%s'\n",
621                          ProgName, Arg);
622                 exit (EXIT_FAILURE);
623             } else {
624                 InFile = Arg;
625             }
626         }
627
628         /* Next argument */
629         ++I;
630     }
631
632     /* Try to read the info file */
633     ReadInfoFile ();
634
635     /* Must have an input file */
636     if (InFile == 0) {
637         AbEnd ("No input file");
638     }
639
640     /* Check the formatting options for reasonable values. Note: We will not
641     ** really check that they make sense, just that they aren't complete
642     ** garbage.
643     */
644     if (MCol >= ACol) {
645         AbEnd ("mnemonic-column value must be smaller than argument-column value");
646     }
647     if (ACol >= CCol) {
648         AbEnd ("argument-column value must be smaller than comment-column value");
649     }
650     if (CCol >= TCol) {
651         AbEnd ("comment-column value must be smaller than text-column value");
652     }
653
654     /* If no CPU given, use the default CPU */
655     if (CPU == CPU_UNKNOWN) {
656         CPU = CPU_6502;
657     }
658
659     /* Get the current time and convert it to string so it can be used in
660     ** the output page headers.
661     */
662     T = time (0);
663     strftime (Now, sizeof (Now), "%Y-%m-%d %H:%M:%S", localtime (&T));
664
665     /* Load the input file */
666     LoadCode ();
667
668     /* Open the output file */
669     OpenOutput (OutFile);
670
671     /* Disassemble the code */
672     Disassemble ();
673
674     /* Close the output file */
675     CloseOutput ();
676
677     /* Done */
678     return EXIT_SUCCESS;
679 }