如何解决汇编程序 - 执行 LGDT 指令后 PC 崩溃
我想在执行 lgdt 指令后使用 jmp 指令跳转到 diskette_initialisation,但它崩溃了 原因是什么? 既然知道原因,那Intel cpu的设计者为什么要这么做?
环境
- cpu:intel(r) core(tm) 17-8550U
- 机器:虚拟盒子
- 辅助存储:软盘
- 编译器:2020 年 8 月 28 日编译的 NASM 版本 2.15.05
bits 16
org 0x7c00
jmp boot_lodaer
boot_lodaer:
cli
mov ax,0
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov sp,0x7c00
call minimum_gdt_andidt
bits 16
call valid_A20
call diskette_initialisation
jmp Protected_Mode
minimum_gdt:
bits 32
lgdt [ndt_setup];https://wiki.osdev.org/GDT_Tutoria
lidt [ndt_setup:
ret
ndt_setup:
dw 23
dd gdt_null
ndt_null:
dq 0x0:
ndt_code:
dw 0xffff
dw 0x0
db 0x0
db 11001111b
db 10011010b
db 0x0
ndt_date:
dw 0xffff
dw 0x0
db 0x0
db 11001111b
db 10011010b
db 0x0
ndit_end:
;Only enable a20. Does not check for anything.
valid_A20:
in al,0x60
mov al,0xad; key off
out 0x60,al
in al,0x60
mov al,0xfe; key initialisation
out 0x60,0xae; key on
out 0x60,al
mov al,0x00
ret
diskette_error:
mov ah,0x0e
mov al,'E'
int 0x10
hlt
diskette_initialisation:
mov ah,0x00
mov dl,0x00
int 0x13
jc diskette_error
mov ah,'2'
int 0x10
ret
Protected_Mode:
cli
mov eax,1
mov cr0,eax
jmp eax:karnel
kanel:
hlt
times 510-($-$$) db 0
dw 0aa55h
解决方法
org 0x7e00
因为这个指令,你代码中的所有位移都会出错!
您需要:ORG 0x7C00
mov ss,ax
如果您不打算同时指定堆栈指针 SP
,则不要更改 SS
。您很有可能继续使用现有的堆栈。
gdt_setup:
dw 24
dd gdt_null
小改动:这里的第一个词是限制而不是大小。你应该写dw 23
。
jmp [diskette_initialisation]
在diskette_initialisation 有代码,而不是指针(首先由@fuz 注意到)。你需要写jmp diskette_initialisation
您在描述符中使用了一些错误的值!主要是由于几个字节的反转。以下是 CODE 和 DATA 的正确设置:
gdt_code:
dw 0xFFFF
dw 0
db 0
db 10011010b
db 11001111b
db 0
gdt_data:
dw 0xFFFF
dw 0
db 0
db 10010010b
db 11001111b
db 0
,
这是英特尔方面的硬件错误,因此无法解决问题。 我什至不能像规范中那样做。再见,暂时,我会因为我在它上花费的时间而怨恨英特尔。
我提到的规格 enter link description here
9.9.1 切换到保护模式
从实模式切换到保护模式之前,系统数据结构和代码模块的最小集合 必须加载到内存中,如第 9.8 节“保护模式操作的软件初始化”中所述。 一旦创建了这些表,软件初始化代码就可以切换到保护模式。 通过执行设置 CR0 寄存器中的 PE 标志的 MOV CR0 指令进入保护模式。 (在里面 同样的指令,寄存器 CR0 中的 PG 标志可以设置为使能分页。)保护模式下的执行开始于 CPL 为 0。 Intel 64 和 IA-32 处理器对切换到保护模式的要求略有不同。投保 向上和向下代码兼容 Intel 64 和 IA-32 处理器,我们建议您遵循 这些步骤:
- 禁用中断。 CLI 指令禁用可屏蔽硬件中断。可以禁用 NMI 中断 带外部电路。 (软件必须保证在执行过程中不产生异常或中断 模式切换操作。)
- 执行 LGDT 指令,将 GDT 的基地址加载到 GDTR 寄存器中。
- 执行一条 MOV CR0 指令,在控制寄存器 CR0 中设置 PE 标志(以及可选的 PG 标志)。
- 在 MOV CR0 指令之后,立即执行远 JMP 或远 CALL 指令。 (这个操作是 通常是对指令流中下一条指令的远跳转或调用。)
- 紧跟在 MOV CR0 指令之后的 JMP 或 CALL 指令改变了执行流程和 序列化处理器。
- 如果启用分页,则 MOV CR0 指令和 JMP 或 CALL 指令的代码必须来自 身份映射的页面(即跳转前的线性地址与物理地址相同) 启用分页和保护模式后)。 JMP 或 CALL 指令的目标指令不 需要进行身份映射。
- 如果要使用本地描述符表,请执行 LLDT 指令以加载段选择器 LDTR 寄存器中的 LDT。
- 执行 LTR 指令将带有段选择器的任务寄存器加载到初始保护模式任务 或可写的内存区域,可用于在任务开关上存储 TSS 信息。
- 进入保护模式后,段寄存器继续保存它们在实地址中的内容 模式。第 4 步中的 JMP 或 CALL 指令复位 CS 寄存器。执行以下操作之一以 更新剩余段寄存器的内容。 — 重新加载段寄存器 DS、SS、ES、FS 和 GS。如果 ES、FS 和/或 GS 寄存器不会被 使用,用空选择器加载它们。 — 对新任务执行 JMP 或 CALL 指令,这会自动重置段的值 注册并分支到新的代码段。
- 执行 LIDT 指令以使用保护模式 IDT 的地址和限制加载 IDTR 寄存器。
- 执行 STI 指令以启用可屏蔽硬件中断并执行必要的硬件 使能 NMI 中断的操作。 如果上述第 3 步和第 4 步之间存在其他指令,则可能会发生随机故障。失败将很容易被看到 在某些情况下,例如当在第 3 步和第 4 步之间插入引用内存的指令时 系统管理模式。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。