1、准备合适的段选择子
在保护模式下,段寄存器存储的是段选择子,而不是实际的段地址。要切换到实模式,需要准备一个适当的段选择子,它指向实模式下要访问的代码段。
示例程序:
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT ; 段选择子
2、保护模式到实模式的准备工作---为回到实模式的跳转指令指定正确的段地址
以下的程序是后面步骤中的跳转回实模式的指令,放在这里是为了简单说明下
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
以下是jmp 0:LABEL_REAL_ENTRY
这条指令的机器码,[LABEL_GO_BACK_TO_REAL]存储的是这条指令的首地址,而[LABEL_GO_BACK_TO_REAL+3]存储的就是段地址
示例程序
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
; 这部分代码将段寄存器 ds、es、ss 设置为代码段的段选择子,即当前执行代码的段选择子。这是为了确保在保护模式下能够正确访问数据和堆栈。
mov sp, 0100h
; sp 寄存器被设置为 0100h,这是为了为堆栈分配一些空间
mov [LABEL_GO_BACK_TO_REAL+3], ax
; 这行代码将当前代码段的段选择子存储在 [LABEL_GO_BACK_TO_REAL+3] 处。这个位置是跳转指令中存储段地址的地方
3、保护模式到实模式---加载段选择子到段寄存器
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
; 跳回实模式:
; 将段寄存器 ds、es、fs、gs、ss 设置为 SelectorNormal,这个值是在程序的开始处设置的正确的段选择子
mov ax, SelectorNormal
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; 这部分代码是关于控制寄存器 cr0 的操作,用于从保护模式切换到实模式。通过清除 cr0 的最低位,可以使处理器进入实模式
mov eax, cr0
and al, 11111110b
mov cr0, eax
; 使用 jmp 指令跳转到定义的 LABEL_REAL_ENTRY,以开始执行实模式下的代码
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值
Code16Len equ $ - LABEL_SEG_CODE16
4、回到实模式
LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
; SPValueInRealMode 是一个在实模式中保存堆栈指针的位置。这行代码将 sp 寄存器设置为这个值,以恢复实模式下的堆栈位置
mov sp, [SPValueInRealMode]
in al, 92h ; 关闭 A20 地址线
and al, 11111101b
out 92h, al
sti ; 开中断
mov ax, 4c00h ; 回到 DOS
int 21h
标签:REAL,保护模式,mov,chapter3,LABEL,模式,------,ax
From: https://www.cnblogs.com/winter-z/p/18328401