Assembly 进入保护模式后尝试jmp时出错-程序集引导加载程序

Assembly 进入保护模式后尝试jmp时出错-程序集引导加载程序,assembly,x86,operating-system,nasm,bootloader,Assembly,X86,Operating System,Nasm,Bootloader,我一整天都在想这个问题。我正在学习编写引导加载程序的教程,当它正确地进入保护模式时,我被卡住了。我不知道我做错了什么,我已经在谷歌上搜索了一整天。我正在使用NASM编译 这是我在Bochs dbg中得到的输出: 这是说CPU确实进入了保护模式,我很快就会出错。 这是我所做的第二次重写,主要目的是在没有错误的情况下进入保护模式。我希望有人能告诉我我做错了什么 我的代码如下: bootloader.asm global _start _start: [bits 16] [org 0x7c00]

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

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

这是说CPU确实进入了保护模式,我很快就会出错。 这是我所做的第二次重写,主要目的是在没有错误的情况下进入保护模式。我希望有人能告诉我我做错了什么

我的代码如下:

bootloader.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
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_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
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
io.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
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_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
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
gdt.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
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_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
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
print.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
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_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
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

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

二进制文件有问题。请注意,bochs显示
jmpf 0:7d1d
,而
code_SEG
应该是
8
。这是因为当您仍在16位代码段中时,您将jmp放入
位32
(因为CS显然没有重新加载)。将其上移到
位32
之前。实际上,甚至你的
print.asm
也有一个
bits 32
,因此由于包含,你已经处于32位模式。您需要在
切换到\u pm
之前粘贴
位16
。或者清理你的包裹。好消息是,它的工作原理不同:)你是一个绝对的传奇人物,我甚至没有意识到我把那个(第32位)放在那里了。我在函数后面添加了一个[bits 16]以将其返回到16位模式,并移动了jmp,它的工作方式正是我想要的。我真是太感谢你了!我真不敢相信是这么小的东西