OXYGENHUB
◆ main enhanced-x16-emulator / src / dbg_info.c
75.4 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
1
#include "dbg_info.h"
2
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <string.h>
6
#include <ctype.h>
7
#include "dirent_win32.h" // portable opendir/readdir (native <dirent.h> elsewhere)
8
9
#ifdef _WIN32
10
#define strcasecmp _stricmp
11
#define strncasecmp _strnicmp
12
#else
13
// The project builds as strict C11, under which glibc leaves strcasecmp out of
14
// <string.h>; it lives here.
15
#include <strings.h>
16
#endif
17
18
/* cc65 `line` record types. The field is omitted entirely when it is zero,
19
 * which is why an assembly-only .dbg appears to have no type field at all. */
20
#define DBG_LI_TYPE_ASM   0
21
#define DBG_LI_TYPE_EXT   1  /* high-level (C) source line */
22
#define DBG_LI_TYPE_MACRO 2
23
24
/* ------------------------------------------------------------------ */
25
/*  Raw parsed records                                                 */
26
/* ------------------------------------------------------------------ */
27
28
typedef struct {
29
	int    id;
30
	char  *name;
31
	int    owner;     /* which .dbg declared it; see dbg_owner_intern() */
32
	bool   hidden;    /* a cc65-generated intermediate; see refresh_line_type_state() */
33
} dbg_file_t;
34
35
typedef struct {
36
	int      id;
37
	uint32_t start;   /* base address */
38
	uint32_t size;
39
	char    *name;    /* segment name, e.g. "BANKCODE" */
40
	int      bank;    /* X16 RAM bank this segment lives in, -1 = unknown */
41
	int      owner;   /* which .dbg declared it; see dbg_owner_intern() */
42
	bool     bank_from_equ; /* bank was inferred from an equate, not observed */
43
} dbg_seg_t;
44
45
typedef struct {
46
	int      id;
47
	int      seg;     /* segment id */
48
	uint32_t start;   /* offset within segment */
49
	uint32_t size;
50
} dbg_span_t;
51
52
typedef struct {
53
	int  id;
54
	int  file;        /* file id */
55
	int  line;        /* source line number */
56
	int  type;        /* DBG_LI_TYPE_*; absent in the record means ASM */
57
	int *spans;       /* array of span ids */
58
	int  span_count;
59
} dbg_line_t;
60
61
/* ------------------------------------------------------------------ */
62
/*  Resolved address map entry (built after parsing)                   */
63
/* ------------------------------------------------------------------ */
64
65
typedef struct {
66
	dbg_addr_t addr;
67
	dbg_addr_t end;     /* last address covered by this line's span (inclusive) */
68
	int      file_id;
69
	int      line_num;
70
	int      type;    /* DBG_LI_TYPE_* of the line record this came from */
71
	int      seg_id;  /* owning segment — disambiguates banked ($A000-$BFFF) code */
72
} addr_map_entry_t;
73
74
/* ------------------------------------------------------------------ */
75
/*  Module state                                                       */
76
/* ------------------------------------------------------------------ */
77
78
static dbg_file_t      *files;
79
static int              file_count;
80
static int              file_cap;
81
82
static dbg_seg_t       *segs;
83
static int              seg_count;
84
static int              seg_cap;
85
86
static dbg_span_t      *spans;
87
static int              span_count;
88
static int              span_cap;
89
90
static dbg_line_t      *lines;
91
static int              line_count;
92
static int              line_cap;
93
94
/* cc65 `sym` records: name → address, for address-typed labels (type=lab).
95
 * Kept sorted by address after each load for fast addr→label lookup. */
96
typedef struct {
97
	char    *name;
98
	dbg_addr_t addr;
99
} dbg_sym_t;
100
101
static dbg_sym_t       *syms;
102
static int              sym_count;
103
static int              sym_cap;
104
105
/* `RAM_BANK_x` / `x_BANK` equates (type=equ, small value) harvested while
106
 * parsing. Used to associate banked segments ($A000-$BFFF) with a RAM bank —
107
 * see seed_banks_from_equates(). */
108
typedef struct {
109
	char *name;
110
	int   bank;
111
	int   owner;      /* which .dbg declared it; see dbg_owner_intern() */
112
	uint64_t gen;     /* which parse of that .dbg produced it */
113
} dbg_bank_equ_t;
114
115
static dbg_bank_equ_t  *bank_equs;
116
static int              bank_equ_count;
117
static int              bank_equ_cap;
118
119
/* cc65 `equ` records: name → value. These are constants rather than program
120
 * addresses (KERNAL entry points such as JOYGET, hardware register names,
121
 * zero-page variable addresses, plain numeric constants), so they are kept out
122
 * of `syms` to leave that a clean address→name map for the disassembler. They
123
 * are still the thing a user hovers most often in source, so keep them here and
124
 * consult them for name→value lookups. */
125
typedef struct {
126
	char    *name;
127
	dbg_addr_t val;
128
	int      owner;   /* which .dbg declared it; see dbg_owner_intern() */
129
	uint64_t gen;     /* which parse of that .dbg produced it */
130
} dbg_equ_t;
131
132
static dbg_equ_t       *equs;
133
static int              equ_count;
134
static int              equ_cap;
135
136
static addr_map_entry_t *addr_map;
137
static int               addr_map_count;
138
static int               addr_map_cap;
139
140
static bool              loaded;
141
142
/* True when any loaded module carries cc65 high-level (C) line info. Every
143
 * behaviour that treats a .dbg as a C program's is gated on this, so an
144
 * assembly-only project takes exactly the code path it always did. Derived
145
 * from lines[]; see refresh_line_type_state(). */
146
static bool              has_high_level_lines;
147
148
// Directory of the most recently loaded .dbg file (for source discovery).
149
static char              dbg_dir[1024];
150
151
// ID offsets for merging multiple .dbg files without ID collisions
152
static int              id_base_file;
153
static int              id_base_seg;
154
static int              id_base_span;
155
static int              id_base_line;
156
157
// Highest ID handed out so far, plus one. IDs must never be reused: unloading a
158
// module compacts segs[]/spans[] but the survivors keep their IDs, so basing
159
// the next module's offset on the array count would hand out IDs that collide
160
// with them.
161
static int              next_id_file;
162
static int              next_id_seg;
163
static int              next_id_span;
164
static int              next_id_line;
165
166
static void note_id(int *high_water, int id)
167
{
168
	if (id >= *high_water) {
169
		*high_water = id + 1;
170
	}
171
}
172
173
/* ------------------------------------------------------------------ */
174
/*  Helpers                                                            */
175
/* ------------------------------------------------------------------ */
176
177
static void seed_banks_from_equates(void); /* defined with the bank helpers below */
178
179
#define INIT_CAP 64
180
181
// Grow a dynamic array in place. Returns false and leaves the array untouched
182
// if the allocation fails, so callers can decline to append rather than write
183
// through a NULL that realloc left behind.
184
static bool grow_array(void **arr, int *cap, size_t elem_size)
185
{
186
	int new_cap = *cap ? *cap * 2 : INIT_CAP;
187
	void *grown = realloc(*arr, (size_t)new_cap * elem_size);
188
	if (!grown) {
189
		return false;
190
	}
191
	*arr = grown;
192
	*cap = new_cap;
193
	return true;
194
}
195
196
#define GROW_ARRAY(arr, count, cap, type) \
197
    ((count) < (cap) || grow_array((void **)&(arr), &(cap), sizeof(type)))
198
199
/* Skip leading whitespace / tabs. */
200
static const char *skip_ws(const char *p)
201
{
202
	while (*p && (*p == ' ' || *p == '\t'))
203
		p++;
204
	return p;
205
}
206
207
/* Extract the value string for a key inside a key=value list.
208
 * `p` points just past the record-type keyword.
209
 * If the key is found, *out receives a pointer to the start of the value
210
 * (inside the original buffer) and the function returns the length.
211
 * For quoted values the quotes are stripped.
212
 * Returns -1 if the key is not found. */
213
static int find_value(const char *p, const char *key, const char **out)
214
{
215
	size_t klen = strlen(key);
216
217
	while (*p) {
218
		p = skip_ws(p);
219
		if (*p == '\0')
220
			break;
221
222
		/* Match key name */
223
		if (strncmp(p, key, klen) == 0 && p[klen] == '=') {
224
			const char *v = p + klen + 1;
225
			if (*v == '"') {
226
				/* Quoted string — find closing quote */
227
				v++;
228
				const char *end = strchr(v, '"');
229
				if (!end)
230
					return -1;
231
				*out = v;
232
				return (int)(end - v);
233
			} else {
234
				const char *end = v;
235
				while (*end && *end != ',' && *end != '\n' && *end != '\r')
236
					end++;
237
				*out = v;
238
				return (int)(end - v);
239
			}
240
		}
241
242
		/* Advance to next key=value pair */
243
		while (*p && *p != ',')
244
			p++;
245
		if (*p == ',')
246
			p++;
247
	}
248
	return -1;
249
}
250
251
/* Parse an integer value for a given key.  Returns -1 if not found. */
252
static long parse_int_key(const char *p, const char *key)
253
{
254
	const char *v;
255
	int len = find_value(p, key, &v);
256
	if (len <= 0)
257
		return -1;
258
259
	char buf[32];
260
	if (len >= (int)sizeof(buf))
261
		len = (int)sizeof(buf) - 1;
262
	memcpy(buf, v, (size_t)len);
263
	buf[len] = '\0';
264
265
	if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X'))
266
		return (long)strtoul(buf, NULL, 16);
267
	return strtol(buf, NULL, 10);
268
}
269
270
/* Parse a quoted-string value for a given key.  Caller must free(). */
271
static char *parse_str_key(const char *p, const char *key)
272
{
273
	const char *v;
274
	int len = find_value(p, key, &v);
275
	if (len < 0)
276
		return NULL;
277
	char *s = (char *)malloc((size_t)len + 1);
278
	if (!s)
279
		return NULL;
280
	memcpy(s, v, (size_t)len);
281
	s[len] = '\0';
282
	return s;
283
}
284
285
/* Parse a span list like "9" or "9+10+11".
286
 * Allocates and returns an int array; sets *out_count. */
287
static int *parse_span_list(const char *p, int *out_count)
288
{
289
	const char *v;
290
	int len = find_value(p, "span", &v);
291
	if (len <= 0) {
292
		*out_count = 0;
293
		return NULL;
294
	}
295
296
	/* Count '+' separators to size the array */
297
	int cap = 1;
298
	for (int i = 0; i < len; i++)
299
		if (v[i] == '+')
300
			cap++;
301
302
	int *arr = (int *)malloc((size_t)cap * sizeof(int));
303
	if (!arr) {
304
		*out_count = 0;
305
		return NULL;
306
	}
307
308
	int n = 0;
309
	const char *end = v + len;
310
	while (v < end && n < cap) {
311
		arr[n++] = (int)strtol(v, NULL, 10);
312
		/* Advance past the number and the '+' */
313
		while (v < end && *v != '+')
314
			v++;
315
		if (v < end)
316
			v++;  /* skip '+' */
317
	}
318
319
	*out_count = n;
320
	return arr;
321
}
322
323
/* Return a pointer to the basename portion of a path. */
324
static const char *basename_ptr(const char *path)
325
{
326
	const char *p = path;
327
	const char *last = path;
328
	while (*p) {
329
		if (*p == '/' || *p == '\\')
330
			last = p + 1;
331
		p++;
332
	}
333
	return last;
334
}
335
336
/* ------------------------------------------------------------------ */
337
/*  Record parsers                                                     */
338
/* ------------------------------------------------------------------ */
339
340
/* Records are deduplicated so that reloading a module does not grow the tables
341
 * without end. That is only correct WITHIN a module: two linked modules may
342
 * legitimately both compile a file called "main.c", and may both define an
343
 * equate called LIMIT with different values. Deduplicating those against each
344
 * other would make one module's debug info describe the other's code. So every
345
 * deduplicated record remembers which .dbg declared it, and a reused record has
346
 * to come from the same one.
347
 *
348
 * The owner is the .dbg path, interned. Comparison is case-insensitive on
349
 * Windows, where the same file reached by two spellings is still one file. */
350
static char **owners      = NULL;
351
static int    owner_count = 0;
352
static int    owner_cap   = 0;
353
static int    cur_owner   = -1;
354
/* Bumped per parse, so records written by the parse in progress can be told
355
 * from ones the same module left behind last time. */
356
static uint64_t cur_gen   = 0;
357
358
static int dbg_owner_intern(const char *path)
359
{
360
	if (!path)
361
		return -1;
362
	for (int i = 0; i < owner_count; i++) {
363
#ifdef _WIN32
364
		if (owners[i] && _stricmp(owners[i], path) == 0)
365
#else
366
		if (owners[i] && strcmp(owners[i], path) == 0)
367
#endif
368
			return i;
369
	}
370
	if (!GROW_ARRAY(owners, owner_count, owner_cap, char *))
371
		return -1;
372
	size_t n = strlen(path);
373
	char  *copy = (char *)malloc(n + 1);
374
	if (!copy)
375
		return -1;
376
	memcpy(copy, path, n + 1);
377
	owners[owner_count] = copy;
378
	return owner_count++;
379
}
380
381
/* Drop the equates a module declared in an earlier generation. Called once the
382
 * file has been read, so a file that turned out to be unreadable or unparseable
383
 * leaves the previous generation intact. Replacing them one by one as they are
384
 * parsed is not enough on its own: an equate the rebuilt file no longer
385
 * declares would never be visited, and would go on seeding banks and answering
386
 * lookups on behalf of a module that has stopped defining it. */
387
static void drop_stale_equates(int owner, uint64_t gen)
388
{
389
	if (owner < 0)
390
		return;
391
	int w = 0;
392
	for (int i = 0; i < equ_count; i++) {
393
		if (equs[i].owner == owner && equs[i].gen != gen) {
394
			free(equs[i].name);
395
			continue;
396
		}
397
		if (w != i)
398
			equs[w] = equs[i];
399
		w++;
400
	}
401
	equ_count = w;
402
403
	w = 0;
404
	for (int i = 0; i < bank_equ_count; i++) {
405
		if (bank_equs[i].owner == owner && bank_equs[i].gen != gen) {
406
			free(bank_equs[i].name);
407
			continue;
408
		}
409
		if (w != i)
410
			bank_equs[w] = bank_equs[i];
411
		w++;
412
	}
413
	bank_equ_count = w;
414
}
415
416
/* Maps this load's `file` IDs onto the record that ends up describing them.
417
 * Needed because a file already known from an earlier load is reused rather than
418
 * appended, so its ID is not this load's ID plus the usual base. Reset per load.
419
 */
420
static int *file_alias      = NULL;
421
static int  file_alias_count = 0;
422
static int  file_alias_cap   = 0;
423
424
static void file_alias_set(long incoming, int resolved)
425
{
426
	if (incoming < 0 || incoming > 0xFFFFF)
427
		return;                     /* absurd ID; the record is unusable anyway */
428
	while (file_alias_count <= (int)incoming) {
429
		if (!GROW_ARRAY(file_alias, file_alias_count, file_alias_cap, int))
430
			return;
431
		file_alias[file_alias_count++] = -1;
432
	}
433
	file_alias[incoming] = resolved;
434
}
435
436
static int file_alias_get(long incoming)
437
{
438
	if (incoming >= 0 && incoming < file_alias_count && file_alias[incoming] >= 0)
439
		return file_alias[incoming];
440
	return (int)incoming + id_base_file;    /* no alias recorded; the usual rule */
441
}
442
443
static void parse_file_record(const char *p)
444
{
445
	long id = parse_int_key(p, "id");
446
	char *name = parse_str_key(p, "name");
447
	if (id < 0 || !name) {
448
		free(name);
449
		return;
450
	}
451
452
	/* Reuse a file this same .dbg already gave us. Unloading a range
453
	 * deliberately leaves file records alone -- callers keep the path pointers
454
	 * we handed them -- so appending here would add a copy of every file on
455
	 * every reload, and an overlay swapped in and out repeatedly would grow the
456
	 * table without end. Restricted to one owner because two modules that both
457
	 * build a "main.c" have two different main.c's. Consumers currently resolve
458
	 * a file by its name string, so merging them is not yet observable beyond
459
	 * the record count; keeping them apart is what lets the ownership work
460
	 * noted at dbg_info_load_for_file() tell them apart later. */
461
	for (int i = 0; i < file_count; i++) {
462
		if (files[i].owner == cur_owner && files[i].name &&
463
		    strcmp(files[i].name, name) == 0) {
464
			file_alias_set(id, files[i].id);
465
			free(name);
466
			return;
467
		}
468
	}
469
470
	if (!GROW_ARRAY(files, file_count, file_cap, dbg_file_t)) { free(name); return; }
471
	files[file_count].id    = (int)id + id_base_file;
472
	note_id(&next_id_file, files[file_count].id);
473
	files[file_count].name  = name;
474
	files[file_count].owner = cur_owner;
475
	files[file_count].hidden = false;   /* decided once the whole load is in */
476
	file_alias_set(id, files[file_count].id);
477
	file_count++;
478
}
479
480
static void parse_seg_record(const char *p)
481
{
482
	long id    = parse_int_key(p, "id");
483
	long start = parse_int_key(p, "start");
484
	long size  = parse_int_key(p, "size");
485
	if (id < 0 || start < 0)
486
		return;
487
	if (size < 0)
488
		size = 0;
489
490
	if (!GROW_ARRAY(segs, seg_count, seg_cap, dbg_seg_t)) return;
491
	segs[seg_count].id    = (int)id + id_base_seg;
492
	note_id(&next_id_seg, segs[seg_count].id);
493
	segs[seg_count].start = (uint32_t)start;
494
	segs[seg_count].size  = (uint32_t)size;
495
	segs[seg_count].name  = parse_str_key(p, "name"); /* may be NULL */
496
	segs[seg_count].bank  = -1;                       /* learned later */
497
	segs[seg_count].owner = cur_owner;
498
	segs[seg_count].bank_from_equ = false;
499
	seg_count++;
500
}
501
502
static void parse_span_record(const char *p)
503
{
504
	long id    = parse_int_key(p, "id");
505
	long seg   = parse_int_key(p, "seg");
506
	long start = parse_int_key(p, "start");
507
	long size  = parse_int_key(p, "size");
508
	if (id < 0 || seg < 0 || start < 0)
509
		return;
510
	if (size < 0)
511
		size = 0;
512
513
	if (!GROW_ARRAY(spans, span_count, span_cap, dbg_span_t)) return;
514
	spans[span_count].id    = (int)id + id_base_span;
515
	note_id(&next_id_span, spans[span_count].id);
516
	spans[span_count].seg   = (int)seg + id_base_seg;
517
	spans[span_count].start = (uint32_t)start;
518
	spans[span_count].size  = (uint32_t)size;
519
	span_count++;
520
}
521
522
static void parse_line_record(const char *p)
523
{
524
	long id   = parse_int_key(p, "id");
525
	long file = parse_int_key(p, "file");
526
	long line = parse_int_key(p, "line");
527
	if (id < 0 || file < 0 || line < 0)
528
		return;
529
530
	/* cc65 omits `type` when it is LI_TYPE_ASM, so an absent field means 0 --
531
	 * not "unknown". Reading it as unknown would put every assembly record in
532
	 * a category of its own and break the ranking that depends on it. */
533
	long type = parse_int_key(p, "type");
534
	if (type < 0)
535
		type = DBG_LI_TYPE_ASM;
536
537
	int  span_n = 0;
538
	int *span_ids = parse_span_list(p, &span_n);
539
	if (span_n == 0) {
540
		free(span_ids);
541
		return;
542
	}
543
544
	// Offset span IDs for multi-file merge
545
	for (int i = 0; i < span_n; i++)
546
		span_ids[i] += id_base_span;
547
548
	if (!GROW_ARRAY(lines, line_count, line_cap, dbg_line_t)) { free(span_ids); return; }
549
	lines[line_count].id         = (int)id + id_base_line;
550
	note_id(&next_id_line, lines[line_count].id);
551
	lines[line_count].file       = (int)file;   /* raw; resolved after the load */
552
	lines[line_count].line       = (int)line;
553
	lines[line_count].type       = (int)type;
554
	lines[line_count].spans      = span_ids;
555
	lines[line_count].span_count = span_n;
556
	line_count++;
557
}
558
559
/* Parse a cc65 `sym` record. We keep address-typed labels (type=lab, and
560
 * records with no explicit type) that carry a 16-bit `val` — i.e. named program
561
 * addresses. Equates/imports/constants (type=equ/imp/…) are skipped so the
562
 * label map stays a clean address→name mapping for the disassembler. */
563
/* Remove a name this parse already filed in the ordinary equate table. Used
564
 * when the same name turns up again with a value that belongs in the other
565
 * table, so that the later definition is the only one left. */
566
static void forget_current_equate(dbg_equ_t *arr, int *count, const char *name)
567
{
568
	int w = 0;
569
	for (int i = 0; i < *count; i++) {
570
		if (arr[i].owner == cur_owner && arr[i].gen == cur_gen && arr[i].name &&
571
		    !strcasecmp(arr[i].name, name)) {
572
			free(arr[i].name);
573
			continue;
574
		}
575
		if (w != i)
576
			arr[w] = arr[i];
577
		w++;
578
	}
579
	*count = w;
580
}
581
582
static void forget_current_bank_equate(const char *name)
583
{
584
	int w = 0;
585
	for (int i = 0; i < bank_equ_count; i++) {
586
		if (bank_equs[i].owner == cur_owner && bank_equs[i].gen == cur_gen &&
587
		    bank_equs[i].name && !strcasecmp(bank_equs[i].name, name)) {
588
			free(bank_equs[i].name);
589
			continue;
590
		}
591
		if (w != i)
592
			bank_equs[w] = bank_equs[i];
593
		w++;
594
	}
595
	bank_equ_count = w;
596
}
597
598
/* Returns true if the record was structurally complete -- it had a name, and
599
 * either a value or a good reason not to -- whether or not we ended up keeping
600
 * it. The caller counts those to tell a file that was written to the end from
601
 * one still being written; it is not a measure of how many records we stored.
602
 * Conflating the two is what made whole files be refused over records that were
603
 * perfectly well-formed and simply not ours to keep. */
604
static bool parse_sym_record(const char *p)
605
{
606
	char *name = parse_str_key(p, "name");
607
	if (!name)
608
		return false;
609
610
	const char *tv;
611
	int tlen = find_value(p, "type", &tv);
612
613
	/* An import names a symbol that another module defines, so it carries no
614
	 * `val` of its own -- but it is a complete record, not a truncated one.
615
	 * Counting it as incomplete made the caller reject the whole file: ld65
616
	 * states every `sym` record in its `info` count, imports included, so any
617
	 * program that links against anything at all -- and every cc65 C program,
618
	 * which imports the C runtime -- declared more symbols than it appeared to
619
	 * deliver and was refused. */
620
	if (tlen == 3 && strncmp(tv, "imp", 3) == 0) {
621
		free(name);
622
		return true;
623
	}
624
625
	/* Absent `val` is the signal of a half-written record; a `val` we cannot
626
	 * use is not. Telling those apart matters because cc65 emits constants
627
	 * that are not addresses at all: C's EOF is -1, written as 0xFFFFFFFF, so
628
	 * every program that includes <stdio.h> carries one, and treating it as
629
	 * truncation refused the entire file over it. */
630
	const char *vv;
631
	if (find_value(p, "val", &vv) <= 0) {
632
		free(name);
633
		return false;
634
	}
635
636
	long val = parse_int_key(p, "val");
637
	if (val < 0 || val > 0xFFFFFF) {
638
		/* Complete, but outside the 24-bit space the label and equate tables
639
		 * describe, so it is counted and then dropped. (`long` is 32-bit on
640
		 * Windows and 64-bit elsewhere, so 0xFFFFFFFF arrives here as either -1
641
		 * or 4294967295; both are caught.) */
642
		free(name);
643
		return true;
644
	}
645
646
	/* Keep only labels. If a `type` field is present it must be "lab"; records
647
	 * without a type field are accepted (older/leaner emitters). */
648
	if (tlen > 0 && !(tlen == 3 && strncmp(tv, "lab", 3) == 0)) {
649
		/* Not a label. Equates that look like a RAM-bank constant
650
		 * (RAM_BANK_x / BANK_x / x_BANK with a 0..255 value) are kept aside to
651
		 * associate banked segments with their RAM bank. */
652
		if (tlen == 3 && strncmp(tv, "equ", 3) == 0 && val >= 0 && val <= 255) {
653
			size_t l = strlen(name);
654
			bool looks_bank = !strncmp(name, "RAM_BANK", 8) ||
655
			                  !strncmp(name, "BANK_", 5) ||
656
			                  (l > 5 && !strcmp(name + l - 5, "_BANK"));
657
			if (looks_bank) {
658
				/* Same-owner only. Pooling these lets a second module's value
659
				 * overwrite the first's, and since equate-derived banks are now
660
				 * re-derived on every load, the first module's own segments
661
				 * would then be re-seeded from a value it never declared. */
662
				for (int i = 0; i < bank_equ_count; i++) {
663
					if (bank_equs[i].owner == cur_owner &&
664
					    bank_equs[i].gen == cur_gen &&
665
					    bank_equs[i].name &&
666
					    !strcasecmp(bank_equs[i].name, name)) {
667
						free(bank_equs[i].name);
668
						bank_equs[i].name = name;   /* takes ownership */
669
						bank_equs[i].bank = (int)val;
670
						return true;
671
					}
672
				}
673
				/* The same name can land in either table depending on its
674
				 * value -- RAM_BANK_CODE=3 is a bank constant, RAM_BANK_CODE
675
				 * =$1234 is not -- so a redefinition can cross tables. Drop any
676
				 * twin on the other side, or the superseded one goes on
677
				 * answering for whichever question it was filed under. */
678
				forget_current_equate(equs, &equ_count, name);
679
				if (!GROW_ARRAY(bank_equs, bank_equ_count, bank_equ_cap, dbg_bank_equ_t)) { free(name); return true; }
680
				bank_equs[bank_equ_count].name  = name;   /* takes ownership */
681
				bank_equs[bank_equ_count].bank  = (int)val;
682
				bank_equs[bank_equ_count].owner = cur_owner;
683
				bank_equs[bank_equ_count].gen   = cur_gen;
684
				bank_equ_count++;
685
				return true;
686
			}
687
		}
688
		/* Every other equate is still a name the user can hover in source
689
		 * (KERNAL vectors, hardware registers, constants), so record it for
690
		 * name→value lookups without polluting the label map. */
691
		if (tlen == 3 && strncmp(tv, "equ", 3) == 0) {
692
			/* Replace rather than append, for the same reason as bank equates:
693
			 * unloading a range never prunes these, and the lookup returns the
694
			 * first match, so a stale value would win forever. Same-owner only:
695
			 * two modules may each define LIMIT with different values, and
696
			 * replacing across them would destroy one of the two outright,
697
			 * since nothing restores it when the other module unloads. */
698
			for (int i = 0; i < equ_count; i++) {
699
				if (equs[i].owner == cur_owner && equs[i].gen == cur_gen &&
700
				    equs[i].name &&
701
				    !strcasecmp(equs[i].name, name)) {
702
					free(equs[i].name);
703
					equs[i].name = name;   /* takes ownership */
704
					equs[i].val  = (dbg_addr_t)val;
705
					return true;
706
				}
707
			}
708
			forget_current_bank_equate(name);
709
			if (!GROW_ARRAY(equs, equ_count, equ_cap, dbg_equ_t)) { free(name); return true; }
710
			equs[equ_count].name  = name;   /* takes ownership */
711
			equs[equ_count].val   = (dbg_addr_t)val;
712
			equs[equ_count].owner = cur_owner;
713
			equs[equ_count].gen   = cur_gen;
714
			equ_count++;
715
			return true;
716
		}
717
		free(name);
718
		return true;
719
	}
720
721
	if (!GROW_ARRAY(syms, sym_count, sym_cap, dbg_sym_t)) { free(name); return true; }
722
	syms[sym_count].name = name;
723
	syms[sym_count].addr = (dbg_addr_t)val;
724
	sym_count++;
725
	return true;
726
}
727
728
/* ------------------------------------------------------------------ */
729
/*  Segment / span / file lookups by id.                                */
730
/* ------------------------------------------------------------------ */
731
732
/* Records are normally appended with increasing IDs, and compaction preserves
733
 * their order, so a binary search finds them in log time. The file supplies the
734
 * IDs, though, and nothing obliges it to list them in order -- so a miss falls
735
 * back to a linear scan, which is correct whatever order they arrive in.
736
 *
737
 * The index shortcut this replaces -- `arr[id].id == id` -- only held on the
738
 * very first load. Unloading a range compacts the array without resetting the ID
739
 * counter, so afterwards element 0 carries a nonzero ID and the shortcut missed
740
 * on every lookup, leaving the linear scan alone. build_addr_map() does one span
741
 * lookup per line, so a rebuild went quadratic exactly where it mattered: the
742
 * auto-load path, which runs from the emulator's main loop. */
743
#define DBG_FIND_BY_ID(arr, count, id)                                    \
744
	do {                                                                  \
745
		int lo_ = 0, hi_ = (count) - 1;                                   \
746
		while (lo_ <= hi_) {                                              \
747
			const int mid_ = lo_ + (hi_ - lo_) / 2;                       \
748
			if ((arr)[mid_].id == (id))                                   \
749
				return &(arr)[mid_];                                      \
750
			if ((arr)[mid_].id < (id))                                    \
751
				lo_ = mid_ + 1;                                           \
752
			else                                                          \
753
				hi_ = mid_ - 1;                                           \
754
		}                                                                 \
755
		for (int i_ = 0; i_ < (count); i_++)                              \
756
			if ((arr)[i_].id == (id))                                     \
757
				return &(arr)[i_];                                        \
758
		return NULL;                                                      \
759
	} while (0)
760
761
static const dbg_seg_t *find_seg(int id)
762
{
763
	DBG_FIND_BY_ID(segs, seg_count, id);
764
}
765
766
static const dbg_span_t *find_span(int id)
767
{
768
	DBG_FIND_BY_ID(spans, span_count, id);
769
}
770
771
static const dbg_file_t *find_file(int id)
772
{
773
	if (id >= 0 && id < file_count && files[id].id == id)
774
		return &files[id];
775
	for (int i = 0; i < file_count; i++)
776
		if (files[i].id == id)
777
			return &files[i];
778
	return NULL;
779
}
780
781
static int find_file_index(int id)
782
{
783
	if (id >= 0 && id < file_count && files[id].id == id)
784
		return id;
785
	for (int i = 0; i < file_count; i++)
786
		if (files[i].id == id)
787
			return i;
788
	return -1;
789
}
790
791
/* True if `name` can be opened, either exactly as the .dbg recorded it or as a
792
 * basename under the .dbg's own directory. That mirrors how source_view.c
793
 * resolves a source file, without making this file depend on it -- dbg_info.c
794
 * links on its own in the tests. */
795
static bool source_file_on_disk(const char *name)
796
{
797
	if (!name || !name[0])
798
		return false;
799
800
	FILE *f = NULL;
801
#ifdef _WIN32
802
	if (fopen_s(&f, name, "rb") == 0 && f) {
803
#else
804
	f = fopen(name, "rb");
805
	if (f) {
806
#endif
807
		fclose(f);
808
		return true;
809
	}
810
811
	if (!dbg_dir[0])
812
		return false;
813
814
	char path[1024];
815
	int  n = snprintf(path, sizeof path, "%s/%s", dbg_dir, basename_ptr(name));
816
	if (n <= 0 || n >= (int)sizeof path)
817
		return false;
818
819
	f = NULL;
820
#ifdef _WIN32
821
	if (fopen_s(&f, path, "rb") == 0 && f) {
822
#else
823
	f = fopen(path, "rb");
824
	if (f) {
825
#endif
826
		fclose(f);
827
		return true;
828
	}
829
	return false;
830
}
831
832
/* True if `base` ends in an extension cc65 compiles from, i.e. the sort of file
833
 * that produces a generated `.s` on its way to the assembler. */
834
static bool has_c_extension(const char *base)
835
{
836
	static const char *const exts[] = { ".c", ".cc", ".cpp", ".cxx", ".c++" };
837
	size_t blen = strlen(base);
838
	for (size_t i = 0; i < sizeof exts / sizeof exts[0]; i++) {
839
		size_t elen = strlen(exts[i]);
840
		if (blen > elen && strcasecmp(base + blen - elen, exts[i]) == 0)
841
			return true;
842
	}
843
	return false;
844
}
845
846
/* Recompute the state that depends on the loaded set as a whole rather than on
847
 * any one record: whether high-level (C) line info is present, and which files
848
 * are cc65-generated intermediates that the source picker should not offer.
849
 *
850
 * Always recomputed from scratch. A module swap can remove the only C module,
851
 * or add one, and the answer to both questions changes with it.
852
 *
853
 * A file is treated as a generated intermediate only when ALL of the following
854
 * hold, because a hand-written .s in a mixed C/assembly project is a real
855
 * source and hiding it would be worse than showing one file too many:
856
 *   1. some module carries high-level line info at all;
857
 *   2. its name ends in ".s";
858
 *   3. it is referenced by at least one line record and every one of them is
859
 *      an assembly record -- a file the C compiler emitted line info for is by
860
 *      definition not an intermediate;
861
 *   4. another file in the same .dbg is the C source it was generated from.
862
 *      cl65 names the intermediate two different ways: `foo.s` when you drive
863
 *      cc65 and ca65 yourself, and a temporary `foo.c.<pid>.<n>.s` when it does
864
 *      the whole job in one step -- so a match is either the same basename stem
865
 *      as the C file, or the C file's whole name followed by a dot. Requiring
866
 *      the pairing is what protects a hand-written util.s that has no C source
867
 *      behind it at all, and a hand-written foo.s could not coexist with a
868
 *      compiled foo.c anyway (they collide at foo.o);
869
 *   5. it is not on disk. cl65 deletes the intermediate by default, so a .s
870
 *      that IS present is one the user kept or wrote, and stays on offer.
871
 * Condition 5 is tested last because it is the only one that touches the
872
 * filesystem, and this runs once per load rather than per UI frame. */
873
static void refresh_line_type_state(void)
874
{
875
	has_high_level_lines = false;
876
	for (int i = 0; i < line_count; i++) {
877
		if (lines[i].type == DBG_LI_TYPE_EXT) {
878
			has_high_level_lines = true;
879
			break;
880
		}
881
	}
882
883
	for (int i = 0; i < file_count; i++)
884
		files[i].hidden = false;
885
886
	if (!has_high_level_lines || file_count <= 0)
887
		return;
888
889
	/* Per file: how many assembly line records name it, and how many
890
	 * high-level ones. Allocation failure just leaves everything visible. */
891
	int *refs_asm = (int *)calloc((size_t)file_count, sizeof(int));
892
	int *refs_hl  = (int *)calloc((size_t)file_count, sizeof(int));
893
	if (!refs_asm || !refs_hl) {
894
		free(refs_asm);
895
		free(refs_hl);
896
		return;
897
	}
898
899
	for (int i = 0; i < line_count; i++) {
900
		int fi = find_file_index(lines[i].file);
901
		if (fi < 0)
902
			continue;
903
		if (lines[i].type == DBG_LI_TYPE_EXT)
904
			refs_hl[fi]++;
905
		else
906
			refs_asm[fi]++;
907
	}
908
909
	for (int i = 0; i < file_count; i++) {
910
		if (!files[i].name || refs_hl[i] > 0 || refs_asm[i] == 0)
911
			continue;
912
913
		const char *base = basename_ptr(files[i].name);
914
		size_t      blen = strlen(base);
915
		if (blen <= 2 || strcasecmp(base + blen - 2, ".s") != 0)
916
			continue;
917
		size_t stem = blen - 2;
918
919
		bool paired = false;
920
		for (int j = 0; j < file_count && !paired; j++) {
921
			if (j == i || !files[j].name || refs_hl[j] == 0)
922
				continue;
923
			const char *cbase = basename_ptr(files[j].name);
924
			if (!has_c_extension(cbase))
925
				continue;
926
			size_t clen = strlen(cbase);
927
928
			/* cc65 and ca65 driven separately: foo.c -> foo.s, same stem. */
929
			const char *dot = strrchr(cbase, '.');
930
			if (dot && dot != cbase && (size_t)(dot - cbase) == stem &&
931
			    strncasecmp(cbase, base, stem) == 0) {
932
				paired = true;
933
				break;
934
			}
935
936
			/* cl65 in one step: foo.c -> foo.c.<pid>.<n>.s, so the whole C file
937
			 * name is the head of the intermediate's, followed by a dot. */
938
			if (stem > clen && base[clen] == '.' &&
939
			    strncasecmp(cbase, base, clen) == 0)
940
				paired = true;
941
		}
942
		if (!paired)
943
			continue;
944
945
		files[i].hidden = !source_file_on_disk(files[i].name);
946
	}
947
948
	free(refs_asm);
949
	free(refs_hl);
950
}
951
952
/* ------------------------------------------------------------------ */
953
/*  Build the sorted address map                                       */
954
/* ------------------------------------------------------------------ */
955
956
static int addr_map_cmp(const void *a, const void *b)
957
{
958
	const addr_map_entry_t *ea = (const addr_map_entry_t *)a;
959
	const addr_map_entry_t *eb = (const addr_map_entry_t *)b;
960
	if (ea->addr != eb->addr)
961
		return (ea->addr < eb->addr) ? -1 : 1;
962
	/* Same start: order by span size ascending so the tightest (innermost)
963
	 * span comes first. qsort is not stable, so without this tie-break the
964
	 * order of equal-start spans was arbitrary and the line reported for a PC
965
	 * could vary between runs. */
966
	if (ea->end != eb->end)
967
		return (ea->end < eb->end) ? -1 : 1;
968
	if (ea->line_num != eb->line_num)
969
		return (ea->line_num < eb->line_num) ? -1 : 1;
970
	return 0;
971
}
972
973
static int sym_cmp(const void *a, const void *b)
974
{
975
	const dbg_sym_t *sa = (const dbg_sym_t *)a;
976
	const dbg_sym_t *sb = (const dbg_sym_t *)b;
977
	if (sa->addr != sb->addr)
978
		return (sa->addr < sb->addr) ? -1 : 1;
979
	return 0;
980
}
981
982
static void build_addr_map(void)
983
{
984
	/* Full rebuild from the raw line records. This must reset, not append:
985
	 * every load regenerates entries for ALL retained lines, so appending
986
	 * duplicated every previously-loaded module's entries. */
987
	addr_map_count = 0;
988
989
	for (int i = 0; i < line_count; i++) {
990
		const dbg_line_t *ln = &lines[i];
991
		for (int j = 0; j < ln->span_count; j++) {
992
			const dbg_span_t *sp = find_span(ln->spans[j]);
993
			if (!sp)
994
				continue;
995
			const dbg_seg_t *sg = find_seg(sp->seg);
996
			if (!sg)
997
				continue;
998
999
			uint32_t abs_addr = sg->start + sp->start;
1000
			if (abs_addr > 0xFFFFFF)
1001
				continue;
1002
1003
			uint32_t abs_end = abs_addr + (sp->size ? sp->size - 1 : 0);
1004
			if (abs_end > 0xFFFFFF)
1005
				abs_end = 0xFFFFFF;
1006
1007
			// Out of memory: stop adding entries, but still sort what we have
1008
			// below -- the lookups binary-search these arrays.
1009
			if (!GROW_ARRAY(addr_map, addr_map_count, addr_map_cap, addr_map_entry_t)) {
1010
				goto sort;
1011
			}
1012
			addr_map[addr_map_count].addr     = (dbg_addr_t)abs_addr;
1013
			addr_map[addr_map_count].end      = (dbg_addr_t)abs_end;
1014
			addr_map[addr_map_count].file_id  = ln->file;
1015
			addr_map[addr_map_count].line_num = ln->line;
1016
			addr_map[addr_map_count].type     = ln->type;
1017
			addr_map[addr_map_count].seg_id   = sp->seg;
1018
			addr_map_count++;
1019
		}
1020
	}
1021
1022
sort:
1023
	if (addr_map_count > 0)
1024
		qsort(addr_map, (size_t)addr_map_count, sizeof(addr_map_entry_t), addr_map_cmp);
1025
1026
	if (sym_count > 0)
1027
		qsort(syms, (size_t)sym_count, sizeof(dbg_sym_t), sym_cmp);
1028
}
1029
1030
/* ------------------------------------------------------------------ */
1031
/*  Public API                                                         */
1032
/* ------------------------------------------------------------------ */
1033
1034
// Read one whole line, however long, into a caller-owned growable buffer.
1035
// Returns NULL at end of file or on allocation failure.
1036
//
1037
// A fixed buffer is not good enough here: cc65 records every reference to a
1038
// symbol in its `sym` line, so for a heavily used label that line runs well
1039
// past any reasonable fixed size -- and the fields that matter (val, type)
1040
// come after the reference list, so a truncated line silently loses the symbol
1041
// rather than failing loudly.
1042
static char *read_line(FILE *f, char **buf, size_t *cap)
1043
{
1044
	size_t len = 0;
1045
	for (;;) {
1046
		if (*cap - len < 2) {
1047
			size_t ncap = *cap ? *cap * 2 : 1024;
1048
			char  *nb   = (char *)realloc(*buf, ncap);
1049
			if (!nb) {
1050
				return NULL;
1051
			}
1052
			*buf = nb;
1053
			*cap = ncap;
1054
		}
1055
		if (!fgets(*buf + len, (int)(*cap - len), f)) {
1056
			return len ? *buf : NULL; // EOF; return a final unterminated line
1057
		}
1058
		len += strlen(*buf + len);
1059
		if (len > 0 && (*buf)[len - 1] == '\n') {
1060
			return *buf;
1061
		}
1062
	}
1063
}
1064
1065
int dbg_info_load(const char *path)
1066
{
1067
	FILE  *f;
1068
	char  *buf = NULL;
1069
	size_t cap = 0;
1070
#ifdef _WIN32
1071
	if (fopen_s(&f, path, "r") != 0 || !f) {
1072
#else
1073
	f = fopen(path, "r");
1074
	if (!f) {
1075
#endif
1076
		fprintf(stderr, "dbg_info: cannot open '%s'\n", path);
1077
		return -1;
1078
	}
1079
1080
	// Only now that the file is definitely readable: don't free existing data,
1081
	// so .dbg files load additively, but offset this file's IDs so they cannot
1082
	// collide with those already loaded. Doing any of this before the open
1083
	// would leave a failed load having quietly discarded the address map of
1084
	// whatever was already loaded.
1085
	id_base_file = next_id_file;
1086
	id_base_seg = next_id_seg;
1087
	id_base_span = next_id_span;
1088
	id_base_line = next_id_line;
1089
1090
	// Per load: this file's `file` IDs mean nothing to the next one.
1091
	file_alias_count = 0;
1092
1093
	// Records deduplicated below are pooled only with others from this same
1094
	// .dbg; see dbg_owner_intern(). Interning can only fail on allocation
1095
	// failure, and carrying on with -1 would pool this module with every other
1096
	// load that failed the same way -- and, worse, hand that shared identity to
1097
	// the generation sweep below, which would then discard all of them. Refuse
1098
	// the load instead; nothing here is destructive yet.
1099
	cur_owner = dbg_owner_intern(path);
1100
	if (cur_owner < 0) {
1101
		fclose(f);
1102
		fprintf(stderr, "dbg_info: out of memory loading '%s'\n", path);
1103
		return -1;
1104
	}
1105
1106
	// Equates this module declared before are left in place while the file is
1107
	// read, and dropped only once it has been read to the end. Sweeping them
1108
	// first meant an empty or corrupt file -- one that opens fine and then
1109
	// parses to nothing -- silently deleted the previous generation and still
1110
	// reported success. Records from this parse are tagged with a new
1111
	// generation so the two can be told apart while both are present.
1112
	cur_gen++;
1113
1114
	// `line` records are resolved through the alias only after the whole file
1115
	// is read, so a `line` that precedes its `file` still lands on the right
1116
	// record. cc65 emits them in the other order today, but nothing promises
1117
	// that, and getting it wrong silently drops source lines.
1118
	int line_first = line_count;
1119
	int records    = 0;
1120
	int syms       = 0;
1121
	long expect_syms = -1;   /* from the `info` record, -1 if it had none */
1122
1123
	// Clear addr_map — it is rebuilt below from all accumulated records.
1124
	addr_map_count = 0;
1125
1126
	while (read_line(f, &buf, &cap)) {
1127
		/* Strip trailing newline / carriage-return */
1128
		size_t len = strlen(buf);
1129
		while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
1130
			buf[--len] = '\0';
1131
1132
		const char *p = skip_ws(buf);
1133
		if (*p == '\0')
1134
			continue;
1135
1136
		if (strncmp(p, "file", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
1137
			parse_file_record(p + 4);
1138
			records++;
1139
		} else if (strncmp(p, "seg", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) {
1140
			parse_seg_record(p + 3);
1141
			records++;
1142
		} else if (strncmp(p, "span", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
1143
			parse_span_record(p + 4);
1144
			records++;
1145
		} else if (strncmp(p, "line", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
1146
			parse_line_record(p + 4);
1147
			records++;
1148
		} else if (strncmp(p, "sym", 3) == 0 && (p[3] == ' ' || p[3] == '\t')) {
1149
			if (parse_sym_record(p + 3))
1150
				syms++;
1151
			records++;
1152
		} else if (strncmp(p, "info", 4) == 0 && (p[4] == ' ' || p[4] == '\t')) {
1153
			/* ld65 states its own record counts up front. `sym=N` is what makes
1154
			 * a complete file distinguishable from one still being written. */
1155
			expect_syms = parse_int_key(p + 4, "sym");
1156
		}
1157
		/* Other record types (version, info, scope, …) are silently skipped. */
1158
	}
1159
1160
	fclose(f);
1161
	free(buf);
1162
1163
	// Decide what the file's own record counts say about it. ld65 states them
1164
	// in the `info` line, so a file that promised N symbols and delivered N
1165
	// structurally complete ones was written to the end; one that delivered
1166
	// fewer was not.
1167
	//
1168
	// Both directions require positive evidence, and a file with no `info` line
1169
	// supplies neither. It is merged, because refusing every .dbg that does not
1170
	// come from ld65 would be a far bigger change than the problem warrants,
1171
	// and nothing of its previous generation is dropped, because there is no
1172
	// reason to believe this reading is the whole of it.
1173
	//
1174
	// An incomplete file is a failed load, not a partial success. It must not
1175
	// report 0: dbg_load records a successful merge in its registry and then
1176
	// skips the whole peek/unload/parse path on every later LOAD of the same
1177
	// program, so a half-written .dbg latched there would stop the finished one
1178
	// from ever being read. Failing sends it back for another go.
1179
	//
1180
	// The address map is still rebuilt, because it was cleared on the way in
1181
	// and everything else that is loaded still needs it.
1182
	bool short_read = (expect_syms >= 0 && syms != (int)expect_syms);
1183
	if (records == 0 || short_read) {
1184
		if (short_read)
1185
			fprintf(stderr, "dbg_info: '%s' declares %ld symbols but has %d; not merging\n",
1186
			        path, expect_syms, syms);
1187
		else
1188
			fprintf(stderr, "dbg_info: no usable records in '%s'\n", path);
1189
		build_addr_map();
1190
		return -1;
1191
	}
1192
1193
	// Read to the end as far as anything here can tell, so this generation is
1194
	// what the module declares now. Anything it declared last time and did not
1195
	// declare again goes. Without an `info` line the best available signal is
1196
	// whether the symbol block was reached at all -- weaker, but it has to be
1197
	// something: the per-name replacement above only matches within a
1198
	// generation, so without this a reload would leave the old value in front
1199
	// of the new one for every lookup.
1200
	if (expect_syms >= 0 || syms > 0)
1201
		drop_stale_equates(cur_owner, cur_gen);
1202
1203
	// Now that every `file` record has been seen, turn this load's raw file IDs
1204
	// into the records that actually describe them.
1205
	for (int i = line_first; i < line_count; i++)
1206
		lines[i].file = file_alias_get(lines[i].file);
1207
1208
	// Remember the directory this .dbg came from, for source-file discovery.
1209
	{
1210
		size_t plen = strlen(path);
1211
		if (plen >= sizeof(dbg_dir))
1212
			plen = sizeof(dbg_dir) - 1;
1213
		memcpy(dbg_dir, path, plen);
1214
		dbg_dir[plen] = '\0';
1215
		char *s1 = strrchr(dbg_dir, '/');
1216
		char *s2 = strrchr(dbg_dir, '\\');
1217
		char *sep = (s2 > s1) ? s2 : s1;
1218
		if (sep)
1219
			*sep = '\0';       /* keep directory portion only */
1220
		else
1221
			dbg_dir[0] = '\0'; /* no directory component */
1222
	}
1223
1224
	seed_banks_from_equates();
1225
	build_addr_map();
1226
	/* Deliberately NOT done inside build_addr_map(): the refusal path above
1227
	 * rebuilds the map too, and its module's records are still in lines[]. A
1228
	 * half-written C .dbg -- exactly what that check exists to catch, and what
1229
	 * the auto-load path sees while cl65 is still writing -- would otherwise
1230
	 * announce high-level info on behalf of a load that was rejected, and
1231
	 * switch every already-loaded assembly module to the C lookup for the rest
1232
	 * of the session. Only an accepted load may move this. It also has to run
1233
	 * after the file-ID resolution above, since it counts line records per
1234
	 * file. */
1235
	refresh_line_type_state();
1236
	loaded = true;
1237
1238
	fprintf(stderr, "dbg_info: loaded %d files, %d segs, %d spans, %d lines, %d addr entries, %d symbols, %d equates\n",
1239
	        file_count, seg_count, span_count, line_count, addr_map_count, sym_count, equ_count);
1240
1241
	return 0;
1242
}
1243
1244
/* ---- Symbol (label) lookups ------------------------------------------------ */
1245
1246
bool dbg_info_addr_to_label(dbg_addr_t addr, const char **name)
1247
{
1248
	if (!loaded || sym_count == 0)
1249
		return false;
1250
1251
	/* syms are sorted by address (build_addr_map). */
1252
	int lo = 0, hi = sym_count - 1;
1253
	while (lo <= hi) {
1254
		int mid = lo + (hi - lo) / 2;
1255
		if (syms[mid].addr == addr) {
1256
			if (name)
1257
				*name = syms[mid].name;
1258
			return true;
1259
		} else if (syms[mid].addr < addr) {
1260
			lo = mid + 1;
1261
		} else {
1262
			hi = mid - 1;
1263
		}
1264
	}
1265
	return false;
1266
}
1267
1268
bool dbg_info_addr_to_label_nearest(dbg_addr_t addr, const char **name, dbg_addr_t *label_addr)
1269
{
1270
	if (!loaded || sym_count == 0)
1271
		return false;
1272
1273
	/* syms are sorted by address (build_addr_map): find the LAST symbol at or
1274
	 * below addr, i.e. the label the address falls inside. Callers that want an
1275
	 * exact hit should use dbg_info_addr_to_label(). */
1276
	int lo = 0, hi = sym_count - 1, best = -1;
1277
	while (lo <= hi) {
1278
		int mid = lo + (hi - lo) / 2;
1279
		if (syms[mid].addr <= addr) {
1280
			best = mid;
1281
			lo = mid + 1;
1282
		} else {
1283
			hi = mid - 1;
1284
		}
1285
	}
1286
	if (best < 0)
1287
		return false;
1288
	if (name)
1289
		*name = syms[best].name;
1290
	if (label_addr)
1291
		*label_addr = syms[best].addr;
1292
	return true;
1293
}
1294
1295
bool dbg_info_label_to_addr(const char *name, dbg_addr_t *addr)
1296
{
1297
	if (!loaded || !name || !name[0])
1298
		return false;
1299
1300
	for (int i = 0; i < sym_count; i++) {
1301
		if (strcasecmp(syms[i].name, name) == 0) {
1302
			if (addr)
1303
				*addr = syms[i].addr;
1304
			return true;
1305
		}
1306
	}
1307
	return false;
1308
}
1309
1310
bool dbg_info_equate_to_value(const char *name, dbg_addr_t *val)
1311
{
1312
	if (!loaded || !name || !name[0])
1313
		return false;
1314
1315
	for (int i = 0; i < equ_count; i++) {
1316
		if (strcasecmp(equs[i].name, name) == 0) {
1317
			if (val)
1318
				*val = equs[i].val;
1319
			return true;
1320
		}
1321
	}
1322
	return false;
1323
}
1324
1325
bool dbg_info_name_to_value(const char *name, dbg_addr_t *val, int *kind)
1326
{
1327
	if (dbg_info_label_to_addr(name, val)) {
1328
		if (kind)
1329
			*kind = DBG_NAME_LABEL;
1330
		return true;
1331
	}
1332
	if (dbg_info_equate_to_value(name, val)) {
1333
		if (kind)
1334
			*kind = DBG_NAME_EQUATE;
1335
		return true;
1336
	}
1337
	return false;
1338
}
1339
1340
int dbg_info_symbol_count(void)
1341
{
1342
	return loaded ? sym_count : 0;
1343
}
1344
1345
bool dbg_info_symbol_at(int index, const char **name, dbg_addr_t *addr)
1346
{
1347
	if (!loaded || index < 0 || index >= sym_count)
1348
		return false;
1349
	if (name)
1350
		*name = syms[index].name;
1351
	if (addr)
1352
		*addr = syms[index].addr;
1353
	return true;
1354
}
1355
1356
/* How much a line record's own type recommends it for DISPLAY. cc65 records the
1357
 * same bytes twice for a C program: once against the C statement (EXT) and once
1358
 * against the generated .s that ca65 actually assembled (ASM). The user is
1359
 * looking at the C, and cl65 deletes the .s by default, so the high-level record
1360
 * wins; a macro record still beats the raw assembly under it.
1361
 *
1362
 * This is a display preference only. Every ASM entry stays in the address map,
1363
 * because dbg_info_is_span_start() -- the anchored disassembler's alignment
1364
 * anchor -- needs the per-instruction precision they and only they carry. */
1365
static int entry_source_rank(int i)
1366
{
1367
	switch (addr_map[i].type) {
1368
	case DBG_LI_TYPE_EXT:   return 3;
1369
	case DBG_LI_TYPE_MACRO: return 2;
1370
	default:                return 1;
1371
	}
1372
}
1373
1374
/* The tightest high-level entry whose span covers `addr`, or -1 if none does.
1375
 * Used where an address has an entry of its own but that entry is generated
1376
 * assembly: the C statement it belongs to began at an earlier address, so
1377
 * nothing anchored at `addr` can find it. The backward horizon matches the one
1378
 * dbg_info_addr_to_source_banked_ex() uses. */
1379
static int find_covering_high_level(dbg_addr_t addr)
1380
{
1381
	int lo = 0, hi = addr_map_count - 1, last = -1;
1382
	while (lo <= hi) {
1383
		int mid = lo + (hi - lo) / 2;
1384
		if (addr_map[mid].addr <= addr) {
1385
			last = mid;
1386
			lo = mid + 1;
1387
		} else {
1388
			hi = mid - 1;
1389
		}
1390
	}
1391
1392
	int pick = -1;
1393
	for (int i = last; i >= 0 && addr_map[i].addr + 256 >= addr; i--) {
1394
		if (addr_map[i].type != DBG_LI_TYPE_EXT || addr > addr_map[i].end)
1395
			continue;
1396
		if (pick < 0 || (addr_map[i].end - addr_map[i].addr)
1397
		                    < (addr_map[pick].end - addr_map[pick].addr))
1398
			pick = i;
1399
	}
1400
	return pick;
1401
}
1402
1403
bool dbg_info_addr_to_source(dbg_addr_t addr, const char **file_path, int *line_num)
1404
{
1405
	if (!loaded || addr_map_count == 0)
1406
		return false;
1407
1408
	/* Binary search for exact address match */
1409
	int lo = 0, hi = addr_map_count - 1;
1410
	while (lo <= hi) {
1411
		int mid = lo + (hi - lo) / 2;
1412
		if (addr_map[mid].addr == addr) {
1413
			int pick = mid;
1414
			/* Several entries share this address in a C program: the statement
1415
			 * and the generated-assembly line for its first instruction. Which
1416
			 * one the binary search happened to land on is arbitrary, so pick
1417
			 * deliberately -- the DAP disassembly view attributes instructions
1418
			 * with this, and must not name a file the picker hides. */
1419
			if (has_high_level_lines) {
1420
				int lo2 = mid, hi2 = mid;
1421
				while (lo2 > 0 && addr_map[lo2 - 1].addr == addr)
1422
					lo2--;
1423
				while (hi2 + 1 < addr_map_count && addr_map[hi2 + 1].addr == addr)
1424
					hi2++;
1425
				for (int i = lo2; i <= hi2; i++) {
1426
					const int rank = entry_source_rank(i);
1427
					const int best = entry_source_rank(pick);
1428
					if (rank > best || (rank == best && addr_map[i].end < addr_map[pick].end))
1429
						pick = i;
1430
				}
1431
				/* Only the first instruction of a C statement has a high-level
1432
				 * entry starting on it; every later one carries nothing but the
1433
				 * generated assembly. Reach out to the statement that encloses
1434
				 * it rather than naming a file cl65 deleted. Whether an address
1435
				 * resolves at all is still decided above, so this changes which
1436
				 * source is reported and never how many addresses have one. */
1437
				if (entry_source_rank(pick) < 3) {
1438
					int hl = find_covering_high_level(addr);
1439
					if (hl >= 0)
1440
						pick = hl;
1441
				}
1442
			}
1443
			const dbg_file_t *fi = find_file(addr_map[pick].file_id);
1444
			if (!fi)
1445
				return false;
1446
			if (file_path)
1447
				*file_path = fi->name;
1448
			if (line_num)
1449
				*line_num = addr_map[pick].line_num;
1450
			return true;
1451
		} else if (addr_map[mid].addr < addr) {
1452
			lo = mid + 1;
1453
		} else {
1454
			hi = mid - 1;
1455
		}
1456
	}
1457
	return false;
1458
}
1459
1460
/* True when entry `i` is eligible for `addr` given the RAM bank currently
1461
 * mapped. Only banked addresses ($A000-$BFFF) are filtered, and only against
1462
 * segments whose bank we actually learned — an unknown-bank segment always
1463
 * stays eligible so we never lose a mapping we used to have. */
1464
static bool entry_bank_ok(int i, dbg_addr_t addr, int ram_bank)
1465
{
1466
	if (ram_bank < 0 || addr < 0xA000 || addr > 0xBFFF)
1467
		return true;
1468
	const dbg_seg_t *sg = find_seg(addr_map[i].seg_id);
1469
	if (!sg || sg->bank < 0)
1470
		return true;
1471
	return sg->bank == ram_bank;
1472
}
1473
1474
/* How well an entry's segment matches the bank we are asking about. A confirmed
1475
 * match beats an unknown one, which beats a mismatch -- entry_bank_ok() treats
1476
 * unknown as eligible so a mapping is never lost, but eligible is not the same
1477
 * as equally good, and picking between two eligible entries on span size alone
1478
 * can swap a confirmed answer for a guess about a different segment. */
1479
static int entry_bank_rank(int i, dbg_addr_t addr, int ram_bank)
1480
{
1481
	if (ram_bank < 0 || addr < 0xA000 || addr > 0xBFFF)
1482
		return 2;
1483
	const dbg_seg_t *sg = find_seg(addr_map[i].seg_id);
1484
	if (!sg || sg->bank < 0)
1485
		return 1;               /* eligible, but only because we do not know */
1486
	return sg->bank == ram_bank ? 2 : 0;
1487
}
1488
1489
bool dbg_info_addr_to_source_banked(dbg_addr_t addr, int ram_bank,
1490
                                    const char **file_path, int *line_num)
1491
{
1492
	return dbg_info_addr_to_source_banked_ex(addr, ram_bank, file_path, line_num)
1493
	       != DBG_BANK_NO_MATCH;
1494
}
1495
1496
dbg_bank_result_t dbg_info_addr_to_source_banked_ex(dbg_addr_t addr, int ram_bank,
1497
                                                    const char **file_path, int *line_num)
1498
{
1499
	if (!loaded || addr_map_count == 0)
1500
		return DBG_BANK_NO_MATCH;
1501
1502
	/* Find the greatest entry with entry.addr <= addr. */
1503
	int lo = 0, hi = addr_map_count - 1, best = -1;
1504
	while (lo <= hi) {
1505
		int mid = lo + (hi - lo) / 2;
1506
		if (addr_map[mid].addr <= addr) {
1507
			best = mid;
1508
			lo = mid + 1;
1509
		} else {
1510
			hi = mid - 1;
1511
		}
1512
	}
1513
	if (best < 0)
1514
		return DBG_BANK_NO_MATCH;
1515
1516
	/* Walk backwards from the nearest-below entry to find the one whose span
1517
	 * actually covers addr (spans are small, so this is cheap). Entries that
1518
	 * belong to a different RAM bank are skipped. */
1519
	int found = -1;
1520
	for (int i = best; i >= 0; i--) {
1521
		if (addr <= addr_map[i].end && entry_bank_ok(i, addr, ram_bank)) {
1522
			found = i;
1523
			break;
1524
		}
1525
		/* Stop once we are clearly past any reasonable span size. */
1526
		if (addr_map[i].addr + 256 < addr)
1527
			break;
1528
	}
1529
	if (found < 0) {
1530
		/* Nothing in this bank — fall back to a bank-agnostic match rather than
1531
		 * showing no source at all, but say that is what happened: the answer
1532
		 * may belong to a different bank's segment at the same address. */
1533
		if (ram_bank < 0)
1534
			return DBG_BANK_NO_MATCH;
1535
		return dbg_info_addr_to_source_banked_ex(addr, -1, file_path, line_num) == DBG_BANK_NO_MATCH
1536
		           ? DBG_BANK_NO_MATCH
1537
		           : DBG_BANK_UNKNOWN;
1538
	}
1539
1540
	/* Several spans commonly START at the same address (the statement, its
1541
	 * enclosing block/function, macro and include spans). Among those that
1542
	 * cover addr, pick the SMALLEST — the innermost, most specific line — so
1543
	 * stepping highlights the actual statement rather than an arbitrary
1544
	 * enclosing span. Equal-start entries are contiguous after the sort.
1545
	 *
1546
	 * Bank confidence outranks span size. Two segments commonly share $A000,
1547
	 * one per bank, and only the one that has been loaded has a known bank; if
1548
	 * the smaller happens to be the unknown one, preferring it reports the
1549
	 * OTHER segment's file and line and demotes a confirmed answer to a guess.
1550
	 * So narrow only within the same confidence. */
1551
	if (!has_high_level_lines) {
1552
		dbg_addr_t start = addr_map[found].addr;
1553
		int lo2 = found, hi2 = found;
1554
		while (lo2 > 0 && addr_map[lo2 - 1].addr == start)
1555
			lo2--;
1556
		while (hi2 + 1 < addr_map_count && addr_map[hi2 + 1].addr == start)
1557
			hi2++;
1558
		int best_rank = entry_bank_rank(found, addr, ram_bank);
1559
		for (int i = lo2; i <= hi2; i++) {
1560
			if (addr > addr_map[i].end)
1561
				continue;
1562
			const int rank = entry_bank_rank(i, addr, ram_bank);
1563
			if (rank == 0)
1564
				continue;                       /* a bank we know it is not */
1565
			if (rank > best_rank
1566
			    || (rank == best_rank && addr_map[i].end < addr_map[found].end)) {
1567
				found     = i;
1568
				best_rank = rank;
1569
			}
1570
		}
1571
	} else {
1572
		/* A C program needs a wider search than the equal-start window above.
1573
		 * The C statement's span opens at its first instruction, but the PC
1574
		 * spends most of its time at the interior instruction boundaries that
1575
		 * follow -- and each of those is the start of its own tiny assembly
1576
		 * span. At such an address the equal-start window holds nothing but
1577
		 * assembly entries, so the statement that encloses them, having begun
1578
		 * earlier, would never be considered and stepping would crawl through
1579
		 * the generated .s.
1580
		 *
1581
		 * So consider every entry that covers addr, over the same backward
1582
		 * horizon the covering-entry walk above already uses. Ranking is
1583
		 * bank confidence first (unchanged, and for the same reason), then the
1584
		 * line type, then the tightest span -- comparing span LENGTH here,
1585
		 * since these candidates no longer share a start address.
1586
		 *
1587
		 * A C statement compiling to more than the horizon's worth of code
1588
		 * falls outside this and degrades to reporting the generated assembly.
1589
		 * Widening the horizon would make the scan cost scale with the largest
1590
		 * span in the map, and this runs per disassembly row per frame. */
1591
		int lo2 = found;
1592
		while (lo2 > 0 && addr_map[lo2 - 1].addr + 256 >= addr)
1593
			lo2--;
1594
		int best_bank_rank = entry_bank_rank(found, addr, ram_bank);
1595
		int best_src_rank  = entry_source_rank(found);
1596
		for (int i = lo2; i <= best; i++) {
1597
			if (addr < addr_map[i].addr || addr > addr_map[i].end)
1598
				continue;
1599
			const int rank = entry_bank_rank(i, addr, ram_bank);
1600
			if (rank == 0)
1601
				continue;                       /* a bank we know it is not */
1602
			const int src = entry_source_rank(i);
1603
			const bool tighter = (addr_map[i].end - addr_map[i].addr)
1604
			                     < (addr_map[found].end - addr_map[found].addr);
1605
			if (rank > best_bank_rank
1606
			    || (rank == best_bank_rank && src > best_src_rank)
1607
			    || (rank == best_bank_rank && src == best_src_rank && tighter)) {
1608
				found          = i;
1609
				best_bank_rank = rank;
1610
				best_src_rank  = src;
1611
			}
1612
		}
1613
	}
1614
1615
	{
1616
		const dbg_file_t *fi = find_file(addr_map[found].file_id);
1617
		if (!fi)
1618
			return DBG_BANK_NO_MATCH;
1619
		if (file_path)
1620
			*file_path = fi->name;
1621
		if (line_num)
1622
			*line_num = addr_map[found].line_num;
1623
1624
		/* Resolved only if the segment we matched actually knows its bank; a
1625
		 * banked address matched against an unknown-bank segment is a guess. */
1626
		if (ram_bank >= 0 && addr >= 0xA000 && addr <= 0xBFFF) {
1627
			const dbg_seg_t *sg = find_seg(addr_map[found].seg_id);
1628
			if (!sg || sg->bank < 0)
1629
				return DBG_BANK_UNKNOWN;
1630
		}
1631
		return DBG_BANK_RESOLVED;
1632
	}
1633
}
1634
1635
bool dbg_info_addr_to_source_nearest(dbg_addr_t addr, const char **file_path, int *line_num)
1636
{
1637
	return dbg_info_addr_to_source_banked(addr, -1, file_path, line_num);
1638
}
1639
1640
const char *dbg_info_get_dbg_dir(void)
1641
{
1642
	return dbg_dir;
1643
}
1644
1645
bool dbg_info_has_high_level(void)
1646
{
1647
	return has_high_level_lines;
1648
}
1649
1650
int dbg_info_file_count(void)
1651
{
1652
	int n = 0;
1653
	for (int i = 0; i < file_count; i++)
1654
		if (!files[i].hidden)
1655
			n++;
1656
	return n;
1657
}
1658
1659
bool dbg_info_file_at(int index, const char **name)
1660
{
1661
	if (index < 0)
1662
		return false;
1663
	/* `index` counts only the files on offer, so that callers can walk
1664
	 * 0..dbg_info_file_count()-1 without ever seeing a gap. The hidden flag is
1665
	 * decided once per load; see refresh_line_type_state(). */
1666
	for (int i = 0; i < file_count; i++) {
1667
		if (files[i].hidden)
1668
			continue;
1669
		if (index-- == 0) {
1670
			if (name)
1671
				*name = files[i].name;
1672
			return true;
1673
		}
1674
	}
1675
	return false;
1676
}
1677
1678
int dbg_info_scan_dbg_files(const char *dir, char out[][DBG_INFO_PATH_MAX], int max)
1679
{
1680
	if (!dir || !dir[0] || !out || max <= 0)
1681
		return 0;
1682
1683
	DIR *dp = opendir(dir);
1684
	if (!dp)
1685
		return 0;
1686
1687
	size_t dlen    = strlen(dir);
1688
	bool   need_sep = dlen > 0 && dir[dlen - 1] != '/' && dir[dlen - 1] != '\\';
1689
1690
	int            n = 0;
1691
	struct dirent *de;
1692
	while (n < max && (de = readdir(dp)) != NULL) {
1693
		const char *nm = de->d_name;
1694
		size_t      l  = strlen(nm);
1695
		if (l < 5 || strcasecmp(nm + l - 4, ".dbg") != 0)
1696
			continue; // want "<something>.dbg"
1697
		int w = snprintf(out[n], DBG_INFO_PATH_MAX, "%s%s%s", dir, need_sep ? "/" : "", nm);
1698
		if (w > 0 && w < DBG_INFO_PATH_MAX)
1699
			n++;
1700
	}
1701
	closedir(dp);
1702
	return n;
1703
}
1704
1705
bool dbg_info_source_to_addr(const char *file_path, int line_num, dbg_addr_t *addr)
1706
{
1707
	if (!loaded || !file_path)
1708
		return false;
1709
1710
	const char *query_base = basename_ptr(file_path);
1711
1712
	/* addr_map is sorted by address, so the first entry that matches is the
1713
	 * lowest address this line generated code at. That is what a line
1714
	 * breakpoint wants: the start of the statement, not some later fragment of
1715
	 * it. A C statement usually owns several spans, so this matters more for a
1716
	 * C program than it ever did for assembly. */
1717
	for (int i = 0; i < addr_map_count; i++) {
1718
		if (addr_map[i].line_num != line_num)
1719
			continue;
1720
1721
		const dbg_file_t *fi = find_file(addr_map[i].file_id);
1722
		if (!fi)
1723
			continue;
1724
1725
		const char *entry_base = basename_ptr(fi->name);
1726
		if (strcasecmp(query_base, entry_base) == 0) {
1727
			if (addr)
1728
				*addr = addr_map[i].addr;
1729
			return true;
1730
		}
1731
	}
1732
	return false;
1733
}
1734
1735
/* ------------------------------------------------------------------ */
1736
/*  Banked-segment -> RAM bank association                             */
1737
/*                                                                     */
1738
/*  Several segments typically all start at $A000 — they are different */
1739
/*  X16 RAM banks. The .dbg records no bank number, so an address in   */
1740
/*  $A000-$BFFF is ambiguous across every banked segment. We learn the  */
1741
/*  mapping two ways:                                                  */
1742
/*    1. `RAM_BANK_<SEG>` / `<SEG>_BANK` equate symbols emitted by the  */
1743
/*       program's own source (covers BSS segments that are never       */
1744
/*       loaded from a file), and                                       */
1745
/*    2. runtime LOADs: a blob of size N landing at the segment's start */
1746
/*       while RAM bank B is mapped means that segment lives in bank B. */
1747
/* ------------------------------------------------------------------ */
1748
1749
/* Squash a name for fuzzy comparison: uppercase, drop '_'. Returns false if the
1750
 * name did not fit, since a truncated name compares equal to anything sharing
1751
 * its head and would invent matches that the full names do not support. */
1752
static bool squash_name(const char *src, char *dst, size_t dstsz)
1753
{
1754
	size_t o = 0;
1755
	for (; src && *src; src++) {
1756
		if (*src == '_')
1757
			continue;
1758
		if (o + 1 >= dstsz) {
1759
			dst[0] = '\0';
1760
			return false;
1761
		}
1762
		char c = *src;
1763
		if (c >= 'a' && c <= 'z')
1764
			c = (char)(c - 'a' + 'A');
1765
		dst[o++] = c;
1766
	}
1767
	dst[o] = '\0';
1768
	return true;
1769
}
1770
1771
/* Reduce a `RAM_BANK_x` / `BANK_x` / `x_BANK` equate name to the squashed
1772
 * segment name it is talking about. Returns false if it is not one of those. */
1773
static bool bank_equ_target(const char *nm, char *out, size_t outsz)
1774
{
1775
	if (!nm)
1776
		return false;
1777
	const char *body;
1778
	size_t      blen;
1779
	if (!strncmp(nm, "RAM_BANK_", 9)) {
1780
		body = nm + 9;
1781
		blen = strlen(body);
1782
	} else if (!strncmp(nm, "BANK_", 5)) {
1783
		body = nm + 5;
1784
		blen = strlen(body);
1785
	} else {
1786
		size_t l = strlen(nm);
1787
		if (l > 5 && !strcmp(nm + l - 5, "_BANK")) {
1788
			body = nm;
1789
			blen = l - 5;
1790
		} else {
1791
			return false;
1792
		}
1793
	}
1794
1795
	/* Truncating here would silently invent matches: two names sharing a long
1796
	 * enough head would reduce to the same target and compare exactly equal.
1797
	 * A name this long is not one of these constants, so refuse it instead. */
1798
	char stripped[128];
1799
	if (blen >= sizeof stripped)
1800
		return false;
1801
	memcpy(stripped, body, blen);
1802
	stripped[blen] = '\0';
1803
1804
	if (!squash_name(stripped, out, outsz))
1805
		return false;
1806
	return out[0] != '\0';
1807
}
1808
/* Seed segment banks from `RAM_BANK_x` / `x_BANK` style equates parsed out of
1809
 * the .dbg. Matching is on the squashed names.
1810
 *
1811
 * Driven from the segment rather than the equate. Walking the equates and
1812
 * claiming every segment that matched put the answer at the mercy of the order
1813
 * the equates happened to be parsed in: a prefix match is accepted in either
1814
 * direction, so RAM_BANK_CODE matches segment CODE2 as readily as CODE, and
1815
 * because a segment that already has a bank is skipped, whichever equate was
1816
 * seen first won -- leaving the exact RAM_BANK_CODE2 to be discarded and CODE2's
1817
 * code attributed to the wrong bank.
1818
 *
1819
 * Per segment, every equate is sorted into one of four ranks: own-exact,
1820
 * other-exact, own-prefix, other-prefix. The best non-empty rank decides, and
1821
 * decides alone -- if its candidates disagree about the bank the segment is
1822
 * left unknown rather than falling through to a weaker rank. Callers report
1823
 * unknown honestly and a runtime observation can still resolve it, whereas a
1824
 * confident wrong answer is not recoverable. */
1825
static void seed_banks_from_equates(void)
1826
{
1827
	/* Equate-derived banks are recomputed from scratch, because the equates
1828
	 * they came from may have changed. A module that reloads with a different
1829
	 * RAM_BANK_x value can have seeded segments belonging to OTHER modules
1830
	 * through the cross-module ranks, and those segments are not unloaded --
1831
	 * so without this they would keep a bank nobody declares any more. Banks
1832
	 * that were observed at runtime are left alone: an observation is evidence,
1833
	 * an equate is only an inference, and the observation must keep winning. */
1834
	for (int i = 0; i < seg_count; i++) {
1835
		if (segs[i].bank_from_equ) {
1836
			segs[i].bank          = -1;
1837
			segs[i].bank_from_equ = false;
1838
		}
1839
	}
1840
1841
	/* Squash each equate's target once rather than once per segment. Matching
1842
	 * is O(segments x equates) either way, but this keeps the string work out
1843
	 * of the inner loop -- seeding runs from the emulator's main loop via the
1844
	 * auto-load path, so a large .dbg must not stall emulation. */
1845
	char *targets = NULL;
1846
	if (bank_equ_count > 0) {
1847
		if ((size_t)bank_equ_count > SIZE_MAX / 128)
1848
			return;   /* not reachable with real input; the product must not wrap */
1849
		targets = (char *)malloc((size_t)bank_equ_count * 128);
1850
		if (!targets)
1851
			return;   /* leaves banks unknown, which callers report honestly */
1852
		for (int b = 0; b < bank_equ_count; b++) {
1853
			if (!bank_equ_target(bank_equs[b].name, targets + (size_t)b * 128, 128))
1854
				targets[(size_t)b * 128] = '\0';
1855
		}
1856
	}
1857
1858
	for (int i = 0; i < seg_count; i++) {
1859
		if (segs[i].bank >= 0 || !segs[i].name || !segs[i].size)
1860
			continue;
1861
		if (segs[i].start < 0xA000 || segs[i].start > 0xBFFF)
1862
			continue;
1863
1864
		char have[128];
1865
		if (!squash_name(segs[i].name, have, sizeof have) || !have[0])
1866
			continue;   /* truncated names would match things they do not equal */
1867
		size_t lh = strlen(have);
1868
1869
		/* Candidates are ranked by how good the name match is first and by who
1870
		 * declared the equate second: own-exact, other-exact, own-prefix,
1871
		 * other-prefix. Gating the whole search on the segment's own module
1872
		 * instead was wrong -- an own-module PREFIX match would end the search
1873
		 * before another module's EXACT match was ever looked at, which is the
1874
		 * same "claimed by a name it merely resembles" failure this ranking
1875
		 * exists to prevent, just across a module boundary.
1876
		 *
1877
		 * Ownership still matters, because two overlays that each define
1878
		 * RAM_BANK_CODE for their own CODE segment -- an ordinary X16
1879
		 * arrangement -- would otherwise see two equally good candidates and
1880
		 * both end up unknown. */
1881
		enum { OWN_EXACT, ANY_EXACT, OWN_PREFIX, ANY_PREFIX, RANK_COUNT };
1882
		int  cand[RANK_COUNT];
1883
		bool ambiguous[RANK_COUNT];
1884
		for (int r = 0; r < RANK_COUNT; r++) {
1885
			cand[r]      = -1;
1886
			ambiguous[r] = false;
1887
		}
1888
1889
		for (int b = 0; b < bank_equ_count; b++) {
1890
			const char *want = targets + (size_t)b * 128;
1891
			if (!want[0])
1892
				continue;
1893
			bool own = (bank_equs[b].owner == segs[i].owner);
1894
			int  rank;
1895
			if (!strcmp(have, want)) {
1896
				rank = own ? OWN_EXACT : ANY_EXACT;
1897
			} else {
1898
				/* Either direction, so RAM_BANK_STORE_TILEMAP pairs with
1899
				 * segment STORETILE, which is what these conventions produce.
1900
				 * Four characters is the shortest overlap worth trusting. */
1901
				size_t lw = strlen(want);
1902
				size_t n  = lh < lw ? lh : lw;
1903
				if (n < 4 || strncmp(have, want, n) != 0)
1904
					continue;
1905
				rank = own ? OWN_PREFIX : ANY_PREFIX;
1906
			}
1907
			if (cand[rank] < 0)
1908
				cand[rank] = b;
1909
			else if (bank_equs[cand[rank]].bank != bank_equs[b].bank)
1910
				ambiguous[rank] = true;
1911
		}
1912
1913
		/* The best kind of match available decides, and it decides alone: if
1914
		 * the candidates of that kind disagree there is no basis for choosing
1915
		 * between them, and falling through to a weaker kind would answer a
1916
		 * question the better evidence just said was undecidable. Unknown is
1917
		 * reported honestly and a runtime observation can still resolve it. */
1918
		for (int r = 0; r < RANK_COUNT; r++) {
1919
			if (cand[r] < 0)
1920
				continue;
1921
			if (!ambiguous[r]) {
1922
				segs[i].bank          = bank_equs[cand[r]].bank;
1923
				segs[i].bank_from_equ = true;
1924
			}
1925
			break;
1926
		}
1927
	}
1928
1929
	free(targets);
1930
}
1931
1932
void dbg_info_note_bank_load(dbg_addr_t load_addr, uint32_t size, uint8_t ram_bank)
1933
{
1934
	if (!loaded || load_addr < 0xA000 || load_addr > 0xBFFF || size == 0)
1935
		return;
1936
1937
	/* Match the loaded blob to exactly one segment by (start,size). A PRG-style
1938
	 * 2-byte load-address header may or may not be counted, so accept both. */
1939
	int hit = -1;
1940
	for (int i = 0; i < seg_count; i++) {
1941
		if (!segs[i].size || segs[i].start != load_addr)
1942
			continue;
1943
		if (segs[i].size == size || segs[i].size + 2 == size) {
1944
			if (hit >= 0)
1945
				return;   /* ambiguous (two segments same start+size) — skip */
1946
			hit = i;
1947
		}
1948
	}
1949
	if (hit >= 0) {
1950
		segs[hit].bank = ram_bank;
1951
		/* Observed, not inferred: re-seeding must not clear this. */
1952
		segs[hit].bank_from_equ = false;
1953
	}
1954
}
1955
1956
bool dbg_info_is_loaded(void)
1957
{
1958
	return loaded;
1959
}
1960
1961
bool dbg_info_is_span_start(dbg_addr_t addr)
1962
{
1963
	if (!loaded || addr_map_count == 0)
1964
		return false;
1965
1966
	/* Binary search the sorted address map for an exact span-start match. */
1967
	int lo = 0, hi = addr_map_count - 1;
1968
	while (lo <= hi) {
1969
		int mid = lo + (hi - lo) / 2;
1970
		if (addr_map[mid].addr == addr)
1971
			return true;
1972
		else if (addr_map[mid].addr < addr)
1973
			lo = mid + 1;
1974
		else
1975
			hi = mid - 1;
1976
	}
1977
	return false;
1978
}
1979
1980
void dbg_info_unload_range(dbg_addr_t start, dbg_addr_t end)
1981
{
1982
	if (!loaded) return;
1983
1984
	// Drop the RAW line records that map into this range FIRST. build_addr_map()
1985
	// regenerates addr_map from lines[] on every load, so pruning only the
1986
	// derived map below would be silently undone by the next dbg_info_load():
1987
	// the replaced module's lines would be re-added and could then win the
1988
	// address lookup (a swapped-out module's file/line shown for the new
1989
	// module's code). Freeing the line's span-id array here also stops the
1990
	// swap from leaking it.
1991
	{
1992
		int ldst = 0;
1993
		for (int i = 0; i < line_count; i++) {
1994
			bool in_range = false;
1995
			for (int j = 0; j < lines[i].span_count && !in_range; j++) {
1996
				const dbg_span_t *sp = find_span(lines[i].spans[j]);
1997
				if (!sp)
1998
					continue;
1999
				const dbg_seg_t *sg = find_seg(sp->seg);
2000
				if (!sg)
2001
					continue;
2002
				uint32_t a = sg->start + sp->start;
2003
				if (a <= 0xFFFFFF && a >= start && a <= end)
2004
					in_range = true;
2005
			}
2006
			if (in_range)
2007
				free(lines[i].spans);
2008
			else
2009
				lines[ldst++] = lines[i];
2010
		}
2011
		line_count = ldst;
2012
	}
2013
2014
	// NOTE: do not early-out on addr_map_count==0 — a symbol-only / line-less
2015
	// .dbg can have zero addr-map entries but non-zero symbols, and the symbol
2016
	// cleanup below must still run (otherwise repeated module swaps leak/stale
2017
	// labels). The addr-map compaction loop is a no-op when the map is empty.
2018
2019
	// Remove all addr_map entries in [start, end]
2020
	int dst = 0;
2021
	for (int src = 0; src < addr_map_count; src++) {
2022
		if (addr_map[src].addr < start || addr_map[src].addr > end) {
2023
			addr_map[dst++] = addr_map[src];
2024
		}
2025
	}
2026
	addr_map_count = dst;
2027
2028
	// Remove symbols in [start, end] too, so a module swap drops its labels
2029
	// (freeing their names). The array stays sorted by address.
2030
	int sdst = 0;
2031
	for (int src = 0; src < sym_count; src++) {
2032
		if (syms[src].addr < start || syms[src].addr > end) {
2033
			syms[sdst++] = syms[src];
2034
		} else {
2035
			free(syms[src].name);
2036
		}
2037
	}
2038
	sym_count = sdst;
2039
2040
	// Finally drop the spans and segments that lived in this range. Without
2041
	// this, reloading a module appends a second copy of its segments, and
2042
	// dbg_info_note_bank_load() -- which needs a unique (start, size) match to
2043
	// decide which RAM bank a segment lives in -- sees two candidates and gives
2044
	// up. From the first module swap onwards no segment would ever be assigned
2045
	// a bank again, and banked $A000-$BFFF addresses would resolve to whichever
2046
	// module happened to match first.
2047
	//
2048
	// Entries keep their IDs; only the arrays are compacted. Lookups fall back
2049
	// to a scan by ID, and new IDs come from a high-water mark, so nothing is
2050
	// invalidated by the holes this leaves. files[] is deliberately left alone:
2051
	// callers hold the file_path pointers it owns.
2052
	{
2053
		int pdst = 0;
2054
		for (int i = 0; i < span_count; i++) {
2055
			const dbg_seg_t *sg = find_seg(spans[i].seg);
2056
			uint32_t a = sg ? (uint32_t)sg->start + spans[i].start : 0xFFFFFFFFu;
2057
			if (sg && a <= 0xFFFFFF && a >= start && a <= end) {
2058
				continue; // in range: drop
2059
			}
2060
			spans[pdst++] = spans[i];
2061
		}
2062
		span_count = pdst;
2063
2064
		int gdst = 0;
2065
		for (int i = 0; i < seg_count; i++) {
2066
			uint32_t s = segs[i].start;
2067
			uint32_t e = segs[i].size ? s + segs[i].size - 1 : s;
2068
			if (s >= start && e <= end) {
2069
				free(segs[i].name); // owned by this module; nothing else refers to it
2070
				continue;           // wholly inside the unloaded range: drop
2071
			}
2072
			segs[gdst++] = segs[i];
2073
		}
2074
		seg_count = gdst;
2075
	}
2076
2077
	/* The line records that were just dropped may have been the only high-level
2078
	 * ones, or the only reason a generated .s was recognised as one. This path
2079
	 * prunes and compacts in place rather than calling build_addr_map(), so it
2080
	 * has to refresh that derived state itself. */
2081
	refresh_line_type_state();
2082
}
2083
2084
// Helper: build .dbg path from a loaded file path. Returns false if the result
2085
// would not fit, rather than writing past the caller's buffer -- loaded_path
2086
// can come from an emulated LOAD, so its length is not ours to trust.
2087
static bool build_dbg_path(const char *loaded_path, char *dbg_path, size_t dbg_path_size) {
2088
	static const char ext[] = ".dbg";
2089
	const size_t ext_len = sizeof(ext) - 1;
2090
2091
	size_t len = strlen(loaded_path);
2092
	if (dbg_path_size <= ext_len || len >= dbg_path_size) {
2093
		return false;
2094
	}
2095
	memcpy(dbg_path, loaded_path, len);
2096
	dbg_path[len] = '\0';
2097
2098
	char *last_sep = strrchr(dbg_path, '/');
2099
	char *last_sep2 = strrchr(dbg_path, '\\');
2100
	if (last_sep2 > last_sep) last_sep = last_sep2;
2101
	char *dot = strrchr(last_sep ? last_sep : dbg_path, '.');
2102
2103
	// Where the extension will start: replacing an existing one, or appended.
2104
	size_t at = dot ? (size_t)(dot - dbg_path) : len;
2105
	if (at + ext_len >= dbg_path_size) {
2106
		return false;
2107
	}
2108
	memcpy(dbg_path + at, ext, ext_len + 1);
2109
	return true;
2110
}
2111
2112
// Helper: scan .dbg file for min/max segment addresses
2113
static bool scan_dbg_seg_range(const char *dbg_path, dbg_addr_t *out_min, dbg_addr_t *out_max) {
2114
	FILE *f;
2115
#ifdef _WIN32
2116
	if (fopen_s(&f, dbg_path, "r") != 0 || !f) return false;
2117
#else
2118
	f = fopen(dbg_path, "r");
2119
	if (!f) return false;
2120
#endif
2121
	dbg_addr_t seg_min = 0xFFFFFF, seg_max = 0;
2122
	char    *buf = NULL;
2123
	size_t   cap = 0;
2124
	while (read_line(f, &buf, &cap)) {
2125
		if (strncmp(buf, "seg", 3) != 0) continue;
2126
2127
		const char *sp = strstr(buf, "start=0x");
2128
		const char *sz = strstr(buf, "size=0x");
2129
		if (!sp || !sz) continue;
2130
		uint32_t start = (uint32_t)strtol(sp + 8, NULL, 16);
2131
		uint32_t size = (uint32_t)strtol(sz + 7, NULL, 16);
2132
		if (size == 0 || start > 0xFFFFFF) continue;
2133
		if (start < seg_min) seg_min = (dbg_addr_t)start;
2134
		uint32_t end = start + size - 1;
2135
		if (end > 0xFFFFFF) end = 0xFFFFFF;
2136
		if (end > seg_max) seg_max = (dbg_addr_t)end;
2137
	}
2138
	free(buf);
2139
	fclose(f);
2140
	if (seg_min > seg_max) return false;
2141
	*out_min = seg_min;
2142
	*out_max = seg_max;
2143
	return true;
2144
}
2145
2146
2147
2148
bool dbg_info_peek_file_range(const char *loaded_path, dbg_addr_t *out_start, dbg_addr_t *out_end) {
2149
	if (!loaded_path) return false;
2150
	char dbg_path[1024];
2151
	if (!build_dbg_path(loaded_path, dbg_path, sizeof(dbg_path))) return false;
2152
	return scan_dbg_seg_range(dbg_path, out_start, out_end);
2153
}
2154
2155
int dbg_info_load_for_file(const char *loaded_path, dbg_addr_t load_addr)
2156
{
2157
	if (!loaded_path) return -1;
2158
2159
	char dbg_path[1024];
2160
	if (!build_dbg_path(loaded_path, dbg_path, sizeof(dbg_path))) return -1;
2161
2162
	dbg_addr_t seg_min, seg_max;
2163
	if (!scan_dbg_seg_range(dbg_path, &seg_min, &seg_max))
2164
		return -1;
2165
2166
	// Unload existing entries in this range, then load new .dbg.
2167
	//
2168
	// This range is the min/max across EVERY segment the file declares,
2169
	// including BSS and the zero page, which for a normal cc65 program runs from
2170
	// low RAM to the top of the program. That is wider than what the load
2171
	// actually overwrote, so it takes other modules in that window with it --
2172
	// including debug info the user named with -dbgfile, which nothing
2173
	// re-merges.
2174
	//
2175
	// Narrowing it to the stored segments alone was tried and is worse:
2176
	// segment, span, line and label records are still appended unconditionally,
2177
	// so this module's own BSS and zero-page records would no longer be pruned
2178
	// but would still be re-added, piling up on every overlay swap until an
2179
	// address could be described by a module that is no longer resident. The
2180
	// two jobs -- evicting other modules, and replacing this one's own records
2181
	// -- need different ranges. Files, equates and segments now carry an owner;
2182
	// finishing this means extending it to spans/lines/syms and having unload
2183
	// consult it. See dbg_info.h.
2184
	dbg_info_unload_range(seg_min, seg_max);
2185
	int result = dbg_info_load(dbg_path);
2186
	if (result == 0) {
2187
		printf("[dap] Auto-loaded debug info: %s ($%04X-$%04X)\n",
2188
		       dbg_path, seg_min, seg_max);
2189
	}
2190
	return result;
2191
}
2192
2193
void dbg_info_free(void)
2194
{
2195
	for (int i = 0; i < owner_count; i++)
2196
		free(owners[i]);
2197
	free(owners);
2198
	owners      = NULL;
2199
	owner_count = 0;
2200
	owner_cap   = 0;   /* GROW_ARRAY keys off this; leaving it set would append
2201
	                    * into a dangling pointer on the next load. */
2202
	cur_owner   = -1;
2203
2204
	free(file_alias);
2205
	file_alias       = NULL;
2206
	file_alias_count = 0;
2207
	file_alias_cap   = 0;
2208
2209
	for (int i = 0; i < file_count; i++)
2210
		free(files[i].name);
2211
	free(files);
2212
	files = NULL;
2213
	file_count = 0;
2214
	file_cap = 0;
2215
2216
	for (int i = 0; i < seg_count; i++)
2217
		free(segs[i].name);
2218
	free(segs);
2219
	segs = NULL;
2220
	seg_count = 0;
2221
	seg_cap = 0;
2222
2223
	for (int i = 0; i < bank_equ_count; i++)
2224
		free(bank_equs[i].name);
2225
	free(bank_equs);
2226
	bank_equs = NULL;
2227
	bank_equ_count = 0;
2228
	bank_equ_cap = 0;
2229
2230
	free(spans);
2231
	spans = NULL;
2232
	span_count = 0;
2233
	span_cap = 0;
2234
2235
	for (int i = 0; i < line_count; i++)
2236
		free(lines[i].spans);
2237
	free(lines);
2238
	lines = NULL;
2239
	line_count = 0;
2240
	line_cap = 0;
2241
2242
	free(addr_map);
2243
	addr_map = NULL;
2244
	addr_map_count = 0;
2245
	addr_map_cap = 0;
2246
2247
	// Nothing is loaded any more, so IDs can start from zero again.
2248
	id_base_file = id_base_seg = id_base_span = id_base_line = 0;
2249
	next_id_file = next_id_seg = next_id_span = next_id_line = 0;
2250
2251
	for (int i = 0; i < sym_count; i++)
2252
		free(syms[i].name);
2253
	free(syms);
2254
	syms = NULL;
2255
	sym_count = 0;
2256
	sym_cap = 0;
2257
2258
	for (int i = 0; i < equ_count; i++)
2259
		free(equs[i].name);
2260
	free(equs);
2261
	equs = NULL;
2262
	equ_count = 0;
2263
	equ_cap = 0;
2264
2265
	dbg_dir[0] = '\0';
2266
	loaded = false;
2267
	has_high_level_lines = false;
2268
}