A program reading a line gets the editing too

osReadLine goes through the shell's editor now, so anything that asks the system
for a line gets arrows, Home, End and Delete. The editor is a program, and a word
typed with two letters the wrong way round can be put right without starting the
line again.

IT DOES NOT GET THE HISTORY, and that is the interesting half. 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" into it. The history belongs to the
thing whose lines are commands. Two entry points rather than a flag the caller
sets first, so a caller cannot forget which it wanted.

And the console is put back the way it was FOUND rather than the way the shell
likes it. A program that had asked for key mode and then read a line through the
system used to be handed back a console in line mode having asked for nothing of
the sort. The status port reports all three things the control port can ask for,
in the same order two bits along, so one shift turns what the console IS into
what to write to make it that again.

Which uncovered a real fault in the console. READING THE STATUS PORT WAS EATING A
KEY: in line mode the poll consumed an arrow key and dropped it, so a program
that looked and then asked for key mode - exactly what reading a line now does -
found the first key it was reaching for already gone. A look must not consume
what it cannot report, because the mode can change. It is held now and delivered
as soon as something will take it. A blocking read still discards it, and must:
that read IS the delivery, and a byte held there would be met again forever.

Four recordings gained a program's echo, and cosmosEdit's went from
"> : : : : > : : > 1: alpha" to a session you can read. VERIFIED THE SAME WAY AS
BEFORE: with only the program side of the echo silenced, all 192 tests pass
against the recordings as they were before this commit, so the echo is the whole
of what changed.

cosmosEditService is the new test and it checks both halves at once. Inside Edit,
Left/Delete/Left puts "alpah" right. Up and Down do nothing there - were a
program's line walking the shell's history, the next line would come out as the
echo command from the top of the file instead of the word. And one press of Up
back at the prompt finds the command typed before Edit was started, which is the
proof that nothing the editor read went into the history at all.

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-01 14:07:09 -04:00
co-authored by Claude Opus 5
parent 71f6e215f9
commit 4d976fc22a
12 changed files with 183 additions and 24 deletions
+9 -3
View File
@@ -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. |
+63 -6
View File
@@ -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