我正在学习x86汇编,并且试图在NASM中制作一个玩具操作系统,但是我不了解某些事情。

我制作了一个可以成功引导内核的引导程序:


从包含内核文件的软盘加载14个扇区;
搜索这些扇区中标记为kernel.feo的文件;
将该文件加载到内存中,偏移量为0x2000
使用远距jmp 0x2000:0x0000执行内核。


因此,我的内核代码位于内存中的0x2000:0处。 CS可能已正确设置,因为使用了远跳。在此内核代码中,我想进入32位保护模式,但是我不确定GDT的工作方式。当我在虚拟机(QEMU)上运行以下代码时,它什么也不做。

我想请您帮助我进入32位保护模式!


也就是说,您有以下问题:


您假定由于0x7c00:0而在org 0处加载了代码,但事实并非如此。唯一保证的是物理地址。您应该跳远到您的入口点,以便正确设置CS
您出于某种原因将DS设置为0x2000,因此您的代码根本找不到任何数据。您应该将DS设置为与CS匹配,或者在任何地方都使用CS替代(不推荐)。
保护模式代码假定从零开始的段,这又意味着它期望org 0x7c00当然会与您的设置冲突。您应该切换到org 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 $

最佳答案

对OS进行编程是一项高级任务。至少应该期望您能够使用调试器来发现自己的错误并了解基本知识。您可能需要重新考虑是否具备此工作的所有先决条件。

也就是说,您有以下问题:


您假定由于0x7c00:0而在org 0处加载了代码,但事实并非如此。唯一保证的是物理地址。您应该跳远到您的入口点,以便正确设置CS
您出于某种原因将DS设置为0x2000,因此您的代码根本找不到任何数据。您应该将DS设置为与CS匹配,或者在任何地方都使用CS替代(不推荐)。
保护模式代码假定从零开始的段,这又意味着它期望org 0x7c00当然会与您的设置冲突。您应该切换到org 0x7c00并细分0
VGA文本模式段位于0xb8000而不是0xb80000(零少一个)。
引导扇区的末尾没有引导签名字节0x55 0xaa


固定代码:

[bits 16]
[org 0x7c00]

    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:
    pusha
    mov ah, 14
    mov bh, 0
.loop:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .loop
.done:
    popa
    ret

uzenet16 db 'uzenet16', 0
uzenet32 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, uzenet16
    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, 0x2000
    mov esp, ebp

    mov ebx, uzenet32
    call print32

    jmp $

[SECTION signature start=0x7dfe]
dw 0AA55h




您更新后的问题似乎仍然对代码的加载位置感到困惑:您说offset 0x2000,然后谈论Executes the kernel using a far jump jmp 0x2000:0x0000当然是错误的,因为它在段中还有一个零,应该是零段。反正跳远:jmp 0:0x2000。除此之外,请确认您的代码确实在正确的位置加载到内存中。学习使用调试器。

这是一个小的引导扇区,它将上述代码从第二扇区加载到地址0x2000。它工作正常,问题出在GDT上,而不是问题,特别是如果您甚至没有打印出实模式消息(您也不清楚)。

[bits 16]
[org 0x7c00]
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 0
mov es, bx
mov bx, 2000h
int 13h
jmp 0:2000h

[SECTION signature start=0x7dfe]
dw 0AA55h

关于assembly - 如何在NASM组件中进入32位保护模式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28645439/

10-11 18:50