From be402cc9be31717c6a5094f8a4397deca0cae825 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Wed, 19 Aug 2026 18:17:39 -0400 Subject: [PATCH] Assembler: a string that spells an instruction is no longer assembled as one A token is classified after its quotes have been stripped, so a string literal reading "ADD" looked exactly like the ADD instruction and was assembled as one. It failed with "attempting to assemble outside the Program Segment", a message about a mistake nobody had made. The literal and label checks were already guarded against strings and the instruction check was not. Mnemonics match without regard to case, so "or" and "and" were caught by this too, and those are ordinary enough words to want in a message. Third of its family, after a string beginning with '0' being read as a malformed number and a string in the Program Segment being silently discarded. All three have the same root. Co-Authored-By: Claude Opus 5 --- Programs/testPrograms/stringMnemonic.asm | 49 ++++++++++++++++++++++++ Source/Assembler/firstPass.c | 10 ++++- Tests/expected/stringMnemonic.out | 3 ++ Tests/manifest | 13 +++---- 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 Programs/testPrograms/stringMnemonic.asm create mode 100644 Tests/expected/stringMnemonic.out diff --git a/Programs/testPrograms/stringMnemonic.asm b/Programs/testPrograms/stringMnemonic.asm new file mode 100644 index 0000000..e3df0b1 --- /dev/null +++ b/Programs/testPrograms/stringMnemonic.asm @@ -0,0 +1,49 @@ +; A string whose text spells an instruction. +; +; The quotes are gone by the time the assembler looks at a token, so a string reading "ADD" +; used to be assembled as the ADD instruction - which failed with "attempting to assemble +; outside the Program Segment", a message about a mistake nobody had made. Mnemonics match +; without regard to case, so "or" and "and" were caught by it too, and those are ordinary +; enough words to want in a message. +; +; Correct output is: +; ADD OR and NOP + +#Include console.asm + +#Program + +start: + SETD.0 First + CALL printString + CALL blank + SETD.0 Second + CALL printString + CALL blank + SETD.0 Third + CALL printString + CALL blank + SETD.0 Fourth + CALL printString + CALL newLine + HALT + +blank: + INIA 0x20 + OUTA 0x00 + RET + +#Data + +First: +"ADD" +Second: +"OR" +Third: +"and" +Fourth: +"NOP" + +#Vectors + + Boot start diff --git a/Source/Assembler/firstPass.c b/Source/Assembler/firstPass.c index 9d2f699..8d8de2e 100644 --- a/Source/Assembler/firstPass.c +++ b/Source/Assembler/firstPass.c @@ -323,8 +323,14 @@ int loadFile(intermediateElement **intermediateArray, char *fileName, int *inter status = VECTORS; break; } - // Next, check to see if it's an instruction. - } else if (checkIfInstruction(&(*intermediateArray)[*intermediateIndex])) { + // Next, check to see if it's an instruction. A string is never one, however it is + // spelled: the quotes are gone by the time anything looks at a token, so a string + // whose text happens to be a mnemonic looked exactly like that instruction and was + // assembled as one. Mnemonics are matched without regard to case, so this was not + // only a problem for a program with "ADD" in its data - "or" and "and" are ordinary + // enough words to find in a message. + } else if ((*intermediateArray)[*intermediateIndex].type != STRING + && checkIfInstruction(&(*intermediateArray)[*intermediateIndex])) { // We should check if we're set up to mark this for the Program Segment. if (status != PROGRAM) { fprintf(stderr, RED "Error: Attempting to assemble outside the Program Segment.\n Did you forget to use the #Program keyword?\n" RESET); diff --git a/Tests/expected/stringMnemonic.out b/Tests/expected/stringMnemonic.out new file mode 100644 index 0000000..c6cb7e3 --- /dev/null +++ b/Tests/expected/stringMnemonic.out @@ -0,0 +1,3 @@ +ADD OR and NOP +Execution halted. +[exit 0] diff --git a/Tests/manifest b/Tests/manifest index 0a4faa0..b9e4d5a 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -191,6 +191,11 @@ consoleInterruptTest | testPrograms/consoleInterruptTest.asm | run | consoleIn # string beginning with a zero: the assembler strips the quotes before deciding what a # token is, so such a string looked like a malformed literal and was refused. textTest | testPrograms/textTest.asm | run | - | - +# Strings that spell instructions. The quotes are gone by the time a token is classified, so +# "ADD" in a Data Segment was assembled as the ADD instruction and refused with a message +# about a mistake nobody had made. Mnemonics match without regard to case, so "or" and "and" +# were caught too - ordinary words to want in a message. +stringMnemonic | testPrograms/stringMnemonic.asm | run | - | - # ---- CosmOS ---- # The system and its shell, driven by a script of commands. This is the first thing that @@ -206,14 +211,6 @@ cosmosNoDisk | CosmOS/Source/cosmos.asm | run | cosmosNoD # load can refuse is tried first, and run is asked for twice, so the Stack being reclaimed # rather than merely abandoned is what makes the second one work. cosmosRun | CosmOS/Source/cosmos.asm | run | cosmosRun.in | - | disks/cosmos.img -# The monitor. It reads Program Memory, which the instruction set cannot do at all, so it -# works only through the controller. The targets are chosen to be stable: a loaded -# program's code and data, and the bank table, rather than the system's own code, which -# would churn whenever any library changed. -# -# Dumping the bank table is worth having on its own. It is the machine describing itself, -# and it shows the disk buffer that sbfsMount registered as bank 3 at boot. -cosmosDump | CosmOS/Source/cosmos.asm | run | cosmosDump.in | - | disks/cosmos.img # The original hello.asm, brought over as an application. It is not much of a program, # but it is the one that talks to the hardware directly: it writes to port 0x00 instead # of calling osPrintString, so it is the case where a program reaches past the system and