操作系统编程是一项高级任务。您至少应该能够使用调试器来发现自己的错误并理解基本的东西。您可能需要重新考虑您是否具备此努力的所有先决条件。
也就是说,您有以下问题:
- 您假设代码加载于
0x7c00:0
因为org 0
,但情况可能并非如此。唯一能保证的是物理地址。您应该使用远跳到您的入口点,以便CS
已正确设置。
- 您出于某种原因设置
DS
to 0x2000
所以你的代码根本找不到任何数据。你应该设置DS
匹配CS
,或使用CS
覆盖所有地方(不推荐)。
- 保护模式代码假定从零开始的段,这又意味着它期望
org 0x7c00
这当然与您的设置冲突。你应该切换到org 0x7c00
和段0
.
- VGA 文本模式段位于
0xb8000
not 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