Hello Again Professor Hyde,We are currently working on ways to publish this text in a form other than HTML (e.g., Postscript, PDF, Frameviewer, hard copy, etc.). This, however, is a low-priority project. Please do not contact Randall Hyde concerning this effort. When something happens, an announcement will appear on "Randall Hyde's Assembly Language Page." Please visit this WEB site at http://webster.ucr.edu for the latest scoop.
Dallas gave me permission to take orders for the Computer Science 13 Manuals. We would need to take charge card orders. The only cards we take are: Master Card, Visa, and Discover. They would need to send the name, numbers, expiration date, type of card, and authorization to charge $95.00 for the manual and shipping, also we should have their phone number in case the company has any trouble delivery. They can use my e-mail address for the orders and I will process them as soon as possible. I would assume that two weeks would be sufficient for printing, packages and delivery time.
I am open to suggestions if you can think of any to make this as easy as possible.
Thank You for your business,
Kathy Chapman, Assistant
Printing and Reprographics
University of California
Riverside
(909) 787-4443/4444
DOS maintains a free memory pointer that points the the beginning of
the block of free memory. When the user runs an application program, DOS
loads this application starting at the address the free memory pointer contains.
Since DOS generally runs only a single application at a time, all the memory
from the free memory pointer to the end of RAM (0BFFFFh) is available for
the application's use:
When the program terminates normally via DOS function 4Ch (the Standard
Library exitpgm
macro), MS-DOS reclaims the memory in use by
the application and resets the free memory pointer to just above DOS in
low memory.
MS-DOS provides a second termination call which is identical to the terminate
call with one exception, it does not reset the free memory pointer to reclaim
all the memory in use by the application. Instead, this terminate and stay
resident call frees all but a specified block of memory. The TSR call (ah
=31h)
requires two parameters, a process termination code in the al
register (usually zero) and dx
must contain the size of the
memory block to protect, in paragraphs. When DOS executes this code, it
adjusts the free memory pointer so that it points at a location dx*16 bytes
above the program's PSP. This leaves memory looking like this:
When the user executes a new application, DOS loads it into memory at
the new free memory pointer address, protecting the resident program in
memory:
When this new application terminates, DOS reclaims its memory and readjusts
the free memory pointer to its location before running the application -
just above the resident program. By using this free memory pointer scheme,
DOS can protect the memory in use by the resident program.
The trick to using the terminate and stay resident call is to figure out
how many paragraphs should remain resident. Most TSRs contain two sections
of code: a resident portion and a transient portion. The transient portion
is the data, main program, and support routines that execute when you run
the program from the command line. This code will probably never execute
again. Therefore, you should not leave it in memory when your program terminates.
After all, every byte consumed by the TSR program is one less byte available
to other application programs.
The resident portion of the program is the code that remains in memory and
provides whatever functions are necessary of the TSR. Since the PSP is usually
right before the first byte of program code, to effectively use the DOS
TSR call, your program must be organized as follows:
To use TSRs effectively, you need to organize your code and data so that
the resident portions of your program loads into lower memory addresses
and the transient portions load into the higher memory addresses. MASM and
the Microsoft Linker both provide facilities that let you control the loading
order of segments within your code. The simple solution, however, is to
put all your resident code and data in a single segment and make sure that
this segment appears first in every source module of your program. In particular,
if you are using the UCR Standard Library SHELL.ASM file, you must make
sure that you define your resident segments before the include directives
for the standard library files. Otherwise MS-DOS will load all the standard
library routines before your resident segment and that would waste considerable
memory. Note that you only need to define your resident segment first, you
do not have to place all the resident code and data before the includes.
The following will work just fine:
ResidentSeg segment para public 'resident' ResidentSeg ends EndResident segment para public 'EndRes' EndResident ends .xlist include stdlib.a includelib stdlib.lib .list ResidentSeg segment para public 'resident' assume cs:ResidentSeg, ds:ResidentSeg PSP word ? ;This var must be here! ; Put resident code and data here ResidentSeg ends dseg segment para public 'data' ; Put transient data here dseg ends cseg segment para public 'code' assume cs:cseg, ds:dseg ; Put Transient code here. cseg ends etc.The purpose of the
EndResident
segment will become clear in
a moment. For more information on DOS memory ordering, see Chapter Six.mov ax, ResidentSeg ;Need access to ResidentSeg mov es, ax mov ah, 62h ;DOS Get PSP call. int 21h mov es:PSP, bx ;Save PSP value in PSP variable. ; The following code computes the sixe of the resident portion of the code. ; The EndResident segment is the first segment in memory after resident code. ; The program's PSP value is the segment address of the start of the resident ; block. By computing EndResident-PSP we compute the size of the resident ; portion in paragraphs. mov dx, EndResident ;Get EndResident segment address. sub dx, bx ;Subtract PSP. ; Okay, execute the TSR call, preserving only the resident code. mov ax, 3100h ;AH=31h (TSR), AL=0 (return code). int 21hExecuting the code above returns control to MS-DOS, preserving your resident code in memory.
ds
or some other segment register(s) upon initial
entry. For example, suppose you have a function, count, that simply counts
the number of times some other code calls it once it has gone resident.
One would thing that the body of this function would contain a single instruction:
inc counter
. Unfortunately, such an instruction would increment
the variable at counter
's offset in the current data segment
(that is, the segment pointed at by the ds
register). It is
unlikely that ds
would be pointing at the data segment associated
with the count procedure. Therefore, you would be incrementing some word
in a different segment (probably the caller's data segment). This would
produce disastrous results. cs:
segment override prefix on all your variables.
For example, to increment the counter
variable you could use
the instruction inc cs:counter
. This technique works fine if
there are only a few variable references in your procedures. However, it
suffers from a few serious drawbacks. First, the segment override prefix
makes your instructions larger and slower; this is a serious problem if
you access many different variables throughout your resident code. Second,
it is easy to forget to place the segment override prefix on a variable,
thereby causing the TSR function to wipe out memory in the caller's data
segment. Another solution to the segment problem is to change the value
in the ds
register upon entry to a resident procedure and restore
it upon exit. The following code demonstrates how to do this:push ds ;Preserve original DS value. push cs ;Copy CS's value to DS. pop ds inc Counter ;Bump the variable's value. pop ds ;Restore original DS value.Of course, using the cs: segment override prefix is a much more reasonable solution here. However, had the code been extensive and had accessed many local variables, loading ds with cs (assuming you put your variables in the resident segment) would be more efficient.