Looking at how you defined your input buffer (buf: db 20 dup ('$')
), I get it
that you want to cut corners and have the input already $-terminated ready for
re-displaying it. Sadly this messes up the required settings for the DOS input
function 0Ah and your program is in serious problems with a potential buffer
overrun.
Moreover using $-termination is not the brightest choice that you can make
since the $ character could already appear amongst the inputted characters.
All the example programs that I present below will use zero-termination
instead.
Inputting text using int 21h AH=0Ah
This Buffered STDIN Input function gets characters from the keyboard and
continues doing so until the user presses the Enter key. All
characters and the final carriage return are placed in the storage space that
starts at the 3rd byte of the input buffer supplied by the calling program
via the pointer in DS:DX
.
The character count, not including the final carriage return, is stored in the
2nd byte of the input buffer.
It's the responsibility of the calling program to tell DOS how large the
storage space is. Therefore you must put its length in the 1st byte of the
input buffer before calling this function. To allow for an input of 1
character you set the storage size at 2. To allow for an input of 254
characters you set the storage size at 255.
If you don't want to be able to recall from the template any previous input,
then it is best to also zero the 2nd byte. Basically the template is the
pre-existing (and valid) content in the input buffer that the calling program
provided. If pre-existing content is invalid then the template is not
available.
Surprisingly this function has limited editing facilities.
- Escape Removes all characters from the current input.
The current input is abandoned but stays on screen and the cursor is placed on
the next row, beneath where the input first started.
- Backspace Removes the last character from the current input.
Works as expected if the input stays within a single row on screen.
If on the other hand the input spans several rows then this backspacing will
stop at the left edge of the screen. From then on there will be a serious
discrepancy between the logical input and the visual input because logically
backspacing will go on until the 1st position in the storage space is reached!
- F6 Inserts an end-of-file character (1Ah) in the current input.
The screen will show "^Z".
- F7 Inserts a zero byte in the current input.
The screen will show "^@".
- ctrlEnter Transitions to the next row (executing a
carriage return and linefeed), nothing is added to the current input, and you
can't go back.
Many more editing keys are available. They are all reminiscent of EDLIN.EXE,
the ancient DOS line editor, which is a text editor where each previous line
becomes the template on which you build the next line.
- F1 Copies one character from the template to the new line.
- F2 + ... Copies all characters from the template to the new line, up to the character specified.
- F3 Copies all remaining characters in the template to the new line.
- F4 + ... Skips over the characters in the template, up
to the character specified.
- F5 Makes the new line the new template.
- Escape Clears the current input and leaves the template unchanged.
- Delete Skips one character in the template.
- Insert Enters or exits insert mode.
- Backspace Deletes the last character of the new line and places the cursor back one character in the template.
- Left Same as Backspace.
- Right Same as F1.
Tabs are expanded by this function. Tab expansion is the process of replacing
ASCII 9 by a series of one or more spaces (ASCII 32) until the cursor reaches
a column position that is a multiple of 8.
This tab expansion only happens on screen. The storage space will hold ASCII 9.
This function does ctrlC/ctrlBreak
checking.
When this function finishes, the cursor will be in the far left column on the
current row.
Example 1, Buffered STDIN input.
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov ah, 0Ah ;DOS.BufferedInput
int 21h
mov si, msg2
call WriteStringDOS
mov si, buf+2
movzx bx, byte [si-1] ;Get character count
mov word [si+bx+1], 10 ;Keep CR, append LF and 0
call WriteStringDOS
mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 255, 16, "I'm the template", 13, 255-16-1+2 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 10, 'You chose ', 0
; --------------------------------------
Inputting text using int 21h AH=3Fh
When used with predefined handle 0 (in BX
) this Read From File Or Device
function gets characters from the keyboard and continues doing so until the
user presses Enter. All characters (never more than 127) and the
final carriage return plus an additional linefeed are placed in a private
buffer within the DOS kernel. This now becomes the new template.
Hereafter the function will write in the buffer provided at DS:DX
, the amount
of bytes that were requested in the CX
parameter. If CX
specified a number
that is less than the number of bytes generated by this input, one or more
additional calls to this function are required to retrieve the complete input.
As long as there are characters remaining to be picked up, this function will
not launch another input session using the keyboard! This is even true between
different programs or sessions of the same program.
All the editing keys described in the previous section are available.
Tabs are expanded on screen only, not in the template.
This function does ctrlC/ctrlBreak
checking.
When this function finishes, the cursor will be in the far left column on the
- current row if the terminating linefeed was not among the returned bytes.
- next row if the terminating linefeed was among the returned bytes.
Example 2a, Read From File Or Device, pick up all at once.
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov cx, 127+2 ;Max input is 127 chars + CR + LF
xor bx, bx ;STDIN=0
mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
mov bx, ax ;Bytes count is less than CX
mov si, msg2
call WriteStringDOS
mov si, buf
mov [si+bx], bh ;Keep CR and LF, append 0 (BH=0)
call WriteStringDOS
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 127+2+1 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 'You chose ', 0
; --------------------------------------
Example 2b, Read From File Or Device, pick up one byte at a time.
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov cx, 1
xor bx, bx ;STDIN=0
mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
mov si, msg2
call WriteStringDOS
mov si, dx ;DX=buf, CX=1, BX=0
Next: mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
call WriteStringDOS ;Display a single byte
cmp byte [si], 10
jne Next
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
msg1: db 'Choose color ? ', 0
msg2: db 10, 'You chose '
buf: db 0, 0
; --------------------------------------
Inputting text using int 2Fh AX=4810h
This DOSKEY Buffered STDIN Input function can only be invoked if the
DOSKEY.COM TSR was installed. It operates much like the regular Buffered
STDIN Input function 0Ah (see above), but has all the same editing
possibilities as the DOS command line, including the ability to use all of
the DOSKEY special keys.
- Up Gets previous item from history.
- Down Gets next item from history.
- F7 Shows a list of all the items in the history.
- AltF7 Clears the history.
- ...F8 Finds item(s) that start with ...
- F9 Selects an item from the history by number.
- AltF10 Removes all macrodefinitions.
On DOS 6.2 the storage space is always limited to 128 bytes, allowing an input
of 127 characters and room for the mandatory carriage return. It's not
possible to pre-load a template, so always set the 2nd byte of the input
buffer to zero.
On DOS Win95 the storage space can be as big as 255 bytes if you installed the
DOSKEY.COM TSR with a command like doskey /line:255
. It's possible to
pre-load the storage space with a template. This brings the Win95 version
very close to what is feasable with input function 0Ah.
This function does ctrlC/ctrlBreak
checking.
When this function finishes, the cursor will be in the far left column on the
current row. If the character count is zero, it means that the user typed in
the name of a DOSKEY macro that was not yet expanded. You don't
get to see the un-expanded line! A second invocation of the function is needed
and upon returning this time, the cursor will be behind the last character of
the expanded text.
A peculiarity is that when a multi-command macro ($T
) gets expanded, you only
get the expanded text of the 1st command. Additional invocations of the
function are needed to get the other expanded texts. Although all of this is
very useful from within a command shell like COMMAND.COM, from within a user
application it's really annoying that you can't know when this happens.
Since the inputted text is added to the command history, it is unavoidable that
the history fills up with unrelated items. Certainly not what you want to see
at the DOS prompt!
Example 3, Invoking DOSKEY.COM.
ORG 256 ;Create .COM program
cld
mov ax, 4800h ;DOSKEY.CheckInstalled
int 2Fh ; -> AL
test al, al
mov si, err1
jz Exit_
Again: mov si, msg1
call WriteStringDOS
mov dx, buf
mov ax, 4810h ;DOSKEY.BufferedInput
int 2Fh ; -> AX
test ax, ax
mov si, err2
jnz Exit_
cmp [buf+1], al ;AL=0
je Again ;Macro expansion needed
mov si, msg2
call WriteStringDOS
mov si, buf+2
movzx bx, byte [si-1] ;Get character count (is GT 0)
mov word [si+bx+1], 10 ;Keep CR, append LF and 0
Exit_: call WriteStringDOS
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 128, 0, 128+2 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 13, 10, 'You chose ', 0
err1: db 'N/A', 13, 10, 0
err2: db 'Failed', 13, 10, 0
; --------------------------------------
Inputting text using int 21h AH=08h
Because of the 30000 byte limit that Stack Overflow imposes the text continues in the below answer...
Problem understanding the source? The assembler I used:
- considers labels that start with a dot ( . ) as 1st level local labels
- considers labels that start with a colon ( : ) as 2nd level local labels
- is Single Instruction Multiple Operands (SIMO), so
push cx si
translates to push cx
push si
.