diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index b97dbf4..8827a43 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -331,8 +331,14 @@ The line holds 127 characters. It held 63 until the shell could edit one, which limit started to be felt: a copy between two disks with a directory on each is most of the way there before anything has been said. -Everything else that reads a line - an application calling `osReadLine`, the editor - is -unchanged and still gets a plain line with no editing in it. +**An application reading a line through `osReadLine` gets the same editing**, which is the +point of the service being there: the editor is a program, and a word typed with two letters +the wrong way round can now be put right without starting the line again. + +What a program does *not* get is the history. Up and Down do nothing while a program is +reading, and nothing it reads is kept. That is not meanness: the editor would otherwise fill +the history with the text of somebody's document, and pressing Up in the middle of writing +one would put `dir` into it. The history belongs to the thing whose lines are commands. ### Paths: @@ -1017,7 +1023,7 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w | Service | Does | | --- | --- | | osPrintString | DP0 names a string ending in a zero byte. Prints it. | -| osReadLine | DP0 names somewhere to put a line, B says how much room there is. Reads one from the console. Q comes back holding how long it was. | +| osReadLine | DP0 names somewhere to put a line, B says how much room there is. Reads one from the console, with the shell's own editing - arrows, Home, End, Delete - but not its history. Q comes back holding how long it was. The console is left in whatever mode it was found in. | | osExit | Gives the machine back. Does not return. | | osArgument | DP0 names somewhere to put whatever followed the run command, B says how much room there is. | | osFileRead | DP0 names a file, DP1 says where to put it. Q is zero if it read, and DP3 comes back holding how many bytes there were. | diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 46ae33f..2a88e4a 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -434,6 +434,17 @@ shellReadRawTyped: ; ConsoleEndOfInput is set if the console ran out instead of a line being finished, which is ; readLine's other promise and the one the shell uses to know when to stop. ; +; ---- Two doors, because the history is the SHELL'S ---- +; +; editLine is what the shell reads through and keeps what it is given. editLinePlain is what +; a program reads through, and does not: Edit would otherwise fill the history with the text +; of somebody's document, and pressing Up in the middle of writing one would put "dir" in +; it. A program gets the editing, which is what it wanted; the history belongs to the thing +; whose lines are commands. +; +; Two entry points rather than a flag a caller sets first, because a caller cannot forget to +; do this one. +; ; ---- Why most keystrokes draw nothing but themselves ---- ; ; A character typed at the END of a line needs no cursor moved: printing it is the whole of @@ -443,6 +454,14 @@ shellReadRawTyped: ; transcript in the test suite with them. So the cheap path is the common one, and the line ; is only reprinted when something happened in the middle of it. editLine: + INIA 0x01 + BRI editLineBegin +editLinePlain: + RSTA +editLineBegin: + SETD.1 EditKeepHistory + STA.1 + SETD.1 EditBase STD.0.1 SETD.1 EditRoom @@ -477,8 +496,29 @@ editWidthKnown: SETD.1 EditWidth STA.1 - ; Key mode, with the cursor the shell keeps. Nothing echoes from here on: everything that - ; appears below is put there by this routine. + ; ---- How the console was found, so it can be put back that way ---- + ; + ; Not "line mode with a cursor", which is only how the SHELL keeps it. A program that had + ; asked for key mode and then read a line through the system would have been handed back a + ; console in line mode, having asked for nothing of the sort. + ; + ; The status port reports every one of the three things the control port can ask for, and + ; reports them in the same order two bits along - so one shift turns what the console IS + ; into what to write to make it that again. + INA 0x01 + RSTB + SHR + SHR + INIB 0d7 + AND + MVQA + SETD.1 EditWasControl + STA.1 + + ; Key mode with a cursor, and INTERRUPTS OFF whatever they were. Nothing echoes from here + ; on: everything that appears below is put there by this routine. Interrupts are off + ; because this is about to block on the data port, and the manual is explicit that a + ; program does one or the other and not both. INIA 0x05 OUTA 0x02 @@ -746,6 +786,9 @@ editEnd: ; makes the padding below matter: a long line replaced by a short one leaves the tail of the ; long one behind unless something rubs it out. editUp: + SETD.1 EditKeepHistory + LDA.1 + BRA editKey ; A program's line has no history behind it. SETD.1 HistoryPick LDA.1 BRA editKey ; Already at the oldest one kept. @@ -769,6 +812,9 @@ editUpMoving: BRI editRecall editDown: + SETD.1 EditKeepHistory + LDA.1 + BRA editKey ; A program's line has no history behind it. SETD.1 HistoryPick LDA.1 SETD.1 HistoryCount @@ -829,12 +875,15 @@ editDoneEnd: ; Kept before the buffer is handed over and while EditLength still says how long it is. ; Only on this path: a console that ran out was not somebody finishing a line. + SETD.1 EditKeepHistory + LDA.1 + BRA editFinish CALL historyAdd editFinish: - ; Line mode, with the cursor still on: that is how boot left the console and how every - ; program that knows nothing of any of this expects to find it. - INIA 0x04 + ; And the console back exactly as it was found. + SETD.1 EditWasControl + LDA.1 OUTA 0x02 SETD.1 EditBase @@ -3239,7 +3288,7 @@ handlePrintString: RETI handleReadLine: - CALL readLine + CALL editLinePlain ; readLine works out how long the line was, and it is already in Q where readLine left ; it. SRET keeps Q and DP3 and puts everything else back, so the answer simply stands. @@ -5629,6 +5678,14 @@ EditWalkColumn: EditWalkBack: 0x00 +; What the control port has to be written to put the console back how this routine found it. +EditWasControl: + 0x00 + +; Whether the line being read is one to remember. The shell's are; a program's are not. +EditKeepHistory: + 0x00 + ; How many characters of the line are on the screen, which is not always how many are in it: ; between a line getting shorter and the screen being put right, the screen still has the ; old one on it. That gap is one character wide for a Delete and a whole line wide for a diff --git a/Source/Emulator/io.c b/Source/Emulator/io.c index 5c3a09a..9942c96 100644 --- a/Source/Emulator/io.c +++ b/Source/Emulator/io.c @@ -602,8 +602,25 @@ static int consoleKeyFromInput(int mayWait) { // Nothing to hand over: either that sequence meant nothing here, or it meant a key // and line mode does not deliver keys. Both are the same answer to a caller - there // is still no byte - so a blocking read asks again and a poll says so and leaves. - if (got < 0 || (!consoleKeyMode && got >= CONSOLE_KEY_FIRST && got <= CONSOLE_KEY_LAST)) { + const int undeliverable = + !consoleKeyMode && got >= CONSOLE_KEY_FIRST && got <= CONSOLE_KEY_LAST; + if (got < 0 || undeliverable) { if (!mayWait) { + // ---- A LOOK MUST NOT CONSUME WHAT IT CANNOT REPORT ---- + // + // This is the status port asking, and either way it has no byte to report. + // But a key that line mode will not deliver is not the same as a key that is + // gone: THE MODE CAN CHANGE. A program that polls and then asks for key mode + // - which is exactly what the shell does before it reads a line - would find + // that the first key it was reaching for had been swallowed by the looking. + // + // So it is held rather than dropped, and delivered as soon as something is + // willing to take it. The blocking read below drops it instead, and must: + // that read IS the delivery, line mode genuinely has no use for the key, and + // a byte held there would be met again forever. + if (undeliverable) { + consoleHeldByte = got; + } return CONSOLE_NOTHING_YET; } continue; diff --git a/SplitBit Programming Manual.md b/SplitBit Programming Manual.md index b74dc37..495298c 100644 --- a/SplitBit Programming Manual.md +++ b/SplitBit Programming Manual.md @@ -309,6 +309,8 @@ Backspace is 0x08 and always has been. It is a different key from Delete and doe **These arrive in key mode only.** Line mode delivers characters, and a program in line mode is being handed a line that something else has already finished editing, so a key meaning "move the cursor left" arrived too late to mean anything. The console drops them there. This is what a terminal does too: it has always given a program in line mode backspace and line kill, and has never given it arrow keys. +**But asking the status port in line mode does not throw one away.** A key line mode will not deliver is not the same as a key that is gone, because the mode can change: a program that looks at the status port and then asks for key mode - which is exactly what a system does before it reads a line - would otherwise find that the first key it was reaching for had been swallowed by the looking. So the console holds it and delivers it as soon as something is willing to take it. Reading the data port in line mode does discard it, and must: that read is the delivery, and a key held there would be met again forever. + **What a key means is not the console's business.** Where the cursor goes, what the line looks like afterwards and what was typed before are all decisions, and decisions belong to whatever is reading - which on this machine is usually CosmOS, whose shell edits its own line. The console says which key was pressed and stops there, exactly as the disk says what a drive is and says nothing about what should be on it. ### Reading Without Waiting: diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index 608bedf..992a8c5 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -79,7 +79,7 @@ from `make`, not from here. ### 1. Recorded output `Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares -everything it printed against a file in `Tests/expected`. 191 tests, of which 129 run, 35 +everything it printed against a file in `Tests/expected`. 192 tests, of which 130 run, 35 only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image given at all. diff --git a/Tests/expected/cosmosEdit.out b/Tests/expected/cosmosEdit.out index 2ed9233..16f17b7 100644 --- a/Tests/expected/cosmosEdit.out +++ b/Tests/expected/cosmosEdit.out @@ -3,22 +3,40 @@ CosmOS loaded, starting at 4000 > run poem.txt poem.txt, new file -> : : : : > : : > 1: alpha +> a +: alpha +: beta +: gamma +: . +> i 2 +: INSERTED +: . +> l +1: alpha 2: INSERTED 3: beta 4: gamma -> : > > 1: CHANGED +> c 1 +: CHANGED +> d 4 +> l +1: CHANGED 2: INSERTED 3: beta -> written, 22 bytes -> finished +> w +written, 22 bytes +> q +finished > run poem.txt poem.txt, 3 lines -> 1: CHANGED +> l +1: CHANGED 2: INSERTED 3: beta -> there is no such line -> finished +> d 99 +there is no such line +> q +finished > exit halted Execution halted. diff --git a/Tests/expected/cosmosEditLong.out b/Tests/expected/cosmosEditLong.out index 9018d6d..6554090 100644 --- a/Tests/expected/cosmosEditLong.out +++ b/Tests/expected/cosmosEditLong.out @@ -1,10 +1,12 @@ CosmOS > Edit hello.asm hello.asm, 31 lines -> finished +> q +finished > Edit hello.asm hello.asm, 31 lines -> finished +> q +finished > Status the last program left 0, which is: it did what it was asked finished diff --git a/Tests/expected/cosmosEditService.out b/Tests/expected/cosmosEditService.out new file mode 100644 index 0000000..faac458 --- /dev/null +++ b/Tests/expected/cosmosEditService.out @@ -0,0 +1,26 @@ +CosmOS +> echo remembered by the shell +remembered by the shell +> load Edit.sbx +loaded, starting at 4000 +> run typed.txt +typed.txt, new file +> a +: alpahalpa alpha +: second +: . +> l +1: alpha +2: second +> w +written, 13 bytes +> q +finished +> run typed.txt +typed.txt, 2 lines +> q +finished +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 637c635..b9bc223 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -33,11 +33,13 @@ not a program loaded, starting at 4000 > run a program, loaded off a disk, running on the system that loaded it -what should I call you? hello, Anachronaut. that is all I do. +what should I call you? Anachronaut +hello, Anachronaut. that is all I do. finished > run a program, loaded off a disk, running on the system that loaded it -what should I call you? hello, Claude. that is all I do. +what should I call you? Claude +hello, Claude. that is all I do. finished > exit halted diff --git a/Tests/expected/cosmosTreeWrite.out b/Tests/expected/cosmosTreeWrite.out index c7c5966..6014cc4 100644 --- a/Tests/expected/cosmosTreeWrite.out +++ b/Tests/expected/cosmosTreeWrite.out @@ -7,8 +7,14 @@ Edit.sbx 2243 loaded, starting at 4000 > run note.txt note.txt, new file -> : : : > written, 67 bytes -> finished +> a +: a line written onto a disk with directories on it +: and a second one +: . +> w +written, 67 bytes +> q +finished > dir Folder Edit.sbx 2243 diff --git a/Tests/input/cosmosEditService.in b/Tests/input/cosmosEditService.in new file mode 100644 index 0000000..9e4cb3b --- /dev/null +++ b/Tests/input/cosmosEditService.in @@ -0,0 +1,13 @@ +echo remembered by the shell +load Edit.sbx +run typed.txt +a +alpah‚†‚h +sec€ond +. +l +w +q +€ +q +exit diff --git a/Tests/manifest b/Tests/manifest index 4056185..76e5393 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -336,6 +336,16 @@ cosmosEditKeys | CosmOS/Source/cosmos.asm | run | cosmosEdi # the tail of the long one on the screen unless something covers it, and one space - which # is all a Delete ever needs - covers nothing. cosmosHistory | CosmOS/Source/cosmos.asm | run | cosmosHistory.in | - | disks/cosmos.img +# The same editing offered to a PROGRAM, through osReadLine. Edit reads its lines that way, +# so a word typed with two letters the wrong way round is put right without starting the line +# again - which is the whole of what A4 buys. +# +# It also says what a program does NOT get. Up and Down do nothing while Edit is reading: +# were a program's line walking the shell's history, the second line here would come out as +# the echo command at the top instead of the word. And the last Up proves the reverse - that +# everything Edit read went nowhere near the history, since one press finds the line typed +# before Edit was started. +cosmosEditService | CosmOS/Source/cosmos.asm | run | cosmosEditService.in | - | disks/editor.img # The shell taking things off a disk and calling them something else, which is the last of # CosmOS's original four verbs to be built and the first time anything has changed a disk # from the shell. Its own image, because it changes what is on it: a fixture named with a