From a842c884e8d52545ad0dda118d3ec4fb22ebe524 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Mon, 17 Aug 2026 20:10:33 -0400 Subject: [PATCH] Added assembler check for string outside Data Segment. --- .../diagnostics/stringInProgram.asm | 19 +++++++++++++++ Source/Assembler/assembly.h | 13 ++++++---- Source/Assembler/firstPass.c | 24 +++++++++++++++++-- Source/Assembler/secondPass.c | 6 ++--- SplitBit Assembler Manual.md | 2 ++ SplitBit Programming Manual.md | 2 +- Tests/manifest | 4 ++++ 7 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 Programs/testPrograms/diagnostics/stringInProgram.asm diff --git a/Programs/testPrograms/diagnostics/stringInProgram.asm b/Programs/testPrograms/diagnostics/stringInProgram.asm new file mode 100644 index 0000000..7e1c6a7 --- /dev/null +++ b/Programs/testPrograms/diagnostics/stringInProgram.asm @@ -0,0 +1,19 @@ +; A string written into the Program Segment. +; +; This used to assemble. The string contributed no bytes and no message: the second pass +; has a case for putting a string in the Data buffer and none for the Program buffer, so +; it fell through the switch and was gone. A program written this way would branch to a +; label that pointed at whatever followed instead. +; +; It is refused rather than emitted, because a string in Program Memory could not be read +; by the program holding it. No instruction on this machine reads Program Memory; only the +; memory controller can. Anybody writing this meant #Data. + +#Program + +start: + SETD.0 Greeting + HALT + +Greeting: +"this is not where this goes" diff --git a/Source/Assembler/assembly.h b/Source/Assembler/assembly.h index 40a7e1f..17d874b 100644 --- a/Source/Assembler/assembly.h +++ b/Source/Assembler/assembly.h @@ -42,7 +42,7 @@ // // The top kilobyte of Program Memory is reserved for vectors. Both tools have to // agree on where it begins: the CPU starts execution through it, and the assembler -// has to refuse program text that would run into it. +// has to refuse a Program Segment that would run into it. // // Entries are two bytes each, most significant byte first, the same order the branch // instructions and this file format already use. @@ -88,9 +88,14 @@ #define VECTOR_FIRST_PINNED 16 #define VECTOR_FIRST_AUTO 64 -// The first address the vector table occupies, and so the first address that program -// text may not use. -#define PROGRAM_TEXT_LIMIT SOFTWARE_VECTOR_BASE +// The first address the vector table occupies, and so the first address the Program +// Segment may not reach. +// +// The Segment rather than the code: instructions are most of what goes there, but not all +// of it. Literal bytes go there, a label named in the Program Segment puts its two byte +// address there, and #Align and #Reserve put runs of zeroes there. What the limit measures +// is how far all of that together has pushed the cursor. +#define PROGRAM_SEGMENT_LIMIT SOFTWARE_VECTOR_BASE #define SPLITBIT_MAGIC "SPBT" #define SPLITBIT_MAGIC_LENGTH 4 diff --git a/Source/Assembler/firstPass.c b/Source/Assembler/firstPass.c index edf301d..3623214 100644 --- a/Source/Assembler/firstPass.c +++ b/Source/Assembler/firstPass.c @@ -330,9 +330,29 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter printf(" File: %s at line %d.\n", fileName, lineNumber); exit(1); } + // A string, which only the Data Segment can hold. Nothing below this line looks at + // strings, so without this they would fall past every check and out the bottom, + // and a string written anywhere else would assemble to nothing at all and say so + // to nobody. + } else if ((*intermediateArray)[*intermediateIndex].type == STRING) { + if (status == PROGRAM) { + fprintf(stderr, RED "Error: A string cannot go in the Program Segment.\n" + " Strings live in Data Memory, which is the only memory an instruction\n" + " can read. A string in Program Memory could not be reached even by the\n" + " program holding it, except through the memory controller.\n" RESET); + printf(" File: %s at line %d.\n", fileName, lineNumber); + printf(" The string: \"%s\"\n", (*intermediateArray)[*intermediateIndex].token); + printf(" Move it below a #Data line.\n"); + exit(1); + } + if (status != DATA) { + fprintf(stderr, RED "Error: Attempting to write a string to nowhere!\n Did you forget to use the #Data keyword?\n" RESET); + printf(" File: %s at line %d.\n", fileName, lineNumber); + printf(" The string: \"%s\"\n", (*intermediateArray)[*intermediateIndex].token); + exit(1); + } // Finally, check if it's a label or label definition. - // First, make sure it hasn't already been marked as a string literal. - } else if ((*intermediateArray)[*intermediateIndex].type != STRING) { + } else { if (checkIfLabel(&(*intermediateArray)[*intermediateIndex])) { if (status == NOWHERE) { fprintf(stderr, RED "Error: Attempting to create or use a label nowhere!\n Did you forget to use the #Program or #Data keyword?\n" RESET); diff --git a/Source/Assembler/secondPass.c b/Source/Assembler/secondPass.c index 224ada7..5d84ef1 100644 --- a/Source/Assembler/secondPass.c +++ b/Source/Assembler/secondPass.c @@ -96,10 +96,10 @@ void populateLabelTable(intermediateElement *intermediateArray, int arraySize) { if (debugSecondPass) printf("Token: %s with byte length %d to destination %d of type %d\n", intermediateArray[i].token ,intermediateArray[i].byteLength, intermediateArray[i].destination, intermediateArray[i].type); } - if (programCount > PROGRAM_TEXT_LIMIT ) { + if (programCount > PROGRAM_SEGMENT_LIMIT ) { fprintf(stderr, RED "Error: Program is too long to fit in Program Memory.\n" - " Program text may not run past 0x%04X, where the vector table begins.\n" RESET, - PROGRAM_TEXT_LIMIT - 1); + " The Program Segment may not run past 0x%04X, where the vector table begins.\n" RESET, + PROGRAM_SEGMENT_LIMIT - 1); exit(1); } if (dataCount > 0xFFFF ) { diff --git a/SplitBit Assembler Manual.md b/SplitBit Assembler Manual.md index 916b172..7f8172b 100644 --- a/SplitBit Assembler Manual.md +++ b/SplitBit Assembler Manual.md @@ -24,6 +24,8 @@ Any token beginning with a '0' is read as a numerical literal, so a malformed on A string may be up to 255 characters. Each one is written down with a zero byte on the end, which is what lets a program find where it stops, and it means two strings written one after the other are not one longer string: there is a zero between them. A run of bytes longer than a string can hold has to be written as literals, or put there by the program itself while it runs. +**Strings belong in the Data Segment, and only there.** This is a Harvard machine: no instruction reads Program Memory, so a string put in the Program Segment could not be read by the program carrying it, and only the memory controller could reach it at all. The assembler refuses one rather than emitting bytes nothing can use. Single byte literals are a different matter and may go in either segment — a table of bytes a program branches through is a reasonable thing to want in Program Memory. + The one exception to the single byte rule is #Align and #Reserve, whose numbers are never emitted as bytes and may go up to 0xFFFF. See Moving The Cursor Along. ## Labels: diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index faf0a5a..f24d76d 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -50,7 +50,7 @@ The top kilobyte of Program Memory is reserved for vectors. Each entry is two by | 0xFC00 | Software vectors 0 to 255 | | 0xFE00 | Hardware vectors 0 to 255, one for each I/O port | -Program text may not run past 0xFBFF. The assembler refuses to assemble a program that would. +The Program Segment may not run past 0xFBFF. The assembler refuses to assemble a program that would. The software vectors are given out like this: diff --git a/Tests/manifest b/Tests/manifest index 7bec3bb..8985432 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -277,6 +277,10 @@ diagDuplicateVector | testPrograms/diagnostics/duplicateVector.asm | xfail | - # The two mistakes that pinning a number makes possible. Numbers the assembler hands out # cannot collide; numbers a person writes down can, and can also be written outside the # range set aside for them. +# A string in the Program Segment, which used to assemble to nothing at all and say so to +# nobody. Strings only have a case for the Data buffer, so one aimed anywhere else fell +# through and vanished. +diagStringInProgram | testPrograms/diagnostics/stringInProgram.asm | xfail | - | - diagPinnedRange | testPrograms/diagnostics/pinnedVectorRange.asm | xfail | - | - diagPinnedTaken | testPrograms/diagnostics/pinnedVectorTaken.asm | xfail | - | - diagBareSWI | testPrograms/diagnostics/bareSWI.asm | xfail | - | -