I am given to understand that if the 80286 is run in protected mode, it is slightly slower than real mode, due to memory protection checks taking extra clock cycles.

Just how much slower is it? Either as an overall percentage on whole programs, or as the number of extra cycles taken for particular instructions?

  • 9
    There's virtually no slow down. It only effects instructions changing segment registers direct (MOV/LDS/LES/POP) or indirect (JMP/CALL/INT/RET/IRET), as in those cases a new descriptor has to be checked and loaded. While it adds a lot to each of them, the total influence is very small.
    – Raffzahn
    Commented Feb 22, 2023 at 17:40
  • 6
    @Raffzahn: Whether the influence is large or small would depend upon whether one was able to avoid having to reload segment registers within a tight loop. If one has pointers to three separately-allocated arrays, and wants to add items from the first two arrays and store them into the third, the only way to accomplish that efficiently would be to copy data from one or both arrays into the main data segment, then do the addition, and then copy the result from the main data segment to the target data segment.
    – supercat
    Commented Feb 22, 2023 at 22:38
  • 8
    @Raffzahn: In real mode, a loop like: lp: MOV DS,CX / LODSW / MOV DS,BP / ADD AX,[BX+SI] / STOSW / LOOP lp would take 2+5+2+7+8 cycles (24 cycles total). Protected mode would add an extra 30 cycles per iteration.
    – supercat
    Commented Feb 22, 2023 at 22:58
  • 7
    @Davislor: The 286 didn't have FS and GS, they came in with the 386. Commented Feb 23, 2023 at 3:44
  • 6
    @Raffzahn: If one looks at code from 16-bit compilers for memory models that support more than 64K of data, it will generally be rife with LES instructions unless code is deliberately written to minimize the use of pointers that aren't qualified "near". If the 80286 had included an FS registers, even if it could only be accessed via an "xchg es,fs" instruction that was processed internally in a manner similar to the Z80's EXX [without having to reload segment descriptors], that would have allowed most loops to operate on "far" data in place without segment reloads. As it is...
    – supercat
    Commented Feb 23, 2023 at 17:36

1 Answer 1


Basically, anything that involves changing segments is slower, sometimes significantly so; this is unsurprising since descriptors have to be checked, privilege levels potentially changed etc. Other operations are unchanged. In particular, segment limit checks on memory accesses don’t affect the clock cycle count. Programs which don’t call the operating system often, and don’t change segments much, won’t see much difference in performance; programs which do, will. In particular, programs which often change DS and ES (e.g. to access video memory using string instructions) will suffer a lot if they’re not adapted for protected mode (but such patterns don’t translate well to a protected environment anyway). Interrupts are also more expensive.

The iAPX 286 Programmer’s Reference gives clock cycle counts for each instruction, in Appendix B; cases where protected mode execution affects the clock cycle counts are marked with “pm”. For example, an inter-segment call with an immediate 4-byte address takes 13 cycles in real mode, 26 cycles in protected mode. Some instructions have variants which only apply to protected mode; these aren’t explicitly marked “pm”, because their cycle count doesn’t vary, but they typically take far longer than non-protected-mode variants. For example, calls through call gates take at least 41 cycles; calls through TSSes take at least 177 cycles; etc.

There aren’t all that many instructions identified as such:

Instruction Real mode Protected mode
CALL 7–16 7–185
INT 23 40+
IRET 17 31+
JMP 7–15 7–183
LDS/LES 7 21
MOV into a segment register 2–5 17–19
POP into a segment register 5 20
RET 11–15 11–55

To understand the impact, consider the CALL instruction in detail:

Variant Clocks
Call near, offset relative to next instruction 7
Call near, offset absolute at EA word 7 from a register, 11 from memory
Call inter-segment 13 in real mode, 26 in protected mode, +3 if indirect¹
Call gate, same privilege 41, +3 if indirect
Call gate, more privilege, no parameters 82, +1 if indirect
Call gate, more privilege, x parameters 86 + 4x, +4 if indirect
Call via Task State Segment 177, +3 if indirect
Call via task gate 182, +3 if indirect
  • Calls within the same segment don’t vary depending on the protection mode; the only variation is related to memory accesses.
  • Calls to another segment, with no possible privilege change (“Call inter-segment”), are twice as expensive in protected mode — the new segment descriptor needs to be checked against the current privilege level.
  • Calls to a call gate, which is how the 286 implements system calls, are three times more expensive if the call doesn’t result in a privilege change, and at least six times more expensive if the call does result in a privilege change. When a call gate changes to a more privileged state, a number of values are copied to a new stack; call gates can also specify that parameters should be copied too, which is why their cost varies depending on the number of parameters. (Parameters are callee-specified.)
  • Calls to a task gate, which is how the 286 implements hardware-mediated task switching, are far more expensive, because the entire task context must be stored.

The Intel manual provides detailed explanations of all the checks and operations involved in the various scenarios.

INT and JMP all have the same potential costs. IRET and RET need to undo whatever changes the corresponding INT and CALL operations made, hence their costs. LDS, LES, MOV and POP can’t change privilege levels, so the cost variation there is similar to the “basic” inter-segment call/jump variation.

The fact that segment limit checks, which apply to all memory accesses, don’t cause the cycle counts to vary can be surprising. But the checks are made all the time, whether in real mode or in protected mode; real mode is really a degenerate variant of protected mode, where segment loads aren’t checked but update the shadow segment descriptors to match the 8086’s memory model. This is what allows LOADALL to work. See also Will the real Real Mode please stand up?

¹ I suspect the Intel manual is wrong here; it says “16,mem=29” for the ed variant, but that should really be “16,pm=29”.

  • 3
    "it seems that segment limit checks on memory accesses don’t affect the clock cycle count" -- Yeah, that makes sense, since x86 Real 86 Mode is presumably implemented in every 286+ machine by initialising the shadow parts of segregs with appropriate limits and types, then running the same checks as in PM whenever a memory access occurs. (Except perhaps writing to a code segment.) This can be checked on a 386's R86M by the fact that accessing a word at offset FFFFh or any data at any higher address causes a R86M-style General Protection Fault (interrupt 0Dh) or Stack Fault (interrupt 0Ch) call.
    – ecm
    Commented Feb 22, 2023 at 17:53
  • 1
    It might be useful to note that a) additional cycles are only added when a segment chance (far call/jmp/retf/iret) is done or a call or task gate is used b) not at any location change within a segment and c) most people do not know about call and task gates, so some differentiation/background information would be helful.
    – Raffzahn
    Commented Feb 22, 2023 at 17:59
  • 3
    @Raffzahn - plus IIRC it is exactly the call and task gates that account for the really high max count of the CALL and JMP instruction - a typical far call/jump isn't really that expensive, is it? (Also the RET back in those cases ...)
    – davidbak
    Commented Feb 22, 2023 at 18:53
  • 3
    Related: <os2museum.com/wp/will-the-real-real-mode-please-stand-up>. Presumably, real mode was always implemented (mostly) as degenerate protected mode, even in the original 286 which introduced the latter. Commented Feb 22, 2023 at 19:28
  • 2
    @user3840170: On 386 and later (where changing back to real mode is possible after entering protected mode), doing so with segment limits other than 64K is called unreal mode. That makes it possible to use 32-bit flat address space in real mode if you set it up that way. (Using address-size overrides to allow 32-bit addressing modes on 386). Even though 286 doesn't allow switching back to real mode except via reset, it's certainly the obvious implementation strategy to just have real mode use the same segment limit hardware that already exists. Commented Feb 24, 2023 at 8:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .