首页 > 解决方案 > 进入保护模式后尝试 jmp 时出错 - 程序集引导加载程序

问题描述

我整天都在努力解决这个问题。我正在学习编写引导加载程序的教程,但在正确进入保护模式时我被卡住了。我不知道我做错了什么,我整天都在谷歌上搜索。我正在使用 NASM 进行编译

这是我在 Bochs dbg 中得到的输出:

在此处输入图像描述

据说 CPU 确实进入了保护模式,之后不久我就遇到了错误。这是我所做的第二次重写,主要目的是进入保护模式而不会出错。如果有人能告诉我我做错了什么,我会很高兴。

我的代码如下:

引导加载程序.asm

global _start

_start:

[bits 16]
[org 0x7c00]

    mov bp, 0x8000
    mov sp, bp

    mov bx, welcomeString
    call print_func


    call print_newline_func

    call switch_to_pm

    jmp $

%include "io.asm"
%include "print.asm"
%include "gdt.asm"

welcomeString:
    db 'Hello. Welcome to OS', 13, 10,0


switch_to_pm:
    cli

    lgdt [gdt_descriptor]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax


[bits 32]

    jmp CODE_SEG:init_pm


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

BEGIN_PM:
    mov ebx, MSG_PM
    call print_string_pm

    jmp $

MSG_PM:
    db 'success', 0

times 510-($-$$) db 0

dw 0xaa55

io.asm

BOOT_DRIVE:
        db 0

    ReadDisk:                   ; Reads from drive dl amount of sectors to read dh

        push dx                 ; Store dx to stack

        mov ah, 0x02            ; BIOS read sector code
        mov al, dh              ; Read dh sectors
        mov ch, 0x00            ; Select cyl 0
        mov dh, 0x00            ; Select 1st track, 
        mov cl, 0x02            ; Select 2nd sector (1st after boot sector)

        int 0x13                ; Read interrupt code

        jc disk_error           ; Jump if error

        pop dx
        cmp dh, al              ; jump if sectors expected != sectors read
        jne disk_error      

        ret

    errorString:
        db 'Disk Read Error.',13,10,0

    disk_error:
        mov bx, errorString
        call print_func
    ret

gdt.asm

gdt_start:
    gdt_null:       ; null descriptor
        dd 0x0
        dd 0x0

    gdt_code:           ; the code segment descriptor
                        ; base =0x0 , limit =0 xfffff ,
                        ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b
                        ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b
                        ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b
        dw 0xffff       ; Limit (bits 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)

    gdt_data:           ; the data segment descriptor
                        ; Same as code segment except for the type flags :
                        ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b
        dw 0xffff       ; Limit (bits 0-15)
        dw 0x0          ; Base (bits 0-15)
        db 0x0          ; Base (bits 16-23)
        db 10010010b    ; 1st flags, type flags
        db 11001111b    ; 2nd flags
        db 0x0          ; Base (bits 24-31)

    gdt_end:            ; Put this label to calculate size of GDT

gdt_descriptor:
    dw gdt_end - gdt_start - 1      ; GDT size, always 1 less than true size
    dd gdt_start                    ; start address of GDT


CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

打印.asm

print_func:
    push bx
        mov ah, 0x0e
        ;add bx, 0x7c00                 ; calculate correct address
        print_loop:
            cmp byte [bx], 0            ; if char at bx == 0, jump to exit.
            je print_exit   
            mov al, [bx]                ; move char at bx into al
            int 0x10                    ; print
            inc bx                      ; increment bx
            jmp print_loop              ; loop to start of func
            print_exit:
            pop bx
                ret

print_newline_func:
    push ax
    mov ah, 0x0e
    mov al, 10
    int 0x10
    mov al, 13
    int 0x10
    pop ax
    ret

print_hex_func:
    push ax
    push bx
    push cx

    mov ah, 0x0e

    mov al, '0'
    int 0x10
    mov al, 'x'
    int 0x10                    ; print 0x          

    ;add bx, 0x7c00

    mov cx, [bx]
    shr cx, 12
    call PrintAsciiFromHex

    mov cx, [bx]
    shr cx, 8
    call PrintAsciiFromHex

    mov cx, [bx]
    shr cx, 4
    call PrintAsciiFromHex

    mov cx, [bx]
    call PrintAsciiFromHex

    pop ax
    pop bx
    pop cx

    ret

PrintAsciiFromHex:
    shl cx, 12
    shr cx, 12
    cmp cx, 9
    jg Add55
    add cx, 48
    jmp Skip
    Add55:
        add cx, 55
    Skip:
    mov al, cl
    int 0x10
    ret

AddressPointer:
    dw 0

PrintAddress:                   ; Moves address of bx into value of AddressPointer
    mov [AddressPointer], bx    ; Passes address of address pointer into bs
    mov bx, AddressPointer      ; prints value of address pointer, therefore printing address
    call print_hex_func
    ret

[bits 32]

                                    ; Define some constants
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

                                    ; prints a null - terminated string pointed to by EDX
print_string_pm:
    pusha
    mov edx, VIDEO_MEMORY               ; Set edx to the start of vid mem.
    print_string_pm_loop :
        mov al , [ ebx ]                    ; Store the char at EBX in AL
        mov ah , WHITE_ON_BLACK             ; Store the attributes in AH
        cmp al , 0                          ; if (al == 0) , at end of string , so
        je print_string_pm_done             ; jump to done
        mov [edx] , ax                      ; Store char and attributes at current
                                            ; character cell.
        add ebx , 1                         ; Increment EBX to the next char in string.
        add edx , 2                         ; Move to next character cell in vid mem.

        jmp print_string_pm_loop            ; loop around to print the next char.
    print_string_pm_done :
        popa
        ret                                 ; Return from the function

标签: assemblyx86operating-systemnasmbootloader

解决方案


通过在 print.asm 中的 32 位函数之后返回 [bits 16] 并将 jmp 移动到更改为 [bits 32] 的上方来解决


推荐阅读