The x86 architecture was created in the late 70th years of XX century. The technology available at that time didn't allow for implementing advanced integrated circuits containing millions of transistors in one silicon die. The 8086, the first processor in the whole family of x86, required additional supporting elements to operate. This led to the necessity of making some decisions about the compromise between efficiency, computational possibilities and the size of the silicon and cost. This is the reason why Intel invented some specific elements of the architecture. One of them is the segmentation mechanism to extend the addressing space from 64 kB typically available to 16-bit processors to 1 MB. We present in this chapter some specific features of the x86 and x64 architectures.
The 8086 can address the memory in so-called real mode only. In this mode, the address is calculated with two 16-bit elements: segment and offset. The 8086 implements four special registers to store the segment part of the address: CS, DS, ES, and SS. During program execution, all addresses are calculated relative to one of these registers. The program is divided into three segments containing the main elements. The code segment contains processor instructions and their immediate operands. The instructions address are related to the CS register. The data segment is related to the DS register. It contains data allocated by the program. The stack segment contains the program stack and is related to the SS register. If needed, it is possible to use an extra segment related to the ES register. It is by default used by string instructions.
Although the 8086 processor has only four segment registers, there can be many segments defined in the program. The limitation is that the processor can access only four of them at the same time, as presented in Fig 1. To access other segments, it must change the content of the segment register.
The address, which consists of two elements, the segment and the offset, is named a logical address. Both numbers which form a logical address are 16-bit numbers. So, how to calculate a 20-bit address with two 16-bit values? It is done in the following way. The segment part, taken always from the chosen segment register, is shifted four bit positions left. Four bits at the right side are filled with zeros, forming a 20-bit value. The offset value is added to the result of the shift. The result of the calculations is named the linear address. It is presented the Fig 2. In the 8086 processor, the linear address equals the physical address, which is provided via the address bus to the memory of the computer.
The segment in the memory is called a physical segment. The maximum size of a single physical segment can't exceed 64 kB (65536 B), and it can start at an address evenly divisible by 16 only. In the program, we define logical segments which can be smaller than 64 kB, can overlap, or even start at the same address.
With the introduction of processors capable of addressing larger memory spaces, the real addressing mode was replaced with addressing with the use of descriptors. We will briefly describe this mechanism, taking 80386 as an example. The 80386 processor is a 32-bit machine built according to IA-32 architecture. Using 32 bits, it is possible to address 4 GB of linear address space. Segmentation in 32-bit processors can be used to implement the protection mechanisms. They prevent access to segments which are created by other processes to ensure that processes can operate simultaneously without interfering. Every segment is described by its descriptor. The descriptor holds important information about the segment, including the starting address, limit (size of the segment), and attributes. As it is possible to define many segments at the same time, descriptors are stored in the memory in descriptor tables. IA-32 processors contain two additional segment registers, FS and GS, which can be used to access two extra data segments (Fig 3), but all segment registers are still 16-bit in size.
The segment register in protected mode holds the segment selector, which is an index in the table of descriptors. The table is stored in memory, created and managed by the operating system. Each segment register has an additional part that is hidden from direct access. To speed up the operation, the descriptor is downloaded from the table into the hidden part automatically, each time the segment register is modified. It is schematically presented in Fig 4.
Although segmentation allows for advanced memory management and the implementation of memory protection, none of the popular operating systems, including Windows, Linux or MacOS, ever used it. In Windows, all segment registers, via descriptors, point to the zero base address and the maximal limit, resulting in the flat memory model. In this approach, the segmentation de facto does not function. Memory protection is implemented at the paging level. The flat memory model is shown in the Fig 5.
Because the segmentation wasn't used by operating systems software producers, AMD and Intel decided to abandon segmentation in 64-bit processors. For backwards compatibility, modern processors can run 32-bit mode with segmentation, but the newest versions of operating systems use 64-bit addressing, named long mode and referred to as x64. The only possible addressing is a flat memory model with segment registers and descriptors set to zero address as the beginning of the linear memory space.