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

 posted on 2024-07-28 16:57  Dylaris  阅读(52)  评论(0)    收藏  举报