程序集 32 位保护模式,标签未指向定义的字符串?

Assembly 32 bit-Protected Mode, label not pointing to defined string?

我正在尝试学习一些 x86 程序集。我已经成功地创建了一个带有引导加载程序的 MBR,加载了另一个扇区,切换到保护模式并执行了远跳转到加载的扇区。

使用环境

我在 64 位 Windows 安装上使用 NASM,并使用 nasm -s -f bin bootloader.asm -o test.img 进行组装。我将 test.img 文件作为软盘加载到 VirtualBox VM 中。

问题

我写了一个子程序(代码部分的子程序定义),它使用 lodsb 从 ESI 加载字符,我之前使用 mov esi, _LOW_KERNEL_MESSAGES_TEST 指向一个标签(其中 _LOW_KERNEL_MESSAGES_TEST是定义为 db "Hello, World!", 0) 的标签。但是,这不会打印任何内容。

预期打印第一个字符'H'的mov al, [_LOW_KERNEL_MESSAGES_TEST]也没有(字符打印子例程将AL作为字符输入)

通过mov al, 't'直接将一个字符移动到AL中,然后调用单字符打印子程序就可以了,我也测试了偏移并成功打印了'Test'这个词,问题仅在于打印的“将标签移动到 esi 然后将其加载到 al”部分

我试过的

密码

应以下评论者的要求,我尝试创建一个最小的可重现示例,其代码可在此处找到:

https://pastebin.com/PaeLErDU

这可以通过使用进一步组装:

nasm -s -f bin <name of the code file here>.asm -o floppy.img

通过将此 floppy.img 文件绑定到 VirtualBox 6.1 VM 中的软盘控制器

和运行它,一个完全黑的屏幕出现在左上角的白色光标和虚拟机停止,并显示“发生严重错误”消息,错误日志没有多大意义对我来说可能会有帮助,可以在 https://pastebin.com/g1C2xf7V

找到

我的文件:

bootloader.asm:

%define KERNEL_LOAD_POSITION 0x1000

[bits 16]
[org 0x0600]

;***********************************************************;
;                       ENTRY SECTION                       ;
;***********************************************************;

__ENTRY:    ; ENTRY POINT
    cli          ; Clear Interrupts
    xor ax, ax   ; Zero out AX (set to 0)
    mov ds, ax   ; Set Data Segment to 0 (AX)
    mov es, ax   ; Set Extra Segment to 0 (AX)
    mov ss, ax   ; Set Stack Segment to 0 (AX)
    mov sp, ax   ; Set Stack Pointer to 0 (AX)
__SET__LOWER__ENTRY:
        mov cx, 0x0100   ; 256 WORDs in MBR
        mov si, 0x7C00   ; Current MBR Address
        mov di, 0x0600   ; New MBR Address
        rep movsw        ; Copy MBR
    jmp 0:__LOWER_ENTRY  ; Jump to new Address


__LOWER_ENTRY:
    sti
    mov al, 1
    call _BOOTLOADER_READSECTORS
    call _BOOTLOADER_ENTER_PROTECTED


;***********************************************************;
;                     FUNCTION SECTION                      ;
;***********************************************************;



_BOOTLOADER_READSECTORS:
    pusha
    mov ah, 02h
    mov ch, 0x0
    mov cl, 0x02
    mov dh, 0x0
    mov dl, 0x0
    mov bx, KERNEL_LOAD_POSITION
    int 13h
    jc _BOOTLOADER_HANG
    popa
    ret

_BOOTLOADER_HANG:
    jmp $
    ret

_BOOTLOADER_ENTER_PROTECTED:
    cli
    lgdt [GDTR]
    pusha

    mov eax, cr0
    or al, 1
    mov cr0, eax

    popa
    jmp 0x08:_BOOTLOADER_PERFORM_PROTECTED_FAR_JUMP

[bits 32]

_BOOTLOADER_PERFORM_PROTECTED_FAR_JUMP:
    mov ax, 0x10
    mov ds, ax
    mov ss, ax
    mov fs, ax
    mov es, ax
    mov gs, ax

    jmp KERNEL_LOAD_POSITION


;***********************************************************;
;                        DATA SECTION                       ;
;***********************************************************;



;***********************************************************;
;                        GDT SECTION                        ;
;***********************************************************;

GDT:
GDT_NULL_DESC:
    dd 0            ; null descriptor
    dd 0

GDT_CODE_DESC:
    dw 0xFFFF       ; limit low
    dw 0            ; base low
    db 0            ; base middle
    db 10011010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

GDT_DATA_DESC:
    dw 0xFFFF       ; data descriptor
    dw 0            ; limit low
    db 0            ; base low
    db 10010010b    ; access
    db 11001111b    ; granularity
    db 0            ; base high

GDTR:
    Limit dw 24         ; length of GDT
    Base dd GDT_NULL_DESC   ; base of GDT



;***********************************************************;
;                          PADDING                          ;
;***********************************************************;

times (440 - ($-$$)) db 0      ; Pad For MBR Partition Table


;***********************************************************;
;                      NON-CODE SECTION                     ;
;***********************************************************;


UID: times 4 db 0              ; Unique Disk ID


Reserved: times 2 db 0         ; Reserved, either 0x0000 (normal) or 0x5a5a (readonly)


;***********************************************************;
;                  PARTITION TABLE SECTION                  ;
;***********************************************************;

PT1:                           ; First Partition Entry ( KERNEL HERE )

    db 0x80                     ; Bootable ( Active ) ( 8 bits )

    db 0x00                     ; Starting Head ( Start at head 0 ) ( 8 bits )
    db 0b00000010               ; Starting Sector ( Start at Sector 2 ) ( 6 bits ) NOTE: 2 LSB are for the following cylinder field
    db 0b00000000               ; Starting Cylinder ( Start at Cylinder 0 ) ( 10 bits )

    db 0x21                     ; System ID ( Filysystem ) ( 21, Reserved )

    db 0x00                     ; Ending Head ( End at head 0 ) ( 8 bits )
    db 0b00000100               ; Ending Sector ( End at Sector 4 ) ( 6 bits ) NOTE: 2 LSB are for the following cylinder field
    db 0b00000000               ; Ending Cylinder ( End at Cylinder 0 ) ( 10 bits )

    db 0x00                     ; | Relative sector ( 32 bits )
    db 0x00                     ; |
    db 0x00                     ; |
    db 0x01                     ; |

    db 0x00                     ; | Total sectors in partition ( 32 bits )
    db 0x00                     ; |
    db 0x00                     ; |
    db 0x02                     ; |


PT2: times 16 db 0             ; Second Partition Entry


PT3: times 16 db 0             ; Third Partition Entry


PT4: times 16 db 0             ; Fourth Partition Entry


dw 0xAA55                     ; Magic Word


%define INCLUDE_OFFSET 512

%include "low_kernel.asm"

low_kernel.asm:

[bits 32]

jmp __KERNEL__ENTRY ; JUMP TO KERNEL ENTRY POINT


;***********************************************************;
;                    MACRO/DEFINE SECTION                   ;
;***********************************************************;

%define _LOW_KERNEL_VIDEO_MEMORY_ADDRESS 0xb8000


%define SECTOR_OFFSET INCLUDE_OFFSET

%macro PAD_SECTOR 0
times (512 - ($-$$) + SECTOR_OFFSET) db 0      ; Pad out sector
%define SECTOR_OFFSET SECTOR_OFFSET+512
%endmacro


;***********************************************************;
;                      KERNEL SECTION                       ;
;***********************************************************;

__KERNEL__ENTRY:
    mov esi, _LOW_KERNEL_MESSAGES_TEST
    mov ah, 0x0F
    call _LOW_KERNEL_PRINT

    call _LOW_KERNEL_HANGKERNEL


;; LOW KERNEL SUBROUTINE
; Desc: Prints one character to the designated offset
;
; _IN_ AL ( CHARACTER )
; _IN_ AH ( COLOR )
; _IN_ ECX ( VIDEO MEMORY OFFSET )
_LOW_KERNEL_CHPRINT:
    pushad
    mov ebx, _LOW_KERNEL_VIDEO_MEMORY_ADDRESS
    add ebx, ecx
    mov [ebx], eax
    popad
    ret


;; LOW KERNEL SUBROUTINE
; Desc: Prints strings from ESI
;
; _IN_ AH ( STRING COLOR )
; _IN_ ESI ( STRING ADDRESS )
; _IN_ ECX ( STARTING VIDEO MEMORY OFFSET )
_LOW_KERNEL_PRINT:
    pushad
.internal_loop:
    lodsb
    cmp al, 0
    je .internal_end
    call _LOW_KERNEL_CHPRINT
    add ecx, 0x02
    jmp .internal_loop
.internal_end:
    mov ah, 0x0F
    mov al, 'T'
    call _LOW_KERNEL_CHPRINT
    popad
    ret


_LOW_KERNEL_HANGKERNEL:
    jmp $
    ret


;***********************************************************;
;                   KERNEL DATA SECTION                     ;
;***********************************************************;

_LOW_KERNEL_MESSAGES_TEST:
    db "Hello, World!", 0


PAD_SECTOR

这些是通过『nasm -s -f bin bootloader.asm -o test.img组装的,其.img结果也作为软盘绑定到VM中,通过运行它我得到了一个没有光标的完全黑屏

GDT和far jump是我在网上找的乱七八糟的东西,所以我怀疑是我对它们的了解不足导致了我的问题

请注意,这是我在 Whosebug 上提出的第一个问题,我不了解如何设置问题的格式以使其可读且每个人都能理解。非常欢迎您在评论中 post 提出一些批评,这样我以后可以提出更好的问题。

您将内核(LBA-numbered 扇区 1 及以下)加载到 KERNEL_LOAD_POSITION,即 1000h (4 KiB)。但是,您的内核紧跟在程序集中的引导扇区加载程序之后,因此它的标签被评估为好像内核将被放置在 600h + 512 = 800h (2 KiB)。 call 指令不受此错误影响,因为它们是相对的,即 position-independent。加载位置可以设置为800h

另一种解决方法是使用 NASM 的 multi-section bin 格式。这就是它的样子:

org 600h
section LOADER start=600h

; loader here

times 510 - ($ - $$) db 0 ; pad to where sector's signature goes
dw 0AA55h ; sector signature

section KERNEL follows=LOADER vstart=KERNEL_LOAD_POSITION

; kernel here

即使加载位置设置为 1000h,这也应该可以工作。