Re: can somebody help me with the problem with tasm models
From: Beth (BethStone21_at_hotmail.NOSPICEDHAM.com)
Date: 03/09/05
- Next message: Betov: "Re: can somebody help me with the problem with tasm models"
- Previous message: Evenbit: "Re: OT: Old keyboard idea"
- In reply to: darkie: "can somebody help me with the problem with tasm models"
- Next in thread: Betov: "Re: can somebody help me with the problem with tasm models"
- Reply: Betov: "Re: can somebody help me with the problem with tasm models"
- Reply: darkie: "Re: can somebody help me with the problem with tasm models"
- Reply: NoDot: "Re: can somebody help me with the problem with tasm models"
- Reply: Frank Kotler: "Re: can somebody help me with the problem with tasm models"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 09 Mar 2005 15:37:34 GMT
darkie wrote:
> I am using tasm and using the assembler in dos ( real mode ). I am
> quite confused with the .model directive. why exactly is the .model
> tiny or large etc are used?
Long story; When Intel created the x86 originally, it had 16-bit
registers...now, when addressing memory with something like "[ bx ]", this
is using the value of BX as an _address_ to fetch values out of
memory...memory itself is "byte addressable", meaning, basically, that it's
just a long "array of bytes", each byte numbered from zero upwards
throughout memory (each byte is like a "house" on a street, each house
numbered from zero upwards until the end of the street, which is its
"address" that pop onto the envelope to get your mail delivered to the
right "house" :)...
Right, if each byte in memory is given an "address", starting from zero and
moving upwards by one for each byte, then, as the register only has
16-bits, there's only so much memory we can "address" with
16-bits...specifically, 16-bits can only cover a range of (integer) numbers
from 0 to 65535 (more generally, for n bits, the range is 0 to
(2^n)-1...the "2" because each bit is binary, of course, to the power of
how many bits and "minus one" because you're starting counting from zero,
not one...there's 2^n possible values but, because we start at zero, the
"maximum value" is one less than that at (2^n)-1 ;)...
As each address is referring to a byte, then that's 65536 bytes (65536 * 8
= 524,288 bits but, well, we never address bits directly but work with
bytes instead :)...or, dividing by 1024 (the amount of bytes in a kilobyte
:), then that's 64KB...
So, with only one 16-bit register, you can only address 64KB (with "byte
addressable" memory but, basically, that's normally almost always the case,
it's usually "taken as read" :)...well, this range was a bit
"limited"...Intel wanted the PC to be able to have more than 64KB of RAM
installed...
Therefore, Intel simply decided to use _two_ 16-bit registers for
"addressing"...more bits and you can "address" more memory...so, there
could be one 16-bit "segment" register and another 16-bit "offset"
register...and "addresses" can be composed of these two together as
"segment:offset"...
But, notoriously, Intel did something actually rather highly weird at this
point...the natural and obvious idea would be to just treat the two 16-bit
registers as if they were just the "high part" and "low part" of a 32-bit
address...you know, "as if" the 16-bit registers were "put together" to be
one longer 32-bit address...
Now, at the time, the notion of 4GB of RAM (the amount 32-bits can
"address") was a little absurd and far in the future...after all, it's
around "now" and "soon" that we're reaching the beginning of hitting that
4GB of RAM "limit"...so, Intel thought "it's a bit of a waste to have so
many extra bits and not use any of them" (not having any idea that their
x86 design would become the _main_ chip in the world and _still_ be strong
coming up to 30 years later, as that anniversary is now only a few years
away...if they'd have "guessed" that the chip design was going to be quite
so important and long-lasting, then they _might_ have decided on putting
some more effort into the initial design...and, in hindsight, they _SHOULD_
have very seriously done so ;)...
Because Intel actually created "special" register - the "segment" registers
= _SOLELY_ for the purposes of being the "higher part" of addresses, then
what they could have done was simply - as, in fact, was _already_ being
done with the "address bus" itself, which only has 20 bits hooked up on
it - say "okay, only the lower 4 bits of the segment registers are
valid...the rest aren't yet wired in and are ignored in memory addressing
and should be considered 'reserve for future expansion' that we might wire
up more of the bits to be valid as RAM sizes get bigger to justify needing
more bits"...so, 4 bits from the "segment register" and 16-bits from the
"offset" register, this would give a 20-bit address...if, in time, they
wanted more, then they could just make more bits in the "segment register"
valid...indeed, this would have allowed the 16-bit architecture to be
expanded all the way up to the 4GB "limit" (same as what we have now but
that's without yet even going 32-bit)...when the PC went 32-bits and
protected mode was added, Intel had to come up with new "addressing modes"
anyway (and did do so)...they could have had 16:16 (for compatibility) and
16:32 (or simply just 32, for an interesting one register "near" addressing
:)...the "meaning" of the segment register could have changed for protected
mode, as happened anyway...and "compatibility" between real mode and
protected mode code (which is now, in fact, totally screwed) would have
been easier that the BIOS could have extended into 32-bits relatively
easily with a "compatibility mode"...this wouldn't have _MANDATED_ the
switch to "device drivers" automatically as did actually happen...Microsoft
wouldn't have got there first and come to "monopolise" it all...with 16:32,
that's 2^48 = 256 exobytes potential addressable range (an "exobyte", if
I'm recalling the sequence properly, is 1024 "terabytes" an a "terabyte" is
a 1024 Gigabytes...so, as you can see, if they'd have taken this approach,
the "limit" on RAM would have been quite absurd to have put "reaching the
limit" way, way into the future even from now...even more absurd, again,
mind you, if they'd also "upgraded" the "segment" part to 32-bits too,
giving an even bigger 64-bit addressable range :)...
This would have been the most logical and useful way to proceed (even for
the time, sorry)...
Hence, this being Intel, that _ISN'T_ what happened...oh no, that's too
"logical" for them...that's too "sensible" for them...that actually might
have been _useful_...
No, instead, Intel invented the most horrid method of "addressing" in the
known universe, which has wrecked and haunted the PC architecture for
decades, forced the introduction of pure crap and, arguably, was the
fundamental mistake that allowed it to be possible for Microsoft to gain
the awful "monopoly" that they know have, by "stealing" the PC industry
wholesale into their _PROPRIETARY_ "device driver" format...if the
addressing mode of 16-bit and 32-bit had been made _COMPATIBLE_, this
wouldn't have rendered the BIOS completely unuseable in protected mode that
device drivers are _MANDATED_...it would have levelled out the playing
field and allowed the _hardware manufacturers_ to continue to jointly "own"
this area of the PC, rather than a lone software company...but, as noted,
this never happened...
Rather Intel thought it would be "clever" to have it that the "segment" and
"offset" register be _added together_ instead...creating a nightmare of a
system for all programming purposes...the formula being:
address = (segment * 16) + offset
...or, in other words, the "segment" is shifted over 4 bits (which is the
same as making it sixteen times larger...2^4 = 16 :) and then the "segment"
and "offset" are _added together_ for the final "linear address" in RAM,
which is sent along the actual "address bus" to access memory...
The nightmare of this "idea" is that we bizarrely have the possibility that
the _SAME ADDRESS_ can actually be referred to in a multitude of _different
ways_...
For example, 0000:7C00h is the same address as 07C0:0000h is the same
address as 0700:0C00h is the same address as 0570:2500h...and, well,
thousands of different possibilities...
Hence, given two addresses, it's not immediately obvious that they are, in
fact, the same address...and, from a programming angle, you cannot simply
do "if (address1 == address2)" calculations because they could be
completely different _values_ but, technically, they are, indeed, "equal"
from the perspective that they _DO_ address exactly the same memory...
Plus, as hinted to above, this totally screwed up everything
thereafter...this system is "hard-wired" to be 20-bits and you _CAN'T_ get
out of that...indeed, when going "protected mode", Intel simply completely
re-worked "addressing" from scratch (adopting the sort of "sensible" scheme
they should have always had from the beginning :)...this makes all real
mode code - such as BIOS routines to access the hardware - _INCOMPATIBLE_
in "protected mode" (without serious modification and "hacks" to handle the
problem)...
Basically, if you, for instance, forget to switch off the BIOS's "timer"
routine - which automatically "ticks" away in the background to update the
time - when switching from real mode to protected mode, you get greeted
with a "general protection fault", if not a full-blown "triple fault",
which is the most serious error condition possible that many CPUs are
actually hard-wired to instantly reboot when it happens (well, there _is_
nothing else sensible you can do from a "triple fault" but reboot, as it's
a completely "unrecoverable" situation)...when, inevitably, the "timer"
finally "ticks" and the CPU tries running real mode code with
_incompatible_ real mode addresses under protected mode, where it's
completely different...it ends up grabbing things from RAM all over the
place and not what was intended at all...a guaranteed crash...
Hence, generally, switch to "protected virtual address mode" (protected
mode's full name there :) and - BANG! - the BIOS becomes
useless...nevermind, eh? We should all be using the much more flexible
"device driver" model, anyway...well, yeah...but Microsoft got there first
with the _PROPRIETARY_ Windows device drivers...and, once they got
"dominance" in this area, they then automatically get "monopoly" without
even trying:
Hardware manufactures want to sell "units" of their hardware (they are
businesses, after all, not charities :)...to do so, hardware needs software
to run properly...so, the manufacturer writes the needed software to make
the hardware work...this is, these days, a case of writing "device drivers"
(though, they still do write BIOS routines for running in real mode as
well, usually, as the machine does boot up in real mode for "compatibility"
and that supports people running DOS as well :)...oh...oh dear...all the
"device driver" formats are "proprietary"...if we write a Windows driver,
then it'll only work on Windows...if we write a Linux driver, then it'll
only work on Linux...and so forth...well, we can't really afford to make
drivers for everyone...and, well, there's no really enough Linux users to
"justify" the costs of developing driver software for Linux...BUT, hmmm,
Windows is certainly worth developing driver software for...Windows is 90+%
of the market right there...if we don't support Windows, then we'll never
sell these things...okay, one Windows driver coming right up...
And - BANG! - there's Microsoft's "monopoly" for you...very little to do
with whether Windows is any good (because, truth is, it isn't...it's quite
pathetic and useless in so many ways, in fact ;)...all to do with Intel
coming up with this stupid "real mode addressing" that couldn't be expanded
without creating a brand new "addressing mode" that's completely
_incompatible_ with anything else...which, by the way, is also the large
part of the reason why the '386 introduced "32-bit protected mode" in 1986
/ 87...BUT, you'll notice that Microsoft made a big, big fuss of being
"32-bit" on all their Windows 95 adverts, nearly _10 years later_...things
were "stuck" in 16-bit for a long time because of Intel's "big mistake"...
As the quote goes: "When I see what tremendous consequences can come from
little things...I'm tempted to think that there are no little things"...
A small, seemingly innocent and harmless "design decision" that _condemned_
the PC for decades...and allowed a certain Redmond company to take over the
universe, destroying all competition and Hope for a bright future for PCs
thereafter...
So, now you know, Intel is the company to blame for the destruction of the
universe...the exact individual engineer who thought "I know! Real mode
addressing!" in Intel being the person to perform unspeakable acts of very
slow painful cruelty upon (just kidding...I don't believe in such things
under any circumstances...unlike the "coalition" troops *ahem*)...
Anyway, the ".model" crap is the legacy you have to put up with because of
the crap Intel invented...let's get back to the programming facts and
forget about the "history lesson", yes?
Addresses in "real mode" are composed of two parts...the 16-bit "segment"
and the 16-bit "offset"...the actual address that these two combine to
produce follows this formula:
address = (segment * 16) + offset
...so that 0A000:0000h (which just happens to be the start of graphics
memory there :) is really (A000h * 10h) + 0000h...or, another way to think
of it, is that, when using hex, you shift the "segment" over by one hex
digit (stick a zero after it) and then add the two parts together...for
example:
0570:2500h =
05700h
2500h +
------
7C00h
------
Which shows that this really is the same as the address 0000:7C00h, as
mentioned earlier (don't forget that, in hex, "7 + 5 = C", not "7 + 5 =
12", which is confusing it for decimal there ;)...
Okay, that's how "addresses" work in "real mode"...what does this have to
do with ".model"?
Well, the x86 CPU has more than one way to address things...that is, it has
"far" addresses which contain both "segment" and "offset" parts...but, for
speeding things up and making things smaller, there's also the possibility
to use "near" addresses that only bothers with the "offset" part (the
"segment" part being "assumed" from the values in the "segment registers"
:)...
For example, the CPU fetches instructions from CS:IP (both 16-bit
registers...CS being the "code segment" segment register and IP being the
"instruction pointer" which is a "hidden" register...you can't access it
directly but it's the address of the current instruction so you're
implicitly changing "IP" when you make "CALL" and "JMP" instructions
(indeed, another way to think of "JMP" is that it's a kind of "MOV IP,
<address>" instruction...but this depends on whether it's "near" or "far"
:)...plus, it automatically always moves forward to the next instruction as
the program is running, of course :)...
Well, when making a "call" or a "jmp" instruction, there are, in fact,
_TWO_ main forms of this instruction in the CPU's instruction set...there
is "call near" and "jmp near", as well as "call far" and "jmp far"...and
the simple difference between the two is that "near" only changes the
"offset" part (and just use the "default segment register" for the other
part...defaults are CS for IP, SS for "stack" operations (SS being "stack
segment") and DS for everything else, standing for "data segment"...there's
also "ES" which is the "extra segment" but this only gets used by the
"string" instructions and when you _explicitly_ ask for it, so it's mostly
a "user" register :)...while "far" changes both the "segment" and "offset"
at the same time...
As such, when you type in "jmp MyLabel" or "call MyProcedure", how does the
assembler know whether to assemble the "far" or the "near" version of this
instruction? Well, you can usually _explicitly_ put "jmp far MyLabel" or
"call near MyProcedure" with most assembler and state which is each and
every time...but, well, that's extra typing and a bit useless when the
entire program is "near" that it would be useful to be able to say "please
assume it's always 'near' unless otherwise told"...
And that, in a sense, is what the ".model" directive is all about...it
tells the assembler what "defaults" to assume for addressing "code" and
"data"...the various "models" that you can specify being simply an
enumeration of the various possibilities available...here's the table:
code data notes
tiny near near all in same segment (CS = DS = ES = SS)
always "near", 64KB limit
small near near code (CS) and data (DS, ES, SS) separate
always "near", 128KB limit
medium far near data (DS, ES, SS) only 64KB, code up to 1MB
"far" on code, "near" on data
compact near far code (CS) only 64KB, data up to 1MB
"near" on code, "far" on data
large far far code and data both "far"; full addressing possible
"far" on code, "far" on data
huge far far traditionally, "huge" memory model with HLLs
is like "large" but static data is permitted
to be over 64KB...in assembly language, though,
this distinction isn't needed so "huge" acts
exactly like "large" and is no different...
And then you put the ".model" directive with one of these to tell the
assembler what "defaults" to assume...so, when you have ".model tiny" then
all code and data references are "assumed" to be "near" and within the
exact same segment...with "large", the code and data is "assumed" to be
both "far"..."compact" for "near" code (64KB limit) and "far"
data..."medium" for the opposite of "far" code and "near" data (if your
program has lots of code but little data)...
Note, though, that most assemblers do still permit you to "override" if you
_explicitly_ use a keyword like "near" or "far"...that is, you can have
".model tiny" but explicitly put "call far ExternalFunction" and that
particular instruction is assembled as "far" because you specifically asked
for it...
Indeed, "memory model" is more a "convenience" in assembly language...a
"just to let the assembler know what's going on" deal...the "memory models"
with HLLs in DOS, though, take only more significance because there's
normally no way (or, at least, no "portable" way :) to specify "near" or
"far" in the HLL, so whatever "model" you pick in the HLL, you're _STUCK_
with it...so you have to make the choice more wisely with the HLLs...with
assembly, you could technically do something silly like ".model large" but
then explicitly put "near" on everything throughout the program...this
would just be "a silly waste of typing" but the assembler should assemble
what you ask for...so, it's not so "sancrosanct" a decision in assembly
language but, sure, makes sense to use the ".model" that fits your program,
then the code and data stuff should be assembled to the correct "near" or
"far" without explicitly typing that in all the time...
Note: This stuff only applies with DOS...under Windows or Linux, they use
"32-bit flat model", which simply uses "near" 32-bit addresses throughout
(you just "ignore" segment registers...note that, in "protected mode", the
"meaning" of "segment registers" is actually different, anyway...they
"index" into a "memory table", which has details about "start address",
"size", "allowed access" and such, instead because, as the name suggests,
"protected mode" adds on special "protection features" to limit access and
keep programs separate in memory so they don't "see" each other and things
like that, for "safer" programming that programs don't do what they
shouldn't...like trying to read RAM that isn't there or overwriting other
programs in RAM and "naughty" stuff like that, which shouldn't happen :)...
Note also: There is a third possibility of "short" on some instructions
like "jmp"...when you use "near", this is 16-bits in size (effects "IP",
the "offset" part)...when you use "far", this is 32-bits in size (effects
"CS:IP"...in fact, it's a literal "overwrite" that the 32-bit address
supplied _replaces_ CS:IP completely :)...the "short" option just uses 8
bits for the address and this is a _signed value_ (so -128 to +127 range :)
that's _ADDED_ to the CS:IP...it's just an "optimisation" kind of thing for
very short-range jumps (like just jumping over a short bit of code for a
simple "if" statement :)...
The choice of ".model" just instructs the assembler what kind of "defaults"
it uses...and also can play a part in its "jump optimisations" (if it's an
assembler that has such things...for example, in assembling the code, it
realises that it has to use a "jmp far" because the distance to jump is
bigger than 64KB...but, wait, you've only got "tiny" - 64KB limit - and it
throws up an "error" to tell you something strange has gone wrong
somewhere, as it's being implicitly asked to do contradictory things at the
same time ;)...
And, of course, the ".model" is used by the "simplified" directives to know
what "defaults" to use too...for example, in "tiny" model for a .COM file,
the segments are already set up properly, no need to do anything in the
"start up"...but "small" and above have DS <> CS, so it's usual (for an
".EXE" file :) that the "start up" has to have some code to set up DS
properly (CS is, by definition, set correctly or, well, the program
wouldn't be running ;)...something like: "mov ax, _data; mov ds, ax" (needs
two instructions and a temporary register as there is no direct "mov ds,
_data" instruction included on the CPU :)...
> Also if there are multiple data segments
> greater than 64 kb can i use these simplified directives or should i
> use the full segement definitions.
You can still use the simplified directives, if you want...
> Do i need to write the .model
> statement even when i am using the full segment definitions?
Not really; Or, at least, if you use full segment directives and properly
"qualify" everything (such as "MyProc proc far" or "extern SomeAPI :far"
and that kind of thing...note that with "MyProc proc far", you shouldn't
therefore need to put "call far MyProc" every time, as it can work it out
from the "definition" :), then you strictly shouldn't need any ".model"
stuff...
BUT, in practice, MASM and TASM have some rather odd "quirks" here and
there (for instance, ".386; .model tiny" has subtle differences in
behaviour to ".model tiny; .386", just because they are in a _different
order_ in the source code ;)...and don't like such things to be
missing...so, you should probably pop a ".model" directive in there, just
to stop it complaining, but then, as I say, if you use full segment
directives and properly "qualify" everything in the right places, then you
can essentially "ignore" the ".model"...just put it in there to stop MASM /
TASM "moaning and complaining" as it likes to do sometimes...it can get
"quirky" with you otherwise and do strange things...much the reason why the
NASM-ites created an assembler that simply doesn't bother with any of the
"simplified directives" and needs "explicit" everything because some of the
"quirks" MASM and TASM have are really rather weird at times (indeed, MASM
even has a ".quirks" directive to tell the assembler "please behave as
weirdly as you did in previous versions for compatibility"...hmmmm, strange
but true ;)...
Really, the ".model" and "simplified directives" really should only be
"conveniences to the programmer"...in practice, MASM and TASM often need
these things just to "shut them up" and make sure they assemble
properly...unless it's "quick and dirty" - some "code snippet" for the
newsgroup - I tend to prefer using the "full segment directives" and
"qualifying" things...it just seems simpler to me (you know, once you
_learn_ how these things work, the "simplified" _REALLY_ doesn't seem worth
the bother, as it does little more than save an insignificantly small
amount of time (a few seconds...like that really amounts to anything in the
long run) but can "rebound" in your face sometimes because of some "quirk"
or "weirdness" in the assembler...and, like anything, once you're _used to_
how these things work, it really doesn't seem "simpler", anyway...you know,
Japanese seems complicated when you don't know it or are still
learning...but once you're fluent in Japanese - or are actually a Japanese
person that this is "native language" to you - then it actually strangely
becomes _everything else_ that seems "complicated"...and, yes, even the
"simplified directives" _CAN_ sometimes seem that way...because, as the
NASM-ites like to say, there seems to be a lot of "red tape" involved that,
really, you don't need if you're just going to use "full segment
directives" and put "short", "near" and "far" explicitly, anyway :)...
As with all things, it's about "what you know", really...if we all "hex
coded" for a few weeks, then we'd probably all end up like wolfgang finding
assembly language mnemonics all a bit "limited" and "weird"...like I say,
it's like learning Japanese...BUT, once you're able to speak Japanese
fluently (which is only a case of "practice, practice, practice" whatever
we're talking about :), it suddenly becomes _everything else_ that's
"complicated" and "weird" for you...
The argument for "doing it all explicitly" is that, to manage that, you
need to _properly understand_ what's going on...so, this is why it's often
referred to as "real" assembly, with those quotation marks to point out
that it's not really any more "real" in actuality but just "seems" that way
when you're doing it...
The argument for the opposite, of course, is that "you need to properly
understand what's going on" was a "requirement" of the above...hence,
"simplified" is nice at the beginning for "learning" what's
what..."placeholders" for knowledge as yet still not acquired...
But it's all a hotly debated and debatable topic because of that "what you
know" and "personal thing" that goes with it...indeed, essentially, that
whole "RosAsm vs. HLA" thing that just won't die in this newsgroup is based
on an "extreme" version of this (you know, RosAsm adovcates extreme "real"
assembly...HLA wants to give you lots of "helpers" and "simplified
directives" to help along with learning...somewhere along the way, though,
it becomes a "religious topic" and sensible discussion - about what is
actually very much "what you know" and "personal preference" and hardly the
most pressing issue in the universe, in comparison to, ooh, "Monsanto
Nazis" or anything - evaporates into wild "fights" over the matter ;)...
> I am
> particularly concerned with the data which is more than 64kb. Please
> give me code example to explain so that it will be easier for me to
> understand.
Right, just a few things to note about assembling / linking files with >
64KB of data with TASM and TLINK...
By default, TASM spits out a "warning" about "Location Counter Overflow"
when a segment is more than 64KB...note that it still does this, even with
the "huge" memory model, because TASM really just looks at "huge" as being
synonymous with "large"...really, TASM's authors should probably have
automatically switched this warning off when "huge" and segments exceed
64KB (as that's what "huge" implies beyond what "large" specifies :) but,
well, they don't actually bother and "large" and "huge" are treated
completely identically (lazy coding?)...it'll _still_ complains about >
64KB, even when you put "huge" (despite "huge" usually implying that you're
_wanting_ > 64KB that it shouldn't really be a warning :)...
But, note, it's a _warning_, not an error...so, it still assembles properly
anyway, regardless of the "complaint" appearing...you can explicitly shut
up the warning using "nowarn lco" (and switch it back on with "warn lco",
if you only want it to "shut up" for one segment but would like the warning
turned back on for other segments...for example, shut it off for one data
segment because you _want_ that to be > 64KB but turn it back on for the
"warning" on other segments...but, most probably, you just want to shut it
off and not turn it back on...but "warn lco" can turn it back on, though,
if you really want it :) in the source code, though, if the "warning"
really does annoy you...
But the main thing is that when you do exceed 64KB, TASM actually -
interestingly - assembles "32-bit records" into the ".obj" object
file...this is, no doubt, because, in exceeding 64KB, that exceeds what
16-bits can handle so it needs a 32-bit record simply to deal with the
"more than 16-bits" size...
Anyway, despite this, the 16-bit TLINK _CAN_ still link such programs
(well, it works just fine with me, anyway ;)...but you need to include the
"/3" (enable 32-bit processing) option on the linker command
line...otherwise, it gives a "fatal: 32-bit record encountered" error,
which is not very good :(
Finally, the strange "quirk" mentioned before...you should also put any CPU
directive, like ".386", _AFTER_ the ".model" directive...because if you
place some ".386" directive before the ".model huge" then it "assumes" that
you want "use32" (32-bit default on the segments)...and that would add
silly "66" prefixes and make the code all wrong for running in DOS...yes,
it's a bit odd that the _order_ of these directives makes a difference but,
well, it does...
Very simple example code:
------------------------------------------
.model huge
.386
.stack 1000h
nowarn lco
DataOne segment word public use16 'data'
BigArrayOne db 72000 dup (00h)
DataOne ends
DataTwo segment word public use16 'data'
BigArrayTwo db 72000 dup (00h)
DataTwo ends
.data
Hello db "Hello World!$"
.code
_start: mov ax, _data
mov ds, ax
mov ah, 9
mov dx, offset Hello
int 21h
mov ah, 4Ch
int 21h
end _start
------------------------------------------
[ To be linked, remember, with the "/3" option for TLINK or linking will
fail ]
...which isn't the greatest example, true...but it shows the basic idea...
The ".model huge" specifies the "huge" memory model...note that, in
practice, TASM does not discern any difference between "large" and
"huge"...but, well, it's stricly "huge" so I'll write that in there for the
sake of human readers to understand it's supposed to be "huge"...
Note, as mentioned, that the ".386" directive - if present - needs to be
_AFTER_ the ".model" directive or it'll generate 32-bit "use32" segments
with the simplified directives...but with ".model" _then_ ".386", it'll
generate proper DOS compatible 16-bit code segments (the little "Hello,
world!" string printing there was just my way of checking that it was,
indeed, assembling the right code, as intended...just a little "test"...but
I thought I'd just leave it in there, anyway...you can use that to check
that it is assembling / linking correctly yourself, maybe :)...
The "nowarn lco" shuts up the "location counter overflow" warning, so that
the >64KB segments don't generate "complaints" when assembling...this, of
course, is not really necessary and you can leave it out and, well, just
put up with the needless "warnings" or whatever...but this is how you "shut
it up", if you want to do that :)...
I use the full segment directives to define the two "big segments"...I
suppose, technically, you could even still use "simplified directives" here
by using "fardata" and specifying different names for each one...but, well,
I just did it this way...each segment has 72000 bytes each (which exceeds
the 65536 16-bit limit :)...both uselessly empty with zeroes but then this
is an example, not a real program...
Then, you'll note, I just use the ".data" and ".code" directives as per
normal...indeed, there's nothing unusual for an ordinary DOS program from
there on...
What's important to remember, of course, is that, to access these big
arrays, you'll have to manually alter the segment registers as well as the
offset registers...the >64KB access has to be managed "manually" by your
own code...
So, for example, if you wanted to fill these "big arrays" or something like
that...then set DS to "DataOne" at first ("mov ax, DataOne; mov ds, ax"),
fill in the first 65535 bytes in the usual way...then add 65536 / 16 = 4096
(or 1000h :) onto DS to access the next 64KB and finish off the remaining
6,464 bytes...it won't work to simply "add 1" when the offset part is
0FFFFh, as it'll just "wrap-around" and not change the segment
register...you have to _manually_ do that kind of thing when you're arrays
and data gets bigger than 64KB...
Also, minor point: In the "huge" model, your code segment is now called
"<modulename>_text" rather than simply "_text" (just in case you have need
to access it directly or something)...this is simply because in "medium",
"large" and "huge" memory models, you can have _more than one_ code
segment, so it's just a "convention" that the assembler has to label the
"main" code segment (the one ".code" accesses :) with the module's
name...usually, though, you probably don't need to ever know this...but
just pointing out, "just in case"...
Basically, the main "trick" here is that you need to give TLINK the "/3"
option (and you can optionally "shut up" the >64KB warning with "nowarn
lco" too)...then just use "huge" memory model (although, "large" actually
ends up identical too...in assembly language, the difference between
"large" and "huge" is not important - it's to do with the >64 KB data but,
of course, in assembly language, this is dealt with by _manually_ changing
the segment registers in the machine instructions themselves - but, well,
"large" and "huge" exist in HLLs for DOS and TASM just uses the same names
for "compatibility" :)...also, be careful of the _order_ of ".model" and
CPU directives like ".386" because of that strange "quirky" behaviour that
it assumes different things, depending on the order of the directives...
If that's all sorted out correctly, then you should find no real problems
with using the "simplified directives"...you can, of course, also use "full
segment directives" and that kind of thing too...up to you...
Ummm, is that everything? I think that's about all there is...tell me if
I've forgotten something or whatever...
Beth :)
- Next message: Betov: "Re: can somebody help me with the problem with tasm models"
- Previous message: Evenbit: "Re: OT: Old keyboard idea"
- In reply to: darkie: "can somebody help me with the problem with tasm models"
- Next in thread: Betov: "Re: can somebody help me with the problem with tasm models"
- Reply: Betov: "Re: can somebody help me with the problem with tasm models"
- Reply: darkie: "Re: can somebody help me with the problem with tasm models"
- Reply: NoDot: "Re: can somebody help me with the problem with tasm models"
- Reply: Frank Kotler: "Re: can somebody help me with the problem with tasm models"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]