HALICERY

free-time coding, hardware dev, articles

Top
Home 8042 Blogs About
Home IntelEssential The 386: IA-32 Protected Mode SegReg write

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:

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:

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.

                                      [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
  1. CPU fetches and interpretes code bytes of JMP based on the current operand-size
  2. First replace CS: if PE=1 then fetch and load a new CS descriptor - otherwise BASE = CS x 16
  3. Replace EIP: if operand-size attribute is 16, upper two bytes of the EIP register are cleared
  4. Now perform EIP limit- and access check
  5. If passed, perform addition of BASE + EIP
  6. 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.