Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Segment size in x86 real mode

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 ?

like image 763
Arka Sharma Avatar asked Jul 22 '13 11:07

Arka Sharma


People also ask

How large is a real mode memory segment?

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).

Why is segment size limited to 64 KB in the real mode?

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.

How many bytes long a real mode segment contains?

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.

What is use of segment register in real mode?

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.


2 Answers

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.

like image 100
nio Avatar answered Sep 20 '22 00:09

nio


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.

like image 27
Peter Cordes Avatar answered Sep 21 '22 00:09

Peter Cordes