]> git.sur5r.net Git - cc65/blob - src/da65/main.c
New --label-break option
[cc65] / src / da65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*                  Main program for the da65 disassembler                   */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2006 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 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 "data.h"
55 #include "error.h"
56 #include "global.h"
57 #include "infofile.h"
58 #include "opctable.h"
59 #include "output.h"
60 #include "scanner.h"
61
62
63
64 /*****************************************************************************/
65 /*                                   Code                                    */
66 /*****************************************************************************/
67
68
69
70 static void Usage (void)
71 /* Print usage information and exit */
72 {
73     printf ("Usage: %s [options] [inputfile]\n"
74             "Short options:\n"
75             "  -g\t\t\tAdd debug info to object file\n"
76             "  -h\t\t\tHelp (this text)\n"
77             "  -i name\t\tSpecify an info file\n"
78             "  -o name\t\tName the output file\n"
79             "  -v\t\t\tIncrease verbosity\n"
80             "  -F\t\t\tAdd formfeeds to the output\n"
81             "  -S addr\t\tSet the start/load address\n"
82             "  -V\t\t\tPrint the disassembler version\n"
83             "\n"
84             "Long options:\n"
85             "  --comments n\t\tSet the comment level for the output\n"
86             "  --cpu type\t\tSet cpu type\n"
87             "  --debug-info\t\tAdd debug info to object file\n"
88             "  --formfeeds\t\tAdd formfeeds to the output\n"
89             "  --help\t\tHelp (this text)\n"
90             "  --hexoffs\t\tUse hexadecimal label offsets\n"
91             "  --info name\t\tSpecify an info file\n"
92             "  --label-break n\tAdd newline if label exceeds length n\n"
93             "  --pagelength n\tSet the page length for the listing\n"
94             "  --start-addr addr\tSet the start/load address\n"
95             "  --verbose\t\tIncrease verbosity\n"
96             "  --version\t\tPrint the disassembler version\n",
97             ProgName);
98 }
99
100
101
102 static unsigned long CvtNumber (const char* Arg, const char* Number)
103 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
104  * numbers.
105  */
106 {
107     unsigned long Val;
108     int           Converted;
109     char          BoundsCheck;
110
111     /* Convert */
112     if (*Number == '$') {
113         ++Number;
114         Converted = sscanf (Number, "%lx%c", &Val, &BoundsCheck);
115     } else {
116         Converted = sscanf (Number, "%li%c", (long*)&Val, &BoundsCheck);
117     }
118
119     /* Check if we do really have a number */
120     if (Converted != 1) {
121         Error ("Invalid number given in argument: %s\n", Arg);
122     }
123
124     /* Return the result */
125     return Val;
126 }
127
128
129
130 static void OptComments (const char* Opt, const char* Arg)
131 /* Handle the --comments option */
132 {
133     /* Convert the argument to a number */
134     unsigned long Val = CvtNumber (Opt, Arg);
135
136     /* Check for a valid range */
137     if (Val > MAX_COMMENTS) {
138         Error ("Argument for %s outside valid range (%d-%d)",
139                Opt, MIN_COMMENTS, MAX_COMMENTS);
140     }
141
142     /* Use the value */
143     Comments = (unsigned char) Val;
144 }
145
146
147
148 static void OptCPU (const char* Opt attribute ((unused)), const char* Arg)
149 /* Handle the --cpu option */
150 {
151     /* Find the CPU from the given name */
152     CPU = FindCPU (Arg);
153     SetOpcTable (CPU);
154 }
155
156
157
158 static void OptDebugInfo (const char* Opt attribute ((unused)),
159                           const char* Arg attribute ((unused)))
160 /* Add debug info to the object file */
161 {
162     DebugInfo = 1;
163 }
164
165
166
167 static void OptFormFeeds (const char* Opt attribute ((unused)),
168                           const char* Arg attribute ((unused)))
169 /* Add form feeds to the output */
170 {
171     FormFeeds = 1;
172 }
173
174
175
176 static void OptHelp (const char* Opt attribute ((unused)),
177                      const char* Arg attribute ((unused)))
178 /* Print usage information and exit */
179 {
180     Usage ();
181     exit (EXIT_SUCCESS);
182 }
183
184
185
186 static void OptHexOffs (const char* Opt attribute ((unused)),
187                         const char* Arg attribute ((unused)))
188 /* Handle the --hexoffs option */
189 {
190     UseHexOffs = 1;
191 }
192
193
194
195 static void OptInfo (const char* Opt attribute ((unused)), const char* Arg)
196 /* Handle the --info option */
197 {
198     InfoSetName (Arg);
199 }
200
201
202
203 static void OptLabelBreak (const char* Opt, const char* Arg)
204 /* Handle the --label-break option */
205 {
206     /* Convert the argument to a number */
207     unsigned long Val = CvtNumber (Opt, Arg);
208
209     /* Check for a valid range */
210     if (Val >= UCHAR_MAX) {
211         Error ("Argument for %s out of valid range (%d)",
212                Opt, UCHAR_MAX);
213     }
214
215     /* Use the value */
216     LBreak = (unsigned char) Val;
217 }
218
219
220
221 static void OptPageLength (const char* Opt attribute ((unused)), const char* Arg)
222 /* Handle the --pagelength option */
223 {
224     int Len = atoi (Arg);
225     if (Len != 0 && (Len < MIN_PAGE_LEN || Len > MAX_PAGE_LEN)) {
226         AbEnd ("Invalid page length: %d", Len);
227     }
228     PageLength = Len;
229 }
230
231
232
233 static void OptStartAddr (const char* Opt, const char* Arg)
234 /* Set the default start address */
235 {
236     StartAddr = CvtNumber (Opt, Arg);
237 }
238
239
240
241 static void OptVerbose (const char* Opt attribute ((unused)),
242                         const char* Arg attribute ((unused)))
243 /* Increase verbosity */
244 {
245     ++Verbosity;
246 }
247
248
249
250 static void OptVersion (const char* Opt attribute ((unused)),
251                         const char* Arg attribute ((unused)))
252 /* Print the disassembler version */
253 {
254     fprintf (stderr,
255              "da65 V%u.%u.%u - (C) Copyright 2005 Ullrich von Bassewitz\n",
256              VER_MAJOR, VER_MINOR, VER_PATCH);
257 }
258
259
260
261 static void OneOpcode (unsigned RemainingBytes)
262 /* Disassemble one opcode */
263 {
264     /* Get the opcode from the current address */
265     unsigned char OPC = GetCodeByte (PC);
266
267     /* Get the opcode description for the opcode byte */
268     const OpcDesc* D = &OpcTable[OPC];
269
270     /* Get the output style for the current PC */
271     attr_t Style = GetStyleAttr (PC);
272
273     /* If we have a label at this address, output the label and an attached
274      * comment, provided that we aren't in a skip area.
275      */
276     if (Style != atSkip && MustDefLabel (PC)) {
277         const char* Comment = GetComment (PC);
278         if (Comment) {
279             UserComment (Comment);
280         }
281         DefLabel (GetLabel (PC));
282     }
283
284     /* Check...
285      *   - ...if we have enough bytes remaining for the code at this address.
286      *   - ...if the current instruction is valid for the given CPU.
287      *   - ...if there is no label somewhere between the instruction bytes.
288      * If any of these conditions is false, switch to data mode.
289      */
290     if (Style == atDefault) {
291         if (D->Size > RemainingBytes) {
292             Style = atIllegal;
293             MarkAddr (PC, Style);
294         } else if (D->Flags & flIllegal) {
295            Style = atIllegal;
296             MarkAddr (PC, Style);
297         } else {
298             unsigned I;
299             for (I = 1; I < D->Size; ++I) {
300                 if (HaveLabel (PC+I)) {
301                     Style = atIllegal;
302                     MarkAddr (PC, Style);
303                     break;
304                 }
305             }
306         }
307     }
308
309     /* Disassemble the line */
310     switch (Style) {
311
312         case atDefault:
313             D->Handler (D);
314             PC += D->Size;
315             break;
316
317         case atCode:
318             /* Beware: If we don't have enough bytes left to disassemble the
319              * following insn, fall through to byte mode.
320              */
321             if (D->Size <= RemainingBytes) {
322                 D->Handler (D);
323                 PC += D->Size;
324                 break;
325             }
326             /* FALLTHROUGH */
327
328         case atByteTab:
329             ByteTable ();
330             break;
331
332         case atDByteTab:
333             DByteTable ();
334             break;
335
336         case atWordTab:
337             WordTable ();
338             break;
339
340         case atDWordTab:
341             DWordTable ();
342             break;
343
344         case atAddrTab:
345             AddrTable ();
346             break;
347
348         case atRtsTab:
349             RtsTable ();
350             break;
351
352         case atTextTab:
353             TextTable ();
354             break;
355
356         case atSkip:
357             ++PC;
358             break;
359
360         default:
361             DataByteLine (1);
362             ++PC;
363             break;
364
365     }
366 }
367
368
369
370 static void OnePass (void)
371 /* Make one pass through the code */
372 {
373     unsigned Count;
374
375     /* Disassemble until nothing left */
376     while ((Count = GetRemainingBytes()) > 0) {
377         OneOpcode (Count);
378     }
379 }
380
381
382
383 static void Disassemble (void)
384 /* Disassemble the code */
385 {
386     /* Pass 1 */
387     Pass = 1;
388     OnePass ();
389
390     Output ("---------------------------");
391     LineFeed ();
392
393     /* Pass 2 */
394     Pass = 2;
395     ResetCode ();
396     OutputSettings ();
397     DefOutOfRangeLabels ();
398     OnePass ();
399 }
400
401
402
403 int main (int argc, char* argv [])
404 /* Assembler main program */
405 {
406     /* Program long options */
407     static const LongOpt OptTab[] = {
408         { "--comments",         1,      OptComments             },
409         { "--cpu",              1,      OptCPU                  },
410         { "--debug-info",       0,      OptDebugInfo            },
411         { "--formfeeds",        0,      OptFormFeeds            },
412         { "--help",             0,      OptHelp                 },
413         { "--hexoffs",          0,      OptHexOffs              },
414         { "--info",             1,      OptInfo                 },
415         { "--label-break",      1,      OptLabelBreak           },
416         { "--pagelength",       1,      OptPageLength           },
417         { "--start-addr",       1,      OptStartAddr            },
418         { "--verbose",          0,      OptVerbose              },
419         { "--version",          0,      OptVersion              },
420     };
421
422     unsigned I;
423     time_t T;
424
425     /* Initialize the cmdline module */
426     InitCmdLine (&argc, &argv, "da65");
427
428     /* Check the parameters */
429     I = 1;
430     while (I < ArgCount) {
431
432         /* Get the argument */
433         const char* Arg = ArgVec[I];
434
435         /* Check for an option */
436         if (Arg [0] == '-') {
437             switch (Arg [1]) {
438
439                 case '-':
440                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
441                     break;
442
443                 case 'g':
444                     OptDebugInfo (Arg, 0);
445                     break;
446
447                 case 'h':
448                     OptHelp (Arg, 0);
449                     break;
450
451                 case 'i':
452                     OptInfo (Arg, GetArg (&I, 2));
453                     break;
454
455                 case 'o':
456                     OutFile = GetArg (&I, 2);
457                     break;
458
459                 case 'v':
460                     OptVerbose (Arg, 0);
461                     break;
462
463                 case 'S':
464                     OptStartAddr (Arg, GetArg (&I, 2));
465                     break;
466
467                 case 'V':
468                     OptVersion (Arg, 0);
469                     break;
470
471                 default:
472                     UnknownOption (Arg);
473                     break;
474
475             }
476         } else {
477             /* Filename. Check if we already had one */
478             if (InFile) {
479                 fprintf (stderr, "%s: Don't know what to do with `%s'\n",
480                          ProgName, Arg);
481                 exit (EXIT_FAILURE);
482             } else {
483                 InFile = Arg;
484             }
485         }
486
487         /* Next argument */
488         ++I;
489     }
490
491     /* Try to read the info file */
492     ReadInfoFile ();
493
494     /* Must have an input file */
495     if (InFile == 0) {
496         AbEnd ("No input file");
497     }
498
499     /* If no CPU given, use the default CPU */
500     if (CPU == CPU_UNKNOWN) {
501         CPU = CPU_6502;
502     }
503
504     /* Get the current time and convert it to string so it can be used in
505      * the output page headers.
506      */
507     T = time (0);
508     strftime (Now, sizeof (Now), "%Y-%m-%d %H:%M:%S", localtime (&T));
509
510     /* Load the input file */
511     LoadCode ();
512
513     /* Open the output file */
514     OpenOutput (OutFile);
515
516     /* Disassemble the code */
517     Disassemble ();
518
519     /* Close the output file */
520     CloseOutput ();
521
522     /* Done */
523     return EXIT_SUCCESS;
524 }
525
526
527