32 位保护模式不适用于多个程序集文件 [英] 32 bit protected mode not working for several assembly files

查看:70
本文介绍了32 位保护模式不适用于多个程序集文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个简单的 NASM 程序集引导扇区.代码应该在 16 位实模式下将文本打印到屏幕上,然后切换到 32 位保护模式并将文本打印到屏幕上.

我使用 QEMU 作为我的 CPU 模拟器,并且它正在从 16 位模式打印文本.但是,在 32 位模式下应该打印的文本没有打印.

我认为这是我的代码的问题,但我也运行了这个

在您的代码中,您似乎在 32 位代码描述符中缺少一个字节:

gdt_code: ;代码段描述符dw 0xffff ;限制(咬 0-15)dw 0x0 ;基数(位 0-15)数据库 0x0 ;基数(位 16-23)分贝 10011010b ;第一个标志,类型标志分贝 11001111b ;第二个标志,限制(位 16-19)

这个条目只有 7 个字节长.看来您错过了最后一个字节,该字节应该是 0 以完成 32 位基地址.它应该是:

gdt_code: ;代码段描述符dw 0xffff ;限制(咬 0-15)dw 0x0 ;基数(位 0-15)数据库 0x0 ;基数(位 16-23)分贝 10011010b ;第一个标志,类型标志分贝 11001111b ;第二个标志,限制(位 16-19)数据库 0x0 ;基数(位 24-31)

当在 QEMU 中使用命令 qemu-system-x86_64 boot_sector.bin 运行时,它应该显示为:

我以红色突出显示了在保护模式下打印的文本.

<小时>

如果您希望在控制台模式下在图形显示之外运行代码,请告诉 QEMU 使用 curses.

 -curses通常,如果 QEMU 是在图形窗口支持下编译的,它显示输出,例如来宾图形、来宾控制台和 QEMU在窗口中监视.使用此选项,QEMU 可以显示 VGA使用curses/ncurses 接口在文本模式下输出.没有什么以图形方式显示.

使用命令行:

qemu-system-x86_64 boot_sector.bin -curses

I am writing a simple NASM assembly boot sector. The code should print text to the screen while in 16 bit real mode, then switches to 32 bit protected mode and prints text to the screen.

I use QEMU as my CPU emulator, and it is printing the text from the 16 bit mode as it should. However, the text that's supposed to be printed while in 32 bit mode doesn't print.

I would assume this is an issue with my code, but I have also ran this similar code, with the same problem of only working while in 16 bit mode.

Am I not using QEMU correctly, or am I messing something else up? My code is:

boot_sector.asm

; Boot sector that enters 32 bit protected mode
[org 0x7c00]

mov bp, 0x9000          ; Set stack
mov sp, bp

mov bx, MSG_REAL_MODE
call print_string

call switch_to_pm       ; We will never return to here

jmp $

%include "print_string.asm"
%include "gdt.asm"
%include "print_string_pm.asm"
%include "switch_to_pm.asm"

[bits 32]
;Where we arrive after switching to PM
BEGIN_PM:
    mov ebx, MSG_PROTECTED_MODE
    call print_string_pm        ; 32 bit routine to print string

    jmp $                       ; Hang


; Global variables
MSG_REAL_MODE: db "Started in 16-bit real mode.", 0
MSG_PROTECTED_MODE: db "Successfully landed in 32-bit protected mode.", 0

; Boot sector padding
times 510-($-$$) db 0
dw 0xaa55

switch_to_pm.asm

[bits 16]
; Switch to protected mode
switch_to_pm:

    mov bx, MSG_SWITCHING       ; Log
    call print_string

    cli                         ; Clear interrupts

    lgdt [gdt_descriptor]       ; Load GDT

    mov eax, cr0                ; Set the first bit of cr0 to move to protected mode, cr0 can't be set directly
    or eax, 0x1                 ; Set first bit only
    mov cr0, eax

    jmp CODE_SEG:init_pm        ; Make far jump to to 32 bit code. Forces CPU to clear cache

[bits 32]
; Initialize registers and the stack once in PM
init_pm:

    mov ax, DATA_SEG            ; Now in PM, our old segments are meaningless
    mov ds, ax                  ; so we point our segment registers to the data selector defined GDT
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp, 0x90000            ; Move stack
    mov esp, ebp

    call BEGIN_PM               ; Call 32 bit PM code


; Global variables
MSG_SWITCHING: db "Switching to 32-bit protected mode...", 0

gdt.asm

gdt_start:

gdt_null:           ; The mandatory null descriptor
    dd 0x0          ; dd = define double word (4 bytes)
    dd 0x0

gdt_code:           ; Code segment descriptor
    dw 0xffff       ; Limit (bites 0-15)
    dw 0x0          ; Base (bits 0-15)
    db 0x0          ; Base (bits 16-23)
    db 10011010b    ; 1st flags, type flags
    db 11001111b    ; 2nd flags, limit (bits 16-19)

gdt_data:
    dw 0xffff       ; Limit (bites 0-15)
    dw 0x0          ; Base (bits 0-15)
    db 0x0          ; Base (bits 16-23)
    db 10010010b    ; 1st flags, type flags
    db 11001111b    ; 2nd flags, limit (bits 16-19)
    db 0x0

gdt_end:            ; necessary so assembler can calculate gdt size below

gdt_descriptor:
    dw gdt_end - gdt_start - 1  ; GDT size

    dd gdt_start                ; Start adress of GDT

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

print_string_pm.asm

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

print_string_pm:
    pusha
    mov edx, VIDEO_MEMORY

print_str_pm_loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK

    cmp al, 0
    je print_str_pm_return

    mov [edx], ax

    add ebx, 1
    add edx, 2

    jmp print_str_pm_loop

print_str_pm_return:
    popa
    ret

print_string.asm

print_string:
    pusha
    mov ah, 0x0e

_print_str_loop:
    mov al, [bx]
    cmp al, 0
    je _print_str_return
    int 0x10
    inc bx
    jmp _print_str_loop

_print_str_return:
    popa
    ret

Commands: To build:

nasm -f bin boot_sector.asm -o boot_sector.bin

Qemu:

qemu-system-x86_64 boot_sector.bin --nographic

解决方案

You don't correctly set a real mode stack pointer and the segment registers. A real mode stack pointer consists of SS:SP. You don't know where in memory the stack is since you've only modified SP. The beginning of your bootloader should be something like:

xor ax, ax              ; Set ES=DS=0 since an ORG of 0x7c00 is used
mov es, ax              ;     0x0000<<4+0x7c00 = physical address 0x07c00
mov ds, ax

mov bp, 0x9000
mov ss, ax              ; Set stack to 0x0000:0x9000
mov sp, bp

Your code doesn't rely on BP so it doesn't have to be set, although it doesn't hurt anything by doing so.


The main problem getting into protected mode is an error in your GDT. Each descriptor entry is 8 bytes and the layout of each descriptor is as follows:

In your code you seem to be missing a byte in the 32-bit code descriptor:

gdt_code:           ; Code segment descriptor
    dw 0xffff       ; Limit (bites 0-15)
    dw 0x0          ; Base (bits 0-15)
    db 0x0          ; Base (bits 16-23)
    db 10011010b    ; 1st flags, type flags
    db 11001111b    ; 2nd flags, limit (bits 16-19)

This entry is only 7 bytes long. It appears you are missing the last byte which should be 0 to complete the 32-bit base address. It should read:

gdt_code:           ; Code segment descriptor
    dw 0xffff       ; Limit (bites 0-15)
    dw 0x0          ; Base (bits 0-15)
    db 0x0          ; Base (bits 16-23)
    db 10011010b    ; 1st flags, type flags
    db 11001111b    ; 2nd flags, limit (bits 16-19)
    db 0x0          ; Base (bits 24-31)

When run in QEMU with the command qemu-system-x86_64 boot_sector.bin it should appear as:

I have highlighted the text printed in protected mode in red.


If you wish to run the code outside of a graphical display in console mode, tell QEMU to use curses.

    -curses
       Normally, if QEMU is compiled with graphical window support, it
       displays output such as guest graphics, guest console, and the QEMU
       monitor in a window. With this option, QEMU can display the VGA
       output when in text mode using a curses/ncurses interface. Nothing
       is displayed in graphical mode.

Use the command line:

qemu-system-x86_64 boot_sector.bin -curses

这篇关于32 位保护模式不适用于多个程序集文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆