Unfortunately, this isn't right. It's too bad, too, as this is the way most people
expect it to work, and is a lot easier to understand than the actual mechanism that is used.
I will do my best to explain this. If you can, try to pick up a copy of
Assembly Language Step by Step (see the book thread for details), as Duntemann explains it better than anyone I know of. You may want to look at a more detailed explanation which I gave in an
earlier thread (reply #23).
Real mode segment base:offset addressing is designed as a way to represent a 20-bit absolute address as two 16-bit addresses. The idea was that, for most programs, the segment base values would be held in the appropriate segment registers (CS for code segments, DS for data segments, SS for stack segments, and ES for extra data segments), and the local addresses used as an offset from the segment base.
The clever (and annoying) part is in how the addresses are combined. The Intel designers knew that the 8086 would only have 20 address lines, and so wanted to have as little excess addressing capacity as possible. Also, they wanted to make sure that there were no large gaps between the beginnings of one segment boundary and another. So they devised a system of segment arithmetic in which the segment base is shifted 4 bits to the right (or multiplied by 16, depending on how you look at it), and the offset then added to that value:
9ED0:8109 becomes
[tt]
9ED00 segment base * 0x0010
+8109 segment offset
------
A6E09 absolute address
[/tt]
A few things should be noted about this system. First, you need to realize that segments are not fixed areas of memory; there is no 'segment 0', segment 1', 'segment 2', etc. Segments are defined as needed by a value in a segment register (or the base of a FAR address), and any base value can be used. A segment base may begin at any address that is an even multiple of 16, but when there is no base pointing to a given area of memory, there is no segment there.
Second, as a corollary of this, real mode segment sizes are defined implicitly by the actual usage of them; a real mode segment may be any size between 1 and 65536 bytes. (This changes in protected mode, of course). To put it another way, every segment base has a corresponding set of potential offsets, starting with 0x0000 and going up to 0xFFFF, but a segment is not automatically 64K in size.
The third important factor is that segments can overlap with each other, either in whole or in part. This means that you can have Code, Data and Stack memory all within what is effectively a single segment, by setting CS == DS == SS. It also measn that, except for the very first 16 bytes of the real address space (and the very last, though on the 8086 they are not accessible), every absolute address can be represented with more than one base:offset pair. To take the earlier example,
[tt]A6E09 == 9ED0:8109 == A6E0:0009 == 97C8:F189[/tt]
and so forth. I recommend trying to add different base:offset pairs together by hand to see for yourself how this works.
I hope that I have clarified more than I have confused with this. If you don't get it immediately, don't worry; this is one of the most confusing aspects of the x86 family, and it usually takes a while for it to sink in. Feel free to ask for any further clarification. Again, I recommend Duntemann's book for it's excellent explanations on this point.