From bffb26b7bb737c6e75ef08967dbc8f06a0ca7d25 Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Sat, 1 Feb 2020 23:38:52 +0100 Subject: [PATCH 1/7] implement recalling last input with up-arrow --- src/DEBUG.ASM | 79 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 042188a..971303e 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8310,27 +8310,19 @@ gl4: gl5: endif mov dx,offset line_in - call InDos - jnz rawinput - mov ah,0ah ;buffered keyboard input - call doscall -gl6: - mov al,10 - call stdoutal - mov si,offset line_in + 2 - call skipwhite - ret - -rawinput: - push di push ds pop es inc dx inc dx mov di,dx + xor cx,cx rawnext: mov ah,00h int 16h + cmp ah, 48h ;up arrow + je recall + cmp ah, 50h ;down arrow + je clear cmp al,0 jz rawnext cmp al,0E0h @@ -8340,25 +8332,67 @@ rawnext: cmp al,7Fh jz del_key stosb + inc cx call stdoutal - cmp al,0Dh + cmp al,CR jnz rawnext + push di dec di sub di,dx mov ax,di mov di,dx mov byte ptr [di-1],al - dec dx - dec dx pop di - jmp gl6 +gl6: + mov al,10 + call stdoutal + mov si,offset line_in + 2 + call skipwhite + cmp si, di + je @F ;don't save empty line + lea cx, [di+1] + sub cx, si + push si + push di + dec si + mov di,offset last_line_in + 2 + rep movsb ;save input for recall + pop di + pop si +@@: ret + del_key: - cmp di,dx - jz rawnext + jcxz rawnext dec di + dec cx call fullbsout jmp rawnext +recall: + jne rawnext + call clearline + mov si,offset last_line_in + 2 + xor cx,cx +@@: lodsb + cmp al,CR + je rawnext + call stdoutal + stosb + inc cx + jmp @B + +clear: + call clearline + jmp rawnext + +clearline: + jcxz cl1 +@@: call fullbsout + loop @B + mov di,dx +cl1: + ret + getline endp ; BUFSETUP - Set up buffer reading. This just means discard an LF @@ -11485,9 +11519,10 @@ _DATA segment ;--- I/O buffers. (End of permanently resident part.) -line_in db 255,0,CR ;length = 257 -line_out equ line_in+LINE_IN_LEN+1;length = 1 + 263 -real_end equ line_in+LINE_IN_LEN+1+264 +last_line_in db 255,0,CR,LINE_IN_LEN-3 dup(0) ;length = 257 +line_in db 255,0,CR ;length = 257 +line_out equ line_in+LINE_IN_LEN+1 ;length = 1 + 263 +real_end equ line_out+264 _DATA ends From fff2e4550024a9d0c0982940bd4d021d29a326bb Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Sun, 2 Feb 2020 00:52:01 +0100 Subject: [PATCH 2/7] prevent buffer overflow in getline. --- src/DEBUG.ASM | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 971303e..4228bb2 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8331,6 +8331,8 @@ rawnext: jz del_key cmp al,7Fh jz del_key + cmp cx, LINE_IN_LEN - 2 + jae rawnext stosb inc cx call stdoutal From bbe1bc0c4ee53063a3485ad17cc8c792c93cfb88 Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Sun, 2 Feb 2020 18:37:17 +0100 Subject: [PATCH 3/7] implement full input history recall using up/down arrow keys. --- src/DEBUG.ASM | 177 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 147 insertions(+), 30 deletions(-) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 4228bb2..47ba061 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -131,7 +131,8 @@ else EXCCSIP equ 0 endif -LINE_IN_LEN equ 257 ;length of line_in (including header stuff) +LINE_IN_LEN equ 257 ;length of line_in (including header stuff) +LINE_HISTORY_LEN equ 512 ;size of line_in history buffer ;--- PSP offsets @@ -8315,14 +8316,15 @@ endif inc dx inc dx mov di,dx + xor si,si xor cx,cx rawnext: mov ah,00h int 16h cmp ah, 48h ;up arrow - je recall + je recall_up cmp ah, 50h ;down arrow - je clear + je recall_dn cmp al,0 jz rawnext cmp al,0E0h @@ -8350,16 +8352,13 @@ gl6: call stdoutal mov si,offset line_in + 2 call skipwhite - cmp si, di - je @F ;don't save empty line - lea cx, [di+1] - sub cx, si + cmp si,di + je @F ;don't save empty line + lea cx,[di+1] + sub cx,si push si - push di dec si - mov di,offset last_line_in + 2 - rep movsb ;save input for recall - pop di + call hist_store pop si @@: ret @@ -8370,32 +8369,143 @@ del_key: call fullbsout jmp rawnext -recall: - jne rawnext +recall_up: + xor al,al + jmp @F +recall_dn: + mov al,1 +@@: call hist_recall + jmp rawnext +getline endp + +; CLEARLINE - Clear current input prompt +; Entry DI Points to current position in input buffer +; DX Begin of input buffer +; CX Line length +; Exit DI Equal to DX +; CX 0 + +clearline proc + jcxz rt + push ax +@@: call fullbsout + loop @B + pop ax + mov di,dx +rt: ret +clearline endp + +; HIST_RECALL - Reload input line from command history +; Entry SI Current node (0 = one past the end) +; AL Search direction (0 = backwards) +; DX Begin of input buffer +; CX Current line length +; Exit DI End of input buffer +; CX New line length +; SI Loaded history node +; Uses AX,BX + +hist_recall proc + mov bx,si + test al,al + jnz forward + test si,si + jnz @f + mov si,[line_hist_end] ;pick last node if past the end + jmp checkvalid +@@: mov si,[si.llnode.prev] +checkvalid: + test si,si + jz rt call clearline - mov si,offset last_line_in + 2 - xor cx,cx -@@: lodsb + mov bx,si + add si,sizeof llnode +@@: lodsb ;reload line from selected node cmp al,CR - je rawnext + je rt call stdoutal stosb inc cx jmp @B +rt: mov si,bx + ret -clear: +forward: call clearline - jmp rawnext - -clearline: - jcxz cl1 -@@: call fullbsout - loop @B - mov di,dx -cl1: + test si,si + jz rt + mov si,[si.llnode.next] + mov bx,si ;might be null, but that's ok to return + jmp checkvalid +hist_recall endp + +; HIST_STORE - Store line in input history +; Entry SI Input line +; CX Line length +; Uses BX,CX,DX,DI + +hist_store proc + push si + push ax + mov dx,si + mov bx,[line_hist_begin] + mov di,[line_hist_end] + test di,di + jnz initialized +first: + mov di,offset line_history ;create first node + mov bx,di + mov [line_hist_begin],di + mov [line_hist_end],di + mov [di.llnode.next],0 + mov [di.llnode.prev],0 + jmp store +initialized: + add di,sizeof llnode + push cx + mov cx,offset line_history + LINE_HISTORY_LEN + sub cx,di + mov al,CR + repne scasb ;find end of last node + pop cx + mov si,di +@@: mov di,si + add di,cx + add di,sizeof llnode ;di = end of new node + cmp di,offset line_history + LINE_HISTORY_LEN + jb no_overflow ;does it fit? + cmp si,offset line_history + je fail ;fail if new node would be larger than buffer + mov si,offset line_history ;restart from beginning + jmp @B +no_overflow: + cmp si,bx + ja no_overlap ;can't overlap if buffer isn't full yet +check_overlap: + cmp di,bx + jb no_overlap ;does it overwrite the first node(s)? + mov bx,[bx.llnode.next] + test bx,bx + jz first ;overwrite all nodes + jmp check_overlap +no_overlap: + mov [bx.llnode.prev],0 ;[bx] is now the first node + mov [line_hist_begin],bx + mov bx,[line_hist_end] + mov [line_hist_end],si ;add node at the end + mov [bx.llnode.next],si + mov [si.llnode.prev],bx + mov [si.llnode.next],0 + mov di,si +store: + add di,sizeof llnode + mov si,dx + rep movsb ;store string in new node +fail: + pop ax + pop si ret - -getline endp +hist_store endp ; BUFSETUP - Set up buffer reading. This just means discard an LF ; if the last character read (as stored in 'notatty') is CR. @@ -11521,10 +11631,17 @@ _DATA segment ;--- I/O buffers. (End of permanently resident part.) -last_line_in db 255,0,CR,LINE_IN_LEN-3 dup(0) ;length = 257 +llnode struct + prev dw ? + next dw ? +llnode ends + +line_hist_begin dw 0 +line_hist_end dw 0 line_in db 255,0,CR ;length = 257 line_out equ line_in+LINE_IN_LEN+1 ;length = 1 + 263 -real_end equ line_out+264 +line_history equ line_out+264 +real_end equ line_history+LINE_HISTORY_LEN _DATA ends From 8d29b8943b671bd87606ca7d024e441692fad47e Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Sun, 2 Feb 2020 18:59:29 +0100 Subject: [PATCH 4/7] prevent entering control characters in getline --- src/DEBUG.ASM | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 47ba061..2225242 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8325,15 +8325,17 @@ rawnext: je recall_up cmp ah, 50h ;down arrow je recall_dn - cmp al,0 - jz rawnext cmp al,0E0h jz rawnext cmp al,08h jz del_key cmp al,7Fh jz del_key - cmp cx, LINE_IN_LEN - 2 + cmp al,CR + je @F + cmp al,20h + jb rawnext +@@: cmp cx, LINE_IN_LEN - 2 jae rawnext stosb inc cx From ec19794b3b5616a10921c69cc316628fa25b967a Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Sun, 2 Feb 2020 19:19:11 +0100 Subject: [PATCH 5/7] prevent storing duplicate entries in command history --- src/DEBUG.ASM | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 2225242..8a36eaf 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8465,6 +8465,10 @@ first: initialized: add di,sizeof llnode push cx + repe cmpsb ;check if last entry is identical + pop cx + je fail ;don't store duplicates + push cx mov cx,offset line_history + LINE_HISTORY_LEN sub cx,di mov al,CR From f7b941e2d1e77fddae9205b57d3708a1783d0bcf Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Fri, 7 Feb 2020 05:14:56 +0100 Subject: [PATCH 6/7] reorder code in hist_store --- src/DEBUG.ASM | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 8a36eaf..89597b4 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8453,16 +8453,7 @@ hist_store proc mov bx,[line_hist_begin] mov di,[line_hist_end] test di,di - jnz initialized -first: - mov di,offset line_history ;create first node - mov bx,di - mov [line_hist_begin],di - mov [line_hist_end],di - mov [di.llnode.next],0 - mov [di.llnode.prev],0 - jmp store -initialized: + jz first add di,sizeof llnode push cx repe cmpsb ;check if last entry is identical @@ -8475,7 +8466,8 @@ initialized: repne scasb ;find end of last node pop cx mov si,di -@@: mov di,si +check_overflow: + mov di,si add di,cx add di,sizeof llnode ;di = end of new node cmp di,offset line_history + LINE_HISTORY_LEN @@ -8483,7 +8475,7 @@ initialized: cmp si,offset line_history je fail ;fail if new node would be larger than buffer mov si,offset line_history ;restart from beginning - jmp @B + jmp check_overflow no_overflow: cmp si,bx ja no_overlap ;can't overlap if buffer isn't full yet @@ -8511,6 +8503,15 @@ fail: pop ax pop si ret + +first: + mov di,offset line_history ;create first node + mov bx,di + mov [line_hist_begin],di + mov [line_hist_end],di + mov [di.llnode.next],0 + mov [di.llnode.prev],0 + jmp store hist_store endp ; BUFSETUP - Set up buffer reading. This just means discard an LF From 52f0e5df844f0cc26d44716879ba9860fc96e93d Mon Sep 17 00:00:00 2001 From: "J.W. Jagersma" Date: Fri, 7 Feb 2020 05:30:25 +0100 Subject: [PATCH 7/7] more efficient register usage in hist_store --- src/DEBUG.ASM | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/DEBUG.ASM b/src/DEBUG.ASM index 89597b4..2cd0946 100644 --- a/src/DEBUG.ASM +++ b/src/DEBUG.ASM @@ -8444,20 +8444,20 @@ hist_recall endp ; HIST_STORE - Store line in input history ; Entry SI Input line ; CX Line length -; Uses BX,CX,DX,DI +; Uses BX,CX,DX,DI,SI hist_store proc - push si push ax - mov dx,si mov bx,[line_hist_begin] mov di,[line_hist_end] test di,di jz first add di,sizeof llnode + push si push cx repe cmpsb ;check if last entry is identical pop cx + pop si je fail ;don't store duplicates push cx mov cx,offset line_history + LINE_HISTORY_LEN @@ -8465,22 +8465,21 @@ hist_store proc mov al,CR repne scasb ;find end of last node pop cx - mov si,di check_overflow: - mov di,si - add di,cx - add di,sizeof llnode ;di = end of new node - cmp di,offset line_history + LINE_HISTORY_LEN + mov dx,di + add dx,cx + add dx,sizeof llnode ;dx = end of new node + cmp dx,offset line_history + LINE_HISTORY_LEN jb no_overflow ;does it fit? - cmp si,offset line_history + cmp di,offset line_history je fail ;fail if new node would be larger than buffer - mov si,offset line_history ;restart from beginning + mov di,offset line_history ;restart from beginning jmp check_overflow no_overflow: - cmp si,bx + cmp di,bx ja no_overlap ;can't overlap if buffer isn't full yet check_overlap: - cmp di,bx + cmp dx,bx jb no_overlap ;does it overwrite the first node(s)? mov bx,[bx.llnode.next] test bx,bx @@ -8490,18 +8489,15 @@ no_overlap: mov [bx.llnode.prev],0 ;[bx] is now the first node mov [line_hist_begin],bx mov bx,[line_hist_end] - mov [line_hist_end],si ;add node at the end - mov [bx.llnode.next],si - mov [si.llnode.prev],bx - mov [si.llnode.next],0 - mov di,si + mov [line_hist_end],di ;add node at the end + mov [bx.llnode.next],di + mov [di.llnode.prev],bx + mov [di.llnode.next],0 store: add di,sizeof llnode - mov si,dx rep movsb ;store string in new node fail: pop ax - pop si ret first: