Last modified: Mon Jun 22 14:32:34 UTC+0200 2026 © A. Tarpai
SR write when PE=1
When PE=1, whenever a Segment Register (SR) is written, the CPU fetches an 8-byte segment descriptor from main memory and loads the hidden part of the SR with base, limit and other attributes (after access-right check).
For the Global- and Local Descriptor Table, this linear memory address is stored on-chip in the GDT Register, GDTR:
_______________________________________________
| CPU |
| | MEMORY
| +--------------+ | (2)
| | GDTR/LDTR | -------------------->--|---> +-----------+
| +--------------+ linear address | | | 0
| of descriptor table | | |
| | +-----------+
| | | | 8
| | | |
| | +-----------+
| | | LIMIT | 16
| | /| BASE ATTR |
| | / +-----------+
| | / | | 24 byte offset
| | / | |
| |/ +-----------+
| / | ... |
| /|
| SEGMENT / | DESCRIPTOR
| REGISTERS PE=1: FETCH../ | TABLE
| (3) / |
| | | - | / |
| +--------+ +----------+----------+------+ |
| | 16 | - | BASE | LIMIT | ATTR | |
| +--------+ +----------+----------+------+ |
| | | - | |
| |
|_______________________________________________|
^
| (1)
|
MOV ES, 16
Descriptor Cache values can be changed only when PE=1 and a SR is written.
The value written acts as SELECTOR.
GDTR/LDTR also stores a 16-bit TABLE LIMIT value for not to overindex the table (max descriptor table size in memory is therefore 64K).
Instructions that change SR:
- For Data/Stack Segment Registers direct load instructions: MOV, POP, LDS, LSS, LGS, LFS. These instructions explicitly reference the segment register.
- For the code segment register, CS – see below
But 386+ can clear the PE-bit anytime (286 cannot). This allows some tweaking possibilities for the Real Mode, eg. extended 4GB limit, D = 1: 32-bit default mode, 32-bit ESP big-stack for implicite stack references etc.
The selector value is also retained in the SR after clearing the PE bit. Going back to RM mode should initialize SR-BASE in the 8086-way again.
CS write and the PE-bit
The code segment register cannot be written explicitly, "MOV CS" or "POP CS" are non-existing instruction. So how to write then the CS Descriptor Cache base/limit-values and attribute bits?
The instructions changing CS are:
- FAR JMP and FAR CALL (both absolute immediate and memory indirect), FAR RET
- the interrupt mechanism and IRET
implicitly reference the CS register and the CPU loads a new CS value.
These instructions will transfer execution to another memory location as well.
How 386 FAR JMP might work (as p-mode jump)
EIP - the 386 CPU Instruction Pointer register is 32 bit.
- the CPU always uses the full EIP register content for code fetch
- the CPU always uses the 32-bit BASE stored in CS hidden, on-chip descriptor for the addition
[BITS 16]
[EA] [IP] [IP] [CS] [CS] 16-bit FAR JMP INSTR BYTES (eg. JMP SELECTOR:offset)
[EA] [IP] [IP] [IP] [IP] [CS] [CS] 32-bit FAR JMP INSTR BYTES (eg. JMP SELECTOR:dword offset)
| |
| v
| ________ PE=0 PE=1
| |___CS___| -----> [ BASE LIMIT ATTR ] <-------
v x16 | FETCH
_______________ |
|___0_______IP__| |
_______|_______ |
|______EIP______| |
| |
| |
LIMIT/ACCESS |
CHECK |
| ___ |
+-------------/ + \---------------+
\___/
|
|
32-BIT PHYSICAL
ADDRESS
- CPU fetches and interpretes code bytes of JMP based on the current operand-size
- First replace CS: if PE=1 then fetch and load a new CS descriptor - otherwise BASE = CS x 16
- Replace EIP: if operand-size attribute is 16, upper two bytes of the EIP register are cleared
- Now perform EIP limit- and access check
- If passed, perform addition of BASE + EIP
- Drive address and fetch next code bytes
By executing the JMP after setting PE=1, at this point whatever value has been loaded into CS DESCRIPTOR D-bit will be in effect.