如何将扇区读入内存并跳转到操作系统?

Tec*_*ech 1 assembly nasm osdev bootloader x86-16

我知道这个问题被问了很多,但我找到的每个答案都不适合我。我正在尝试加载stage 2位于图像文件第二个扇区的操作系统(0x200

这是我尝试使用的代码:


bits 16                             ; Starting at 16 bits
org 0x0                               ; And starting at 0

jmp main                            ; Hop to main!


; TODO: copy comment from prev. loader
; args: SI
print:
    lodsb                           ; Load the next/first character to AL
    or al, al                       ; Is it 0?
    jz donePrint                    ; Yes - Done.
    mov ah, 0eh                     ; No - keep going.
    int 10h                         ; Print character.
    jmp print                       ; Repeat
donePrint:
    ret                             ; Return


; todo: args
readSector:
    mov ah, 02h
    mov al, 1
    mov dl, 0x80
    mov ch, 0
    mov dh, 0
    mov cl, 2

    mov bx, 0x500


    int 13h
    jnc good
    jmp fail

main:
    ; First, setup some registers.
    cli                             ; Clear interrupts
    mov ax, 0x07C0                  ; Point all registers to segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; Create the stack(0x0000-0xFFFF).
    mov ax, 0x0000
    mov ss, ax                      ; Point SS to 0x0000
    mov sp, 0xFFFF                  ; Stack pointer at 0xFFFF
    sti                             ; Restore interrupts

    mov si, LOADING
    call print

    call readSector

    

    
fail:
    mov si, FAILURE_MSG
    call print
    
good:
    mov si, LOADOK 
    call print
    jmp 0x500

LOADING        db 0x0D, 0x0A, "Booting loader...", 0x0D, 0x0A, 0x00
FAILURE_MSG    db 0x0D, 0x0A, "ERROR: Press any key to reboot.", 0x0A, 0x00
LOADOK    db 0x0D, 0x0A, "load ok", 0x0A, 0x00



TIMES 510 - ($-$$) DB 0
DW 0xAA55
Run Code Online (Sandbox Code Playgroud)

但它只是引导循环。我尝试了其他解决方案但无济于事。我究竟做错了什么?如果我需要更新问题,请告诉我。

谢谢你!

编辑#1:根据 Sep Roland 的回答,我更新了我的代码,但它仍然无法正常工作。如果有任何帮助,我将更新的代码放在这里。另外,如果需要,我可以发布我的第二阶段代码。它应该使用 0x500 作为 org. 新代码:

bits 16                             ; Starting at 16 bits
org 0x0                             ; And starting at 0

jmp main                            ; Hop to main!


; TODO: copy comment from prev. loader
; args: SI
print:
    lodsb                           ; Load the next/first character to AL
    or al, al                       ; Is it 0?
    jz donePrint                    ; Yes - Done.
    mov ah, 0eh                     ; No - keep going.
    int 10h                         ; Print character.
    jmp print                       ; Repeat
donePrint:
    ret                             ; Return


; todo: args
readSector:
    mov ah, 02h
    mov al, 1
    mov ch, 0
    mov dh, 0
    mov cl, 2

    mov bx, 0x500


    int 13h
    jnc good
    jmp fail

main:
    ; First, setup some registers.
    cli                             ; Clear interrupts
    mov ax, 0x07C0                  ; Point all registers to segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; Create the stack(0x0000-0xFFFF).
    mov ax, 0x0000
    mov ss, ax                      ; Point SS to 0x0000
    mov sp, 0xFFFE                  ; Stack pointer at 0xFFFE
    sti                             ; Restore interrupts

    mov si, LOADING
    call print

    call readSector

    

    
fail:
    mov si, FAILURE_MSG
    call print
end:
    cli
    hlt
    jmp end
    
good:
    mov si, LOADOK 
    call print
    jmp 0x07C0:0x0500

LOADING        db 0x0D, 0x0A, "Booting loader...", 0x0D, 0x0A, 0x00
FAILURE_MSG    db 0x0D, 0x0A, "ERROR: Press any key to reboot.", 0x0A, 0x00
LOADOK    db 0x0D, 0x0A, "load ok", 0x0A, 0x00



TIMES 510 - ($-$$) DB 0
DW 0xAA55
Run Code Online (Sandbox Code Playgroud)

编辑#2:发布第二阶段代码,包括gdt.inc因为提到的某人LGDT可能导致了问题:

主要代码(某些部分已被删除,但它们不是必需的,例如字符串)

bits 16                                 ; We start at 16 bits

org 0x500                               ; We are loaded in at 0x500

jmp main                                ; Jump to main code.


; ----------------------------------------
; Includes
; ----------------------------------------
%include "include/stdio.inc"
%include "include/gdt.inc"
%include "include/a20.inc"


; ---------------------------------------
; Data and strings
; ---------------------------------------

stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00
stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00

; ---------------------------------------------------------------------
; main - 16-bit entry point
; Installing GDT, storing BIOS info, and enabling protected mode
; ---------------------------------------------------------------------

main:
    ; Our goal is jump to main32 to become 32-bit

    ; Setup segments and stack
    cli                                 ; Clear interrupts
    xor ax, ax                          ; Null segments AX, DS, and ES
    mov ds, ax
    mov es, ax
    mov ax, 0x9000                      ; Stack begins at 0x9000-0xFFFF
    mov ss, ax
    mov sp, 0xFFFF                      ; Stack pointer is 0xFFFF
    sti                                 ; Enable interrupts

    

    ; Install the GDT
    call installGDT                     ; Install the GDT

    ; Enable A20
    call enableA20_KKbrd_Out            ; Enable A20 through output port

    ; Print loading messages
    mov si, msg1
    call print16                        ; Print the message

    mov si, msg2                        ; A message
    call print16                        ; Print the message

    ; Enter protected mode
    cli                                 ; Clear interrupts
    mov eax, cr0                        ; Set bit 0 in CR0--ENTER protected mode
    or eax, 1
    mov cr0, eax

    jmp CODE_DESC:main32                ; Far jump to fix CS
    
    ; We can't re-enable interrupts because that would triple-fault. This will be fixed in main32.


bits 32                                 ; We are now 32 bit!

%include "include/stdio32.inc"

main32:
    ; Set registers up
    mov ax, 0x10                        ; Setup data segments to 0x10(data selector)
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov esp, 90000h                     ; Stack begins from 90000h
    
    call clear32                        ; Clear screen
    mov ebx, MSGHIDDEN                   ; Setup params for our message
    call puts32                         ; Call puts32 to print

    cli                                 ; Clear interrupts
    hlt                                 ; Halt the processor
Run Code Online (Sandbox Code Playgroud)

LGD 代码:

%ifndef __GDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __GDT_INC_67343546FDCC56AAB872_INCLUDED__

bits 16                     ; We are in 16-bit mode


; -----------------------------------------
; installGDT - install the GDT
; -----------------------------------------
installGDT:
    cli                     ; Clear interrupts
    pusha                   ; Save the registers
    lgdt [toc]              ; Load GDT into GDTR
    sti                     ; Re-enable interrupts
    popa                    ; Restore registers
    ret                     ; Return!


; ----------------------------------------
; Global Descriptor Table data
; ----------------------------------------

gdt_data:
    dd 0                    ; Null descriptor
    dd 0
    
    ; GDT code starts here
    dw 0FFFFh               ; Limit low
    dw 0                    ; Base low
    db 0                    ; Base middle
    db 10011010b            ; Access
    db 11001111b            ; Granularity
    db 0                    ; Base high

    ; GDT data starts here(mostly same as code, only difference is access)
    dw 0FFFFh               ; Limit low, again.
    dw 0                    ; Base low
    db 0                    ; Base middle
    db 10010010b            ; Access - different
    db 11001111b            ; Granularity
    db 0

gdt_end:
toc:
    dw gdt_end - gdt_data - 1
    dd gdt_data             ; Base of GDT

; Descriptor offsets names

%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10

; End of GDT code.
%endif ;__GDT_INC_67343546FDCC56AAB872_INCLUDED__
Run Code Online (Sandbox Code Playgroud)

编辑 #3:stdio 和 stdio32 可能存在问题,因此将它们放在这里

stdio.inc:

; ==============================================
; stdio.inc - IO routines
; Thanks to BrokenThorn Entertainment
; ==============================================

; First, show that we are defining stdio.inc
%ifndef __STDIO_INC_67343546FDCC56AAB872_INCLUDED__
%define __STDIO_INC_67343546FDCC56AAB872_INCLUDED__

; ------------------------------------------------
; Print16 - printing a null terminated string
; SI - 0 terminated string
; ------------------------------------------------

print16:
    pusha                                       ; Save registers for later
.loop1:
    lodsb                                       ; Load the next byte from the string into AL
    or al, al                                   ; Is AL 0?
    jz print16done                              ; Yes - we are done.
    mov ah, 0eh                                 ; No - print next character
    int 10h                                     ; Call BIOS
    jmp .loop1                                  ; Repeat!
print16done:
    popa                                        ; Restore registers
    ret                                         ; Return



%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__
Run Code Online (Sandbox Code Playgroud)

stdio32.inc:

; ==================================================
; stdio32.inc - Handles 32-bit graphics
; ==================================================

%ifndef __GFX_INC_67343546FDCC56AAB872_INCLUDED__
%define __GFX_INC_67343546FDCC56AAB872_INCLUDED__

bits 32                                         ; 32-bits

%define VIDEO_MEMORY 0xB8000                    ; Video memory address
%define COLS 80                                 ; Width of the screen
%define LINES 25                                ; Height of the string
%define CHARACTER_ATTRIBURE 63                  ; White text on Cyan background

_CurrentXPos db 0
_CurrentYPos db 0

; ---------------------------------------------------------
; char32 - Print a character to the screen(32-bit)
;   BL - Character to print
; ---------------------------------------------------------

char32:
    pusha                                       ; Save registers
    mov edi, VIDEO_MEMORY                       ; Get the pointer to the video memory

    ; Get current position
    xor eax, eax                                ; Zero-out EAX

    mov ecx, COLS*2                             ; Mode 7 has 2 bytes per character - and so COLS*2 bytes per line.
    mov al, byte [_CurrentYPos]                 ; Get Y position
    mul ecx                                     ; Multiply COLS * Y
    push eax                                    ; Save EAX--the multiplication

    mov al, byte [_CurrentXPos]                 ; Multiply _CurrentXPos by 2 because 2 bytes per char(Mode 7)
    mov cl, 2
    mul cl
    pop ecx                                     ; Pop Y*COLS result
    add eax, ecx
    
    xor ecx, ecx
    add edi, eax                                ; Add to base address

    ; Watch for a new line!
    cmp bl, 0x0A                                ; 0x0A - newline character.
    je .row                                     ; Jump to .row if newline char

    ; Print the character
    mov dl, bl                                  ; Get character
    mov dh, CHARACTER_ATTRIBURE                 ; Change DH to Character Attribute
    mov word [edi], dx                          ; Write to video memory
    
    ; Update next pos
    inc byte [_CurrentXPos]                     ; Go to next character
    ;cmp byte [_CurrentXPos], COLS               ; EOL?
    ;je .row                                     ; Yep - move to next row
    jmp .done                                   ; Nope - BAIL!
.row:
    ; Goto next row.
    mov byte [_CurrentXPos], 0                  ; Return to col 0
    inc byte [_CurrentYPos]                     ; Go to next row.

.done:
    ; Return
    popa
    ret


; ---------------------------------------------------------
; puts32 - print a null terminated string
;   EBX - String to print
; ---------------------------------------------------------

puts32:

    ; Store registers(EBX and EDI)
    pusha                                       ; Save registers
    push ebx                                    ; Copy string
    pop edi

.loop:
    
    mov bl, byte [edi]                          ; Get next character
    cmp bl, 0                                   ; Check if it's null
    je .done                                    ; It is - done printing.

    call char32                                 ; It isn't - print the character

    inc edi                                     ; Increment EDI for next character
    jmp .loop                                   ; Restart loop

.done:
    ; Update the hardware cursor

    mov bh, byte [_CurrentXPos]                 ; BH and BL are the params for movecursor
    mov bl, byte [_CurrentYPos]                 
    call movecursor                             ; Update cursor position

    popa                                        ; Restore registers
    ret                                         ; Return!

bits 32

; ---------------------------------------------------------
; movecursor - Move the cursor to an X and Y position
;   BH - X position
;   BL - Y position
; ---------------------------------------------------------

movecursor:
    
    pusha                                       ; Save registers

    ; Get current position(BH and BL are relative to the current position on screen, not memory)

    xor eax, eax                                ; Clear EAX
    mov ecx, COLS                               ; Store COLS in ECX for multiplication
    mov al, bh                                  ; Get Y position
    mul ecx                                     ; Multiply Y by cols
    add al, bl                                  ; Add X
    mov ebx, eax

    ; Set low byte index to VGA register

    mov al, 0x0f
    mov dx, 0x03D4
    out dx, al

    mov al, bl
    mov dx, 0x03D5
    out dx, al

    ; Do the same but for high byte

    xor eax, eax
    
    mov al, 0x0e
    mov dx, 0x03D4
    out dx, al

    mov al, bl
    mov dx, 0x03D5
    out dx, al

    popa                                        ; Restore registers
    ret                                         ; Return



; ---------------------------------------------------------
; clear32 - clearing the screen
; ---------------------------------------------------------

clear32:
    pusha                                       ; Save registers

    cld
    mov edi, VIDEO_MEMORY                       ; Set EDI to video memory
    mov cx, 2000                                
    mov ah, CHARACTER_ATTRIBURE                 ; Clear screen with character attribute
    mov al, ' '                                 ; Replace all chars with space
    rep stosw

    mov byte [_CurrentXPos], 0                  ; Reset X and Y position
    mov byte [_CurrentYPos], 0
    popa                                        ; Restore registers
    ret

    

; ---------------------------------------------------------
; gotoxy - Set X and Y position
;   AL - X position
;   AH - Y position
; ---------------------------------------------------------

gotoxy:
    pusha
    mov [_CurrentXPos], al                      ; Set X and Y position
    mov [_CurrentYPos], ah                      
    popa
    ret

%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__
Run Code Online (Sandbox Code Playgroud)

Sep*_*and 8

EDIT0 评论第一阶段
EDIT1 评论第二阶段
EDIT2 评论包含的stdio32.inc

[编辑0]

您自己加载的扇区是在 0x7C0 的额外段中的偏移 0x500 处加载的。
jmp 0x500指令跳转到代码段中的偏移量0x500。
无法保证 CS==0x7C0。使用远跳代替:

jmp 0x07C0:0x0500
Run Code Online (Sandbox Code Playgroud)
mov dl, 0x80
Run Code Online (Sandbox Code Playgroud)

您确定这个驱动器号吗?当引导加载程序获得控制权时,最好使用 BIOS 在 DL 寄存器中提供的值。


mov sp, 0xFFFF
Run Code Online (Sandbox Code Playgroud)

字对齐的堆栈指针会好得多!

使用mov sp, 0xFFFE或什至xor sp, sp(信任环绕)。


fail:
  mov si, FAILURE_MSG
  call print

good:
  mov si, LOADOK 
  call print
  jmp 0x500
Run Code Online (Sandbox Code Playgroud)

如果加载扇区失败,您会跳转到failed但在显示消息后,您会愉快地继续(失败)使用good 的代码。你需要停止:

fail:
    mov  si, FAILURE_MSG
    call print
theEnd:
    cli
    hlt
    jmp  theEnd
good:
    mov  si, LOADOK
    call print
    jmp  0x07C0:0x0500
Run Code Online (Sandbox Code Playgroud)

[编辑1]

xor ax, ax   ; Null segments AX, DS, and ES
mov ds, ax
mov es, ax
Run Code Online (Sandbox Code Playgroud)

您已对第一阶段引导加载程序应用了更正。控制权已成功传递至 0x07C0:0x0500。由于第二阶段使用org 0x500段寄存器(至少 DS)保持在 0x07C0,这一点至关重要。但我看到的第一件事是你用 0 重新加载 DS 和 ES。这不会起作用,因为它会在汇编器生成的偏移量(根据 )org和实际的偏移量(相对 DS)之间产生不匹配。数据驻留。

这种不匹配的第一个表现是指令lgdt [toc]
第二阶段的所有数据都驻留在内存中 0x8100 标记 (0x7C00 + 0x0500) 上方。另一方面,toc
标签将被汇编器转换为略高于 0x0500 的偏移地址。当 DS=0 时,该地址的内存地址远低于 0x8100 标记。根本没有有效的数据可以采取行动,因此崩溃(或类似)!

问题不仅仅在于指令lgdt [toc]mov si, msg1mov si, msg2将以同样的方式失败,并且mov ebx, MSGHIDDEN和根本dd gdt_data不是线性地址也会失败。dw gdt_end - gdt_data - 1不会受到损害,因为当两个量值都错误时(以相同的方式),差异不会改变。

您应该做的是将段寄存器保持在 0x07C0 与 一致org 0x0500,或者更好地将第一阶段引导加载程序中已有的段寄存器清零,并org 0x7C00为第一阶段使用 an ,org 0x8100为第二阶段使用 an 。这些设置会将所有内容保留在内存中的同一位置,第一阶段和第二阶段之间有 768 字节的间隙,这是我们每天都不会看到的。
然而,更优选的方法是将第一级引导加载程序中已有的段寄存器清零,并org 0x7C00为第一级使用 an,org 0x0600为第二级使用 an。这将第二阶段置于低内存 BIOS 变量之后。MS-DOS 是我们很好的例子。

进一步审查

installGDT:
    cli                     ; Clear interrupts
    pusha                   ; Save the registers
    lgdt [toc]              ; Load GDT into GDTR
    sti                     ; Re-enable interrupts
    popa                    ; Restore registers
    ret                     ; Return!
Run Code Online (Sandbox Code Playgroud)

在这段代码中保留通用寄存器是多余的。该lgdt指令不会改变其中任何一个。


; Create the stack(0x0000-0xFFFF).
mov ax, 0x0000
mov ss, ax        ; Point SS to 0x0000
mov sp, 0xFFFE    ; Stack pointer at 0xFFFE
Run Code Online (Sandbox Code Playgroud)
mov ax, 0x9000    ; Stack begins at 0x9000-0xFFFF
mov ss, ax
mov sp, 0xFFFF    ; Stack pointer is 0xFFFF
Run Code Online (Sandbox Code Playgroud)
mov ax, 0x10      ; Setup data segments to 0x10(data selector)
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h   ; Stack begins from 90000h
Run Code Online (Sandbox Code Playgroud)

(1) SP 请勿使用奇数。
(2) 加载SS 和ESP 之间不要放置任何东西。
(3) 不要对堆栈的低端和高端都使用“begin”一词。

您将设置堆栈 3 次,每次都在不同的位置和大小!第一次从0x00000000运行到0x0000FFFD,第二次从0x00090000运行到0x0009FFFE,第三次从0x00000000运行到0x0008FFFF。
我建议设置堆栈,使实模式地址对应于保护模式地址。至少对于顶部 64KB 而言。
在第一阶段使用:

mov  ax, 0x8000
mov  ss, ax
xor  sp, sp           ; 0x00080000 - 0x0008FFFF (64KB)
Run Code Online (Sandbox Code Playgroud)

在第二阶段使用:

mov  ax, DATA_DESC
mov  ss, ax
mov  esp, 0x00090000  ; 0x00000000 - 0x0008FFFF (576KB)
Run Code Online (Sandbox Code Playgroud)

第一阶段

bits 16
org  0x7C00

jmp  main

; args: SI
print:
    lodsb
    or   al, al
    jz   donePrint
    mov  bx, 0007h
    mov  ah, 0Eh
    int  10h
    jmp  print
donePrint:
    ret

main:
    cli
    xor  ax, ax
    mov  ds, ax
    mov  es, ax
    mov  fs, ax
    mov  gs, ax
    mov  ax, 0x8000    ; Stack between 0x8000:0x0000
    mov  ss, ax        ;           and 0x8000:0xFFFF (64KB)
    xor  sp, sp
    sti

    mov  si, LOADING
    call print

readSector:
    mov  dh, 0
    mov  cx, 0002h
    mov  bx, 0x0600     ; Sector buffer at 0x0000:0x0600
    mov  ax, 0201h
    int  13h
    jnc  good
    
fail:
    mov  si, FAILURE_MSG
    call print
end:
    cli
    hlt
    jmp  end
    
good:
    mov  si, LOADOK 
    call print
    jmp  0x0000:0x0600  ; Start second stage

LOADING     db 13, 10, "Booting loader...", 13, 10, 0
FAILURE_MSG db 13, 10, "ERROR: Press any key to reboot.", 10, 0
LOADOK      db 13, 10, "load ok", 10, 0

TIMES 510 - ($-$$) DB 0
DW 0xAA55
Run Code Online (Sandbox Code Playgroud)

第二阶段

bits 16
org  0x0600

jmp  main
; ---------------------------------------
; Includes
; ---------------------------------------
%include "include/stdio.inc"
%include "include/gdt.inc"
%include "include/a20.inc"
; ---------------------------------------
; Data and strings
; ---------------------------------------
stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00
stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00
; ---------------------------------------
; main - 16-bit entry point
; Installing GDT, storing BIOS info, and enabling protected mode
; ---------------------------------------

main:
    call installGDT
    call enableA20_KKbrd_Out
    mov  si, msg1
    call print16
    mov  si, msg2
    call print16

    ; Enter protected mode
    cli
    mov  eax, cr0
    or   eax, 1
    mov  cr0, eax

    jmp  CODE_DESC:main32 ; Far jump to fix CS
    
    ; We can't re-enable interrupts because that would triple-fault. This will be fixed in main32.


bits 32                   ; We are now 32 bit!

%include "include/stdio32.inc"

main32:
    ; Set registers up
    mov  ax, DATA_DESC
    mov  ds, ax
    mov  es, ax
    mov  fs, ax
    mov  gs, ax
    mov  ss, ax           ; Stack between 0x00000000
    mov  esp, 0x00090000  ;           and 0x0008FFFF (576KB)

    call clear32          ; Clear screen
    mov  ebx, MSGHIDDEN
    call puts32           ; Call puts32 to print

    cli
    hlt

    ...
Run Code Online (Sandbox Code Playgroud)

[编辑2]

@Sep Roland您的更改大部分有效,但不幸的是我的视频代码也可能有问题。系统似乎在一点点(24字节)后停止输入并放弃。我发布了视频代码,但如果这看起来要求太多,我可以到此为止。谢谢你!

我检查了您的stdio32.inc,发现其中有许多错误!

  • char32代码在注释中提到“;模式 7 每个字符有 2 个字节...”如果您确实正在使用单色视频模式 7,则 VIDEO_MEMORY 应设置为 0xB0000 而不是 0xB8000。
  • Clear32代码在 CX 中移动 2000,但rep stosw将使用 ECX。ECX高位字中的垃圾会造成很大的危害。使用mov ecx, 2000
  • movecursor代码在BH中接收X,在BL中接收Y,这与我的预期相反,您正在计算X * 80 + Y,它需要是Y * 80 + X,上述加法使用了错误的大小,并且out两倍低字节,而不是低字节然后高字节。

改进的stdio32.inc

; ==================================================
; stdio32.inc - Handles 32-bit graphics
; ==================================================

%ifndef __GFX_INC_67343546FDCC56AAB872_INCLUDED__
%define __GFX_INC_67343546FDCC56AAB872_INCLUDED__

bits 32

%define VIDEO_MEMORY 0xB8000  ; Video memory address
%define COLS 80               ; Width of the screen
%define LINES 25              ; Height of the screen
%define ATTRIB 0x3F           ; WhiteOnCyan
_CurrentXPos db 0
_CurrentYPos db 0

; ---------------------------------------------------------
; char32 - Print a character to the screen(32-bit)
;   BL - Character to print
; ---------------------------------------------------------

char32:
    cmp   bl, 10
    je    .row
    push  eax
    push  edi
    movzx edi, byte [_CurrentYPos]
    imul  edi, COLS*2
    movzx eax, byte [_CurrentXPos]
    lea   edi, [VIDEO_MEMORY + edi + eax * 2]
    mov   al, bl
    mov   ah, ATTRIB
    mov   [edi], ax
    pop   edi
    pop   eax
    inc   byte [_CurrentXPos]
    cmp   byte [_CurrentXPos], COLS               ; EOL?
    je    .row
    ret
.row:
    mov   byte [_CurrentXPos], 0
    inc   byte [_CurrentYPos]
    ret

; ---------------------------------------------------------
; puts32 - print a null terminated string
;   EBX - String to print
; ---------------------------------------------------------

puts32:

    push  ebx
    push  edi
    mov   edi, ebx
    jmp   .start
.loop:
    call  char32
    inc   edi
.start:
    mov   bl, [edi]
    test  bl, bl
    jnz   .loop
    movzx ebx, word [_CurrentXPos] ; Load XPos and YPos together!
    call  movecursor               ; Update hardware cursor
    pop   edi
    pop   ebx
    ret

; ---------------------------------------------------------
; movecursor - Move the cursor to an X and Y position
;   BL - X position
;   BH - Y position
;   BH and BL are relative to the current position on screen
; ---------------------------------------------------------

movecursor:
    pushad
    movzx eax, bh             ; BH * COLS + BL
    imul  eax, COLS
    movzx ebx, bl
    add   ebx, eax
    ; Set low byte index to VGA register
    mov   al, 0x0F
    mov   dx, 0x03D4
    out   dx, al
    mov   al, bl
    inc   dx
    out   dx, al
    ; Do the same but for high byte
    mov   al, 0x0E
    dec   dx
    out   dx, al
    mov   al, bh
    inc   dx
    out   dx, al
    popad
    ret

; ---------------------------------------------------------
; clear32 - clearing the screen
; ---------------------------------------------------------

clear32:
    pushad
    mov   edi, VIDEO_MEMORY
    mov   ecx, 1000            ; 2000 words
    mov   eax, ((ATTRIB * 256 + 32) * 256 + ATTRIB) * 256 + 32
    rep stosd
    mov   [_CurrentXPos], cx   ; Reset XPos and YPos together!
    popad
    ret

; ---------------------------------------------------------
; gotoxy - Set X and Y position
;   AL - X position
;   AH - Y position
; ---------------------------------------------------------

gotoxy:
    mov   [_CurrentXPos], ax   ; Set XPos and YPos together!
    ret

%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__
Run Code Online (Sandbox Code Playgroud)

在优化代码的同时,代码也变短了很多
我现在的想法是,您的代码已超过 512 字节,并且第一阶段的单扇区加载并未将其全部带入内存。这当然可以解释您所遇到的一些部分字符串输出。

我不记得了(我的手册也没有提到),但是对于 NASM 来说与激活时pusha相同? 我是这样处理的:pushadbits 32

  • 始终pushad使用 32 位代码编写。
  • 避免pushad并倾向于使用push单独的寄存器,因为这样更快。

  • 作为实践问题,我发现最好通过跳转到“07c0:xxxx”或“0000:7cxx”来启动引导块,并相应地设置所有其他段寄存器。不同的 BIOS 总是会做不同的奇怪的事情,你永远无法假设你所得到的是什么。(就此而言,过去有一些 BIOS 甚至没有正确设置“dl”) (2认同)
  • @sj95126:不设置 CS 只会影响一小部分使用间接近 CALL/JMP 到绝对内存偏移量的指令。这在引导加载程序中并不常见。如果您知道自己没有使用任何此类指令,那么不设置 CS 是安全的,特别是当您的引导加载程序空间不足时。如果空间不是问题,那么显式设置 CS 根本就不是问题。我从其他网站上提出了各种问题,并将它们合并到此处的自我问答中:/sf/ask/2418382781/ (2认同)