1 /*****************************************************************************/
5 /* Main program for the da65 disassembler */
9 /* (C) 1998-2014, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
67 /*****************************************************************************/
69 /*****************************************************************************/
73 static void Usage (void)
74 /* Print usage information and exit */
76 printf ("Usage: %s [options] [inputfile]\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 " -V\t\t\tPrint the disassembler version\n"
88 " --argument-column n\tSpecify argument start column\n"
89 " --comment-column n\tSpecify comment start column\n"
90 " --comments n\t\tSet the comment level for the output\n"
91 " --cpu type\t\tSet cpu type\n"
92 " --debug-info\t\tAdd debug info to object file\n"
93 " --formfeeds\t\tAdd formfeeds to the output\n"
94 " --help\t\tHelp (this text)\n"
95 " --hexoffs\t\tUse hexadecimal label offsets\n"
96 " --info name\t\tSpecify an info file\n"
97 " --label-break n\tAdd newline if label exceeds length n\n"
98 " --mnemonic-column n\tSpecify mnemonic start column\n"
99 " --pagelength n\tSet the page length for the listing\n"
100 " --start-addr addr\tSet the start/load address\n"
101 " --text-column n\tSpecify text start column\n"
102 " --verbose\t\tIncrease verbosity\n"
103 " --version\t\tPrint the disassembler version\n",
109 static void RangeCheck (const char* Opt, unsigned long Val,
110 unsigned long Min, unsigned long Max)
111 /* Do a range check for the given option and abort if there's a range
115 if (Val < Min || Val > Max) {
116 Error ("Argument for %s outside valid range (%ld-%ld)", Opt, Min, Max);
122 static unsigned long CvtNumber (const char* Arg, const char* Number)
123 /* Convert a number from a string. Allow '$' and '0x' prefixes for hex
132 if (*Number == '$') {
134 Converted = sscanf (Number, "%lx%c", &Val, &BoundsCheck);
136 Converted = sscanf (Number, "%li%c", (long*)&Val, &BoundsCheck);
139 /* Check if we do really have a number */
140 if (Converted != 1) {
141 Error ("Invalid number given in argument: %s\n", Arg);
144 /* Return the result */
150 static void OptArgumentColumn (const char* Opt, const char* Arg)
151 /* Handle the --argument-column option */
153 /* Convert the argument to a number */
154 unsigned long Val = CvtNumber (Opt, Arg);
156 /* Check for a valid range */
157 RangeCheck (Opt, Val, MIN_ACOL, MAX_ACOL);
160 ACol = (unsigned char) Val;
165 static void OptBytesPerLine (const char* Opt, const char* Arg)
166 /* Handle the --bytes-per-line option */
168 /* Convert the argument to a number */
169 unsigned long Val = CvtNumber (Opt, Arg);
171 /* Check for a valid range */
172 RangeCheck (Opt, Val, MIN_BYTESPERLINE, MAX_BYTESPERLINE);
175 BytesPerLine = (unsigned char) Val;
180 static void OptCommentColumn (const char* Opt, const char* Arg)
181 /* Handle the --comment-column option */
183 /* Convert the argument to a number */
184 unsigned long Val = CvtNumber (Opt, Arg);
186 /* Check for a valid range */
187 RangeCheck (Opt, Val, MIN_CCOL, MAX_CCOL);
190 CCol = (unsigned char) Val;
195 static void OptComments (const char* Opt, const char* Arg)
196 /* Handle the --comments option */
198 /* Convert the argument to a number */
199 unsigned long Val = CvtNumber (Opt, Arg);
201 /* Check for a valid range */
202 RangeCheck (Opt, Val, MIN_COMMENTS, MAX_COMMENTS);
205 Comments = (unsigned char) Val;
210 static void OptCPU (const char* Opt attribute ((unused)), const char* Arg)
211 /* Handle the --cpu option */
213 /* Find the CPU from the given name */
220 static void OptDebugInfo (const char* Opt attribute ((unused)),
221 const char* Arg attribute ((unused)))
222 /* Add debug info to the object file */
229 static void OptFormFeeds (const char* Opt attribute ((unused)),
230 const char* Arg attribute ((unused)))
231 /* Add form feeds to the output */
238 static void OptHelp (const char* Opt attribute ((unused)),
239 const char* Arg attribute ((unused)))
240 /* Print usage information and exit */
248 static void OptHexOffs (const char* Opt attribute ((unused)),
249 const char* Arg attribute ((unused)))
250 /* Handle the --hexoffs option */
257 static void OptInfo (const char* Opt attribute ((unused)), const char* Arg)
258 /* Handle the --info option */
265 static void OptLabelBreak (const char* Opt, const char* Arg)
266 /* Handle the --label-break option */
268 /* Convert the argument to a number */
269 unsigned long Val = CvtNumber (Opt, Arg);
271 /* Check for a valid range */
272 RangeCheck (Opt, Val, MIN_LABELBREAK, MAX_LABELBREAK);
275 LBreak = (unsigned char) Val;
280 static void OptMnemonicColumn (const char* Opt, const char* Arg)
281 /* Handle the --mnemonic-column option */
283 /* Convert the argument to a number */
284 unsigned long Val = CvtNumber (Opt, Arg);
286 /* Check for a valid range */
287 RangeCheck (Opt, Val, MIN_MCOL, MAX_MCOL);
290 MCol = (unsigned char) Val;
295 static void OptPageLength (const char* Opt attribute ((unused)), const char* Arg)
296 /* Handle the --pagelength option */
298 int Len = atoi (Arg);
300 RangeCheck (Opt, Len, MIN_PAGE_LEN, MAX_PAGE_LEN);
307 static void OptStartAddr (const char* Opt, const char* Arg)
308 /* Set the default start address */
310 StartAddr = CvtNumber (Opt, Arg);
315 static void OptTextColumn (const char* Opt, const char* Arg)
316 /* Handle the --text-column option */
318 /* Convert the argument to a number */
319 unsigned long Val = CvtNumber (Opt, Arg);
321 /* Check for a valid range */
322 RangeCheck (Opt, Val, MIN_TCOL, MAX_TCOL);
325 TCol = (unsigned char) Val;
330 static void OptVerbose (const char* Opt attribute ((unused)),
331 const char* Arg attribute ((unused)))
332 /* Increase verbosity */
339 static void OptVersion (const char* Opt attribute ((unused)),
340 const char* Arg attribute ((unused)))
341 /* Print the disassembler version */
343 fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ());
349 static void OneOpcode (unsigned RemainingBytes)
350 /* Disassemble one opcode */
355 /* Get the opcode from the current address */
356 unsigned char OPC = GetCodeByte (PC);
358 /* Get the opcode description for the opcode byte */
359 const OpcDesc* D = &OpcTable[OPC];
361 /* Get the output style for the current PC */
362 attr_t Style = GetStyleAttr (PC);
364 /* If a segment begins here, then name that segment.
365 ** Note that the segment is named even if its code is being skipped,
366 ** because some of its later code might not be skipped.
368 if (IsSegmentStart (PC)) {
369 StartSegment (GetSegmentStartName (PC), GetSegmentAddrSize (PC));
372 /* If we have a label at this address, output the label and an attached
373 ** comment, provided that we aren't in a skip area.
375 if (Style != atSkip && MustDefLabel (PC)) {
376 const char* Comment = GetComment (PC);
378 UserComment (Comment);
380 DefLabel (GetLabelName (PC));
384 ** - ...if we have enough bytes remaining for the code at this address.
385 ** - ...if the current instruction is valid for the given CPU.
386 ** - ...if there is no label somewhere between the instruction bytes.
387 ** - ...if there is no segment change between the instruction bytes.
388 ** If any one of those conditions is false, switch to data mode.
390 if (Style == atDefault) {
391 if (D->Size > RemainingBytes) {
393 MarkAddr (PC, Style);
394 } else if (D->Flags & flIllegal) {
396 MarkAddr (PC, Style);
398 for (I = PC + D->Size; --I > PC; ) {
399 if (HaveLabel (I) || IsSegmentStart (I)) {
401 MarkAddr (PC, Style);
405 for (I = 0; I < D->Size - 1u; ++I) {
406 if (IsSegmentEnd (PC + I)) {
408 MarkAddr (PC, Style);
415 /* Disassemble the line */
424 /* Beware: If we don't have enough bytes left to disassemble the
425 ** following insn, fall through to byte mode.
427 if (D->Size <= RemainingBytes) {
428 /* Output labels within the next insn */
429 for (I = 1; I < D->Size; ++I) {
432 /* Output the insn */
477 /* Change back to the default CODE segment if
478 ** a named segment stops at the current address.
480 for (I = PC - OldPC; I > 0; --I) {
481 if (IsSegmentEnd (PC - I)) {
490 static void OnePass (void)
491 /* Make one pass through the code */
495 /* Disassemble until nothing left */
496 while ((Count = GetRemainingBytes()) > 0) {
503 static void Disassemble (void)
504 /* Disassemble the code */
510 Output ("---------------------------");
517 DefOutOfRangeLabels ();
523 int main (int argc, char* argv [])
524 /* Assembler main program */
526 /* Program long options */
527 static const LongOpt OptTab[] = {
528 { "--argument-column", 1, OptArgumentColumn },
529 { "--bytes-per-line", 1, OptBytesPerLine },
530 { "--comment-column", 1, OptCommentColumn },
531 { "--comments", 1, OptComments },
532 { "--cpu", 1, OptCPU },
533 { "--debug-info", 0, OptDebugInfo },
534 { "--formfeeds", 0, OptFormFeeds },
535 { "--help", 0, OptHelp },
536 { "--hexoffs", 0, OptHexOffs },
537 { "--info", 1, OptInfo },
538 { "--label-break", 1, OptLabelBreak },
539 { "--mnemonic-column", 1, OptMnemonicColumn },
540 { "--pagelength", 1, OptPageLength },
541 { "--start-addr", 1, OptStartAddr },
542 { "--text-column", 1, OptTextColumn },
543 { "--verbose", 0, OptVerbose },
544 { "--version", 0, OptVersion },
550 /* Initialize the cmdline module */
551 InitCmdLine (&argc, &argv, "da65");
553 /* Check the parameters */
555 while (I < ArgCount) {
557 /* Get the argument */
558 const char* Arg = ArgVec[I];
560 /* Check for an option */
561 if (Arg [0] == '-') {
565 LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
569 OptDebugInfo (Arg, 0);
577 OptInfo (Arg, GetArg (&I, 2));
581 OutFile = GetArg (&I, 2);
589 OptStartAddr (Arg, GetArg (&I, 2));
602 /* Filename. Check if we already had one */
604 fprintf (stderr, "%s: Don't know what to do with `%s'\n",
616 /* Try to read the info file */
619 /* Must have an input file */
621 AbEnd ("No input file");
624 /* Check the formatting options for reasonable values. Note: We will not
625 ** really check that they make sense, just that they aren't complete
629 AbEnd ("mnemonic-column value must be smaller than argument-column value");
632 AbEnd ("argument-column value must be smaller than comment-column value");
635 AbEnd ("comment-column value must be smaller than text-column value");
638 /* If no CPU given, use the default CPU */
639 if (CPU == CPU_UNKNOWN) {
643 /* Get the current time and convert it to string so it can be used in
644 ** the output page headers.
647 strftime (Now, sizeof (Now), "%Y-%m-%d %H:%M:%S", localtime (&T));
649 /* Load the input file */
652 /* Open the output file */
653 OpenOutput (OutFile);
655 /* Disassemble the code */
658 /* Close the output file */