我尝试在切换到保护模式后打印字符“C”,但它不起作用,我不知道为什么。我正在使用汇编 AT&T/GAS 语法
这是代码:
.code16
.global _start
_start:
cli
gdt_start:
.quad 0x0
gdt_data:
.short 0xffff
.short 0x0
.byte 0x0
.byte 0b10010010
.byte 0b11001111
.byte 0x0
gdt_code:
.short 0xffff
.short 0x0
.byte 0x0
.byte 0b10011010
.byte 0b11001111
.byte 0x0
gdt_end:
DATASEG = gdt_data-gdt_start
CODESEG = gdt_code-gdt_start
gdt_pointer:
.short gdt_end - gdt_start - 1
.long gdt_start
lgdt (gdt_pointer)
mov %cr0, %eax
or $1, %eax
mov %eax, %cr0
ljmp $CODESEG, $protected_mode
.code32
protected_mode:
mov $'C', %al
mov 0x0f, %ah
mov %ax, (0xb8000)
jmp .
.fill 510-(.-_start), 1, 0
.word 0xaa55
Makefile 选项:
as main.s -o boot.o
ld -Ttext 0x7c00 -o boot.bin --oformat binary boot.o
我认为问题是我的GDT表或远跳转,但我不知道在哪里。不管怎样,我尝试搜索其他人的代码,但没有找到任何东西。
我认为问题是我的 GDT 表或远跳,
.fill
指令之前。lgdt (gdt_pointer)
和mov %ax, (0xb8000)
指令取决于DS段寄存器。您必须确保正确设置。因为您使用的原点为 0x7C00
,在第一种情况下为 DS=0,在第二种情况下 DS=DATASEG,段基数为 0。对于这个简化的练习(切换到保护模式后打印字符“C”),没有必要设置堆栈,特别是因为
cli
,但无论如何我已经包含了它:
.code16
.global _start
_start:
cli
xor %ax, %ax
mov %ax, %ds
mov %ax, %ss
mov $0x7C00, sp
lgdt (gdt_start)
mov %cr0, %eax
or $1, %al
mov %eax, %cr0
ljmp $CODESEG, $PM
.code32
PM:
mov $DATASEG, %ax
mov %ax, %ds
mov %ax, %ss
mov $0x00007C00, %esp
movw $0x0F43, (0x000B8000)
jmp .
gdt_start:
.short gdt_end - gdt_start - 1
.long gdt_start
.short 0x0
gdt_data:
.short 0xFFFF
.short 0x0
.byte 0x0
.byte 0b10010010
.byte 0b11001111
.byte 0x0
gdt_code:
.short 0xFFFF
.short 0x0
.byte 0x0
.byte 0b10011010
.byte 0b11001111
.byte 0x0
gdt_end:
DATASEG = gdt_data - gdt_start
CODESEG = gdt_code - gdt_start
.fill 510-(.-_start), 1, 0
.word 0xAA55
GDT 的第一个槽未使用,通常用零填充。然而,考虑到其 512 字节的大小限制,可以将 GDT_POINTER 存储在那里并保存一些宝贵的引导扇区字节。