Reputation: 285
I have one doubt regarding the size of segments in real mode as they can't be more than 64K but can be less than that.
My question is how these segment size and base address is initialized? Like there are GDT's and LDT's in protected mode. Real mode segments can also overlapped,disjoint or adjacent.
Like BIOS has some reserved area for specific things like boot code,video buffer etc does assembly programs need to do something like that ?
Upvotes: 8
Views: 3758
Reputation: 364160
The segment limit in real mode is 64K, even on a 386 or later CPU where you can use 32-bit address-size via prefixes. e.g. mov ax, [edx + ecx*4]
is still limited to offsets of 64 KiB in real mode.
If you exceed this limit, it raises a #GP exception on 286+. (Or #SS
if the segment was SS).
8086 didn't have #SS or #GP exceptions, it had no protection general or otherwise, just using Sreg << 4
added to the offset to form a linear address.
16-bit address-size can exceed the 64K segment limit via a word or wider access at seg:FFFF
. On 8086, the higher byte comes from seg:0000
(wrapping of the offset in the logical address before computing a new linear address for the 2nd memory bus transaction, not accessing outside the 64K linear range of the segment).
On 286 and later, #GP
or #SS
for both data and instructions in this case as well. https://www.os2museum.com/wp/does-eip-wrap-around-in-16-bit-segments/
In general, addressing modes like [bx + si + 1]
wrap at 16 bits. (And push word
with SP=0 wraps to SP=FFFEh, no problem as long as the stack is aligned). So only code using the 0x67
address-size prefix (added in 386) for addressing modes like [eax]
can exceed segment limits in real mode, except for word or wider accesses at the very end of a segment.
Segments that start within 64K of the highest possible address wrap around at 1MiB on 8086, and on later CPUs if A20 is disabled. Otherwise they extend past 1MiB for an address like FFFF:FFFF
seg:off = 0x10ffef
linear. See What are Segments and how can they be addressed in 8086 mode?
If you switch to protected mode and set a segment register, the CPU keeps the segment description (base + limit) cached internally, even across switching back to 16-bit real mode. This situation is called unreal mode.
Writing to a segment register in 16-bit mode only sets the segment base to value << 4
without changing the limit, so unreal mode is somewhat durable for segments other than CS. CS:EIP is special, especially if you need to avoid truncating EIP to 16 bits when returning from interrupts or whatever. See the osdev wiki linked earlier.
push
/pop
/call
/ret
use SS:ESP
or SS:SP
according to the B
flag in the current stack-segment descriptor; the address-size prefix only affects stuff like push word [eax]
vs. push word [si]
.
The GDT / LDT are ignored when you write a value to a segment register in real mode. The value is used directly to set the cached segment base, not as a selector at all.
(Each segment is separate; unreal mode isn't an actual mode like protected vs. real; the CPU is in real mode. Writing the FS register, for example, puts that segment back into normal real-mode behaviour except for its limit, but doesn't change the others. It's just a name for being in real mode with cached segment descriptors with larger limits, so you can use 32-bit address-size for a larger flat address space. Often with base=0 and limit=4G)
AFAIK, there's no way to query the internal limit value of a segment in real mode. lsl
loads the segment-limit value directly from a descriptor in the GDT / LDT in memory, not from the internal value (so it's not what you want), and it's not available in real mode anyway.
See comments on this answer for more details about taking segments out of unreal mode intentionally or unintentionally.
286 and 386 CPUs supported a LOADALL
instruction which could set segment limits from real mode, but later CPUs don't have it. Commenters say that SMM (system management mode) may be able to do something similar on modern x86.
Upvotes: 7
Reputation: 5289
In real mode segmented addresses are hardwired into memory. To get a physical address you can use this equation:
physical address = segment * 16 + offset
Both segment and offset addreses are 16 bit. By using this equation you can make one 20 bit address and access low 640kB of RAM with no problem.
There is no table that holds where some segment is located. The problem is that you have to set both segment and offset registers to be able to access any address. So you could access maximum of 64k of RAM bytes with a simple loop that just increments the offset register, which makes memory access to larger buffers less comfortable than in flat model.
Upvotes: 3