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
73static 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
111static 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
124static 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
152static 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
167static 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
182static 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
197static 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
212static 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
222static 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
231static 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
240static 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
250static 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
259static void OptInfo (const char* Opt attribute ((unused)), const char* Arg)
260/* Handle the --info option */
261{
262 InfoSetName (Arg);
263}
264
265
266
267static 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
282static 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
297static 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
309static void OptStartAddr (const char* Opt, const char* Arg)
310/* Set the default start address */
311{
312 StartAddr = CvtNumber (Opt, Arg);
313}
314
315
316
317static void OptSyncLines (const char* Opt attribute ((unused)),
318 const char* Arg attribute ((unused)))
319/* Handle the --sync-lines option */
320{
321 SyncLines = 1;
322}
323
324
325
326static 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
341static void OptVerbose (const char* Opt attribute ((unused)),
342 const char* Arg attribute ((unused)))
343/* Increase verbosity */
344{
345 ++Verbosity;
346}
347
348
349
350static 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
360static 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
501static 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
514static 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
534int 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}
680