HALICERY

free-time coding, hardware dev, articles

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

Last modified: Mon Jun 22 15:36:51 UTC+0200 2026 © A. Tarpai


The IDT interrupt call mechanism

IDT: Interrupt Descriptor Table

Little simplified, without protection details, access right- and limit checks.

IDT system architecture (since 286)

 _______________________________________________
|  CPU                                          |         MEMORY
|                                               |
|   +--------------+                            |         (1)                 IDT
|   |    IDTR      |       --------------->---- | ---------------------->  +---------+
|   +--------------+         linear address     |                          |         |
|                          of descriptor table  |                          |         |
|   +--------------+                            | (3)   GDT/LDT            +---------+            Handler
|   |  GDTR/LDTR   |       --------------->---- | --> +---------+          | CS:IP   |    JUMP    address
|   +--------------+         linear address     |     |         |          | AR      |  --------> ...
|                          of descriptor table  |     |         |          +---------+     (5)    ...
|                                               |     +---------+          | CS:EIP  |            ...
|                                               |     |  LIMIT  |          | AR      |            ...
|                                               |    /|BASE ATTR|          +---------+            ...
|                                               |   / +---------+          |         |            IRET (6)
|                                               |  /  |         |          |         |
|                                               | /   |         |          +---------+
|                                               |/    +---------+          |   ...   |
|                                               |     |         |
|                                              /|     |         |           DESCRIPTOR
|                                          (4)/ |     +---------+            TABLE OF
|                                            /  |     |   ...   |        "INTERRUPT GATES"
|                             PE=1: FETCH   /   |
|   SEGMENT                                /    |      DESCRIPTOR
|  REGISTERS                              /     |        TABLE
|                                        /      |
| |        | - |                        /       |
| +--------+   +----------+----------+------+   |
| |   CS   | - |   BASE   |   LIMIT  | ATTR |   |
| +--------+   +----------+----------+------+   |
| |        | - |                                |
|    (2)                                        |
|_______________________________________________|

When PE=1 and interrupt vector has been received:

  1. Interrupt Handler address is fetched from a GATE DESCRIPTOR in memory. It contains a FAR address with CSIP (286 Gate) or CSEIP (386 Gate), where CS is a Selector. CPU register IDTR points to this table, vector number is the index.
    The size of gate (286-type = 16 bits; 386-type = 32 bits) determines WORD- or DWORD stack frame for push(!) – see below.
  2. CS Selector from GATE written into CS register
  3. Results in SEGMENT DESCRIPTOR fetch from GLOBAL- or LOCAL TABLE.
  4. CS DESCRIPTOR CACHE values are loaded.
  5. Execution transfer to handler code.
    The size of gate (286-type = 16 bits; 386-type = 32 bits) determines IP- (with EIP HI zero) or EIP load from GATE.
  6. IRET operand-size determines WORD- or DWORD stack pop – and should match GATE format.

16/32-variations for PE = 1 mechanism: it is completely possible to mix 16/32-bit interrupted- and handler code watching these rules.

Debugging 16/32-bit CPU interrupt stack frame PE=1

What determines the CPU interrupt stack frame format when PE=1?

This was debugged in boot block code.

Surprisingly, it is the GATE DESCRIPTOR 16/32-bit format, i.e. 286/386 format. The interrupted- or handler code D-bit has no effect.

Note. When PE = 0, all push by CPU is 16-bit and there is nothing we can do about this. See IVT Emulation.

CPU interrupt stack frame:


 PE = 1 AND 286 Gate                            PE = 1 AND 386 Gate


31                  0                          31                  0
|                   |  <-- ESP                 |                   |  <-- ESP
+---------+---------+                          +---------+---------+
|  FLAGS      CS    |                          |       EFLAGS      |
+---------+---------+                          +---------+---------+
|    IP   |         |  <-- ESP - 6             |             CS    |
+---------+---------+                          +---------+---------+
|                   |                          |        EIP        |  <-- ESP - 12
+---------+---------+                          +---------+---------+
|                   |                          |                   |

CPU pushes 3 WORDS (6 bytes)                   CPU pushes 3 DWORDS (12 bytes)
16-bit PUSH: EIP HI lost!

               |                                         |
               |                                         |
               v                                         v
+---------+---------+                          +---------+---------+
| 0 0 0 0     IP    | EIP                      |                   |  EIP
+---------+---------+                          +---------+---------+

Transfer to GATE CS:IP (<64K)                  Transfer to GATE CS:EIP

Handler should return                          Handler should return
with 16-bit IRET:                              with 32-bit IRET:
IRET when D=0 or IRETW when D=1                IRET when D=1 or IRETD when D=0

Makes a little sense: old 286 interrupt handlers are 16-bit code and return with 16-bit IRET. This made it possible to reuse 286 OS code on the 386.

Tested (GitHub): change Gate type to 0x6 from 0xE: CPU made 16-bit stack frame and handler should return with 16-bit IRET.