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 ?
In real mode or V86 mode, the size of a segment can range from 1 byte up to 65,536 bytes (using 16-bit offsets).
7–4 In the real mode, a segment is limited to 64 KB due to the fact that 16 bits are used to indicate the offset value into a segment. This magic number 16 is due to the 16-bit registers used 8086 processor.
Segments in the real mode always have a length of 64K bytes. Figure 2-3 shows how the segment plus offset addressing scheme selects a memory location.
Segment register contains a selector that selects a descriptor from the descriptor table. The descriptor contains information about the segment, e.g., it's base address, length and access rights. The offset can be 32-bits.
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.
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. (Or #SS
if the segment was SS).
16-bit address-size can't exceed 64k segment limits, because addressing modes like [bx + si]
wrap at 16 bits. So only code using the 0x67
address-size prefix (added in 386) in real mode can run into segment limits. 8086 didn't have to check limits, just add Sreg << 4
to the offset from an addressing mode, making the limit implicitly 64k.
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 that osdev wiki link.
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, 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With