The other answer provided a working solution that is based on a string terminator like 0 or 13.
This answer chooses to use the string length as it is already available from DOS, and also more in line with what the OP is asking about.
.data
message db 'String: $'
string db 10 dup(' '), '$'
.stack 256h
"I figured out how to input string, ..." Not really!
That string line is supposed to define the input structure for the DOS.BufferedInput function 0Ah. DOS expects to find the storage length in the first byte and will return the length of the actual input in the second byte.
How buffered input works has the details.
What you have written for string translates to a 10-byte region of memory entirely filled with the number 32 (ASCII of ' '), and optimistically followed by a dollar character (the linked post explains why this is not a good idea). Because the first byte is 32, it will permit DOS to legally use the next 34 bytes for inputting purposes. You have a serious buffer overflow!
If you want to allow for an user input of 10 characters, then the correct definition is string db 11, 0, 11 dup(0)
.
There are 2 problems:
- I do not know how to get length of the string, so there are too much blank lines.
- There is not one character per line in the output.
- DOS already gave you the length of the string in the second byte of the string input structure. Just fetch it:
xor cx, cx
mov cl, string[1]
.
- Your code (
lea dx, string[si]
mov ah, 09h
int 21h
) is using a DOS function (09h) that outputs a string of characters. What did you expect? Use a single character output function instead: mov dl, string[si]
mov ah, 02h
int 21h
.
Your code (with comments)
lea dx, message
mov ah, 09h
int 21h
lea dx, string
mov ah, 0Ah
int 21h
mov dl, 10 ; (*)
mov ah, 02h
int 21h
; output string char by char
mov si, 2 ; Characters start at offset 2
xor cx, cx
mov cl, string[1] ; Count of characters
jcxz done
output:
mov dl, string[si] ; Fetch one character
mov ah, 02h
int 21h ; Print one character
mov dl, 13
mov ah, 02h
int 21h ; Print carriage return
mov dl, 10
mov ah, 02h
int 21h ; Print linefeed
inc si ; Move to next character
loop output ; Repeat for all characters
done:
(*) At the conclusion of the DOS.BufferedInput function 0Ah, the cursor will be in the first column on the current row. You don't need to output a carriage return (13). Just a linefeed (10) will do fine. This is certainly true for a regular MS-DOS, but as @ecm wrote in her comment exceptions to the rule exist.
My code
The above output loop does 3 system calls per iteration. If we reduce this number to 1, the loop can run about 10% faster1.
On each iteration we copy the current character to a $-terminated string that has the carriage return and linefeed bytes included. A single invokation of the DOS.PrintString function 09h then does the outputting. This speed gain does come with one drawback: if the input string has an embedded $ character, it will not get displayed.
1 True as long as the screen does not have to scroll, because screen scrolling is comparatively very slow.
.data
message db 'String: $'
string db 11, 0, 11 dup(0)
TheChar db 0, 13, 10, '$'
...
lea dx, message
mov ah, 09h
int 21h
lea dx, string
mov ah, 0Ah
int 21h
mov dl, 10 ; (*)
mov ah, 02h
int 21h
; output string char by char
mov si, 2 ; Characters start at offset 2
lea dx, TheChar
xor cx, cx
mov cl, string[1] ; Count of characters
jcxz done
output:
mov al, string[si] ; Fetch one character
mov TheChar, al
mov ah, 09h
int 21h ; Print one character plus CR plus LF
inc si ; Move to next character
loop output ; Repeat for all characters
done: