kirsch@braggvax.arpa (David Kirschbaum) (08/24/88)
NetLandians,
Attached is a new Turbo Pascal procedure to permit keyboard input (with
"full screen" editing) of strings up to 255 chars in length. (Turbo Pascal
and its READLN is limited to 128 chars, and is horrible for editing beyond the
screen's "wraparound".)
Regrets for the size of this thing .. I usually upload stuff like this to
SIMTEL20 and announce a pointer to there .. but good old SIMTEL's been down
for a week or so.
You'll need INLINE.COM to compile the .ASM code to Turbo Inline code.
Yell directly if you want this to do more/less than it does already.
Regards,
David Kirschbaum
Toad Hall
kirsch@braggvax.ARPA
------ Test program, then .ASM source follows -----
(*
TESTLN.PAS
Test program for new Procedure ToadLn
Like READLN, but:
- Strings only (e.g., don't Toadln(VAR i : INTEGER)
like you could READLN(VAR i : INTEGER)!
- TOADLN does NOT "initialize" the string VAR.
In other words, you can edit an existing string as you desire.
WATCH OUT FOR "NONINITIALIZED" STRINGS! No telling WHAT they might
contain .. Turbo Pascal does NOT initialize a string variable
to length 0, clear it, or anything else! String length and contents
will be unknown.
- up to 255 chars with "full screen" cursor control and
editing:
^U - clear string, home cursor
Home - move cursor to string start
End - move cursor to actual string end (last str char +1 or 255th char)
Lft Arr - move cursor 1 char left (up to string start)
Rt Arr - move cursor 1 char right (will not go beyond last char)
Dn Arr - move cursor 1 line down (or to last string char +1)
Up Arr - move cursor 1 line up (or to string start)
BackSpace - Move cursor 1 character left, do a Delete.
If at "Home", acts like Delete.
Delete - Delete character at current cursor position,
move rest of string left 1 char.
Return,
^Z - Done, CR/LF, returns string. (Ignores cursor position.)
Other Chars:
- Gobbles any other function or cursor keys.
- Passes through other Control chars as the PC "graphics" character.
(You'll get funny-looking characters.)
- Unlike READLN, passes through the ESCape key as its ASCII value
(so you can do ANSI command sequences if you want).
Released to Public Domain.
David Kirschbaum
Toad Hall
kirsch@braggvax.ARPA
*)
TYPE
Str255 = STRING[255];
VAR
S : Str255;
PROCEDURE ToadLn(VAR S : Str255);
VAR xy, width : INTEGER; {required local variables}
BEGIN
{$I TOADLN4.OBJ} {you'd BETTER have it}
END; {of ToadLn}
BEGIN
S := '';
WHILE LENGTH(S) < 140 DO {build a long string}
S := S + '0123456789';
ClrScr;
GotoXY(1,8);
Write('Prompt at line 8: ');
Toadln(S);
Writeln('Your answer: [', S, '] ', LENGTH(S));
GotoXY(1,25);
Write('Prompt at screen bottom (notice the automatic scroll): ');
Toadln(S);
Writeln('Your answer: [', S, '] ', LENGTH(S));
END.
;TOADLN - Turbo Pascal procedure
; Accept keyboard input for strings up to 255 chars in length,
; with full "screen" editing (cursor keys, Delete, BS).
; Terminate input with Ctrl C or Ctrl Z.
;
; Released to Public Domain
; David Kirschbaum
; Toad Hall
; kirsch@braggvax.ARPA
;
;Globally requires:
; TYPE Str255 = STRING[255];
;
;Use the following for your procedure header:
;PROCEDURE ToadLn(VAR S : Str255);
; VAR xy,width : INTEGER;
push DS ;save DS
call InitScr ;init screen vars
;
lds si,>S[bp] ;DS:SI = string vector
mov ax,DS
mov ES,ax ;ES:DI also string vector
call PadStr ;pad, display, home cursor
;
;Clear keyboard buffer
ChkKbd:
mov ah,1 ;report if char is ready
int $16 ;BIOS
jz KeyIn ;kbd buff is empty
xor ah,ah ; Svc 0, read next kbd char
int $16 ; BIOS
jmp short ChkKbd ; Until kbd buff is empty
;
;Kbd buffer is now empty
;Now get/process the user's keyboard input.
KeyIn:
call GetKey ;Read, process kbd char
or ax,ax ;no cursor moving?
je KeyIn ;right, next key
cmp ax,$1C0D ; Was it a CR or ^Z?
je ReturnStr ; yep, done
;insure cursor is updated
call AbsCur ; repsn cursor, update len
jmp short Keyin ; next key, please
;
ReturnStr:
mov di,$FFFF ;force char ptr to end
call AbsCur ;cursor to screen end
call ShowStr ;update length, display
mov ax,$0E0D ;CR
int $10
mov al,$0A ;LF
int $10
jmp Done ;finished
;
;Codes returned in AX from a svc 0, Int 16H call:
;CTRLU EQU 1615H ;^U
;CTRLZ EQU 2C1AH ;^Z
;DNARR EQU 5000H ;Cursor down
;UPARR EQU 4800H ;Cursor up
;HOMKEY EQU 4700H ;Home key
;ENDKEY EQU 4F00H ;End key
;LFTARR EQU 4B00H ;Cursor left
;RTARR EQU 4D00H ;Cursor right
;INSKEY EQU 5200H ;Insert key
;DELKEY EQU 5300H ;Delete key
;BSKEY EQU 0E08H ;Backspace/Rubout key
;CRKEY EQU 1C0DH ;Return key
;
GetKey:
;Processes the keyboard char, acts as required.
xor ah,ah ;svc 0, read next kbd char
int $16 ;BIOS
cmp ax,$1615 ;Is it a ^U?
jne LftArrTst ;nope
;^U clears the string and screen
mov byte [si],0 ; clear str length
call PadStr ; clear screen/string
ret
;
LftArrTst:
cmp ax,$4B00 ;how about cursor left?
jne RtArrTst ;nope
dec di ; back up ptr
ret
;
RtArrTst:
cmp ax,$4D00 ;right cursor?
jne DelTst ;nope
inc di ; bump 1 to right
ret
;
DelTst:
;Delete key rubs out the CURRENT character, does NOT
;move cursor to left, moves rest of string left.
;We can Del a single char that we can NOT Backspace over.
cmp ax,$5300 ;Delete key?
jne BSTst ;nope
jmp short DoBS ; yep
;
BSTst:
cmp ax,$0E08 ;Is it a BS? (Rubout)
jne DnArrTst ;nope
;BS is just like Delete, except we must be able
;to move left. No action if we're at first char
;
dec di ;back up next char ptr
cmp di,si ;did we back up to length byte
jbe NoBS ;yep, can't do that
DoBS:
mov byte [di],$B0 ;"delete" char right here
call AbsCur ;fix cursor psn,current str psn
mov ax,di ;new current psn
sub ax,si ;- start = next char ofs
cmp ax,cx ;is cursor within string? (not at end)
jae BSShow ;nope, at end or beyond
;
DoMov1:
sub cx,ax ;len - cur psn = bytes to move
push si ;save str start
push di ;save this new psn
mov si,di ;new char ptr
inc si ;move from old char ptr
cld ;insure fwd
rep movsb
mov byte [di],$B0 ;clear last char
pop di ;restore current psn
pop si ;restore str start
BSShow:
call ShowStr
ret
NoBS:
xor di,di ;force to start
xor ax,ax ;flag no cursor move
ret
;
DnArrTst:
cmp ax,$5000 ;down cursor?
jne EndTst ;nope
add di,>width[bp] ; 1 line down
jc End1 ; went beyond MAXINT
ret ; done
;
EndTst:
cmp ax,$4F00 ;End key?
jne UpArrTst ;nope
End1:
mov di,$FFFF ; max out next char ptr
ret
;
UpArrTst:
cmp ax,$4800 ;up cursor?
jne HomTst ;nope
sub di,>width[bp] ;1 line up
jb DoHome ;went negative, home
ret ;done
;
HomTst:
cmp ax,$4700 ;home key?
jne CrTst ;nope
DoHome:
xor di,di ; back to start
ret
;
CrTst:
cmp ax,$1C0D ;Is it a CR?
je GetKeyX ;yep, done
cmp ax,$2C1A ; how about ^Z
jne FunTst ; nope
mov ax,$1C0D ; force to CR
ret
;
FunTst:
xor ah,ah ;clear msb
or al,al ;is it a special? (cursor/function)
je GetKeyX ;yep, ignore it (AX=0)
;
;We assume it's a legal character now, so we display it.
PrChr:
stosb ;stuff ASCII char, bump DI
mov ah,$0E ;write char TTY
int $10 ;BIOS
GetKeyX:
ret ;AX <> 0, so cursor, len is tested
;
PadStr:
;Pads from past last char to 255 chars with spaces
;displays str, homes cursor
;returns CX=str length, DI=str start
;
xor ch,ch ;clear msb
mov cl,[si] ;get str length
mov di,si ;current char = str start
inc di ;bump past len byte
push si ;str start
push di ;and next char ptr
;
add di,cx ;add in length (if any)
not cl ;255-str len
mov al,$B0 ;pad with graphic char
cld ;insure fwd (sigh...)
rep stosb ;do the pad
pop di ;restore str start
pop si ;and next char ptr
mov dx,>xy[bp] ;home cursor
;fall thru to ShowStr and return
;
ShowStr:
; Display string at starting coordinates,
; Clear to EOL (e.g., full 255 chars),
;Exit with CX=current str length,DI unchanged,
;cursor psn unchanged.
push dx ;remember current cursor psn
mov ah,3 ;read cur psn (want cursor size)
int $10 ;BIOS CX = current cursor size
;
push cx ;save cursor size
mov ch,$20 ;turn cursor off
mov ah,1 ;set cursor size
int $10 ;BIOS
mov dx,>xy[bp] ;"home" cursor
mov ah,2 ;position cursor
int $10 ;BIOS
;
;We display all 255 chars. Str buffer may be padded
;with spaces (not part of real length), but we show
;them to "Clr EOL".
push si ;str start
inc si ;bump past length byte
mov cx,255 ;255 chars
mov ah,$0E ;BIOS display char TTY
SL1:
lodsb ;next string char
int $10 ;display it
loop SL1 ;do them all
pop si ;restore str start
;
pop cx ;old cursor size
;
mov ah,1 ;set cursor size
int $10 ;BIOS
pop dx ;old cursor psn
mov ah,2 ;set cursor psn
int $10 ;BIOS
call GetLen ;update CX=len
mov [si],cl ;and force into len byte
xor ax,ax ;so we don't call AbsCur
ret
;
AbsCur:
;Absolute cursor movement to next char ptr (DI).
;Enter with DI = next str char.
;Test to insure DI doesn't point beyond
; 255 chars past start (from FixLen)
;Exit with
; DX= adjusted xy coords
; DI = adjusted current char ptr (from FixLen)
; CX = str length (from GetLen)
; cursor pointing to next str char
;
mov dx,>xy[bp] ;get str's starting cursor psn
call FixLen ;check str len,char ptr
;Returns CX=str len, AX=next char ofs
or ax,ax ;curr char = start?
je PsnCur ;yep, go "home" cursor
;
dec ax
push cx ;save str len
mov cx,>width[bp] ;get screen width
add al,dl ;add in starting col
adc ah,0 ;in case of carry
AL1:
cmp ax,cx ;less than 1 line?
jbe A3 ;yep
sub ax,cx ;>width, subtract width
inc dh ;bump row
jmp short AL1 ;until col < = width
A3: ;updated DL=col,DH=row
pop cx ;restore length
mov dl,al ;update row
;
PsnCur:
mov ah,2 ;svc 2, position cursor
int $10 ;BIOS
ret
;
FixLen:
;Insures str len (and CX) are legal,
;keeps DI within legal limits (start + 0..254)
;Enters with DI = current char ptr (could be beyond str length),
;Exits with CX = str len, AX=rel cursor psn within string
;first scan for our terminating $B0 graphics char
call GetLen ;returns with CX=len
jcxz F0 ;no str length, force to start
;now insure str ptr is legal (within string)
mov ax,di ;str ptr
sub ax,si ;- str start = next char ofs
ja F1 ;ok, next char > start
F0:
xor ax,ax ; next char ofs = 0
mov di,si ; force next char to start
inc di ; bump to 1st char
ret ; done
;
;at or above 1st char, how about beyond str end?
F1:
cmp ax,cx ;< len?
jbe F2 ;yep
mov di,si ; start
mov ax,cx ; get length
cmp al,255 ; maxed out?
je F1A ; yep
inc ax ; no, so bump to next char
F1A:
add di,ax ; point to last char
F2:
ret
;
GetLen:
push di ;save next char ptr
mov di,si ;start
mov cx,255 ;max possible len
add di,cx ;point to end
std ;scan backwards
mov al,$B0 ;graphics char ends it
repe scasb ;scan until we run out of $B0's
cld
;CX points to the non-$B0 char (or 0)
jz G1 ;didn't find ANY
inc cx ; adjust from the scasb
G1:
pop di ;restore next char ptr
ret ;with CX=len
;
InitScr:
;Get required screen stuff
mov ah,$0F ;get current video mode
int $10 ;BIOS
;BH = active display page (protect it!)
mov al,ah ;need width as LSB
xor ah,ah ;clear msb
mov >width[bp],ax ;save current screen width
;
;We need 255 chars of screen space WITHOUT SCROLLING,
;or our cursor positioning will be screwed up.
;Test now to see if we have enough room.
;If not, do our scrolling NOW instead of letting BIOS do it.
;
mov si,ax ;save width in SI
mov ah,3 ;get current cursor psn in DX
int $10 ;BIOS
cmp dh,21 ;row 21 or less?
jbe NoScroll ;yep, scroll testing
;
mov al,dh ;current row
mov cx,si ;CL = width multiplier
mul cl ;AX=row * width
C1:
add al,dl ;add in current col
adc ah,0 ;in case of carry
mov cx,ax ;remember as abs scr psn
add ax,255 ;plus full line length
mov di,ax ;abs scrn psn + string
CL1:
cmp di,cx ;less than 1 line?
jbe CDone ;yep, ok
mov ax,$0E0A ; display LF via BIOS
int $10 ; BIOS
sub di,si ; subtract width
dec dh ; back up 1 row
jmp short CL1
CDone:
mov ah,2 ;svc 2, position cursor
int $10 ;BIOS
NoScroll:
mov >xy[bp],dx ;now save current cursor psn
;
;Get screen attributes at current cursor psn
mov ah,8 ;Read char & attrib
int $10 ;BIOS
mov bl,ah
;BL = screen attribute (protect it!)
ret
;
Done:
pop DS ;restore DS
;let Turbo do the rest