THE ASSEMBLY LANGUAGE "MAGAZINE" #2 1989
THE ASSEMBLY LANGUAGE "MAGAZINE" VOL 1 NUMBER 2
March 1989
## #### #### ####### ## ## ###### #### ## ##
#### ## ## ## ## ## # ### ### ## ## ## ## ##
## ## ### ### ## # ####### ## ## ## ## ##
## ## ### ### #### ####### ##### ## ####
###### ### ### ## # ## # ## ## ## ## # ##
## ## ## ## ## ## ## # ## ## ## ## ## ## ##
## ## #### #### ####### ## ## ###### ####### ####
#### ## ## ## #### ## ## ## #### #######
## #### ### ## ## ## ## ## #### ## ## ## #
## ## ## #### ## ## ## ## ## ## ## ## #
## ## ## ## #### ## ## ## ## ## ## ####
## # ###### ## ### ## ### ## ## ###### ## ### ## #
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #
####### ## ## ## ## ##### ###### ## ## ##### #######
Written by and for assembly language programmers.
March 1989 Contents
Policy................................................... 3
Editorial................................................ 4
Beginners Corner......................................... 5
What is SDN?............................................. 7
FASTPRINT................................................ 8
by Dennis Yelle
The EXEC Function........................................ 11
by Patrick O'Riva
TSR's.................................................... 13
by David O'Riva
Book Reviews............................................. 17
Program Spotlight........................................ 18
Source listings in order of appearance
It has been suggested that the source listings be placed
at the end of the "Magazine" rather than following the
associated article. If you prefer it one way or the other
please advise the editor.
Address of The Assembly Language "Magazine"
AsmLang and CFS FidoNet 143/37 408-259-2223
Patrick O'Riva
2726 Hostetter Rd.
San Jose, CA 95132
2
GUIDE LINES FOR CONTRIBUTORS AND 'ADVERTISERS'
Name and address must be included with all articles and
files. Executable file size and percent of assembly code
(when available) should be included when a program is
mentioned and is required from an author or publisher.
Any article of interest to Assembly language programmers
will be considered for inclusion. Quality of writing will not
be a factor, but I reserve the right to try and correct
spelling errors and minor mistakes in grammar. Non-exclusive
copyright must be given. No monetary compensation will be
made.
Outlines of projects that might be undertaken jointly are
welcome. For example: One person who is capable with hardware
needs support from a user friendly programmer and a math whiz.
Advertisements as such are not acceptable. Authors and
publishers wishing to contribute reviews of their own products
will be considered and included as space and time permit.
These must include executable file size, percent of assembly
code and time comparisons.
Your editor would like information on math libraries, and
reviews of such.
Articles must be submitted in pclone readable format or
sent E-mail.
Money: Your editor has none. Therefore no compensation
can be made for articles included. Subscription fees
obviously don't exist. Publication costs I expect to be nil
(NUL). Small contributions will be accepted to support the BBS
where back issues are available as well as files and programs
mentioned in articles(if PD or Shareware ONLY).
Shareware-- Many of the programs mentioned in the
"Magazine" are Shareware. Most of the readers are prospective
authors of programs that can be successfully marketed as
Shareware. If you make significant use of these programs the
author is entitled to his registration fee or donation.
Please help Shareware to continue to be a viable marketing
method for all of us by urging everyone to register and by
helping to distribute quality programs.
3
Editorial
This is the second monthly edition of the Assembly
Language Magazine. I have been disappointed in the response
to the first issue both in the number of comments I have
received and in the lack of articles submitted for inclusion
in this issue. I would like to give a special thank you to
Richard Hendricks for his letter of suggestions and comments,
most of which I hope to address in this issue. He felt that a
less full look to the pages would improve the readability and
appearance.
My original idea was to fill the page as much as possible
to reduce the amount of print out. Many of his other comments
reflect my lack of experience as an editor, and I will try to
improve.
Without some articles being sent in this will be near the
last issue. The time is not available for your editors to
write the entire magazine every month, and even if it were,
the quality would suffer from lack of variety. Please send
those submissions.
Please excuse the late publication this month.
ERRATA
The name of the Author of the A86 Assembler was
misspelled. The correct spelling is: Eric Isaacson
In the article about fast memory moves I repeatedly
referred to the 8259 as the DMA processor. The 8259 is the
interrupt controller and has nothing to do with DMA transfers.
4
Beginners Corner.
Obviously the first requirement for learning any assembly
language is to have a list of the instructions. This may
sound easy and it is something that most novice level books
claim to have, but it is one of the most difficult things to
find.
Every source I have found is very incomplete and/or
unclear. The exact use of each command and the hundreds of
variations on each forces even scarred veterans to look back
at a reference now and again.
At least one of your references must contain the clocks
data for each instruction. For the 80XXX series of processors
this is a very confusing table of adds for all occasions. The
basic time for the instruction is given plus the number of
accesses plus EA calculation. It is different for a
conditional jump taken and when it is not. To make matters
even worse we are dealing with not one processor, but with at
least 5: 8088, 80286, 80386, V20, V30. Each of these works
differently and thus each has its own timing. Because it is
the worst case and because there are so many installed it is
always best to use the timing for the 8088 unless you are
writing for a particular application or system.
A first glance at the EA calculation table will be very
enlightening to those who have used a high level language.
Most of the variables passed and most all of the structures
addressed are done with pointers and offsets to pointers.
These are VERY costly time wise. Even pushes and pops take
far longer than one would think. A memory access (fetching
the value of a variable) especially using the AX register pair
is faster than a push and pop. Be familiar with your
instructions and how long they take. Only use half of the
register pair if that is all you need because it saves an
additional fetch, cutting the time almost in half.
A clear verbal description of each instructions including
its peculiarities is essential. This is true for all, but the
more complex instructions such as rep movsb make it mandatory.
Each instruction should list the flags it can affect.
These are not always what you want or need. For example: the
INC instruction does not affect the carry flag. As silly as
it may sound you have to use an instruction that affects the
flag you want to test for.
The Rotate and Shift group of instructions should be
accompanied with diagrams showing their operation. A glance at
the diagrams will show exactly which one to use where it could
take reading pages to do the same thing.
5
One final note of caution that seems to fit in here --
unless you are doing signed arithmetic operations avoid the
use of the JG and JL type instructions. They take the sign
bit into account and can cause very hard to trace bugs. Use
the JA and JB instructions instead.
Your next most important reference is a description of
the BIOS calls. Most of these are very straight forward and
require little by way of explanation.
Next comes a DOS reference. The critical ones of these
are also quite simple to understand and use. Don't get
involved with FCB's; the handle method of file access is the
only way to go.
For your BIOS and DOS references almost any will work,
but your basic instruction description look long and hard. You
will probably end up with several you use regularly.
With all of your reference works stacked up around you,
sitting at your computer, what next? You need to choose an
assembler. There are at least 4 major packages available,
each with its supporters and its weaknesses. The Microsoft
MASM is complete and the de facto standard. It is also large
and slow. TASM from Borland is faster, and is mostly
compatible, but tricky for beginners. Optasm I am not very
familiar with, though it sounds pretty good. It is much faster
than MASM, and is a full 2 pass assembler. If you are not
concerned with compatibility A86 is fast, small and simple to
use, and the D86 debugger is adequate.
The text editor you use must produce a pure ASCII file.
Whatever you enjoy is the one to use. Qedit from Semware can
be configured to act like almost any other, and is fast and
small.
Next month: Segmentation and Memory allocation.
Useful Shareware:
1029ref DOS and Pc Tech Reference
inter Complete interrupt listing
@last200 Pop up tables
LW86 Pop up instruction summary
Qedit Text editor
A86 Assembler
D86 Symbolic Debugger
6
What is SDN?
SDN stands for Shareware Distribution Network. It is a
relatively new organization operating largely within FidoNet
but in no way affiliated with it. SDN receives software direct
from the author on disk, compresses it using the PAK facility
from No Gate and distributes it to affiliated BBS's throughout
the country, in a highly traceable manner.
This distribution method is advantageous for both the
author and the user. It assures the author of quick and
complete distribution nationwide, and it assure the user of
complete and uncontaminated software.
There is no charge to either the author or the user for
this service, but the author must comply with the instructions
for transmitting the software to the SDN home point.
Complete information is available from your local SDN BBS
or as a last resort you can get the informational files from
AsmLang.
7
FASTPRINT: The fix for one of PRINT's bugs
by Dennis Yelle
Does your printer slow down when you run LIST or your
favorite editor, word processor, debugger, or other program
that spends most of its time just waiting for you to type at
it? Does this annoy you? It annoyed me! It annoyed me
enough so that I did something about it.
How to use it:
--------------
Just type FASTPRINT at the DOS prompt, either before or
after you run PRINT. Or put the FASTPRINT command in your
AUTOEXEC.BAT file, that's what I do. If you put FASTPRINT in
your AUTOEXEC.BAT file, it should normally be placed AFTER any
utility that increases the size of the keyboard type-ahead
buffer.
How it works, if you like grubby details:
-----------------------------------------
The PRINT command installs a TSR that prints files in the
background. This allows you to run other programs on your
computer while the file(s) print. The PRINT TSR "steals" some
computer time from you, but usually not enough to bother you.
The problem is that sometimes (usually) it doesn't steal
enough to keep the printer running at anywhere near full
speed. Fortunately, there is a way to "give" the PRINT TSR
more time. And, it turns out that COMMAND.COM does just that
when it is waiting for you to type a DOS command. The problem
is that most programs that you run don't do this. And so the
printer slows down. ugh! Now we get to the good part.
FASTPRINT looks for some signs that indicate that the
foreground program is waiting for something, like a keystroke,
and tells PRINT to do some printing while we wait.
How it works, only for those who like the REALLY grubby
details:
-------------------------------------------------------
The way to "give" the PRINT TSR some computer time is to
put 0080H in AX, and do an INT 2FH. What FASTPRINT does, is
link itself into interrupts 15H and 16H. INT 16H is used to
read the keyboard. When a program tries to read a key that
the human has not typed yet, FASTPRINT gives PRINT a bit of
computer time, and then checks to see if the human has typed
the key yet. If not, then it gives PRINT some more time and
checks the keyboard again...INT 15H function 90H is used on
the AT only to tell other programs that we are waiting for
something that may take some time. So, if this happens, then
FASTPRINT gives some time to PRINT.
8
To make FASTPRINT even more useful to those who like
grubby details, I am including the A86 source code for the
program.
How to pay for it:
------------------
1. If it is before March 30, 1989 then just: Send a message
to me reporting how well FASTPRINT worked for you, including:
(A. the hardware you are running on;
(B. the version of DOS you are using;
(C. how well it works, that is, for example, how many seconds
does it take to print a www byte xxx line file with a yyy
printer with and without using FASTPRINT while inside program
zzz; and
(D. anything else you have discovered about it, including any
incompatibility with other programs.
You may send this message to my PO Box, or to me at any
of these BBS'S:
AsmLang and CFS (Opus 1:143/37) 408-259-2223
HomeBase 408-988-4004
PDSE 408-745-0880
2. Just send a check for about 1/4 of what you think it is
worth to you to:
Dennis Yelle
P. O. Box 62276
Sunnyvale, CA 94088
If you want to donate, but don't know how much it is
worth, then just send me $10.
3. FOR PROGRAMMERS ONLY: If you want to use any or all of the
source code in the file FASTPRIN.8 in your own program(s), you
may buy the right to do so for $25.
Legal mumbo jumbo
-----------------
This document and the program files FASTPRIN.COM and
FASTPRIN.8 are copyrighted by the author. The copyright owner
hereby licenses you to: use the software; make as many copies
of it as you wish; give the copies to anyone; and to post this
.ARC file on BBS'S, if the BBS is free. There is no charge
for any of the above.
However, you are specifically prohibited from charging,
or requesting donations, for any copies, however made; and
from distributing this software and/or documentation with
commercial products without prior permission.
No copy of this software may be distributed or given away
without this document; and this notice must not be removed.
There is no warranty of any kind, and the copyright owner is
not liable for damages of any kind. By using this software,
you agree to this.
9
--------------------------------------------------------------
Editors note: This is the documentation for FASTPRINT and
has been included in the magazine at the author's suggestion
as it describes the operation, is written in assembly and
includes the source.
10
The EXEC Function
by
Patrick O'Riva
The EXEC function is one of the most powerful features of
DOS. It allows shelling out of an application, and even going
as far as invoking another command interpreter. It is also
rather complex to use and there is no debugging help from DOS.
The EXEC command comes in three basic flavors; Load and
execute a file, Load and overlay, and the 3rd is an internal
DOS function of load a file. Only the load and execute a file
will be covered in detail this month.
I freely admit I'm not an expert on the use of the EXEC
function, but I have convinced it to work for me and done so
recently enough I can remember most of the mistakes. If anyone
reading this is better informed, I encourage them to write a
supplementary article.
DOS requires that you supply three separate pieces of
information. The program <filename> fully qualified as to
drive, path, filename and extension. My experience is that it
will not make a path search, but this might be inaccurate and
caused by a slight error in syntax. The second item is the
command line arguments in DOS format, where the first byte
specifies the length of the argument including the length
byte. The third item is an environment block. For this one
you are allowed to default to current block as passed to the
program you are EXEC'ing out of. To execute a batch file or
use the built-in DOS commands you must EXEC command.com itself
and pass the program name in the arguments.
For "load and execute" a program the syntax is as
follows:
AH=4Bh EXEC function number within INT 21h
AL=0 specify "load and execute"
DX points to program name,
i.e. 'C:\MYSTUFF\RUNME.EXE'
ES:BX points to the "EXEC control block", defined
as follows:
WORD segment of environment block to pass
if 0 defaults to parent block.
environment is assumed to be at offset 0
within this segment
DWORD pointer to command tail
DWORD pointer to 1st FCB
DWORD pointer to 2nd FCB
"COMMAND TAIL" is defined as that portion of a command
line following the program name, preceded by a one byte count
11
of the length of the tail plus 1 for the terminating CR (0Dh),
i.e. DB 12,'/h /Ox1.obj',0Dh
The first job most any program should do is return to DOS
any portion of memory that is not required. Without this
operation there would be no place for DOS to load the new
program. It is also best to do this to inform your program how
much memory is available to it to use for data storage and
other general uses. If FFFF paragraphs are requested from DOS
naturally a fail will be returned, but more importantly the
amount available will also be returned. After determining that
there is sufficient to at least attempt the EXEC the program
can continue.
Let's use the terms parent and child for the two
programs. Once the EXEC is called the parent has no further
control over the operation of the child. All control must be
exercised through the information passed in the parameter
block. You can tailor the environment block, specify the
command tail, and point the 2 FCB's if the program uses them.
If the program doesn't use them you can just point them to 5CH
and 6CH of the parent's PSP(The segment address is supplied by
DOS in ES and DS after the parent is loaded).
I am including a short program that does nothing except
EXEC according to your instructions. With some playing around
it should be a good introductory tool. A sample program name
and command tail are included, but these you will want to
change.
Some of the code in there is unnecessary, but may make it
easier to customize or illustrates some point. With relatively
small changes this could be included in another program to
serve as an exec subroutine. The source is compatible with
both MASM and A86.
12
TSR's
How to get user input, Part Two
(along with lotsa other neat stuff)
by David O'Riva
This article is a multi-part package, all rolled into one
month, mainly because a good deal of it comes in one chunk:
How to write a well-behaved TSR. For those of you who aren't
quite sure what a TSR really is, a short definition follows.
TSR, n, 1. Terminate and Stay Resident. 2. A program or
routine that, after being loaded, carves a niche of memory for
itself and holes up there, only showing itself when a certain
circumstance occurs. This is what is meant by"serially
multitasking." You press a "hotkey", and for some length of
time the program you were working on is subjugated to the
background, and the TSR gets to use the computer.
3. A real pain to write.
TSR'S have numerous uses: print spoolers, expanded
keyboard buffers, text editors, directory manipulators,
background disk formatters, display managers, background file
transferors - anything you may want to do while you're in the
middle of another application.
TSR's usually have the following basic structure
(depending on what they do):
jump to initialization
main body of resident code
keyboard interrupt interceptor
timer tick interceptor
DOS console interceptor
un-installation routine
initialization code
An explanation of each of these follows:
Initialization Code
The initialization code must check to see if the TSR is
already resident in memory (aborting if so), hook all of the
interrupt servers into their respective chains, perform any
other tasks necessary to install the program, then deallocate
all unused memory and exit to DOS with a TSR call.
13
To determine whether or not a copy of the TSR is already
resident, you could assign an unused interrupt to it. The
example uses INT 69H, function ABCD hex. If the example is
already installed, it returns with DCBA hex in AX.
This "residency check" interrupt could also handle
functions for the TSR: i.e. if the TSR was a print
spooler,and there was no "hot key" code written for it (like
the DOS PRINT command), then the program could call INT 69H
with function AB00H to add a file to the print queue.
To hook an interrupt into a chain, you must replace the
appropriate vector with a pointer to your interrupt server,
and your interrupt server must jump to or call the code that
the vector used to point to. Failure to do this caused many
early TSR's to refuse to co-habit with other TSR's,
occasionally making the machine bomb, etc... Unless you have
a very good reason, ALWAYS continue the interrupt chain.
Two TSR exits are available: The first is INT 27H,which
has some limitations (only 64K of your program can remain
resident, which shouldn't be too onerous), and which Microsoft
does not recommend for anything except DOS 1. The second is
INT 21H function 31H, which allows up to a meg to remain
resident (right) and allows you to send a return code back to
the caller.
The initialization code usually de-allocates itself upon
exiting.
Keyboard Interrupt Interceptor
This is only required for programs that wish to pop up
when the hot key is pressed or wish to monitor the keyboard
for some other reason. Much of what they have to do was
discussed last month, with two exceptions.
The first is how to prevent a keystroke from reaching the
BIOS code without leaving the keyboard locked up. To do this,
you have to tell the keyboard that you received the character,
then reset the interrupt controller and exit (without chaining
to the next interrupt.) The keyboard is cleared by toggling
bit 7 of port 61H on, then off. The interrupt controller is
cleared by sending an EOI(End Of Interrupt) code 20H to the
8259 at port 20H (weird,huh?). At that point you should do an
IRET.
The second is the necessity of monitoring the InDOS flag.
Note that YOU ONLY HAVE TO DO THIS IF YOUR TSR USES DOS
FUNCTIONS. Also note that this flag is NOT documented by IBM
OR Microsoft, and that it is not strictly "compatible."
14
The address of the InDOS flag can be acquired by a call
to INT 21H function 34H. The flag is non-zero if a DOS
function is currently active. You need this flag because DOS
is not re-entrant. That is, if you call DOS from your TSR,and
DOS was active when the TSR gained control, the TSR will work
fine, but when you return from it, the function in progress
will have lost its entire stack and much of its data area.
Nasty stuff.
If the InDOS flag is set when the keyboard trap is
activated, the keyboard trap should set a flag saying "I want
to activate but I can't" and return.
Timer Tick Interceptor
These are always fun. 18.2 times per second, you get
control of the machine. This is useful for print spoolers,
resident screen clocks, and TSR'S that need to perform DOS
functions.
In the last case, the timer tick trap should check to see
if the TSR wants to activate, and if so whether the InDOS flag
is set. If the InDOS flag is clear, then the TSR can be
activated.
DOS Console Interceptor
This interrupt (INT 28H) is called by DOS whenever a DOS
console function is in progress. This is useful to have
because during a console input, the InDOS flag is set, but it
is actually safe to use any DOS function ABOVE 0CH.
This trap should check to see if the TSR wants to
activate, and if so then activate it.
Main Body of the TSR
The only requirement for this is that if you use more
than two words (yes, this is an arbitrary measure that seems
safe to me) of stack, you should use your internal stack
instead of the one that you were called with. This is because
you have no idea how large the stack you're working on is, and
no way of telling how far you can go before you start to
overwrite data...
The Un-Installation routine
This is a very nice thing to have. The only problem is
that it needs to use a DOS call to de-allocate its memory.
15
Unless, of course, you want to directly modify the memory
control blocks... which could be very dangerous.
The routine in the example TSR does check the memory
control block immediately after it to make sure that there are
no programs installed in memory after the TSR. DOS gets upset
when there are "holes" in memory. At least, it's supposed to.
The structure included for the MCB is complete as far as I
know, but it is also *extremely* undocumented. Microsoft
calls it an "arena header" and tells programmers to keep their
hands off of it.
All vectors must be unhooked.
The DOS calls to de-allocate the memory are necessary.
Neat tricks you can pull...
The command line area in the PSP makes for a convenient
40 word internal stack. If you aren't using the FCBS, the FCB
storage area can be commandeered as well.
The environment segment's size can be checked (with a
look at the MCB) and that can be used for stack, data or code
as well.
The Example TSR
As far as actual use goes, the example TSR is a very
simple beast. When resident, press LSHIFT-RSHIFT-T (it'll
beep). Then press "Q" to return to normal processing, or
press "N" to Nuke the TSR (remove it from memory). If it
cannot remove itself, it will beep three times.
Well, that's about all there is to it. With all these
routines (or at least the ones you need) combined in one
cohesive package, you have a full-fledged TSR skeleton. The
example TSR includes most of these techniques. Use and enjoy.
Comments on this article, or expansions on the subject matter
are greatly appreciated and will be published in later issues
of the magazine.
16
Book Reviews by Patrick O'Riva
PROGRAMMER'S TECHNICAL REFERENCE FOR MSDOS AND THE IBM PC
By Dave Williams P.O. box 181, Jacksonville, AR 72087
This is a first glance review that I wanted to get into
this month's issue. It is a user supported book on disk. I
haven't had the time to read through it, but it appears to be
one of the best available in any format. In compressed form
it is over 200k, and expands to over 500k. It is very
complete and includes some hard to find tables and version
histories. Requested $15 support includes 2 additional disks
and updates. Probably one of the best buys around.
It is available on various BBS's (AsmLang included)
though its 200k+ size makes for slow distribution. The
complete package direct from the author is probably best.
IBM Technical Reference- Personal Computer XT
This comes in the standard binder/box and is available
from IBM corporation at a hefty $50. In addition to some
information on port assignments and memory maps it contains
two sets of information that I have found nowhere else.
A complete listing of the ROM BIOS for the XT that is
invaluable as both an example of programming each piece of
hardware and clues as to how to optimize for a specific
application.
Complete schematics of the motherboard of the XT that can
help to explain why something won't work if you are
technically inclined.
I have found it invaluable, but it is not for everyone.
Similar publications are available for the other IBM
products and for the PCDOS.
For further information contact IBM at 1-800-426-7282
17
Program Spotlight
Exceptional Programs
This is really a nonexistent column this month as nothing
new has come to my notice that meets the qualifications of
high speed and small size.
There is a nice piece of source code available under the
name of CRC16 that calculates and verifies 16 bit CRC values.
If this meets a need of yours keep a look out for it.
Although I haven't tried it out yet, a program called the
Brand X symbolic debugger has been highly recommended to me.
Other comments would be welcomed.
QFILE31G handles copying, deleting and moving files, as
well as maintaining and listing ARC's and ZIP's. Nicely done,
but too slow and too large to really fit here, and hangs up on
some error conditions.
WHIZ1 a file finding program certainly qualifies in speed
as a multi disk search with wild card only takes a few
seconds. It is a bit larger than it might be, but at the
least it is enhanced with assembly routines. It is a shareware
offering and is widely available.
18
;Ed -This is the source code on FASTPRINT
jmp install ; This will be a .COM
db cr, ' ', cr, lf ; On most displays, if this file is TYPEed,
; these 3 spaces will erase
; the 3 characters in the JMP instruction above.
message0:
db 'FASTPRIN 0.0 Copyright 1989 by Dennis Yelle,'
db ' PO Box 62276, Sunnyvale CA 94088'
db cr, lf
message0_len = $ - message0
db 'Last change: Mar 4, 1989', cr, lf
db 26 ; This 26 is a control-Z, the DOS EOF character.
; I put it here so that if someone TYPEs this .COM file,
; only the characters before the 26 will be printed on the screen.
id_size = $ - 0100
; To assemble this program, put the source in a file called
; FASTPRIN.8 and then type
; A86 FASTPRIN.8
; This will produce a file called FASTPRIN.COM directly,
; in about 2 seconds! No need to run a linker!
; The program A86.COM is available from many BBSs in an .ARC file
; that starts with A86.
; In particular, A86.COM is available from the "AsmLang and CFS" BBS
; (Opus 1:143/37) 408-259-2223 in the file A86V319A.ZIP.
; Or HomeBase 408-988-4004 in the file A86V309A.ARC.
; Or PDSE 408-745-0880 in the file A86V314A.ARC.
;
; FASTPRIN installs a TSR which speeds up the printing done by the
; DOS PRINT command by calling INT 02FH with AX = 0080H whenever the
; system is idle. We know the system is idle whenever:
;
; 1. INT 016 is called with AH = 0 and there are no characters in
; the keyboard buffer. or
; 2. INT 015 is called with AH = 090. This will only happen on an AT.
;
; Termination codes:
; 0 - Successfully installed.
; 1 - Unable to determine if already installed, installed anyway.
; 2 - Already installed.
;
; If this program has been helpful to you, then please send a
; small gift to the author, Dennis Yelle, PO Box 62276,
; Sunnyvale, CA 94088. A gift of $10 is suggested.
cr = 0d
lf = 0a
even
old_int_15 dw 2 dup (?)
old_int_16 dw 2 dup (?)
get_vect macro
push es
#rx1l
mov ax, 03500 + 0#x
int 021
mov old_int_#x, bx
mov old_int_#x[2], es
#er
pop es
#em
set_vect macro
#rx1l
mov dx, offset new_int_#x
mov ax, 02500 + 0#x
int 021
#er
#em
new_int_16:
pushf
sti
test ah,ah
jz wait_16
popf
jmp cs: d old_int_16
wait_16_loop:
mov ax, 0080
int 02f ; Do some printing.
wait_16:
mov ah, 1
int 016 ; Are any chars ready?
jz wait_16_loop ; Jump if not.
mov ah, 0
popf
jmp cs: d old_int_16
new_int_15:
pushf
sti
cmp ah, 090
je idle
popf
jmp cs: d old_int_15
idle:
push ax
mov ax, 0080
int 02f ; Do some printing.
pop ax
popf
jmp cs:d old_int_15
end_of_resident:
;---------------------------------------------------------
install:
mov ah, 040
mov bx, 1 ; Write to stdout
mov dx, message0
mov cx, message0_len
int 021
mov ah, 052
int 021
mov ax, es:[bx-2]
mov dx, ds
dec dx
cld
find:
cmp ax, dx
je install_it
ja mem_bad
mov es, ax
mov si, 0100
mov di, 0110
mov cx, id_size/2
repe cmpsw
je already_installed
stc
adc ax, es:[3]
jnc find
mem_bad:
mov w installed_msg, mem_bad_msg
inc b term_code
install_it:
get_vect 15, 16
set_vect 15, 16
mov ah, 049
mov es, [02c]
int 021 ; Free the ENV segment.
mov dx, installed_msg
mov ah, 9
int 021 ; Print the INSTALLED message.
mov ah, 031
mov al, term_code
mov dx, (end_of_resident+15)/16
int 021 ; Terminate and stay resident.
;-------------------------------------------------------------
already_installed:
mov dx, ai_msg
mov ah, 9
int 021 ; Print the message.
mov ax, 04c02
int 021
installed: db 'FASTPRIN is now installed.', cr, lf, '$'
mem_bad_msg:
db 'FASTPRIN was unable to determine if it was previously installed, or not,'
db cr, lf,
db 'so it installed itself anyway.', cr, lf, '$'
ai_msg: db 'FASTPRIN was already installed.', cr, lf, '$'
installed_msg dw installed
term_code db 0
;THIS IS THE SOURCE CODE FOR THE EXEC FUNCTION ARTICLE
;
;THIS PROGRAM WITH FEW IF ANY SYNTAX CHANGES SHOULD BE COMPATIBLE WITH BOTH A86
;AND MASM.
;IT IS NOT DESIGNED TO BE A STAND ALONE PROGRAM OR EVEN USER FRIENDLY BUT A
;BARE SHELL WITH WHICH TO PLAY AROUND WITH IN LEARNING TO USE THE DOS EXEC
;FUNCTION
;THE PROGRAM NAME SHOULD BE ENTERED BETWEEN THE QUOTE MARKS AT THE VARIABLE
;-PROGRAM- AND ANY OTHER COMMAND LINE ARGUMENTS SHOULD BE ENTERED BETWEEN THE
;QUOTE MARKS AT THE VARIABLE -COMMAND- BEFORE ASSEMBLY. MANY IMPROVEMENTS ARE
;OBVIOUS, SUCH AS BEING ABLE TO ENTER THE NAMES AND ARGUMENTS FROM THE CONSOLE
;BUT THESE ARE LEFT TO EXPERIMENTER.
;THE ONE SOPHISTICATION IF YOU COULD CALL IT THAT IS THAT THE ERROR LEVEL OF
;THE EXEC'D PROGRAM IS FETCHED, BUT NOTHING IS DONE WITH IT.
CODE SEGMENT 'CODE'
ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE
org 0
PROG_ORG LABEL BYTE
org 0100
ENTRY: JMP START
db 32 DUP ('STACK ') ;THIS SETS ASIDE 256 BYTES-
;REQUIRED DOS STACK
STAK LABEL BYTE ;THIS MARKS TOP OF STACK
THIS_SEG DW 0 ;STORAGE FOR ADDRESS OF THIS SEGMENT
CTL_LEN DW 0 ;THIS IS THE START OF THE *****
PROGRAM DB 'c:\dos\list.com',0 ;ASCIIZ STRING OF PROGRAM TO
EXEC
CMDL LABEL BYTE ;BEG ADDRESS OF COMMAND TAIL
CMDLEN DB 0 ;OFFSET CR - OFFSET COMMAND
COMMAND DB 'exec.asm' ;THE COMMAND TAIL ITSELF
CR DB 0DH ;TERMINATING CARRIAGE RETURN
PARMS LABEL BYTE ;ADDRESS OF PARAMETER TABLE
ENV DW 0 ;POINTER TO ENVIRONMENT BLOCK
;0 DEFAULTS TO PARENT
CMDLNLOW DW 0 ;OFFSET OF COMMAND TAIL
CMDLNHI DW 0 ;SEGMENT OF COMMAND TAIL
FCB11 DW 0 ;OFFSET OF FCB1
FCB12 DW 0 ;SEGMENT OF FCB1
FCB21 DW 0 ;OFFSETT OF FCB2
FCB22 DW 0 ;SEGMENT OF FCB2
MS1 DB 'DEALLOCATION ERROR',0DH,'$' ;MESSAGE #1
MS2 DB 'EXEC ERROR',0DH,'$' ;MESSAGE #2
START:
CLI ;TURN OFF INTERRUPTS WHILE SWITCHING
;STACK FRAMES
MOV SP,OFFSET STAK ;POINT STACK WHERE WE WANT IT
STI ;INTS OK NOW
MOV AX,DS ;BEING A COM PROGRAM DS CONTAINS THIS
;SEGMENT
MOV THIS_SEG,AX ;PUT IT WHERE WE CAN GET IT EASILY
RESIZE:
MOV BX,(OFFSET PROG_END - OFFSET PROG_ORG) ;FIND OUT
;HOW BIG THE PROGRAM IS
MOV CL,4 ;TURN THIS VALUE INTO PARAGRAPHS
SHR BX,CL
;
INC BX ;NEEDS 1 MORE TO CATCH OVERFLOW
MOV AH,04AH ;ASK FOR DEALLOCATION OF ALL BUT THIS
INT 021H
JNC S1 ;IF CARRY BAD PROBLEM
MOV AX,OFFSET MS1 ;POINT AX AT MESSAGE 1
JMP END_ERROR ;NOW LEAVE
S1:
;AN ASSUMPTION IS MADE AT THIS POINT, THAT
;THERE IS ENOUGH MEMORY FOR THE PROGRAM TO BE
;EXEC'D TO OPERATE IN. IF NOT AN EXEC ERROR
;WILL BE RETURNED.
MOV AX,OFFSET CR ;FIND LENGTH OF COMMAND TAIL
SUB AX,OFFSET CMDL ;AND
MOV CMDLEN,AL ;PUT IT IN CMDLEN
MOV DX,OFFSET CMDL ;FILL IN THE COMMAND TAIL POINTER IN
;THE PARAMETER BLOCK
MOV CMDLNLOW,DX
MOV CMDLNHI,DS
MOV DX,OFFSET PROGRAM ;POINT DX AT PROGRAM NAME
MOV ES,THIS_SEG ;MAKE SURE ES IS POINTING AT THIS SEG
MOV BX,OFFSET PARMS ;POINT BX AT PARAMETER BLOCK
MOV AX,05CH ;THIS IS OFFSET OF PARENT FCB1 BUT
;COULD BE ANY INFO DESIRED TO PASS TO
;CHILD THAT WOULD BE LOOKED FOR AT PSP
;5CH
MOV FCB11,AX ;PUT IT IN PARAMETER BLOCK
MOV FCB12,ES ;DESIRED SEGMENT OF FCB1 DATA
MOV AX,06CH ;FILL IN FCB2. ABOVE COMMENTS APPLY
MOV FCB21,AX
MOV FCB22,ES
MOV AH,04BH ;THE EXEC FUNCTION
MOV AL,0 ;LOAD AND EXECUTE
PREX:
INT 021H ;DOS
JNC FINISHED ;ALL ACCORDING TO PLAN
MOV AX,OFFSET MS2 ;SOMETHING WRONG IN EXEC
JMP SHORT END_ERROR
FINISHED:
MOV AH,04DH ;TO REACH HERE THE CHILD MUST HAVE
;TERMINATED. THIS REQUESTS THE
;TERMINATION CODE
INT 021H ;DOS TERMINATION CODE RETURNED IN AH
;CHILD'S AL PASSED THROUGH
MOV AH,04CH ;TERMINATE THIS PROG
INT 021H
END_ERROR:
MOV DX,AX ;SAVE THE MESSAGE ADDRESS TO DX
MOV BX,1 ;DEVICE HANDLE IS CONSOLE
MOV CX,20 ;20 BYTES IS ENOUGH FOR THE MESSAGE
MOV AH,040H ;WRITE TO DEVICE (CONSOLE)
INT 021H ;DOS
MOV AH,04CH ;TERMINATE THIS PROGRAM
INT 021H
PROG_END LABEL BYTE
CODE ENDS
END ENTRY
PAGE 60,132
TITLE EXMPLTSR.ASM - An example of a TSR program
COMMENT~*********************************************************************
* --++**> This file is Copyright 1989 by David O'Riva <**++-- *
*****************************************************************************
* *
* Written for the Microsoft Macro Assembler version 5.1, DOS v2.0 - 3.3 *
* *
* MUST BE CONVERTED INTO A .COM FILE BEFORE RUNNING!!! *
* *
* Anyone who wants to incorporate this code into their programs is *
* welcome to, as long as they don't try to sell this file as it is or any *
* unmodified piece of it, and leave this message in when distributing it. *
* If you do not abide by the rules, poltergeists will invade your home *
* and chain letters and junk mail will arrive by the ton. *
* *
* *
* Short advertisement - use QEdit! *
* Available on your local friendly Bulletin Board *
* *
****************************************************************************~
.XLIST
;
; Macros used by this program
;
?PLevel = 0
PNPROC MACRO PNAME ;;declare near public procedure
IF2
%OUT Routine: &PNAME
ENDIF
PUBLIC &PNAME
&PNAME PROC NEAR
?PLevel = ?PLevel+1 ;;next level of nesting
@@SAVE_NAME &PNAME,%?PLevel
ENDM
PFPROC MACRO PNAME ;;declare near public procedure
IF2
%OUT Routine: &PNAME
ENDIF
PUBLIC &PNAME
&PNAME PROC FAR
?PLevel = ?PLevel+1 ;;next level of nesting
@@SAVE_NAME &PNAME,%?PLevel
ENDM
ENDPROC MACRO
@@REC_NAME %?PLevel
@@EP1 %@@TEMP
?PLevel = ?PLevel-1
ENDM
@@SAVE_NAME MACRO PNAME,LVL
?PN&LVL EQU <&PNAME>
ENDM
@@REC_NAME MACRO LVL
@@TEMP EQU <?PN&LVL>
ENDM
@@EP1 MACRO PNAME
&PNAME ENDP
ENDM
PUSHM MACRO LST
IRP REG,<&LST&>
PUSH REG
ENDM
ENDM
POPM MACRO LST
IRP REG,<&LST&>
POP REG
ENDM
ENDM
UPCASE MACRO REG
LOCAL NOUP
CMP REG,'a'
JB NOUP
CMP REG,'z'
JA NOUP
SUB REG,'a'-'A'
NOUP:
ENDM
@CHANGE_VECT MACRO INUM,GARB,NEW,GARB2,SAVEAREA
MOV AX,0
MOV ES,AX
MOV AX,ES:[INUM*4]
MOV DX,ES:[INUM*4+2]
MOV WPTR CS:[SAVEAREA],AX
MOV WPTR CS:[SAVEAREA+2],DX
MOV AX,OFFSET CS:NEW
CLI
MOV ES:[INUM*4],AX
MOV ES:[INUM*4+2],CS
STI
ENDM
@RESTORE_VECT MACRO INUM,GARB,SAVEAREA
MOV AX,WPTR CS:[SAVEAREA]
MOV DX,WPTR CS:[SAVEAREA+2]
CLI
MOV DS:[INUM*4],AX
MOV DS:[INUM*4+2],DX
STI
ENDM
BPTR EQU <BYTE PTR>
WPTR EQU <WORD PTR>
DPTR EQU <DWORD PTR>
CR EQU <13>
LF EQU <10>
JR EQU <JMP SHORT>
FALSE EQU <000H>
TRUE EQU <0FFH>
INT_CTRL EQU 020H ;Interrupt control port
EOI EQU 020H ;Reset interrupt controller command
KB_DATA EQU 060H ;Keyboard data port
KB_CTRL EQU 061H ;Keyboard control port
;
;****************************************************************************
;
BIOSDATA SEGMENT AT 00040H
;----------------------------------------------------------------------------
;Keyboard Data Area
;----------------------------------------------------------------------------
ORG 00017H
KB_FLAG LABEL BYTE
;----- Shift flag equates within KB_FLAG
INS_STATE EQU 80H ;INSERT state is active
CAPS_STATE EQU 40H ;CAPS LOCK state toggled
NUM_STATE EQU 20H ;NUM LOCK state toggled
SCROLL_STATE EQU 10H ;SCROLL LOCK state toggled
ALT_SHIFT EQU 08H ;ALT key depressed
CTRL_SHIFT EQU 04H ;CTRL key depressed
LEFT_SHIFT EQU 02H ;left SHIFT key depressed
RIGHT_SHIFT EQU 01H ;right SHIFT key depressed
ORG 00018H
KB_FLAG_1 LABEL BYTE
;----- Shift flag equates within KB_FLAG_1
INS_SHIFT EQU 80H ;INSERT key depressed
CAPS_SHIFT EQU 40H ;CAPS LOCK key depressed
NUM_SHIFT EQU 20H ;NUM LOCK key depressed
SCROLL_SHIFT EQU 10H ;SCROLL LOCK key depressed
HOLD_STATE EQU 08H ;suspend key has been toggled
ORG 00019H
ALT_INPUT LABEL BYTE ;storage for alternate keypad entry
ORG 0001AH
BUFFER_HEAD LABEL WORD ;pointer to head of keyboard buffer
ORG 0001CH
BUFFER_TAIL LABEL WORD ;pointer to tail of keyboard buffer
ORG 0001EH
KB_BUFFER LABEL WORD ;keyboard buffer
ORG 0003EH
KB_BUFFER_END LABEL WORD
;
;----- HEAD = TAIL indicates that the buffer is empty
NUM_KEY EQU 69 ;scan code for NUM LOCK
SCROLL_KEY EQU 70 ;sc for SCROLL LOCK
ALT_KEY EQU 56 ;sc for ALT key
BIOSDATA ENDS
.list
.lall
;
;****************************************************************************
;
CODE SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE
ORG 02CH
ENVIRONMENTSEG LABEL WORD
ORG 100H
MAIN PROC FAR
STACKTOP: ;allow the stack to overwrite the
;command line in the PSP
ENTRY: JMP INSTALL
;============================================================================
;
; MY LOCAL DATA
;
MCBOVL STRUC ;definition of DOS's memory control
; ;block structure
MCB_KIND DB ' '
MCB_PSP_ADDR DW 0
MCB_LENGTH DW 0
MCB_UNDEFINED DB 11 DUP(?)
;
MCBOVL ENDS
m@BLOCK EQU <'M'> ;possible entries in the MCB_TYPE field
m@LAST EQU <'Z'>
OLD9 DD ? ;old INT 09H vector
OLD08 DD ?
OLD28 DD ?
OLD69 DD ?
InDOS DD ? ;address of the InDOS flag
CODSEG DW ? ;code segment
AllowPop DB FALSE ;TRUE = the INT 09H server is allowed
; to try to run the TSR.
TryPop DB FALSE ;TRUE = The INT 09H server couldn't
; run the TSR because DOS was
; processing a command at the
; time.
;
InPop DB FALSE ;TRUE = the popped-up code is running
; at the moment.
OLDSS DW ? ;storage for stack frame information
OLDSP DW ?
PopShifts DB LEFT_SHIFT+RIGHT_SHIFT ;shift state necessary for
; pop-up
PopKey DB 14H ;key # to press to pop up ('T')
;============================================================================
PAGE
;****************************************************************************
; START - main program loop
;
;
; ENTRY: from hot key
;
; EXIT: nothing
;
; DESTROYED: none
;
;----------------------------------------------------------------------------
PNPROC START
;------------------------------------------------------------------------------
; set up my stack frame
;------------------------------------------------------------------------------
MOV CS:OLDSS,SS
MOV CS:OLDSP,SP
CLI
MOV SS,CS:CODSEG
MOV SP,OFFSET STACKTOP
STI
PUSH AX
;------------------------------------------------------------------------------
; MAIN LOOP
;------------------------------------------------------------------------------
CALL BEEP
GETAKEY:
MOV AH,0 ;get a key
INT 016H
UPCASE AL
CMP AL,'N' ;'N' for NUKE THE TSR!
JNE NOT_NUKE
CALL UNLOAD ;try to unload myself
JNC LEAVE
CALL BEEP ;string of beeps if I can't
CALL BEEP
CALL BEEP
JMP GETAKEY
NOT_NUKE: CMP AL,'Q' ;'Q' for quit TSR
JE LEAVE
;
CALL BEEP ;unrecognized key, complain
JMP GETAKEY ;... and get another
LEAVE: POP AX ;recover * ALL * modified registers
; -------
;------------------------------------------------------------------------------
; recover original stack frame
;------------------------------------------------------------------------------
CLI
MOV SS,CS:OLDSS
MOV SP,CS:OLDSP
STI
RET
ENDPROC
PAGE
;******************************************************************************
; BEEP - Beeps the speaker
;
;
; ENTRY: nothing
;
; EXIT: nothing
;
; DESTROYED: none
;
;------------------------------------------------------------------------------
PNPROC BEEP
PUSH AX
PUSH CX
MOV AL,10110110B
OUT 043H,AL
MOV AX,1000
OUT 042H,AL
MOV AL,AH
OUT 042H,AL
IN AL,061H
MOV AH,AL
OR AL,3
OUT 061H,AL
SUB CX,CX
LOOP $
MOV AL,AH
OUT 061H,AL
POP CX
POP AX
RET
ENDPROC
; PAGE
;;****************************************************************************
;; REGISTERTOTEXT - Converts AL into ASCII hex digits in CS:[SI]
;;
;; For debugging - uncomment this routine if you need it.
;; ;
;; ENTRY: AL = register to translate
;; SI = place to put translated digits
;;
;; EXIT: AX = hex digits
;;
;; DESTROYED: AX
;;
;;----------------------------------------------------------------------------
;ASSUME ds:NOTHING,es:NOTHING
;PNPROC REGISTERTOTEXT
;;----------------------------------------------------------------------------
;; split AL into two nibbles
;;----------------------------------------------------------------------------
; MOV AH,AL
; SHR AH,1
; SHR AH,1
; SHR AH,1
; SHR AH,1
; AND AL,0FH
;;----------------------------------------------------------------------------
;; convert AL into a hex digit
;;----------------------------------------------------------------------------
; ADD AL,'0' ;AL = actual digit
; CMP AL,'9'
; JBE R_1
; ADD AL,'A'-'0'-10
;;----------------------------------------------------------------------------
;; convert AH into a hex digit
;;----------------------------------------------------------------------------
;R_1: ADD AH,'0' ;AH = actual digit
; CMP AH,'9'
; JBE R_2
; ADD AH,'A'-'0'-10
;;----------------------------------------------------------------------------
;; store hex number in [SI]
;;----------------------------------------------------------------------------
;R_2: MOV CS:[SI],AH
; MOV CS:[SI+1],AL
; RET
;ENDPROC
;
PAGE
;****************************************************************************
; TRAPPER9 - Intercepts the incoming keyboard scan code
;
; Looks for the hot key in the incoming scan codes. If found, it
; check the InDOS flag to see if DOS is currently in the middle of something.
; If not, the TSR code (at START) is invoked. Otherwise, a flag is set and
; control is passed back to DOS.
;
; ENTRY: from IRQ 1, machine state is ???
;
; EXIT: continues KB interrupt chain
;
; DESTROYED: ALL PRESERVED
;
;----------------------------------------------------------------------------
ASSUME ds:BIOSDATA,es:NOTHING
PFPROC TRAPPER9
PUSHM <AX,BX,DS,ES> ;save everthing I use
MOV AX,SEG BIOSDATA ;DS-> BIOS's data seg
MOV DS,AX
;----------------------------------------------------------------------------
; are we in the correct shift state?
;----------------------------------------------------------------------------
MOV AL,KB_FLAG ;get current shift states
AND AL,00FH ;clean up the byte
CMP AL,CS:PopShifts ;is it the right shift?
JNE T_chainon ;no, go to next handler
IN AL,KB_DATA ;Poll keyboard controller
MOV BH,AL ;save keypress
AND AL,07FH ;strip off the MAKE/BREAK bit
CMP AL,CS:PopKey ;is this our key?
JNE T_ChainOn ;If it's not our code, ignore it...
;----------------------------------------------------------------------------
; reset the keyboard controller
;----------------------------------------------------------------------------
IN AL,KB_CTRL ;get multi-purpose control byte
MOV AH,AL ;save original value
OR AL,080H ;set "character recieved" bit
OUT KB_CTRL,AL ;send it
MOV AL,AH ;get original value back
OUT KB_CTRL,AL ;send it
;------------------------------------------------------------------------------
; what do we do with this key?
;------------------------------------------------------------------------------
CMP CS:AllowPop,FALSE ;Are we allowed to pop up now?
JE T_ResetLeave ;if not, exit here...
TEST BH,080H ;was it the BREAK code?
JZ T_WasMake ;no, go run the pop-up
JMP T_ResetLeave ;otherwise, ignore this code
T_WasMake: MOV CS:AllowPop,FALSE ;can't pop up again
PUSHM <ES,BX> ;check the InDOS flag...
LES BX,CS:InDOS
CMP BPTR ES:[BX],0
POPM <BX,ES>
JNZ T_InDOSnow ;if in DOS, don't invoke TSR
MOV AL,EOI ;otherwise, set interrupts on...
OUT INT_CTRL,AL
MOV CS:InPop,TRUE ;...set a flag...
STI
CALL START ;...and run the TSR.
CLI
MOV CS:AllowPop,TRUE
MOV CS:InPop,FALSE
JMP T_Leave
T_InDOSnow: MOV CS:TryPop,TRUE ;DOS call in progress, try later...
;
T_ResetLeave: MOV AL,EOI ;enable the KB interrupt again
OUT 020H,AL
T_Leave: POPM <ES,DS,BX,AX> ;throw away this key code
IRET
;----------------------------------------------------------------------------
; Continue down the KB handler chain...
;----------------------------------------------------------------------------
T_chainon: POPM <ES,DS,BX,AX>
JMP DWORD PTR CS:OLD9
ENDPROC
PAGE
;******************************************************************************
; TRAPPER08 - Intercepts the timer tick to try to pop up the TSR
;
; Upon being called, this routine determines whether:
; a) the TSR wants to pop up, and
; b) there is no DOS call in progress
; If these are BOTH true, then the TSR is invoked from here.
;
; NOTE: Technically, you are supposed to trap vector 1CH for timer
; ticks instead of INT 08, but some BIOSes do not issue an EOI for the timer
; until AFTER 1C is called, and you don't want to mess up the system clock...
;
; ENTRY: on timer tick
;
; EXIT: nothing
;
; DESTROYED: ALL PRESERVED
;
;------------------------------------------------------------------------------
PFPROC TRAPPER08
ASSUME ds:NOTHING,es:NOTHING
PUSHF
CALL DWORD PTR CS:[OLD08]
CLI
CMP CS:TryPop,FALSE ;Are we trying to pop up?
JE C_OUT ;if not, leave now
PUSHM <ES,BX> ;Is a DOS call in progress?
LES BX,CS:InDOS
CMP BPTR ES:[BX],0
POPM <BX,ES>
JE C_Invoke ;if not, then fire it up...
C_OUT: IRET
C_Invoke: MOV CS:TryPop,FALSE ;set the right flags...
MOV CS:InPop,TRUE
STI
CALL START ;run the TSR...
MOV CS:InPop,FALSE ;set some more flags...
MOV CS:AllowPop,TRUE
;
IRET ;and leave.
ENDPROC
PAGE
;******************************************************************************
; TRAPPER28 - Intercepts the DOS "console wait" loop
;
; This routine allows the TSR to be invoked during DOS console operations
; where the InDOS flag is set but it is actually safe to use any DOS function
; above 0CH.
;
; ENTRY: During DOS console operations
;
; EXIT: nothing
;
; DESTROYED: ALL PRESERVED
;
;------------------------------------------------------------------------------
ASSUME ds:NOTHING,es:NOTHING
PFPROC TRAPPER28
CMP CS:TryPop,FALSE ;does the TSR want to pop up?
JE C1_OUT ;if not, get out of here now
MOV CS:TryPop,FALSE ;prevent re-entrant interrupts
MOV CS:InPop,TRUE ;set the "in TSR" flag
STI
CALL START ;run it!
CLI
MOV CS:AllowPop,TRUE;finished, we can pop up again
MOV CS:InPop,FALSE ;no longer running the TSR
C1_OUT: JMP DWORD PTR CS:OLD28 ;continue down the console chain
ENDPROC
PAGE
;******************************************************************************
; TRAPPER69 - Chains to INT 69H so we can determine if the TSR's already here
;
; This routine provides a method of checking to see whether a copy of
; this TSR already exists in memory. This entry could also be used to
; reconfigure the resident portion of the program (i.e. to reset a clock,
; to add a file to a print queue... etc.)
;
; ENTRY: AX = 0ABCDH
;
; EXIT: If the TSR is already resident:
; AX = 0DCBAH
;
; If the TSR is NOT resident:
; AX = 0ABCDH
;
; DESTROYED: AX if resident
;
;------------------------------------------------------------------------------
ASSUME ds:NOTHING,es:NOTHING
PFPROC TRAPPER69
;
CMP AX,0ABCDH ;Is it the correct function?
JE CK_OURS ;yes, respond to it
JMP DWORD PTR CS:OLD69
CK_OURS: MOV AX,0DCBAH ;set return code
IRET
ENDPROC
PAGE
;****************************************************************************
; UNLOAD - Unhooks all vectors and exits
;
; This routine removes the TSR from memory, if possible. The sequence
; of events is:
; a) a check is made to be sure that we are the LAST program in memory
; b) all vectors are unhooked
; c) the environment segment that DOS gave me is deallocated
; d) the current code segment is deallocated
; e) control is returned to whatever program was running before
;
; ENTRY: nothing
;
; EXIT: nothing
;
; DESTROYED: this program, hopefully
;
;----------------------------------------------------------------------------
ASSUME ds:CODE,es:CODE
PNPROC UNLOAD
;------------------------------------------------------------------------------
; see if there are active memory control blocks after mine
;------------------------------------------------------------------------------
PUSH AX
PUSH ES
MOV AX,CS ;my MCB starts 1 seg before
DEC AX ; my code seg
MOV ES,AX
ADD AX,ES:[0].MCB_LENGTH ;find out how long it is
INC AX ;fiddle the value
MOV ES,AX
MOV AL,ES:[0].MCB_KIND ;what kind is the next MCB?
CMP AL,m@LAST ;if it's not the last...
POP ES
POP AX
JNZ BAD_UNLOAD ;...then leave now
;------------------------------------------------------------------------------
; unhook all the vectors
;------------------------------------------------------------------------------
PUSH AX
PUSH DX
PUSH ES
PUSH DS
MOV AX,0
MOV DS,AX
@RESTORE_VECT 009H FROM OLD9 ;Restore keyboard vector
@RESTORE_VECT 008H FROM OLD08 ;Restore timer vector
@RESTORE_VECT 028H FROM OLD28 ;Restore console vector
@RESTORE_VECT 069H FROM OLD69 ;Restore my TSR vector
;------------------------------------------------------------------------------
; deallocate everything
;------------------------------------------------------------------------------
MOV ES,CS:ENVIRONMENTSEG
MOV AH,049H ;de-allocate the environment
INT 021H ; block
PUSH CS
POP ES
MOV AH,049H ;de-allocate the code block
INT 021H
POP DS
POP ES
POP DX
POP AX
CLC
RET
;------------------------------------------------------------------------------
;can't unload: memory control blocks after mine...
;------------------------------------------------------------------------------
BAD_UNLOAD: STC
RET
ENDPROC
PAGE
;****************************************************************************
; INSTALL - Installs traps, then runs the program.
;
; ENTRY: called on entry to the program
;
; EXIT: TSR's the main program and exits to DOS
;
; DESTROYED: ALL
;
;----------------------------------------------------------------------------
ASSUME ds:CODE,es:CODE
PNPROC INSTALL
PUSH CS
PUSH CS
POP DS
POP ES
MOV CS:CODSEG,CS
;------------------------------------------------------------------------------
; see if this program is already in memory
;------------------------------------------------------------------------------
MOV AX,0ABCDH
INT 069H
CMP AX,0DCBAH
JNE NOT_THERE
JMP ALREADY_THERE
;----------------------------------------------------------------------------
; install our keyboard hardware interrupt trap
;----------------------------------------------------------------------------
NOT_THERE: MOV AllowPop,FALSE
MOV TryPop,FALSE
MOV AX,03400H ;get the InDOS flag's address
INT 21H
MOV WPTR CS:InDOS,BX ;...and save it
MOV WPTR CS:InDOS[2],ES
@CHANGE_VECT 009H TO TRAPPER9 SAVEIN OLD9
@CHANGE_VECT 008H TO TRAPPER08 SAVEIN OLD08
@CHANGE_VECT 028H TO TRAPPER28 SAVEIN OLD28
@CHANGE_VECT 069H TO TRAPPER69 SAVEIN OLD69
;----------------------------------------------------------------------------
; print copyright notice
;----------------------------------------------------------------------------
MOV AH,9
MOV DX,OFFSET NOTICE
INT 021H
MOV CS:AllowPop,TRUE
;------------------------------------------------------------------------------
; calculate total program size and leave
;------------------------------------------------------------------------------
TSR_OUT: MOV AX,03100H
MOV DX,OFFSET INSTALL
SHR DX,1
SHR DX,1
SHR DX,1
SHR DX,1
INC DX
INT 021H
;------------------------------------------------------------------------------
; ERROR: program is already resident!
;------------------------------------------------------------------------------
ALREADY_THERE: MOV AH,9
MOV DX,OFFSET ALREADY
INT 021H
MOV AX,04C01H
INT 021H
ENDPROC
;==============================================================================
; DATA THAT'S ONLY USED DURING INSTALLATION
;
;
NOTICE LABEL BYTE
DB CR,LF
DB 'Example TSR v1.0 Sun 03-19-1989',CR,LF
DB 'Copyright (C) 1988 ORivation',CR,LF
DB CR,LF
DB 'Press LSHIFT-RSHIFT-T to execute',CR,LF,'$'
ALREADY LABEL BYTE
DB CR,LF,'The Example TSR is already installed!',7,CR,LF,'$'
MAIN ENDP
;
;****************************************************************************
;
CODE ENDS
END ENTRY
Comments
Post a Comment