(A brief and simplified explanation of memory models.)
(This article is intended for "REAL" DOS programmers:
those who don't eat quiche or drive little plastic cars.)
by: Victor E. Cummings
Trademarks contained herein are attributed to their respective owners.
This is a frequently asked question: what model to I select when
compiling my programs? The first versions of C compilers for the
personal computer and CPM (Control Program Microprocessor) did not
have a model. C was C and memory was 64 kilo bytes. You had to
be really conscious of variable size and array sizes and all code
was packed into the same and single segment that the 8080
microprocessor addressed. That is now called the tiny model. Now
adays, you have one mega byte addressable, and an almost unlimited
amount of dynamically allocated array space for the 80386 and 80486
microprocessors. You may store up to 4 billion bytes of
information and code. This roughly equals the amount of
information that can be contained in 10,000 400 page books. The
amount of information that you can address is roughly proportional
to the thickness of your wallet, while the amount of power that you
can unleash from that toy sized power horse on your desk is
directly proportional to how much you can understand about it.
Unleashing the power of the C or C++ languages comes from
understanding and implementing the various syntax shortcuts that
the language provides as well as the numerous types and structures
that the language provides. If you are a C programmer with only
a rough understanding of the C language who received the C++
compiler in the mail, you probably were wondering which book to
pick up first. The answer: the hardest one to read - the
programmer's reference. The model is the least of your worries,
because these models and your immediate attention should be on the
language itself and what you can do with it - not what it can do
Have you heard the skit by Steve Martin where the police officer
stops him for being really really small? Well lets get really
What model do I select when I am starting to develop my code?
This question should only be difficult for the beginner, but here
is the answer simplified: if you do not have extensive experience
in C or assembler use the largest model that is allowable for the
type of program you are writing.
What model do I select for my completed program?
The answer is: use the smallest allowable model for the type of
program that you are writing.
The style of this article is intended to add humor to a dry topic.
The Whys and Wherefores:
--- More --- --- More --- --- More ---
Isn't this what your users are always asking for? How often as a
programmer have you sat down to demo a program that you sweated and
toiled over for weeks only to have the users say: but it
--- But it doesn't... --- --- But it doesn't... --- --- But it doesn't... ---
User: A person who controls and totally dominates every waking
second of a computer professional's life, because of their
incessant demands for larger and faster programs (without bugs)
which will improve the quality of their lives, working conditions
and free time, cost them absolutely nothing and take up only five
bytes on their hard disk.
Simply stated, you must take the above comical definition seriously
to an extent.
Using the tiny memory model.
If you are not writing a small utility program, device driver(one
that is loaded by DOS in CONFIG.SYS to control an external device),
or TSR (terminate stay resident) program which has all of its code
and data in the same segment then you can place this model in its
final resting place. (RIP).
Suggestion on writing small utility programs: DON'T
If you are writing a device driver which must by definition be a
tiny model .COM file and originate at ORG 0 then this is the
largest and only model that you should use. This same advice
applies to EPROM programs.
Using the small memory model.
If you are writing a TSR program you should start in small model,
and convert to tiny model if possible after completing your
program. If your program exceeds the small model, you should
consider not making it TSR. In this day and age where base memory
is at a premium, if you can't write a TSR in small model then you
should probably not. Also if you do not know how to make a TSR
release itself or detect its own presence then don't write it TSR,
or you will probably have few users of your program.
Its hard now adays to find a program to write which does not
address more than 64 kilo bytes of static data. So the only model
that we are really left with is HUGE. Your program may run slower,
but you will reduce your compiler errors along the way and speed
your development in the long run if you just start out right away
in the huge model. If you find when your code is finished that you
are not addressing more than 64 kilo bytes of static data and are
not having to dynamically address array structures larger than 64
kilo bytes, then you can switch to a smaller model to increase
speed and optimize at the final compile. Note that it is possible
to address arrays larger than 64 kilo bytes without using the HUGE
memory models when using LIM EMS memory or XMS extended memory, so
this rule does not apply if you are using one of these and not
addressing more than 64K of static data. See the article contained
in EMMXMS.ZIP on extended and expanded memory arrays. Static data
is data storage assigned to public variables from all the combined
modules in your program. You can get a handle on the static data
size by looking at the BSS segment names in the map file for your
program produced by the compiler. Please note that this is not a
suggestion for you to code in a sloppy manner. It is just a
suggestion which will reduce the compiler errors that you will have
to pass before program completion.
Assembler Code Interface Modules:
This section is intended for those who intend to do assembly level
code calls from C or C++. Though the compiler's scope contains six
models, your assembler code interfaces really only need to contain
far calls. Far calls are allowable from all models. The far
refers only to the method of the call.
Suggestion: In your assembler interface routines default to all
far calls and optimize later. This will save you some development
time. The overhead for far calls is only two bytes and a little
time. This practice will save you some debugging time also, for
having to track a bug caused by near call in an assembler routine
in a completed program can take quite some time. If you replace
a far call from an assembler interface before the final compile and
have unexpected results, you can simply replace the far call
without worrying about the whys and wherefores or starting up your
debugger unless you are interested in the underlying cause.
In my next article I will discuss assembler interfacing to Turbo
C and Turbo C++ and give some examples. It is hoped that this
article will help you to get really really enormous.