In the real mode, we only worked with register like AX
, BX
, CX
, and DX
and segments like CS
, DS
, ES
, and SS
, the instruction pointer IP
and the stack pointer SP
. The 80386+ processors actually brought new registers.
When we switch to 32-bit programming.
The existing register become wider. Like AX
(16 bits wide), will soon have access to EAX
(32-bits wide), as well as EBX
, ECX
and EDX
. We will have additional segment registers as well (FS
and GS
). Similarly, the instruction pointer (IP
) becomes (EIP
) (32 bits) and SP
as ESP
.
- Here,
E
prefix stands forExtended
.
However, we gain other registers as well. The Intel 80386
processor comes armed with a set of control registers, and we’ll need one of them to switch to protected mode so we might as well talk about it now. These control registers change or control the behavior of the CPU. This includes interrupt control, switching addressing mode, paging and coprocessor control. The new registers are called CR0
, CR1
, CR2
, CR3
and CR4
.
These CR*
registers control and modify the behavior of the CPU.
The first control register, CR0
, has various control flags that modify the basic operation of the processor.
Bit | Name | Full name | Description |
---|---|---|---|
31 | PG | Paging | If 1, enable paging and use the CR3 register, else disable paging |
30 | CD | Cache disable | Globally enables/disable the memory cache |
29 | NW | Not-write through | Globally enables/disable write-back caching |
18 | AM | Alignment mask | Alignment check enabled if AM set, AC flag (in EFLAGS register) set, and privilege level is 3 |
16 | WP | Write protect | Determines whether the CPU can write to pages marked read-only |
5 | NE | Numeric error | Enable internal x87 floating point error reporting when set, else enables PC style x87 error detection |
4 | ET | Extension type | On the 386, it allowed to specify whether the external math coprocessor was an 80287 or 80387 |
3 | TS | Task switched | Allows saving x87 task context upon a task switch only after x87 instruction used |
2 | EM | Emulation | If set, no x87 floating point unit present, if clear, x87 FPU present |
1 | MP | Monitor co-processor | Controls interaction of WAIT/FWAIT instructions with TS flag in CR0 |
0 | PE | Protected mode enable | If 1, system is in protected mode, else system is in real mode |
Some of these bits will become important for us later on, but for now, we are interested in the very first bit: the PE - Protection Enable
bit. It enables protected mode.
Switching to Protected Mode
In order to make the switch to protected mode, all we have to do is enable the PE
bit in the CR0
register: like so:
; Read current value of CR0 into EAX
mov eax, cr0
; Set the PE (Protection Enable) bit (bit 0) in CR0
or eax, 0x00000001
; Write the modified value back to CR0
mov cr0, eax
Setting up the 80386's registers
We’ll set all our data segments (ds
, es
, fs
and gs
) as well as the stack segment (ss) to use selector 2 from the global descriptor table, which corresponds to the data segment that we had defined in our GDT:
; Set up data segment registers
mov ax, 0x10 ; Data segment selector (GDT index 1)
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
[bits 16]
[org 0x7c00]
kernel16_start:
xor ax, ax
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov sp, 0xfff
cli
lgdt[GDT_DESCRIPTOR]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEGMENT:kernel32_start
[bits 32]
kernel32_start:
mov ax, DATA_SEGMENT
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x0ffffff
mov esp, ebp
mov esi, hello_world
call print_string
.loopk:
jmp .loopk
print_string:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [esi]
cmp al, 0
je .exit
mov ah, 0x0f
mov [edx], ax
add esi, 1
add edx, 2
jmp .loop
.exit:
popa
ret
; Global Descriptor Table definition
GDT_START:
; GDT null segment (2 dwords)
GDT_NULL:
dd 0x0
dd 0x0
; GDT Code segment (2 dwords)
GDT_CODE:
dw 0xffff ; segment limit first 0-15 bits
dw 0x0 ; base first 0-15 bits
db 0x0 ; base 16-23 bits
db 0x9a ; access byte
db 11001111b ; high 4 bits (flags) low 4 bits (limit 4 last bits)(limit is 20 bit wide)
db 0x0 ; base 24-31 bits
; GDT Data Segment (2 dwords)
GDT_DATA:
dw 0xffff ; segment limit first 0-15 bits
dw 0x0 ; base first 0-15 bits
db 0x0 ; base 16-23 bits
db 0x92 ; access byte
db 11001111b ; high 4 bits (flags) low 4 bits (limit 4 last bits)(limit is 20 bit wide)
db 0x0 ; base 24-31 bits
GDT_END:
GDT_DESCRIPTOR:
dw GDT_END - GDT_START
dd GDT_START
CODE_SEGMENT equ GDT_CODE + 4
DATA_SEGMENT equ GDT_DATA + 4
hello_world db 'HelloWorld', 0
VIDEO_MEMORY equ 0xB8000
times (510 - ($ - $$)) db 0x00
dw 0AA55h