The shell remembers what was typed before it
Up walks back through the last eight lines and Down forward again. It exists only because the keys reach the system now: until A1 and A2 there was nothing to press Up at, and the line was assembled somewhere the shell could not see. A RING RATHER THAN A LIST. A ninth line pushes the oldest out by moving where the ring starts, not by moving any of the lines - so keeping a line costs a copy of that line and nothing else, however full the history is. Eight is a power of two, so which slot an entry lives in is an AND. The ISA had the awkward part already: A and B are a sixteen bit shift register, so one SHR with B empty turns a slot number into the offset of a 128 byte slot, high byte and low, ready for DPUW. A NINTH SLOT HOLDS WHAT WAS BEING TYPED when Up left it, and Down brings it back. Losing a half written line to a keypress is the sort of small rudeness that makes a thing unpleasant to use, and it costs one slot to avoid. An empty line is not kept, and neither is one the same as the line already at the top. The test proves the second by looking one further back: if a repeated command were kept twice, the line behind the newest would be the same line again. The redraw had to learn to rub out. One space was enough while the only thing that shortened a line was taking one character out of it; a recalled line replaces the whole of it, and a short line over a long one left the tail of the long one on screen looking like part of what you were typing. It now covers exactly what was lost - which turned out to be one space fewer than before in the cases that GREW, so two lines of cosmosEditKeys lost a trailing space that was never doing anything. Costs 1157 bytes of Data Memory, taking CosmOS to 6220 of the 8192 it has before a loaded program's data begins. Worth writing down: that is the budget, and this is the largest single thing in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
81e544eb3d
commit
71f6e215f9
@@ -294,6 +294,7 @@ line can be moved about in at all.
|
||||
| Home, End | Go to the start of the line or the end of it. |
|
||||
| Backspace | Take out the character before the cursor. |
|
||||
| Delete | Take out the one under it. |
|
||||
| Up, Down | Walk back through the last eight lines, and forward again. |
|
||||
| Return | Finish the line, wherever the cursor happens to be sitting. |
|
||||
|
||||
Anything typed goes in where the cursor is, so a word left out of the middle of a line is
|
||||
@@ -307,6 +308,25 @@ line, and nowhere for a history to live. Now the console delivers keys and says
|
||||
about what they mean, the same way it reports what a drive is and says nothing about what
|
||||
should be on it, and the shell decides.
|
||||
|
||||
### The History:
|
||||
|
||||
The last eight lines are kept, and Up walks back through them. It is a ring: a ninth line
|
||||
pushes the oldest out by moving where the ring starts rather than by moving any of the
|
||||
lines.
|
||||
|
||||
An empty line is not kept - that is somebody pressing Return - and neither is a line the
|
||||
same as the one already at the top, so running a command twice does not put it in twice.
|
||||
|
||||
**What you were typing is kept too.** Pressing Up when you were half way through a line puts
|
||||
that line somewhere and Down brings it back, so looking at what you did before does not cost
|
||||
you what you were doing.
|
||||
|
||||
The history belongs to the shell rather than to the console, and that is the whole reason it
|
||||
can exist: until the keys reached the system there was nothing to press Up at. It is shared
|
||||
with the monitor, which reads its lines the same way - and since the monitor reads into a
|
||||
buffer of forty characters where the shell reads into one of 127, a longer line recalled
|
||||
there is cut short rather than written past the end of it.
|
||||
|
||||
The line holds 127 characters. It held 63 until the shell could edit one, which is when the
|
||||
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.
|
||||
|
||||
@@ -453,6 +453,8 @@ editLine:
|
||||
STA.1
|
||||
SETD.1 EditAt
|
||||
STA.1
|
||||
SETD.1 EditDrawn
|
||||
STA.1
|
||||
SETD.1 ConsoleEndOfInput
|
||||
STA.1
|
||||
|
||||
@@ -531,6 +533,12 @@ editNotEnd:
|
||||
INIB 0x85
|
||||
XOR
|
||||
BRQ editEnd
|
||||
INIB 0x80
|
||||
XOR
|
||||
BRQ editUp
|
||||
INIB 0x81
|
||||
XOR
|
||||
BRQ editDown
|
||||
|
||||
; Anything else is a character if it is printable and nothing at all if it is not. The
|
||||
; console's own keys that this shell has no use for land here and are ignored rather than
|
||||
@@ -600,6 +608,10 @@ editInsertPut:
|
||||
SETD.1 EditChar
|
||||
LDA.1
|
||||
OUTA 0x00
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
SETD.1 EditDrawn
|
||||
STA.1
|
||||
BRI editKey
|
||||
editInsertRedraw:
|
||||
CALL editRedraw
|
||||
@@ -636,6 +648,10 @@ editBack:
|
||||
OUTA 0x00
|
||||
INIA 0x08
|
||||
OUTA 0x00
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
SETD.1 EditDrawn
|
||||
STA.1
|
||||
BRI editKey
|
||||
editBackMiddle:
|
||||
CALL editTakeOut
|
||||
@@ -722,6 +738,64 @@ editEnd:
|
||||
LDA.1
|
||||
SETD.1 EditAt
|
||||
STA.1
|
||||
BRI editShow
|
||||
|
||||
; ---- Backwards and forwards through what was typed before ----
|
||||
;
|
||||
; The line on the screen is replaced outright, so both of these redraw. That is also what
|
||||
; 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 HistoryPick
|
||||
LDA.1
|
||||
BRA editKey ; Already at the oldest one kept.
|
||||
|
||||
; Leaving the line being typed for the first time, so it is put somewhere. Down brings it
|
||||
; back, and a person who pressed Up to look at something gets their line returned rather
|
||||
; than taken.
|
||||
SETD.1 HistoryCount
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BNQ editUpMoving
|
||||
SETD.3 HistoryTyped
|
||||
CALL historyPut
|
||||
editUpMoving:
|
||||
SETD.1 HistoryPick
|
||||
LDA.1
|
||||
DECA
|
||||
STA.1
|
||||
CALL historySlot
|
||||
BRI editRecall
|
||||
|
||||
editDown:
|
||||
SETD.1 HistoryPick
|
||||
LDA.1
|
||||
SETD.1 HistoryCount
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BRQ editKey ; Already back at the line being typed.
|
||||
SETD.1 HistoryPick
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
SETD.1 HistoryCount
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BRQ editDownTyped
|
||||
SETD.1 HistoryPick
|
||||
LDA.1
|
||||
CALL historySlot
|
||||
BRI editRecall
|
||||
editDownTyped:
|
||||
SETD.3 HistoryTyped
|
||||
editRecall:
|
||||
CALL historyTake
|
||||
CALL editRedraw
|
||||
BRI editKey
|
||||
|
||||
editShow:
|
||||
SETD.1 EditAt
|
||||
LDA.1
|
||||
@@ -752,6 +826,10 @@ editDone:
|
||||
CALL editPlace
|
||||
editDoneEnd:
|
||||
CALL newLine
|
||||
|
||||
; 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.
|
||||
CALL historyAdd
|
||||
editFinish:
|
||||
|
||||
; Line mode, with the cursor still on: that is how boot left the console and how every
|
||||
@@ -799,10 +877,48 @@ editRedrawNext:
|
||||
DECB
|
||||
BRI editRedrawNext
|
||||
editRedrawTail:
|
||||
; One space after it. A line that has just got shorter has a character left over on the
|
||||
; end, and printing over it costs less than working out whether there is one.
|
||||
INIA 0x20
|
||||
OUTA 0x00
|
||||
; ---- Rubbing out what is no longer there ----
|
||||
;
|
||||
; One space was enough while the only thing that ever shortened a line was taking one
|
||||
; character out of it. Pressing Up replaces the whole line, and a long one replaced by a
|
||||
; short one leaves the tail of the long one sitting on the screen looking like part of
|
||||
; what you are typing.
|
||||
;
|
||||
; So the spaces go on for exactly as far as the line has shrunk, which is one after a
|
||||
; character was taken out and none at all when one was put in. printSpaces takes none for
|
||||
; an answer and prints nothing, which is what makes that case cost nothing to allow.
|
||||
SETD.1 EditDrawn
|
||||
LDA.1
|
||||
SETD.1 EditLength
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BRC editRedrawGrew ; Borrowed, so the line is longer than what is up there.
|
||||
MVQA
|
||||
BRI editRedrawPad
|
||||
editRedrawGrew:
|
||||
RSTA
|
||||
editRedrawPad:
|
||||
SETD.1 EditPrinted
|
||||
STA.1
|
||||
CALL printSpaces
|
||||
|
||||
; How much went to the screen altogether, which is how far back the start of the line is.
|
||||
SETD.1 EditPrinted
|
||||
LDA.1
|
||||
SETD.1 EditLength
|
||||
LDB.1
|
||||
CCF
|
||||
ADD
|
||||
MVQA
|
||||
SETD.1 EditPrinted
|
||||
STA.1
|
||||
|
||||
; And what is up there now is exactly the line.
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
SETD.1 EditDrawn
|
||||
STA.1
|
||||
|
||||
CALL editAnchor
|
||||
SETD.1 EditAt
|
||||
@@ -823,9 +939,8 @@ editAnchor:
|
||||
INA 0x04
|
||||
SETD.1 EditWalkColumn
|
||||
STA.1
|
||||
SETD.1 EditLength
|
||||
SETD.1 EditPrinted
|
||||
LDA.1
|
||||
INCA ; The space was printed too.
|
||||
SETD.1 EditWalkBack
|
||||
STA.1
|
||||
editAnchorWalk:
|
||||
@@ -919,6 +1034,183 @@ editPlaceDone:
|
||||
OUTA 0x04
|
||||
RET
|
||||
|
||||
; ---- What was typed before ----
|
||||
;
|
||||
; Eight lines, oldest first, in a RING: a ninth pushes the oldest out by moving where the
|
||||
; ring starts rather than by moving any of the lines. Eight is a power of two, so which slot
|
||||
; an entry lives in is an AND rather than a division, and nothing is ever copied to make
|
||||
; room.
|
||||
;
|
||||
; A NINTH SLOT HOLDS WHAT WAS BEING TYPED when Up was first pressed, and Down walks back into
|
||||
; it. Losing a half written line to a keypress is exactly the sort of small rudeness that
|
||||
; makes a thing unpleasant to use, and it costs one slot to avoid.
|
||||
;
|
||||
; The history belongs to the SHELL rather than to the console, which is the whole reason it
|
||||
; can exist at all: until the keys arrived here, there was nothing to press Up at.
|
||||
|
||||
; DP3 to history entry A, where entry nought is the oldest one still kept.
|
||||
historySlot:
|
||||
SETD.1 HistoryStart
|
||||
LDB.1
|
||||
CCF
|
||||
ADD
|
||||
MVQA
|
||||
INIB 0d7
|
||||
AND
|
||||
MVQA
|
||||
SETD.3 HistoryLines
|
||||
|
||||
; A slot is 128 bytes, and A and B are a sixteen bit shift register: one rotation right
|
||||
; with B empty turns the slot number into the offset of the slot, high byte and low.
|
||||
RSTB
|
||||
SHR
|
||||
DPUW.3
|
||||
RET
|
||||
|
||||
; Copies the line being edited into the slot DP3 names.
|
||||
historyPut:
|
||||
SETD.1 EditBase
|
||||
LDD.0.1
|
||||
SETD.1 EditLength
|
||||
LDB.1
|
||||
historyPutNext:
|
||||
BRB historyPutEnd
|
||||
LDA.0
|
||||
STA.3
|
||||
INCD.0
|
||||
INCD.3
|
||||
DECB
|
||||
BRI historyPutNext
|
||||
historyPutEnd:
|
||||
RSTA
|
||||
STA.3
|
||||
RET
|
||||
|
||||
; Copies the line in the slot DP3 names into the buffer, and puts the cursor at the end of
|
||||
; it, which is where somebody who has just recalled a line wants to be.
|
||||
;
|
||||
; A LINE TOO LONG FOR THE BUFFER IS CUT SHORT rather than written past the end of it. The
|
||||
; history is shared with the monitor, which reads into forty bytes where the shell reads
|
||||
; into a hundred and twenty seven, so this is a real case and not a defensive one.
|
||||
historyTake:
|
||||
SETD.1 EditBase
|
||||
LDD.0.1
|
||||
RSTA
|
||||
SETD.1 EditLength
|
||||
STA.1
|
||||
historyTakeNext:
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
SETD.1 EditRoom
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BNC historyTakeDone ; No room for another character.
|
||||
LDA.3
|
||||
BRA historyTakeDone
|
||||
STA.0
|
||||
INCD.0
|
||||
INCD.3
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
BRI historyTakeNext
|
||||
historyTakeDone:
|
||||
RSTA
|
||||
STA.0
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
SETD.1 EditAt
|
||||
STA.1
|
||||
RET
|
||||
|
||||
; DP0 and DP3 name strings ending in zero bytes. Q is zero if they are the same.
|
||||
;
|
||||
; textSame does this already and takes its two strings in DP0 and DP1, and getting a pointer
|
||||
; from DP3 into DP1 costs a store, a scratch word and a load. This is shorter than that and
|
||||
; says what it is doing.
|
||||
historySameAs:
|
||||
LDA.0
|
||||
LDB.3
|
||||
XOR
|
||||
BNQ historySameNo
|
||||
LDA.0
|
||||
BRA historySameYes ; They matched and they both ended.
|
||||
INCD.0
|
||||
INCD.3
|
||||
BRI historySameAs
|
||||
historySameNo:
|
||||
INIA 0x01
|
||||
RSTB
|
||||
CCF
|
||||
ADD ; Q is one.
|
||||
RET
|
||||
historySameYes:
|
||||
RSTA
|
||||
RSTB
|
||||
CCF
|
||||
ADD ; Q is zero.
|
||||
RET
|
||||
|
||||
; The line just finished, kept. An empty one is not - that is somebody pressing Return - and
|
||||
; neither is one the same as the newest already there, because running a command twice
|
||||
; should not put it in twice.
|
||||
historyAdd:
|
||||
SETD.1 EditLength
|
||||
LDA.1
|
||||
BRA historyAddDone
|
||||
|
||||
SETD.1 HistoryCount
|
||||
LDA.1
|
||||
BRA historyAddPut ; Nothing kept yet, so nothing to be the same as.
|
||||
DECA
|
||||
CALL historySlot
|
||||
SETD.1 EditBase
|
||||
LDD.0.1
|
||||
CALL historySameAs
|
||||
BRQ historyAddDone
|
||||
|
||||
historyAddPut:
|
||||
SETD.1 HistoryCount
|
||||
LDA.1
|
||||
INIB 0d8
|
||||
CCF
|
||||
SUB
|
||||
BRC historyAddRoom ; Fewer than eight, so there is a slot on the end.
|
||||
|
||||
; Full. The new line goes over the oldest and the ring starts one further along, which
|
||||
; forgets the oldest without moving any of the others.
|
||||
INIA 0d8
|
||||
CALL historySlot
|
||||
CALL historyPut
|
||||
SETD.1 HistoryStart
|
||||
LDA.1
|
||||
INCA
|
||||
INIB 0d7
|
||||
AND
|
||||
MVQA
|
||||
STA.1
|
||||
BRI historyAddDone
|
||||
|
||||
historyAddRoom:
|
||||
SETD.1 HistoryCount
|
||||
LDA.1
|
||||
CALL historySlot
|
||||
CALL historyPut
|
||||
SETD.1 HistoryCount
|
||||
LDA.1
|
||||
INCA
|
||||
STA.1
|
||||
|
||||
historyAddDone:
|
||||
; However that went, the next Up starts from the newest again.
|
||||
SETD.1 HistoryCount
|
||||
LDA.1
|
||||
SETD.1 HistoryPick
|
||||
STA.1
|
||||
RET
|
||||
|
||||
; ---- A command that did not work ----
|
||||
;
|
||||
; The one place a failure is recorded, so that the thing reading lines out of a file can
|
||||
@@ -5337,6 +5629,36 @@ EditWalkColumn:
|
||||
EditWalkBack:
|
||||
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
|
||||
; recalled one.
|
||||
EditDrawn:
|
||||
0x00
|
||||
|
||||
; How much a redraw sent to the screen, line and rubbing out together. The start of the line
|
||||
; is that far back from where the cursor ended up.
|
||||
EditPrinted:
|
||||
0x00
|
||||
|
||||
; Eight lines of a hundred and twenty eight bytes, and one more holding whatever was being
|
||||
; typed when somebody pressed Up.
|
||||
HistoryLines:
|
||||
#Reserve 0d1024
|
||||
HistoryTyped:
|
||||
#Reserve 0d128
|
||||
|
||||
; Which slot holds the oldest, which is what moves when a ninth line pushes one out.
|
||||
HistoryStart:
|
||||
0x00
|
||||
; How many of the eight hold anything.
|
||||
HistoryCount:
|
||||
0x00
|
||||
; Which one is on the screen, counting the oldest as nought. HistoryCount means none of them
|
||||
; is: what is on the screen is the line being typed.
|
||||
HistoryPick:
|
||||
0x00
|
||||
|
||||
; A hundred and twenty seven characters and the zero byte that ends them. It was sixty three
|
||||
; until the shell learned to edit a line, which is when the limit started to be felt: a copy
|
||||
; between two disks with a directory on each is most of the way there before anything has
|
||||
|
||||
Reference in New Issue
Block a user