Re: input & output in assembly
From: Beth (BethStone21_at_hotmail.NOSPICEDHAM.com)
Date: 01/25/04
- Next message: August Derleth: "Re: input & output in assembly"
- Previous message: Brandon J. Van Every: "Re: Intel SSE sucks dog*** for 3D graphics"
- In reply to: Dpak Cherian: "input & output in assembly"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 24 Jan 2004 23:00:50 +0000 (UTC)
Dpak Cherian wrote:
> in 16-bit assembly is there any way to accept numbers as input and
display them.
> btw, is there anything like cin & cout in assembly
>
> PS. I'm a complete newbie so plz excuse
Theoretical bit:
There isn't anything like "cin" / "cout" in assembly; Assembly
language is simply a human-readable form of the CPU's actual
"language"...the _real_ instructions that the CPUs processes...there's
no "abstraction" applied, _except_ for re-writing it as human-readable
text (the CPU actually sees just "binary numbers" :) so that _people_
can actually _use_ it more happily and comfortably than sifting
through tons of numbers all day long...
High-level languages, like C++, "abstract" away from the actual
machine-specifics and are more "problem-orientated" than
"machine-orientated" (though, actually, as an aside, C / C++ is the
HLL that tends to do this _least_..."cin" / "cout" are, of course,
merely "standard additions" in the "standard library" with C++ and, in
fact, even C++ doesn't really have these things "built-in"
either...but if you didn't "#include" the right header file then even
C++ doesn't actually have "cin / cout" as _part of the language_
either...it has them as "standard additions" in the packaged libraries
that usually come along with a C++ compiler :)...so, they like to
provide these kinds of things to help a programmer with their
"problem"...
Assembly language, though, is "machine-orientated"...basically because
assembly language _IS_ the machine, so to speak...or, at least, it's a
human-readable equivalent to the machine instructions that will
finally go through the CPU (i.e. it looks different but, to all
intents and purposes, assembly language is equivalent to machine code
for most of our purposes)...unlike high-level languages, which
"abstract" away from the machine and move more towards the so-called
"problem domain" (defining things in terms of the problem itself
rather than in the terms the machine actually sees things), assembly
language by its nature, _reflects_ the true nature of the CPU and the
system...
And in hardware terms, the keyboard and monitor (for input and output)
are _separate_ items from the CPU...hence, the CPU has no "keyboard"
or "monitor" instructions because those are separate hardware
devices...in assembly language, you control the _CPU_ by talking
directly to it...all other hardware is controlled by instructing the
CPU to "talk" to the other hardware...it's done "indirectly" via the
CPU...there are no "keyboard" instructions but there are instructions
to tell the CPU to send data to other hardware and mechanisms for
accepting data back...or, alternatively, there are instructions to
call into OS or BIOS services which can do the actual low-level
hardware interfacing on behalf of the application...usually used to
provide "portable" access to hardware or simply because they are
easier, comparable to calling C functions like "printf" to do the
same...the following gives a basic practical "starting point" for
looking further into this subject, by covering some of the basic DOS
I/O functions...hardly "comprehensive" but intended to give a basic
set of illustrative examples in how to go about this, from which you
can Hopefully get a reasonable idea of what's going on to be able to
search through "HelpPC" or Ralf Brown's list or some other resources
yourself and being to know what to do and what to expect...
Practical bit:
[ As you've not specified OS or assembler, then the following example
code is for DOS using MASM / TASM syntax, as the most likely
combination you'd probably need :) ]
1. Download "HelpPC" and unzip it somewhere useful on your hard drive:
http://cs.nyu.edu/~yap/classes/machineOrg/helppc/helppc21.zip
2. Run "HelpPC.com"...
3. Select the "Interrupt Services" menu option...
4. Select the "DOS functions" item in the next menu...
5. The DOS functions of interest (that is, that I actually go on with
examples in this post...they are all "interesting" for some purpose
and you might want to look them over and see if you can get them to
work on your own, once you've got a "handle" on how these basic ones
work to give you the basic pattern :) in this list are:
High-level functions:
INT 21h, AH = 09h: Print String
INT 21h, AH = 0Ah: Buffered Keyboard Input (with echo)
[ These functions deal with complete strings under DOS's control; Easy
to use but can't be "customised" much so you have to accept however
DOS deals with the user I/O... ]
Low-level functions:
INT 21h, AH = 01h: Keyboard Input with Echo
INT 21h, AH = 02h: Display Output
[ These functions only deal with I/O of individual characters; Can be
called repeatedly with your own code managing the individual
characters into complete strings itself...useful if you need
functionality that's not provided by the "high-level" functions
above - e.g. printing strings with a "$" sign inside them or providing
"non-standard" input facilities - because you can take more control by
using individual character I/O and handling the rest yourself in your
own code :) ]
5a. The DOS "Print String" function (INT 21h, AH = 09h) prints out a
string of characters, pointed to by DS:DX, up to the first dollar sign
("$") it finds in that string, to the "stdout" device...
Example of use (MASM / TASM syntax):
Print.asm
---------------- 8< ------------------
.model tiny
.data
MyString db "Hello, World!$"
.code
org 100h
; Print string passing the
; address of our "$"-terminated
; string in DS:DX...
;
_start: mov ah, 09h
mov dx, offset MyString
int 21h
; Exit back to DOS prompt
;
mov ah, 4Ch
int 21h
end _start
---------------- >8 ------------------
Note that the address of the string is DS:DX, not just DX...but the DS
segment register is automatically pointing to the right place in a
..COM file, so it doesn't need to be set in this example...
As it writes to "stdout", which is the console by default, but this
may be "redirected" using the standard DOS methods...for example,
"Print.com > Hello.txt" will redirect the output of the program into
the "Hello.txt" file by using the ">" DOS command to change "stdout"
to a file...
5b. The DOS "Buffered Keyboard Input" function (INT 21h, AH = 0Ah)
accepts the address to a specially-formatted "buffer" in the DS:DX
register pair...and then DOS takes over the control of input until the
ENTER key is pressed (maximum buffer size: 255 bytes, minimum buffer
size: 1 byte :)...input is read from "stdin" and the function won't
accept more characters than is specified as the "max character
count"...DOS editing keys are available with the function...
The buffer is of the following format (use fixed pitch font to view):
+-------+-------+---------------------------------------+
| Max | Count | Buffer (N bytes) |
+--/|\--+--/|\--+--/|\----------------------------------+
| | |
| | +--- Input buffer (N bytes)
| | (returns actual input when input complete)
| |
| +----------- Number of characters returned (byte)
| (returned by function when input complete)
|
+------------------- Maximum number of characters to be read
(byte)
(specified to function to define the maximum
amount of characters in buffer; That is,
this
value defines the size of "N bytes" to the
function and is placed in the buffer
_before_
calling the function)
Example of usage (MASM / TASM syntax):
BufInput.asm:
---------------- 8< ------------------
.model tiny
MAX_CHARACTERS equ 12
.data
Buffer db MAX_CHARACTERS
db ?
db MAX_CHARACTERS dup (?)
.code
org 100h
; Call buffered keyboard input
; passing the address of our
; buffer in DS:DX...
;
_start: mov ah, 0Ah
mov dx, offset Buffer
int 21h
; Exit back to DOS prompt...
;
mov ah, 4Ch
int 21h
end _start
---------------- >8 ------------------
Note that the address of the string is DS:DX, not just DX...but the DS
segment register is automatically pointing to the right place in a
..COM file, so it doesn't need to be set in this example...
Input is taken from "stdin" and output is written to "stdout"...either
or both may be "redirected" using the standard DOS mechanisms...for
example, "bufinput > input.txt" will "echo" the input to the
"input.txt" file (input won't be visible as it's gone to the
file)...and then "bufinput < input.txt" will read the input from the
"input.txt" file and redirect it to the screen (previous input can now
be read back out of the file and "echoed" onto the screen)...and then
"bufinput < input.txt > output.txt" will read in from "input.txt" and
echo the input to "output.txt" (effectively, just copying it from one
file to the other :)...
[ Be careful of this redirection in that if you supply a file that
doesn't have a proper terminator, the DOS function simply sits waiting
for one, which will never come because it's not in the file...and
pressing the keyboard ENTER is useless because, now that it's
"redirected", the program isn't looking to the keyboard for input
anymore so never gets the message...and, therefore, the program waits
forever :( ]
5c. The DOS "Display Output" function (INT 21h, AH = 02h) simply
prints out the character specified in the DL register to "stdout"...
Example of usage (MASM / TASM syntax):
Print2.asm:
---------------- 8< ------------------
.model tiny
.data
MyString db "Micro$oft $uck$!", 0
.code
org 100h
; Call custom "Print" routine
; passing address of string in
; DS:SI...
;
_start: mov si, offset MyString
call Print
; Exit back to DOS prompt...
;
mov ah, 4Ch
int 21h
; Print:
;
; DS:SI = offset to zero-terminated ASCII string
;
Print: mov ah, 02h ; Select DOS "Display output"
function
NextChar: mov dl, [ si ] ; Grab byte from SI
cmp dl, 00h ; Have we reached end of string?
je EndOfPrint ; Yes, then jump out of loop
int 21h ; Print character to "stdout"
inc si ; Increment SI to move to next
character
jmp NextChar
EndOfPrint: ret
end _start
---------------- >8 ------------------
Note how I've created a custom "Print" routine using this "low-level"
function that uses a zero byte to specify the end of the string,
rather than DOS's usual "$" terminator in their "high-level" print
string function...the string to be printed has been suitably decorated
with plenty of "$" signs to demonstrate this (apologies for to
Microsoft fans ;)...
Of course, you need not write any "custom" routine this way (or at
all, even ;) and you can do anything you like...that is the beauty of
these "low-level" DOS functions in comparison to the "high-level"
ones...this function just spits out a character to "stdout" and
nothing else...it's up to your own code to use this function how you
see fit...for clever and devious purposes, no doubt ;)...
Another possibility, for example, is to use Pascal-like strings where
the first byte specifies the size of the string (and isn't printed)
and there isn't a "terminator" on the string at all (similar to that
"buffered input" function earlier, in a sense...except there's only a
single "max" figure because a "count of characters input" doesn't make
sense for an output-only function :)...
Alternative example of usage (MASM / TASM syntax):
Pascal.asm:
---------------- 8< ------------------
.model tiny
.data
MyString db 22
db "A Pascal-style string!"
.code
org 100h
; Call custom "PascalPrint"
; routine passing address of
; Pascal-style string in
; DS:SI...
;
_start: mov si, offset MyString
call PascalPrint
; Exit back to DOS prompt...
;
mov ah, 4Ch
int 21h
; PascalPrint:
; DS:SI = offset to zero-terminated ASCII string
;
PascalPrint: mov ah, 02h ; Select DOS "Display output"
function
mov cl, [ si ] ; Grab "count of characters" into CL
inc si ; Advance to start of actual string
NextChar: mov dl, [ si ] ; Grab character from string
int 21h ; Print it to "stdout"
inc si ; Advance to next character
dec cl ; Decrement count
jnz NextChar ; Jump back to print next character
if
; not at end of string yet
ret
end _start
---------------- >8 ------------------
This shows how, though the "low-level" functions don't offer as much
functionality, this is actually useful for creating "custom" print
routines...
Note, output can be redirected - just as before - using the DOS ">"
mechanism...for example, "Pascal > Pascal.txt" writes the string to
the file "Pascal.txt"...
5d. The DOS "Keyboard Input with Echo" function (INT 21h, AH = 01h)
waits for a character to be input on "stdin" and then "echoes" that
character to "stdout"...
Example of usage (MASM / TASM syntax):
Input.asm:
---------------- 8< ------------------
.model tiny
MAX_CHARACTERS equ 12
.data
Buffer db MAX_CHARACTERS
db MAX_CHARACTERS dup (?)
.code
org 100h
_start: mov di, offset Buffer
call Input
; Exit back to DOS prompt...
;
mov ah, 4Ch
int 21h
; Input:
;
; DS:DI = offset to input buffer
; (first byte specifies size)
;
Input: mov ah, 01h ; Select DOS "Input with echo"
function
mov cl, [ di ] ; Grab size of buffer into CL (max
size)
mov ch, cl ; Copy into CH (current count)
inc di ; Advance to start of actual
buffer
NextChar: int 21h ; Input character from "stdin"
cmp al, 0Dh ; Is it ENTER?
je EndOfInput ; Yes, then terminate early
cmp al, 08h ; Is it backspace?
jne CarryOn ; No, then skip over backspace
processing
cmp ch, cl ; Are we at the start of the
buffer?
je NextChar ; Yes, ignore backspace as we
can't go back
; before the beginning of the
buffer
dec di ; Move back a character in the
buffer
inc ch ; Increment count back up
mov ah, 02h ; Select DOS "display output"
mov dl, 20h ; A space to clear old character
int 21h ; Print it!
mov dl, 08h ; A backspace character moves left
; (non-destructive)
int 21h ; Print it!
mov ah, 01h ; Restore DOS "input with echo"
function
jmp NextChar ; Jump to get next character
CarryOn: mov [ di ], al ; Store input into buffer
inc di ; Advance to next character in
buffer
dec ch ; Decrement count of characters
jnz NextChar ; While there's still room in
buffer,
; do it all again
EndOfInput: ret
end _start
---------------- >8 ------------------
This custom "input" routine is a little bit more complicated than the
others so far, due to the unique problem of the backspace character
(allowing the last character - _IF_ there's a last character - to be
"undone" :)...to be honest, this _isn't_ the best input routine you'll
ever see...but input is always inherently a little more complicated
because the user could type practically anything and we have to
account for it all...I've bothered to deal with the "backspace" key to
give you an appreciation of what I mean by the added complexity just
this one "undo" key adds to the input routine (we now need _two_
"count" variables - in CH and CL - to handle the "special case" of
backspace when the buffer is actually empty, which, of course, should
just be ignored as things could go dreadfully, dreadfully wrong if you
allowed a user to backspace passed the beginning of the buffer! ;)...
I make no guarantees about the "redirection" - because I didn't write
it taking that into account - but I just ran a simple test or two and
it _seemed_ to work okay...it's probably better NOT to use the "echo"
input DOS function, more generally, for this...but something like
"input without echo" (INT 21h, AH = 07h), which works just like this
function _without_ the "echo" to "stdout", though...and then use
"display output" (INT 21h, AH = 02h) to only print things that are
valid, if you need better control over that...
Because though you may not see it _visibly_ when you look at the
output of a "redirected" file (as the backspace character is being
_performed_ as part of the output of the file :), if you put it into a
hex viewer, you can actually see "20h 08h" (space backspace...the
"hack" used to make the input look right on the screen by wiping out
the old characters with a space when you tap backspace) characters in
the output file(!!)...which would _look_ okay (and the actual contents
of the buffer will be okay too, so they'd be no problems with the
variables inside the program :) but any comparison of the redirected
"Hello, world" to "Hello(space backspace)o, world" would clearly not
give a "match"...this problem, though, won't effect the program at all
when things aren't redirected...the problem is basically that
"backspace" literally moves backwards and wipes things out on the
screen, as we want, but just gets written to a _file_ like any other
character when redirected...this is a particularly tricky
problem...perhaps, simply, don't "redirect" any of the input (don't
write to "stdout") but use some "direct console" output function which
_forces_ things onto the screen without redirection and then "echo"
out the _finished_ input when ENTER is pressed...
Like I was saying, input - because the user is permitted to hit _any_
key they like on the keyboard at any point (even if it's really
annoying that they are doing silly things like trying to backspace
passed the start of the buffer, silly users! ;) - is an inherently
more complicated problem...I've given a "taste" of how to go about
solving these types of problems in the example but don't take the
example above as "role model" code to "cut and paste" into other
programs...it's "purely illustrative" because even _I_ wouldn't trust
that quick and nasty "hack" with any important user input, anyway
;)...
The benefit, of course, of the "low-level" input function over the
"high-level" one is much the same as it was with the output
functions...you get better "control" over input and can write custom
input routines...you could, for instance, use these functions to
create an input routine that only accepts hex characters or decimal
floating-point numbers (giving a "beep" if something invalid is typed
;) or that allows using the TAB key for moving around between "fields"
in a "form" (pretending that you're in a GUI...although, that makes
the already complicated "input" problem even more confusing because
you've got to handle multiple input fields at the same time and, at
the drop of a hat, allow the user to move back and forth
between...but, if you want real "user-friendliness" people are so used
to this ability from GUIs that anything less tends to kind of
"disappoint" your users ;) or whatever...
Of course, now that you've downloaded the handy "HelpPC" program,
which neatly divides up lots of ASM, DOS and BIOS stuff into nice
menus, you can just "browse" and try out code using the functions and
stuff...see what happens...
You can also grab Ralf Brown's Interrupt List from:
http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
Which is even more comprehensive (but, thus, a little more difficult
to search than "HelpPC" :) and is indispensable if you're programming
in DOS...
Beth :)
- Next message: August Derleth: "Re: input & output in assembly"
- Previous message: Brandon J. Van Every: "Re: Intel SSE sucks dog*** for 3D graphics"
- In reply to: Dpak Cherian: "input & output in assembly"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]