我正在学习x86程序集,我正在尝试在NASM中制作玩具操作系统,但我不了解一些东西 .
我做了一个成功启动内核的bootloader:
-
从包含内核文件的软盘加载14个扇区;
-
在标有
kernel.feo
的这些部门中搜索文件; -
将该文件加载到内存中以偏移
0x2000
; -
使用远跳
jmp 0x2000:0x0000
执行内核 .
所以我的内核代码位于 0x2000:0
内存中 . CS
可能已正确设置,因为使用远跳 . 在这个内核代码中,我想进入32位保护模式,但我不确定GDT是如何工作的 . 当我在虚拟机 (QEMU)
上运行下面的代码时,它不会做任何事情 .
我想请你帮我进入32位保护模式!
那就是说,你有以下问题:由于组织0,你假设代码加载在0x7c00:0,但情况可能并非如此 . 唯一保证的是物理地址 . 您应该使用远程跳转到您的入口点,以便正确设置CS . 您出于某种原因将DS设置为0x2000,因此您的代码根本找不到任何数据 . 您应该将DS设置为匹配CS,或者在任何地方使用CS覆盖(不推荐) . 受保护的模式代码假设从零开始,这意味着它需要org 0x7c00,这当然与您的设置冲突 . 你应该切换到组织0x7c00和段0. VGA文本模式段是0xb8000而不是0xb80000(少一个零) . 您在引导扇区末尾没有引导签名字节0x55 0xaa .
我在代码中更正了这些内容:
-
[org 0x0]
更正为[org 0x2000]
,并且段设置为0
; -
DS
更正为0
而不是0x2000
,所以现在它与CS
匹配; -
VGA文本模式段已更正为
0xb8000
;
但代码不适用于这些更正,它应该打印两个字符串但它什么都不做!
请注意,此内核代码不应以引导签名 0x55 0xAA
结尾,因为它不是引导扇区 .
这是更正的内核代码(不起作用):
[bits 16]
[org 0x2000]
jmp 0:kernel_start
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
mov ah, 14
mov bh, 0
lodsb
cmp al, 0
je .done
int 0x10
jmp print
.done:
ret
uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0
kernel_start:
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, uzenet_real
call print
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp
mov ebx, uzenet_prot
call print32
jmp $
1 回答
编程OS是一项高级任务 . 您至少应该能够使用调试器来查找自己的错误并理解基本知识 . 您可能想重新考虑是否具备此项工作的所有先决条件 .
也就是说,您有以下问题:
由于
org 0
,您假设代码在0x7c00:0
加载,但情况可能并非如此 . 唯一保证的是物理地址 . 您应该使用远程跳转到您的入口点,以便正确设置CS
.您出于某种原因将
DS
设置为0x2000
,因此您的代码根本找不到任何数据 . 您应该将DS
设置为匹配CS
,或者在任何地方使用CS
覆盖(不推荐) .保护模式代码假定从零开始,这意味着它需要
org 0x7c00
,这当然与您的设置冲突 . 你应该切换到org 0x7c00
和段0
.VGA文本模式段位于
0xb8000
而不是0xb80000
(少一个零) .在引导扇区末尾没有引导签名字节
0x55 0xaa
.固定代码:
您的更新问题似乎仍然与代码加载的位置相混淆:您说
offset 0x2000
然后谈论Executes the kernel using a far jump jmp 0x2000:0x0000
这当然是错误的,因为它在段中有一个零,并且应该是零段远跳:jmp 0:0x2000
. 除此之外,验证您的代码确实已在正确的位置加载到内存中 . 学习使用调试器 .这是一个小的引导扇区,它从第二个扇区加载上述代码以寻址
0x2000
. 它工作正常,问题不在于GDT的东西,特别是如果你甚至没有打印出真实的模式信息(你也不清楚) .