The Assembly Language Magazine 2
VOL 1 NUMBER 2
March 1989
Written by and for assembly language programmers.
Contents
- Policy
- Editorial
- Beginners Corner
- What is SDN?
- FASTPRINT by Dennis Yelle
- The EXEC Function by Patrick O'Riva
- TSR's by David O'Riva
- Book Reviews
- Program Spotlight
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
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.
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.
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.
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
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.
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.
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.
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.
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 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.
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.
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."
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.
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.
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
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.
;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
;---------------------------------------------------------------