切换至GDT/保护模式后print.asm无法输出问题求助
问题分析与修复方案
你的代码存在几个关键问题,导致保护模式下无法输出内容,同时实模式阶段的调用逻辑也存在错误,逐一说明如下:
1. 实模式错误调用32位print函数
boot.asm的start段处于16位实模式,但你引入的print.asm是[bits 32]标记的保护模式函数。实模式下执行32位指令会直接引发崩溃,程序根本无法进入保护模式,更无法执行后续的输出逻辑。
修复:
- 拆分print模块,分别实现实模式16位print和保护模式32位print,调用时严格匹配当前运行模式。
- 实模式print使用BIOS中断
int 0x10(ah=0x0e)实现字符输出,保护模式直接操作显存地址0xb8000。
2. 保护模式print函数的寄存器传递错误
print.asm中,你将字符串地址存入ecx,但lodsb指令默认从esi寄存器读取数据,未将ecx的值传递给esi,导致无法正确读取字符串内容,自然无法输出。
修复:
在print函数开头添加mov esi, ecx,将字符串地址从ecx转移到esi,供lodsb指令使用。
3. 磁盘读取功能号错误
disk.asm中int 0x13的功能号ah=0x01是读取磁盘状态,而非读取扇区。正确的读扇区功能号是ah=0x02,否则磁盘读取逻辑完全失效,还会触发diskErr调用32位print函数,进一步引发崩溃。
修复:
将mov ah, 0x01改为mov ah, 0x02,同时添加重试次数限制,避免无限死循环。
修正后的关键代码片段
实模式print模块(print16.asm)
[bits 16] print16: pusha mov ah, 0x0e .print_loop: lodsb cmp al, 0 je .print_done int 0x10 jmp .print_loop .print_done: popa ret
保护模式print模块(print32.asm)
[bits 32] videoMemory equ 0xb8000 whiteOnBlack equ 0x0f print32: pusha mov edx, videoMemory mov esi, ecx ; 将字符串地址从ecx转到esi,供lodsb使用 mov ebx, whiteOnBlack call printString32 popa ret printString32: pusha .loop: lodsb cmp al, 0 je .done mov [edx], al mov [edx+1], bl ; 单独写入属性字节,避免ax寄存器高位干扰 add edx, 2 jmp .loop .done: popa ret
修正后的boot.asm调用逻辑
[ORG 0x7C00] jmp start %include "boot/disk.asm" %include "boot/print16.asm" ; 引入实模式print %include "boot/gdt.asm" %include "boot/switch.asm" %include "boot/print32.asm" ; 引入保护模式print hang: jmp hang start: mov bp, 0x9000 mov sp, bp mov si, realmsg ; 实模式用si传递字符串地址 call print16 mov si, loadmsg call print16 mov si, protmsg call print16 call switchPM ; 以下代码不会执行,因为switchPM直接跳转到beginPM mov si, diskmsg call print16 call diskLoad jc diskErr jmp hang ; 常量定义保持不变 realmsg db "Loading real mode...", 0x0D, 0x0A, 0 loadmsg db "Loading OS...", 0x0D, 0x0A, 0 diskmsg db "Loading disk sectors...", 0x0D, 0x0A, 0 diskmsgdone db "Disk sectors loaded", 0x0D, 0x0A, 0 diskerr db "Error loading disk sectors", 0x0D, 0x0A, 0 protmsg db "Loading protected mode...", 0x0D, 0x0A, 0 protmsgdone db "Protected mode loaded", 0x0D, 0x0A, 0 times 510-($-$$) db 0 db 0x55 db 0xAA [bits 32] beginPM: mov ecx, protmsgdone call print32 jmp hang ; 停在此处查看保护模式输出
修正后的disk.asm
diskLoad: mov bx, 0x8000 mov dh, 0x00 mov dl, 0x80 mov ch, 0x00 mov cl, 0x02 mov al, 0x01 mov ah, 0x02 ; 改为读扇区功能号 int 0x13 jc .retry ret .retry: dec byte [retry_count] jnz diskLoad jmp diskErr retry_count db 3 ; 限制重试3次,避免死循环 diskErr: mov si, diskerr call print16 jmp hang
额外注意事项
switchPM执行后会直接跳转到32位的initPM,再调用beginPM,因此boot.asm中call switchPM之后的代码永远不会执行。你需要把磁盘读取逻辑移到保护模式下实现,或者在进入保护模式前完成磁盘读取。- 进入保护模式后,BIOS中断无法使用,若需磁盘操作,需自行实现32位ATA磁盘驱动。
内容的提问来源于stack exchange,提问作者Pigioty
相关产品推荐
相关产品推荐

