The unreal mode exploits two facts on how both Intel and AMD processors load and save the information to describe a segment.
The processor caches the descriptor information fetched during a move in a selector register in protected mode.
These information are stored in an architectural invisible part of the selector register themselves.
In real mode the selector registers are called segment registers but, other than that, they designate the same set of registers and as such they also have an invisible part. These parts are filled with fixed values, but for the base which is derived from the value just loaded.
In such view, real mode is just a special case of protected mode: where the information of a segment, suchlike the base and limit, are fetched without a GDT/LDT but still read from the segment register hidden part.
By switching in protected mode and crafting a GDT is possible to create a segment with the desired attributes, for example a base of 0 and a limit of 4GiB.
Through a successive loading of a selector register such attributes are cached, it is then possible to switch back in real mode and have a segment register through which access the whole 32 bit address space.
BITS 16 jmp 7c0h:__START__ __START__: push cs pop ds push ds pop ss xor sp, sp lgdt [GDT] ;Set the GDTR register cli ;We don't have an IDT set, we can't handle interrupts ;Entering protected mode mov eax, cr0 or ax, 01h ;Set bit PE (bit 0) of CR0 mov cr0, eax ;Apply ;We are now in Protected mode mov bx, 08h ;Selector to use, RPL = 0, Table = 0 (GDT), Index = 1 mov fs, bx ;Load FS with descriptor 1 info mov gs, bx ;Load GS with descriptor 1 info ;Exit protected mode and ax, 0fffeh ;Clear bit PE (bit0) of CR0 mov cr0, eax ;Apply sti ;Back to real mode ;Do nothing cli hlt GDT: ;First entry, number 0 ;Null descriptor ;Used to store a m16&32 object that tells the GDT start and size dw 0fh ;Size in byte -1 of the GDT (2 descriptors = 16 bytes) dd GDT + 7c00h ;Linear address of GDT start (24 bits) dw 00h ;Pad dd 0000ffffh ;Base[15:00] = 0, Limit[15:00] = 0ffffh dd 00cf9200h ;Base[31:24] = 0, G = 1, B = 1, Limit[19:16] = 0fh, ;P = 1, DPL = 0, E = 0, W = 1, A = 0, Base[23:16] = 00h TIMES 510-($-$$) db 00h dw 0aa55h
gsto hold the "extended" segments: such registers are less likely to be used/saved/restored by the various 16 bit services.
lgdtinstruction doesn't load a far pointer to the GDT, instead it loads a 24 bit (can be overridden to 32 bit) linear address. This is not a near address, it is the physical address (since paging must be disabled). That's the reason of
sstp 7c00h and start the location counter from 0. So a byte at offset X in the file is at offset X in the segment 7c00h and at the linear address 7c00h + X.
lgdtis saved in the... GDT itself, in the null descriptor (the first descriptor).
For a description of the GDT descriptors see Chapter 3.4.3 of Intel Manual Volume 3A.