The top answer to this question on Super User gave an explanation that was satisfactory to me at first as to why the reset vector is not at address 0 (afterwards , I realized that it doesn't why the end of RAM couldn't be put at 0xFFFFFFFF and then grow downwards). But, 0xFFFFFFF0 is such a strange address. Why does x86 begin executing at 16 bytes before the top of the 32-bit address space? Are those 16 bytes used for something special?


2 Answers 2


x86 instructions often take up more than one byte, and a reasonable reset routine will almost certainly point to multiple instructions.

If the reset vector were to refer to 0xFFFF_FFFF, then only a single byte instruction would fit into that memory mapping; almost any useful reset functionality would thus require instructions that cross the 0xFFFF_FFFF/0xFFFF_0000 linear address boundary (since the code segment is set up with base 0xFFFF_0000)

By placing the reset code at this address, it's possible to fit a few instructions (including a jump) without requiring valid memory at 0xFFFF_0000.

  • 3
    \$\begingroup\$ Actually, even for a jump a minimum size would be 2 bytes, and a jump is the only useful instruction that could fit into such a small space. \$\endgroup\$
    – Ruslan
    Jul 6, 2020 at 9:47
  • \$\begingroup\$ @ChrisStratton, thanks, made a quick fix on terminology \$\endgroup\$
    – nanofarad
    Jul 6, 2020 at 13:30
  • 3
    \$\begingroup\$ Couple of possible initialization routines: cli ; jmp far .... (6 bytes); cli; cld; mov ax, 0xFFFF, mov ds, ax, jmp far [address] (12 bytes). When I disassembled a 2001 era bios it used a different variant of a jump table. \$\endgroup\$
    – Joshua
    Jul 6, 2020 at 15:59
  • \$\begingroup\$ @Joshua Intel programming manuals say that typically there is nothing more than a jump, and it's typically a 5-byte FAR jump. There should be no reason for any other kind of startup code at that point, except a jump. IBM did put a FAR jump, and since there was unused bytes, the 5-byte jump follows a BIOS date string or 6 characters and two bytes of machine ID data. Note that 2001 era BIOSes might have uncompressed themselves into RAM already, so actually, what you see after booting may not match what there physically is after a cold boot. \$\endgroup\$
    – Justme
    Jul 6, 2020 at 16:36
  • 5
    \$\begingroup\$ @Joshua CLI instruction is irrelevant. Reset button gives the CPU a hardware reset which starts executing the BIOS code from a defined state, which I assure to you has the interrupts disabled. After a hardware reset, there will also be no RAM available until the RAM controller is initialized, and after that all the vectors are set in place. No chance a virus will survive a reset button, unless it has written itself into BIOS. And that has happened. \$\endgroup\$
    – Justme
    Jul 6, 2020 at 17:09

The reason why it is 16 bytes below the top of memory comes from how the original 8086 CPU registers were loaded during reset. And even that may have something to do with compatibility for older 8085 CPU. And this compatibility has been carried over to later chips, such as 80286, 80386, etc.

Sure, they could have selected any value, but since interrupt vectors are fixed at the bottom of the memory area where it is intended to have RAM, the program ROM is intended to be put into the top of the memory area.

While any address in the top of the memory could be selected, it makes sense to be as close as possible to the last memory addresses, so that it does not dictate how large the ROM area must be, and you can use as small ROM as you can.

And it must not be too close to the last memory address either, to provide enough space for instructions to at least jump to execute code somewhere else. A short jump opcode is two bytes, near jump is three bytes, and the far jump takes 5 bytes, so it makes sense to reserve at least 5 bytes for the area.

As the 8086 has a 20-bit (or 1 megabyte) address space, and the designers chose it uses segmented memory approach where you have a 16-bit segment and 16-bit offset to point to a linear 20-bit address. It means that each segment register can select a base address for a 16-bit offset with a base address granularity of 16 bytes. Basically, the addressing of the 16-bit 8085 was just expanded with the addition of segment registers.

So during hardware reset, the program counter (IP) register is set to 0x0000, just like on a 8085 CPU. And to get to the end of memory, the code segment (CS) register is set to 0xFFFF. This makes the CPU start from 16 bytes from end of memory.

There are many valid CS:IP combinations that add up to the linear address of 0xFFFF0, but this really was most likely the simplest method, as all of the bits in the register are loaded with the same value, IP with zero bits, and CS with one bits, so loading a special combination of bits to either register was not necessary.

However, later CPUs already broke this compatibility somewhat, but in a way that it does not matter much.

For example, a 80286 loads the IP register with 0xFFF0. And since it uses 24-bit memory bus to access 16 Mbytes of memory, and the ROM must still be at the end of the 16 Mbyte addressable memory, the CS register value is reset to 0xF000, so that real mode CS:IP points to 0xFFFF0, and the CS selector base is set to base address of 0xFF0000 to set the physical memory base, so that the physical address is 0xFFFFF0. This way, if you just redesigned the old 8086 system to have a 80286, it could be made compatible to boot the original ROM.

When that got expanded to the 32-bit 80386, it also loads the 32-bit IP register with 0x0000FFF0, the CS register with 0xF000, and the CS selector base with 0xFFFF0000, so the physical address is 0xFFFFFFF0. So if a 80286 system was redesigned to take a 80386SX CPU, it could be made compatible to boot the original ROM.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.