Real Mode
From OSDev Wiki
Real Mode is the initial 16 bit operating mode of all x86 Intel processors (and clones). Its presence in modern x86 CPUs is required for backwards compatibility with pre-80386 processors and applications.
Contents |
Information
All modern operating systems (Windows, Linux, ...) run in Protected Mode, due to the many limitations and problems that Real Mode presents (see below). Older operating systems (such as DOS) and programs ran in Real Mode because it was the only mode available at the time. For information on how to switch from Real Mode to Protected Mode, see the corresponding article.
Note: There is a mode called Virtual 8086 Mode which allows operating systems running in Protected mode to emulate the Real Mode segmented model for individual applications. This can be used to allow a Protected Mode operating system to still have access to e.g. BIOS functions, whenever needed.
Below you'll find a list of cons and pros. These are mostly 'in comparison to Protected Mode'.
Cons
- Less than 1 MB of RAM is available for use.
- There is no hardware-based memory protection (GDT), nor virtual memory.
- There is no way to restrict the instructions that any user program can execute.
- The default CPU operand length is only 16 bits.
- AX, CX, and DX aren't "general purpose" registers, i.e. they cannot be used for addresses.
- The memory addressing modes are different and more complicated.
- Modern compilers aren't capable of generating code that is compatible with Real Mode.
- ... so you either have to find a very old compiler, or use Assembly.
Pros
- BIOS functions implemented in the system's BIOS work without any issues.
- The BIOS has 'drivers' to handle most of the hardware for you.
Memory Addressing
In Real Mode, there is a little over 1 MB of "addressable" memory (including the High Memory Area). See Detecting Memory (x86) and Memory Map (x86) to determine how much is actually usable. The usable amount will be much less than 1 MB. Memory access is done using Segmentation via a segment:offset system.
There are six 16-bit segment registers: CS, DS, ES, FS, GS, and SS. When using segment registers, addresses are given with the following notation (where 'Segment' is a value in a segment register and 'Offset' is a value in an address register):
12F3 : 4B27 ^ ^ Segment Offset
Segments and Offsets are related to physical addresses by the equation:
PhysicalAddress = Segment * 16 + Offset
Thus, 12F3:4B27 corresponds to the physical address 0x17A57. Any physical address can be represented in multiple ways, with different segments and offsets. For example, physical address 0x210 can be 0020:0010, 0000:0210, or 0021:0000.
The Stack
SS and SP are 16-bit segment:offset registers that specify a 20-bit physical address (described above), which is the current "top" of the stack. The stack stores 16-bit wordS, grows downwards, and must be aligned on a word (16-bit) boundary. It is used every time a program does a PUSH, POP, CALL, INT, or RET opcode and also when the BIOS handles any hardware interrupt.
High Memory Area
If you set DS (or any segment register) to a value of 0xFFFF, it points to an address that is 16 bytes below 1 MB. If you then use that segment register as a base, with an offset of 0x10 to 0xFFFF, you can access physical memory addresses from 0x100000 to 0x10FFEF. This (almost 64 kB) area above 1 MB is called the "High Memory Area" in Real Mode.
SIB Modes
In the Assembly language, SIB modes are complicated memory addressing modes, such as [ES:ECX + EAX * 8 - 0x1000000]. In Real Mode, the selection of "encodable" SIB modes is much more limited than in Protected Mode:
- [BX + val]
- [SI + val]
- [DI + val]
- [BP + val]
- [BX + SI + val]
- [BX + DI + val]
- [BP + SI + val]
- [BP + DI + val]
- [val]
Note: You can still specify any segment for each mode. -32768 <= val <= 32767
Switching from Protected Mode to Real Mode
As noted above, it is possible for a Protected mode operating system to use Virtual 8086 Mode mode to access BIOS functions. However, VM86 mode has its own complications and difficulties. Some OS designers think that it is simpler and cleaner to temporarily return to Real Mode on those occasions when it is necessary to access a BIOS function. This requires creating a special Ring 0 program, and placing it in a physical memory address that can be accessed in Real Mode.
The OS usually needs to pass an information packet about which BIOS function to execute.
The program needs to go through the following steps:
- Turn off hardware interrupts (CLI).
- Turn off paging.
- Perform a "far jump" to set CS with real-mode like values.
- Reload the other segment registers with real-mode values.
- Turn off protected mode.
- Point IDTR to the real mode IVT.
- Set up a Real Mode stack.
- Set up the registers needed for the BIOS call.
- Execute the requested BIOS function.
- Save all the "return values" from the BIOS function in memory somewhere.
- Turn Protected Mode on again.
- Do a "far jump" to set CS to a legal value.
- Possibly also do the following (depends on your kernel)
- Reload segment registers.
- Re-enable paging.
- Point IDTR back to the Protected Mode IDT and re-enable interrupts.
- Retrieve the return values.
- "Exit" (go back to the operating system).
x86 Assembly Example
[bits 16] idt_real: dw 0x3ff ; 256 entries, 4b each = 1K dd 0 ; Real Mode IVT @ 0x0000 savcr0: dd 0 ; Storage location for pmode CR0. Entry16: ; We are already in 16-bit mode here! cli ; Disable interrupts. ; Need 16-bit Protected Mode GDT entries! mov eax, DATASEL16 ; 16-bit Protected Mode data selector. mov ds, eax mov es, eax mov fs, eax mov gs, eax ; Disable paging (we need everything to be 1:1 mapped). mov eax, cr0 mov [savcr0], eax ; save pmode CR0 and eax, 0x7FFFFFFe ; Disable paging bit & enable 16-bit pmode. mov cr0, eax jump 0:GoRMode ; Perform Far jump to set CS. GoRMode: mov sp, 0x8000 ; pick a stack pointer. mov ax, 0 ; Reset segment registers to 0. mov ds, ax mov es, ax mov fs, ax mov gs, ax lidt [idt_real] sti ; Restore interrupts -- be careful, unhandled int's will kill it.
