从实模式跳转到保护模式再跳回实模式的程序

 

这是一段用nasm编写的程序,有2个文件组成,第一个头文件为pm.inc,第2个文件为pmtest.asm,编译成.com文件在DOS下运行。
程序在保护模式打印一串字符串,然后返回实模式退回dos 

文件pm.inc:


文件pmtest.asm:


进入保护模式的主要步骤:

 1、准备GDT

 2、用lgdt加载gdtr
 3、打开A20
 4、置cr0的PE位

 5、跳转,进入保护模式

 

返回实模式的要点:
首先我们不能从32位代码段返回实模式,只能从16位代码段返回
其次,需要在返回时让寄存器符合实模式的要求。所以上面代码中有一个Normal的描述符,在返回实模式前把对应选择子SelectNormal加载回寄存器,就是以下代码实现的:
   mov    ax, SelectorNormal       
  mov    ds,ax      

mov    es,ax      

  mov    fs,ax        

mov    gs,ax      

mov    ss,ax  
然后清除cr0的PE位

 

接下来有段代码:  jmp 0:LABEL_REAL_ENTRY,段地址是0,非常诡异。

其实在之前的这段代码:

43.mov [LABEL_GO_BACK_TO_REAL+3], ax ,把这条指令的段地址已经设置好了。因为实模式跳转指令的机器码

 

OEAH(1个字节)  offset(2个字节)   segment(2个字节) 第3个字节开始正好是段地址,实际的代码是:

jmp cs_real_mode:LABEL_REAL_ENTRY

 

最后程序在dos模拟器的运行结果如下图:
 

<think>好的,我现在需要帮助用户找到一个关于如何编写从实模式切换到保护模式再进入长模式的x86汇编程序示例。首先,我得回忆一下x86架构的启动过程和相关模式切换的基本知识。 用户的问题涉及到实模式保护模式和长模式(即IA-32e模式,通常指64位长模式)的切换流程。这需要逐步引导处理器从16位实模式进入32位保护模式,然后再切换到64位长模式。这个过程通常发生在系统启动时,由引导加载程序(bootloader)完成。 首先,实模式是x86处理器启动时的默认模式,使用16位寄存器和20位地址总线,最大寻址1MB内存。切换到保护模式需要设置全局描述符表(GDT),启用A20地址线,并设置控制寄存器CR0的PE位。进入保护模式后,处理器可以使用32位寄存器和更复杂的内存管理功能。 接下来,进入长模式需要先检查CPU是否支持长模式,然后设置页表结构,启用PAE(物理地址扩展),设置EFER寄存器的LME位,最后启用分页并设置CR0的PG位。这会切换到64位模式,允许使用64位寄存器和更大的地址空间。 用户需要示例代码,因此我需要提供一个基本的汇编程序结构,包括必要的步骤和注释。需要注意,这种代码通常运行在裸机环境下,可能需要使用汇编器如NASM进行编译,并生成引导扇区。 在编写示例代码时,需要确保每一步的正确性,比如正确设置GDT和页表,以及正确切换处理器模式。同时,代码需要处理必要的设置,如关闭中断,设置段寄存器,以及跳转到适当的代码段。 可能遇到的问题包括:如何正确设置GDT条目,确保A20线已启用,页表结构是否符合长模式的要求,以及如何处理模式切换后的跳转。需要确保每个步骤的顺序正确,避免遗漏关键操作,如启用PAE或设置EFER寄存器。 另外,用户提供的引用[1]和[2]提到了保护模式的内存管理和分页机制,因此在回答中需要引用这些内容,特别是在解释GDT和分页设置时。例如,在保护模式下配置段描述符和分页机制是必要的步骤,这些内容在引用中有提及,可以引用相关部分来支持回答的正确性。 最后,需要验证示例代码的逻辑是否正确,是否覆盖了所有必要的步骤,并且注释清晰,方便用户理解每个部分的作用。同时,生成的相关问题应该覆盖用户可能进一步询问的方向,比如如何启用A20线,长模式下的中断处理,或者更详细的分页设置说明。</think>以下是x86汇编语言中从实模式切换到保护模式,再进入长模式的简化代码示例及分步说明: ```assembly ; 16位实模式代码 [bits 16] org 0x7C00 start: cli ; 关闭中断 lgdt [gdt_desc] ; 加载GDT[^2] ; 启用A20地址线 in al, 0x92 or al, 2 out 0x92, al ; 切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x08:protected_mode ; 远跳转刷新流水线 [bits 32] protected_mode: mov ax, 0x10 ; 设置数据段 mov ds, ax mov es, ax mov ss, ax ; 检查长模式支持 mov eax, 0x80000001 cpuid test edx, (1 << 29) jz no_long_mode ; 设置页表 mov eax, pml4 mov cr3, eax ; 启用PAE mov eax, cr4 or eax, (1 << 5) mov cr4, eax ; 启用长模式 mov ecx, 0xC0000080 rdmsr or eax, (1 << 8) wrmsr ; 启用分页 mov eax, cr0 or eax, (1 << 31) mov cr0, eax jmp 0x18:long_mode ; 进入64位代码段 [bits 64] long_mode: ; 这里可以添加64位代码 hlt no_long_mode: hlt ; GDT定义 gdt: dq 0 ; 空描述符 dw 0xFFFF, 0, 0x9A00, 0xCF ; 代码段 (0x08) dw 0xFFFF, 0, 0x9200, 0xCF ; 数据段 (0x10) dw 0xFFFF, 0, 0x9A00, 0xAF ; 长模式代码段 (0x18) gdt_desc: dw gdt_desc - gdt - 1 dd gdt ; 页表结构 pml4: dq pdpt + 0x7 ; Present, RW, User times 511 dq 0 pdpt: dq pd + 0x7 times 511 dq 0 pd: dq 0x83 ; 2MB大页 times 511 dq 0 times 510-($-$$) db 0 dw 0xAA55 ``` 关键步骤说明: 1. **实模式初始化**:设置GDT并启用A20地址线[^2] 2. **进入保护模式**:设置CR0.PE位后立即远跳转 3. **准备长模式**: - 检查CPUID是否支持长模式 - 建立四级页表结构(PML4-PDPT-PD) - 启用PAE(CR4.PAE) - 设置EFER.LME(MSR 0xC0000080) 4. **激活长模式**:启用分页(CR0.PG)后远跳转至64位代码段
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值