这个问题并不是一个最小完整可验证的例子。我需要进行一些创意性的填补和清理工作,以弥补一些空缺。
明显的问题:
主要问题在于您的第二阶段实际上并没有在32位保护模式下运行。当您从引导加载程序跳转到它时,它仍处于16位实模式。您需要启用A20线,设置GDT并切换到保护模式。您的代码是围绕
现有项目和教程建立的,通常包括
gdt.asm
、
switch_pm.asm
和
32print_pm.asm
。下面的代码使用这些文件进入保护模式并使用32位的
print_string_pm
函数进行打印。在保护模式下打印不能使用BIOS。您的第二阶段将有效地需要一个16位实模式部分和一个32位保护模式部分。
您的实模式代码同时调用了
print
和
print_string
。我已经将其全部修改为一致的
print_string
。
您需要在设置DS之后设置BOOT_DRIVE,而不是之前。
您在第2阶段开头有签名
0xface
。您需要跳转到它之后的2个字节,以避免执行签名作为代码。您还依赖于
CS的设置,因为您将其复制到
DS和
ES中。您需要确保先设置
CS。您可以使用0x0000:0x9002来纠正FAR JMP。这将设置
CS为0x0000,IP为0x9002。
修改后的代码可能如下所示:
boot.asm:
bits 16
org 0x7c00
xor ax, ax
mov es, ax
mov ds, ax
mov ss, ax
mov sp, 0x7c00
mov bp, sp
mov [BOOT_DRIVE], dl
cld
mov bx, boot_msg
call print_string
mov dx, [0x9000]
call print_hex
mov bx, 0x9000
mov dh, 2
mov dl, [BOOT_DRIVE]
call load_disk
mov dx, [0x9000]
call print_hex
jmp 0x0000:0x9002
boot_msg db "Booting cornoS", 0
%include "print.asm"
%include "print_hex.asm"
%include "load_disk.asm"
BOOT_DRIVE db 0
times 510-($-$$) db 0
dw 0xaa55
stage2.asm:
bits 16
dw 0xface
stage2:
mov ax, cs
mov ds, ax
mov es, ax
mov bx, stage2_called
call print_string
call enable_a20
call switch_to_pm
%include "print.asm"
%include "a20.asm"
%include "switch_pm.asm"
%include "gdt.asm"
bits 32
BEGIN_PM:
mov ebx, stage2_in_pm
call print_string_pm
jmp $
%include "32print_pm.asm"
stage2_called db "Stage two successfully called!", 0
stage2_in_pm db "In protected mode!", 0
load_disk.asm:
load_disk:
pusha
push dx
mov ah, 0x02
mov al, dh
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
int 0x13
jc disk_error
pop dx
cmp al, dh
jne sectors_error
mov bx, read_disk_success
call print_string
popa
ret
disk_error:
mov bx, read_disk_failed
call print_string
mov dh, ah
call print_hex
jmp $
sectors_error:
mov bx, incorrect_sectors
call print_string
jmp $
read_disk_success db "Successfully read disk!", 0
read_disk_failed db "Failed to read disk", 0
incorrect_sectors db "Incorrect number of sectors read", 0
print.asm:
print_string:
pusha
start:
mov al, [bx]
cmp al, 0
je done
mov ah, 0x0e
int 0x10
add bx, 1
jmp start
done:
popa
ret
print_nl:
pusha
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov al, 0x0d
int 0x10
popa
ret
print_hex.asm:
print_hex:
mov cx, dx
and cx, 0xf000
shr cx, 12
call to_char
mov [HEX_OUT + 2], cx
mov cx, dx
and cx, 0x0f00
shr cx, 8
call to_char
mov [HEX_OUT + 3], cx
mov cx, dx
and cx, 0x00f0
shr cx, 4
call to_char
mov [HEX_OUT + 4], cx
mov cx, dx
and cx, 0x000f
call to_char
mov [HEX_OUT + 5], cx
mov bx, HEX_OUT
call print_string
mov byte [HEX_OUT + 2], '0'
mov byte [HEX_OUT + 3], '0'
mov byte [HEX_OUT + 4], '0'
mov byte [HEX_OUT + 5], '0'
ret
to_char:
cmp cx, 0xa
jl digits
sub cx, 0xa
add cx, 'a'
ret
digits:
add cx, '0'
ret
HEX_OUT: db '0x0000', 0
a20.asm:
enable_a20:
cli
call a20wait
mov al,0xAD
out 0x64,al
call a20wait
mov al,0xD0
out 0x64,al
call a20wait2
in al,0x60
push eax
call a20wait
mov al,0xD1
out 0x64,al
call a20wait
pop eax
or al,2
out 0x60,al
call a20wait
mov al,0xAE
out 0x64,al
call a20wait
sti
ret
a20wait:
in al,0x64
test al,2
jnz a20wait
ret
a20wait2:
in al,0x64
test al,1
jz a20wait2
ret
gdt.asm:
gdt_start:
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 - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
switch_pm.asm:
[bits 16]
switch_to_pm:
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm
[bits 32]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call BEGIN_PM
32print_pm.asm:
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_OB_BLACK equ 0x0f
print_string_pm:
pusha
mov edx, VIDEO_MEMORY
print_string_pm_loop:
mov al, [ebx]
mov ah, WHITE_OB_BLACK
cmp al, 0
je print_string_pm_done
mov [edx], ax
add ebx, 1
add edx, 2
jmp print_string_pm_loop
print_string_pm_done:
popa
ret
您可以使用之前使用过的相同命令来构建磁盘映像:
nasm -f bin -o boot.bin boot.asm
nasm -f elf32 -o stage2.o stage2.asm
ld -melf_i386 -Ttext=0x9000 -nostdlib --nmagic -o stage2.elf stage2.o
objcopy -O binary stage2.elf stage2.bin
dd if=/dev/zero of=corn.img bs=512 count=2880
dd if=boot.bin of=corn.img bs=512 conv=notrunc
dd if=stage2.bin of=corn.img bs=512 seek=1 conv=notrunc
qemu-system-i386 -fda corn.img
输出应该类似于:
输出应该类似于:
![enter image description here](https://istack.dev59.com/5iiXE.webp)
更高级的print_string_pm函数
大多数教程中的通用print_string_pm
从屏幕左上角开始打印,非常基本。我编写了一些示例代码,用于更高级的print_string_pm
函数,它具有以下功能:
- 在BIOS TTY函数停止的地方继续以保护方式打印
- 支持向下滚动
- 带有颜色属性进行打印
- 在发生滚动时带有颜色属性填充底部行
- 处理换行
- 支持退格,但不能超出屏幕开头
- 处理回车和换行符
- 忽略TAB字符
- 进入保护模式后需要调用
call update_screen_state_from_bios
一次
- 完成后更新硬件光标
32print_pm.asm:
bits 32
VIDEO_TEXT_ADDR EQU 0xb8000
CR EQU 0x0d
LF EQU 0x0a
BS EQU 0x08
TAB EQU 0x09
update_screen_state_from_bios:
xor eax, eax
mov al, [0x450]
mov [cur_col], eax
mov al, [0x451]
mov [cur_row], eax
mov al, [0x484]
mov [screen_height],eax
mov ax, [0x44a]
mov [screen_width], eax
ret
set_cursor:
mov ecx, [cur_row]
imul ecx, [screen_width]
add ecx, [cur_col]
mov edx, 0x3d4
mov al, 0x0f
out dx, al
inc edx
mov al, cl
out dx, al
dec edx
mov al, 0x0e
out dx, al
inc edx
mov al, ch
out dx, al
ret
print_string_pm:
push edi
push esi
push eax
push ebx
push ebp
mov ebx, VIDEO_TEXT_ADDR
mov cl, al
call .init
jmp .getch
.repeat:
cmp al, CR
jne .chk_lf
lea edi, [ebx + edx * 2]
mov dword [cur_col], 0
xor al, al
jmp .chk_bounds
.chk_lf:
cmp al, LF
jne .chk_bs
mov ebp, [screen_width]
lea edi, [edi + ebp * 2]
inc dword [cur_row]
xor al, al
jmp .chk_bounds
.chk_bs:
cmp al, BS
jne .chk_tab
cmp edi, ebx
je .getch
dec dword [cur_col]
jmp .chk_bounds
.chk_tab:
cmp al, TAB
je .getch
.chk_bounds:
mov ebp, [screen_width]
cmp [cur_col], ebp
jl .chk_col_start
mov dword [cur_col], 0
inc dword [cur_row]
jmp .chk_rows
.chk_col_start:
cmp dword [cur_col], 0
jge .chk_rows
mov dword [cur_col], 0
.chk_rows:
mov ebp, [screen_height]
cmp [cur_row], ebp
jle .test_char
dec dword [cur_row]
call .scroll_down_one_line
call .init
.test_char:
test al, al
jz .getch
cmp al, BS
jne .not_bs
mov al, ' '
sub edi, 2
mov [es:edi], al
jmp .getch
.not_bs:
stosw
inc dword [cur_col]
.getch:
lodsb
test al, al
jnz .repeat
.end:
call set_cursor
pop ebp
pop ebx
pop eax
pop esi
pop edi
ret
.scroll_down_one_line:
pusha
mov ebp, [screen_height]
mov eax, [screen_width]
lea esi, [ebx + eax * 2]
mov edi, ebx
mul ebp
mov ecx, eax
rep movsw
lea edi, [ebx + eax * 2]
mov ecx, [screen_width]
mov ah, cl
mov al, ' '
rep stosw
popa
ret
.init:
push eax
mov eax, [cur_row]
mul dword [screen_width]
mov edx, eax
add eax, [cur_col]
lea edi, [ebx + eax * 2]
pop eax
ret
align 4
cur_row: dd 0x00
cur_col: dd 0x00
screen_width: dd 0x00
screen_height:dd 0x00
stage2.asm:
ATTR_WHITE_ON_BLACK EQU 0x07
bits 16
dw 0xface
stage2:
mov ax, cs
mov ds, ax
mov es, ax
mov bx, stage2_called
call print_string
call enable_a20
call switch_to_pm
%include "print.asm"
%include "a20.asm"
%include "switch_pm.asm"
%include "gdt.asm"
bits 32
BEGIN_PM:
call update_screen_state_from_bios
mov esi, stage2_in_pm
mov ah, ATTR_WHITE_ON_BLACK
mov al, ah
call print_string_pm
jmp $
%include "32print_pm.asm"
stage2_called db "Stage two successfully called!", 0
stage2_in_pm db "In protected mode!", 0
输出应该类似于:
输出应该类似于:
![enter image description here](https://istack.dev59.com/5zFFx.webp)
%include "print.asm"
被注释掉了?为什么没有因为print
而得到链接错误?sti
,至少在 print 正常工作之前,因为很明显对于 print 不是必要的。