From 4c81eacefebc6e53903abc80d211b1c1b0f18c7a Mon Sep 17 00:00:00 2001
From: Kugel Fuhr <98353208+kugelfuhr@users.noreply.github.com>
Date: Fri, 20 Jun 2025 13:00:12 +0200
Subject: [PATCH] Added -d/--debug and -m/--multi-pass switches to the
disassembler. The latter will make the disassembler run multiple preparation
passes to find all addresses where labels must be placed. Without -m some
label addresses are found in the final pass, where the disassembler cannot
make use of them.
---
doc/da65.sgml | 21 ++++++++++++++-
src/da65/global.c | 2 +-
src/da65/global.h | 4 ++-
src/da65/labels.c | 14 ++++++++++
src/da65/labels.h | 3 +++
src/da65/main.c | 68 ++++++++++++++++++++++++++++++++++++++++++++---
src/da65/output.c | 16 +++++------
7 files changed, 113 insertions(+), 15 deletions(-)
diff --git a/doc/da65.sgml b/doc/da65.sgml
index dd51a4764..314e552a9 100644
--- a/doc/da65.sgml
+++ b/doc/da65.sgml
@@ -46,14 +46,16 @@ The assembler accepts the following options:
---------------------------------------------------------------------------
Usage: da65 [options] [inputfile]
Short options:
+ -d Debug mode
-g Add debug info to object file
-h Help (this text)
-i name Specify an info file
+ -m Run multiple passes to resolve labels
-o name Name the output file
-v Increase verbosity
-F Add formfeeds to the output
- -s Accept line markers in the info file
-S addr Set the start/load address
+ -s Accept line markers in the info file
-V Print the disassembler version
Long options:
@@ -61,6 +63,7 @@ Long options:
--comment-column n Specify comment start column
--comments n Set the comment level for the output
--cpu type Set cpu type
+ --debug Debug mode
--debug-info Add debug info to object file
--formfeeds Add formfeeds to the output
--help Help (this text)
@@ -68,6 +71,7 @@ Long options:
--info name Specify an info file
--label-break n Add newline if label exceeds length n
--mnemonic-column n Specify mnemonic start column
+ --multi-pass Run multiple passes to resolve labels
--pagelength n Set the page length for the listing
--start-addr addr Set the start/load address
--sync-lines Accept line markers in the info file
@@ -123,6 +127,12 @@ Here is a description of all the command line options:
+ -d, --debug
+
+ Enables debug mode, something that should not be needed for mere
+ mortals:-)
+
+
-F, --formfeeds
@@ -179,6 +189,15 @@ Here is a description of all the command line options:
.
+ -m, --multi-pass
+
+ This option causes the disassembler to run multiple passes over the input
+ binary to find and create all necessary labels. Without this option the
+ disassembler may detect the necessity for a label in the final pass, when
+ output was already partially generated. It will output numerical addresses
+ or program counter relative expressions in this case.
+
+
--mnemonic-column n
diff --git a/src/da65/global.c b/src/da65/global.c
index 7e5cabf54..74f70b6fd 100644
--- a/src/da65/global.c
+++ b/src/da65/global.c
@@ -54,8 +54,8 @@ const char CfgExt[] = ".cfg"; /* Config file extension */
/* Flags and other command line stuff */
unsigned char DebugInfo = 0; /* Add debug info to the object file */
unsigned char FormFeeds = 0; /* Add form feeds to the output? */
+unsigned char MultiPass = 0; /* Run several passes to resolve labels */
unsigned char UseHexOffs = 0; /* Use hexadecimal label offsets */
-unsigned char PassCount = 2; /* How many passed do we do? */
signed char NewlineAfterJMP = -1; /* Add a newline after a JMP insn? */
signed char NewlineAfterRTS = -1; /* Add a newline after a RTS insn? */
unsigned char HaveStartAddr = 0; /* Flag for start address given */
diff --git a/src/da65/global.h b/src/da65/global.h
index 2e558d6a9..8e3fe90ee 100644
--- a/src/da65/global.h
+++ b/src/da65/global.h
@@ -59,8 +59,8 @@ extern const char CfgExt[]; /* Config file extension */
/* Flags and other command line stuff */
extern unsigned char DebugInfo; /* Add debug info to the object file */
extern unsigned char FormFeeds; /* Add form feeds to the output? */
+extern unsigned char MultiPass; /* Run several passes to resolve labels */
extern unsigned char UseHexOffs; /* Use hexadecimal label offsets */
-extern unsigned char PassCount; /* How many passed do we do? */
extern signed char NewlineAfterJMP;/* Add a newline after a JMP insn? */
extern signed char NewlineAfterRTS;/* Add a newline after a RTS insn? */
extern unsigned char HaveStartAddr; /* Flag for start address given */
@@ -70,6 +70,8 @@ extern long InputOffs; /* Offset into input file */
extern long InputSize; /* Number of bytes to read from input */
/* Stuff needed by many routines */
+#define PASS_PREP 1 /* Preparation pass */
+#define PASS_FINAL 2 /* Final pass generating output */
extern unsigned Pass; /* Disassembler pass */
extern char Now[128]; /* Current time as string */
diff --git a/src/da65/labels.c b/src/da65/labels.c
index 6f6bce3ed..a2c027705 100644
--- a/src/da65/labels.c
+++ b/src/da65/labels.c
@@ -74,6 +74,9 @@ struct Label {
#define LABEL_HASH_SIZE 4096u /* Must be power of two */
static Label* LabelTab[LABEL_HASH_SIZE];
+/* Total number of labels */
+static unsigned long LabelCount = 0;
+
/*****************************************************************************/
@@ -187,6 +190,9 @@ static void AddLabel (uint32_t Addr, attr_t Attr, const char* Name)
/* Remember the attribute */
MarkAddr (Addr, Attr);
+
+ /* Count labels */
+ ++LabelCount;
}
@@ -538,3 +544,11 @@ void DefOutOfRangeLabels (void)
/* Free allocated storage */
DoneCollection (&Labels);
}
+
+
+
+unsigned long GetLabelCount (void)
+/* Return the total number of labels defined so far */
+{
+ return LabelCount;
+}
diff --git a/src/da65/labels.h b/src/da65/labels.h
index 37f2daeab..923a4bd43 100644
--- a/src/da65/labels.h
+++ b/src/da65/labels.h
@@ -100,6 +100,9 @@ void ForwardLabel (unsigned Offs);
void DefOutOfRangeLabels (void);
/* Output any labels that are out of the loaded code range */
+unsigned long GetLabelCount (void);
+/* Return the total number of labels defined so far */
+
/* End of labels.h */
diff --git a/src/da65/main.c b/src/da65/main.c
index a00bf588a..03dbcaccf 100644
--- a/src/da65/main.c
+++ b/src/da65/main.c
@@ -42,8 +42,10 @@
/* common */
#include "abend.h"
+#include "check.h"
#include "cmdline.h"
#include "cpu.h"
+#include "debugflag.h"
#include "fname.h"
#include "print.h"
#include "version.h"
@@ -79,9 +81,11 @@ static void Usage (void)
{
printf ("Usage: %s [options] [inputfile]\n"
"Short options:\n"
+ " -d\t\t\tDebug mode\n"
" -g\t\t\tAdd debug info to object file\n"
" -h\t\t\tHelp (this text)\n"
" -i name\t\tSpecify an info file\n"
+ " -m\t\t\tRun multiple passes to resolve labels\n"
" -o name\t\tName the output file\n"
" -v\t\t\tIncrease verbosity\n"
" -F\t\t\tAdd formfeeds to the output\n"
@@ -94,6 +98,7 @@ static void Usage (void)
" --comment-column n\tSpecify comment start column\n"
" --comments n\t\tSet the comment level for the output\n"
" --cpu type\t\tSet cpu type\n"
+ " --debug\t\tDebug mode\n"
" --debug-info\t\tAdd debug info to object file\n"
" --formfeeds\t\tAdd formfeeds to the output\n"
" --help\t\tHelp (this text)\n"
@@ -101,6 +106,7 @@ static void Usage (void)
" --info name\t\tSpecify an info file\n"
" --label-break n\tAdd newline if label exceeds length n\n"
" --mnemonic-column n\tSpecify mnemonic start column\n"
+ " --multi-pass\t\tRun multiple passes to resolve labels\n"
" --pagelength n\tSet the page length for the listing\n"
" --start-addr addr\tSet the start/load address\n"
" --sync-lines\t\tAccept line markers in the info file\n"
@@ -223,6 +229,15 @@ static void OptCPU (const char* Opt attribute ((unused)), const char* Arg)
+static void OptDebug (const char* Opt attribute ((unused)),
+ const char* Arg attribute ((unused)))
+/* Disassembler debug mode */
+{
+ ++Debug;
+}
+
+
+
static void OptDebugInfo (const char* Opt attribute ((unused)),
const char* Arg attribute ((unused)))
/* Add debug info to the object file */
@@ -298,6 +313,15 @@ static void OptMnemonicColumn (const char* Opt, const char* Arg)
+static void OptMultiPass (const char* Opt attribute ((unused)),
+ const char* Arg attribute ((unused)))
+/* Handle the --multi-pass option */
+{
+ MultiPass = 1;
+}
+
+
+
static void OptPageLength (const char* Opt attribute ((unused)), const char* Arg)
/* Handle the --pagelength option */
{
@@ -590,15 +614,37 @@ static void OnePass (void)
static void Disassemble (void)
/* Disassemble the code */
{
- /* Pass 1 */
- Pass = 1;
+ /* Preparation pass */
+ Pass = PASS_PREP;
OnePass ();
+ /* If the --multi-pass option is given, repeat this pass until we have no
+ ** new labels.
+ */
+ if (MultiPass) {
+ unsigned long LabelCount = GetLabelCount ();
+ unsigned Passes = 1;
+ while (1) {
+ unsigned long NewLabelCount;
+ ResetCode ();
+ OnePass ();
+ CHECK(++Passes <= 4096); /* Safety measure */
+ NewLabelCount = GetLabelCount ();
+ if (NewLabelCount <= LabelCount) {
+ break;
+ }
+ LabelCount = NewLabelCount;
+ }
+ if (Debug) {
+ printf ("Run %u preparation passes to resolve labels\n", Passes);
+ }
+ }
+
Output ("---------------------------");
LineFeed ();
- /* Pass 2 */
- Pass = 2;
+ /* Final pass */
+ Pass = PASS_FINAL;
ResetCode ();
OutputSettings ();
DefOutOfRangeLabels ();
@@ -617,6 +663,7 @@ int main (int argc, char* argv [])
{ "--comment-column", 1, OptCommentColumn },
{ "--comments", 1, OptComments },
{ "--cpu", 1, OptCPU },
+ { "--debug", 0, OptDebug },
{ "--debug-info", 0, OptDebugInfo },
{ "--formfeeds", 0, OptFormFeeds },
{ "--help", 0, OptHelp },
@@ -624,6 +671,7 @@ int main (int argc, char* argv [])
{ "--info", 1, OptInfo },
{ "--label-break", 1, OptLabelBreak },
{ "--mnemonic-column", 1, OptMnemonicColumn },
+ { "--multi-pass", 0, OptMultiPass },
{ "--pagelength", 1, OptPageLength },
{ "--start-addr", 1, OptStartAddr },
{ "--sync-lines", 0, OptSyncLines },
@@ -653,6 +701,14 @@ int main (int argc, char* argv [])
LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
break;
+ case 'd':
+ if (Arg[2] == '\0') {
+ OptDebug (Arg, 0);
+ } else {
+ UnknownOption (Arg);
+ }
+ break;
+
case 'g':
OptDebugInfo (Arg, 0);
break;
@@ -665,6 +721,10 @@ int main (int argc, char* argv [])
OptInfo (Arg, GetArg (&I, 2));
break;
+ case 'm':
+ OptMultiPass (Arg, 0);
+ break;
+
case 'o':
OutFile = GetArg (&I, 2);
break;
diff --git a/src/da65/output.c b/src/da65/output.c
index bc5f7d5c2..13cb3e127 100644
--- a/src/da65/output.c
+++ b/src/da65/output.c
@@ -124,7 +124,7 @@ void CloseOutput (void)
void Output (const char* Format, ...)
/* Write to the output file */
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
va_list ap;
va_start (ap, Format);
Col += vfprintf (F, Format, ap);
@@ -137,7 +137,7 @@ void Output (const char* Format, ...)
void Indent (unsigned N)
/* Make sure the current line column is at position N (zero based) */
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
while (Col < N) {
fputc (' ', F);
++Col;
@@ -150,7 +150,7 @@ void Indent (unsigned N)
void LineFeed (void)
/* Add a linefeed to the output file */
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
fputc ('\n', F);
if (PageLength > 0 && ++Line >= PageLength) {
if (FormFeeds) {
@@ -185,7 +185,7 @@ void DefForward (const char* Name, const char* Comment, unsigned Offs)
** current PC.
*/
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
/* Flush existing output if necessary */
if (Col > 1) {
LineFeed ();
@@ -212,7 +212,7 @@ void DefForward (const char* Name, const char* Comment, unsigned Offs)
void DefConst (const char* Name, const char* Comment, uint32_t Addr)
/* Define an address constant */
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
Output ("%s", Name);
Indent (ACol);
Output (":= $%04" PRIX32, Addr);
@@ -313,7 +313,7 @@ void DataDWordLine (uint32_t ByteCount)
void SeparatorLine (void)
/* Print a separator line */
{
- if (Pass == PassCount && Comments >= 1) {
+ if (Pass == PASS_FINAL && Comments >= 1) {
Output ("; ----------------------------------------------------------------------------");
LineFeed ();
}
@@ -324,7 +324,7 @@ void SeparatorLine (void)
void StartSegment (const char* Name, unsigned AddrSize)
/* Start a segment */
{
- if (Pass == PassCount) {
+ if (Pass == PASS_FINAL) {
LineFeed ();
Output (".segment");
Indent (ACol);
@@ -368,7 +368,7 @@ void LineComment (unsigned PC, unsigned Count)
{
unsigned I;
- if (Pass == PassCount && Comments >= 2) {
+ if (Pass == PASS_FINAL && Comments >= 2) {
Indent (CCol);
Output ("; %04X", PC);
if (Comments >= 3) {