The vector's number goes beside its kind, not at the end of the line

Kind and Number together are what names a thing: Vector 16 and Device 32
are identifiers in a way that Vector on its own is not. Everything after
them - where it lives, what it is called, where it was written - says
something about it rather than naming it, so with the number at the far
end a line opened with a bare kind and closed with the fact that would
have told you what you were reading.

Fields are now Kind, Number, Address, Name, File, Line. A label still
carries a dash where its number would be, which reads as "this kind is
not numbered" rather than as a field that went missing.

Manual and the docs check follow. Verified with break.sh by swapping the
number and the address back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-05 11:03:11 -04:00
co-authored by Claude Opus 5
parent f3d8985bc4
commit 7dca141aae
3 changed files with 27 additions and 17 deletions
+14 -6
View File
@@ -66,6 +66,14 @@ int vectorArrayCount = 0;
// and what a memory dump shows. Neither can be worked out from the other without knowing // and what a memory dump shows. Neither can be worked out from the other without knowing
// which table it is in, so the file says both. // which table it is in, so the file says both.
// //
// THE NUMBER SITS SECOND, beside the kind rather than out at the end, because the two of
// them together are what names the thing: "Vector 26" and "Device 32" are single identifiers
// in the way "Vector" alone is not. Everything after them - where it lives, what it is
// called, where it was written - is something about the thing rather than part of naming it,
// so the line reads as an identity followed by its properties. A label has no number and
// carries a dash in that column, which is the shape of the answer "this kind is not numbered"
// rather than an empty space that could be a missing field.
//
// Sorted by kind and then address rather than by name, because the question asked of it is // Sorted by kind and then address rather than by name, because the question asked of it is
// always "what is at this address"; sorting by address alone would interleave separate // always "what is at this address"; sorting by address alone would interleave separate
// address spaces and make the first column flicker between them. The table is small enough // address spaces and make the first column flicker between them. The table is small enough
@@ -146,17 +154,17 @@ void writeSymbolFile(const char *path) {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
char number[12]; // Wide enough for any int, which is more than a vector needs. char number[12]; // Wide enough for any int, which is more than a vector needs.
if (rows[i].number < 0) { if (rows[i].number < 0) {
// A LABEL HAS NO NUMBER, and the field says so rather than being left empty: // A label has no number, and the field says so rather than being left empty: a
// a run of tabs with nothing between them is the one thing a reader of this // run of tabs with nothing between them is the one thing a reader of this file,
// file, human or otherwise, can miscount. // human or otherwise, can miscount.
snprintf(number, sizeof(number), "-"); snprintf(number, sizeof(number), "-");
} else { } else {
snprintf(number, sizeof(number), "%d", rows[i].number); snprintf(number, sizeof(number), "%d", rows[i].number);
} }
fprintf(file, "%s\t%04X\t%s\t%s\t%d\t%s\n", fprintf(file, "%s\t%s\t%04X\t%s\t%s\t%d\n",
kindName[rows[i].kind], rows[i].address, rows[i].name, kindName[rows[i].kind], number, rows[i].address, rows[i].name,
rows[i].fileName ? rows[i].fileName : "?", rows[i].fileName ? rows[i].fileName : "?",
rows[i].lineNumber, number); rows[i].lineNumber);
} }
free(rows); free(rows);
fclose(file); fclose(file);
+9 -7
View File
@@ -430,21 +430,23 @@ Every option above that takes a \<file\> names a file the assembler WRITES, and
| Field | Meaning | | Field | Meaning |
| -- | -- | | -- | -- |
| Kind | Program, Data, Vector or Device. | | Kind | Program, Data, Vector or Device. |
| Number | Which vector it is: the vector number, or the port for a device. A label has no number and the field is a dash. |
| Address | Four hexadecimal digits: where the thing lives. For a label, the address it was given. For a vector, the slot in the vector table that holds its handler's address. | | Address | Four hexadecimal digits: where the thing lives. For a label, the address it was given. For a vector, the slot in the vector table that holds its handler's address. |
| Name | The label, or the vector's name. A device has no name of its own, so it is listed under its handler. | | Name | The label, or the vector's name. A device has no name of its own, so it is listed under its handler. |
| File | The source file it was written in, named the way the assembler was given it. | | File | The source file it was written in, named the way the assembler was given it. |
| Line | Which line of that file, counting from one. | | Line | Which line of that file, counting from one. |
| Number | Which vector it is: the vector number, or the port for a device. A label has no number and the field is a dash. |
``` ```
Program 5000 start game.asm 31 - Program - 5000 start game.asm 31
Program 670D int16add Libraries/math.asm 5 - Program - 670D int16add Libraries/math.asm 5
Vector FC20 osPrintString services.asm 24 16 Vector 16 FC20 osPrintString services.asm 24
Vector FC80 gameFrame game.asm 402 64 Vector 64 FC80 gameFrame game.asm 402
Device FE40 diskDone system.asm 812 32 Device 32 FE40 diskDone system.asm 812
Data 3000 Score game.asm 118 - Data - 3000 Score game.asm 118
``` ```
The kind and the number sit together at the front because the two of them are what names the thing: Vector 16 and Device 32 are identifiers in a way that Vector on its own is not. Everything after them says something about it rather than naming it, so a line reads as a thing followed by what is known about it.
Tabs rather than aligned columns, so that the file is a table every ordinary tool already reads without being taught anything, and no heading line, so that nothing has to know to skip one. The file field is what makes it worth having on a big program: a name defined once and called in forty places is hard to find by searching, and this says where it was written. Tabs rather than aligned columns, so that the file is a table every ordinary tool already reads without being taught anything, and no heading line, so that nothing has to know to skip one. The file field is what makes it worth having on a big program: a name defined once and called in forty places is hard to find by searching, and this says where it was written.
The kind comes first because Program and Data are separate memories on this machine, so an address on its own does not say where something is. In a loadable program the two segments are usually based far enough apart that the difference is easy to overlook. In a boot image both start at 0x0000, and every address in the table appears twice. The kind comes first because Program and Data are separate memories on this machine, so an address on its own does not say where something is. In a loadable program the two segments are usually based far enough apart that the difference is easy to overlook. In a boot image both start at 0x0000, and every address in the table appears twice.
+4 -4
View File
@@ -838,8 +838,8 @@ if "## Running the Assembler:" not in am:
else: else:
section = am.split("## Running the Assembler:")[1].split("\n## ")[0] section = am.split("## Running the Assembler:")[1].split("\n## ")[0]
said = [row.split(" |")[0] for row in re.findall(r'^\| (\w+ \|.*)$', section, re.M) said = [row.split(" |")[0] for row in re.findall(r'^\| (\w+ \|.*)$', section, re.M)
if row.split(" |")[0] in ("Kind", "Address", "Name", "File", "Line", "Number")] if row.split(" |")[0] in ("Kind", "Number", "Address", "Name", "File", "Line")]
if said != ["Kind", "Address", "Name", "File", "Line", "Number"]: if said != ["Kind", "Number", "Address", "Name", "File", "Line"]:
problems.append("the Assembler Manual does not describe the symbol file's six" problems.append("the Assembler Manual does not describe the symbol file's six"
" fields in order; it lists %s" % (said or "none")) " fields in order; it lists %s" % (said or "none"))
else: else:
@@ -875,7 +875,7 @@ else:
" Assembler Manual describes six" " Assembler Manual describes six"
% (number, len(fields))) % (number, len(fields)))
break break
kind, address, name, source, where, index = fields kind, index, address, name, source, where = fields
kinds.add(kind) kinds.add(kind)
if kind not in ("Program", "Data", "Vector", "Device"): if kind not in ("Program", "Data", "Vector", "Device"):
problems.append("a symbol file calls something a %r, and the" problems.append("a symbol file calls something a %r, and the"
@@ -934,7 +934,7 @@ else:
# drift lands on a line that has nothing to do with the name. # drift lands on a line that has nothing to do with the name.
sources = {} sources = {}
for row in open(dump).read().splitlines(): for row in open(dump).read().splitlines():
kind, address, name, source, where, index = row.split("\t") kind, index, address, name, source, where = row.split("\t")
if source not in sources: if source not in sources:
try: try:
sources[source] = open(source).read().splitlines() sources[source] = open(source).read().splitlines()