程序集 – 通过USB驱动器引导的自定义引导加载程序在某些计算机上产生错误的输出

我对装配很新,但我正试图深入了解低级计算的世界.我正在尝试学习如何编写将作为引导加载程序代码运行的汇编代码;所以独立于任何其他操作系统,如Linux或Windows.在阅读了this page和其他几个x86指令集列表之后,我想出了一些汇编代码,它应该在屏幕上打印10个A,然后是1个B.

      BITS 16
start: 
    mov ax, 07C0h       ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax

    mov cl, 10          ; Use this register as our loop counter
    mov ah, 0Eh         ; This register holds our BIOS instruction

.repeat:
    mov al, 41h         ; Put ASCII 'A' into this register
    int 10h             ; Execute our BIOS print instruction
    cmp cl, 0           ; Find out if we've reached the end of our loop
    dec cl              ; Decrement our loop counter
    jnz .repeat         ; Jump back to the beginning of our loop
    jmp .done           ; Finish the program when our loop is done

.done:
    mov al, 42h         ; Put ASCII 'B' into this register
    int 10h             ; Execute BIOS print instruction
    ret


times 510-($-$$) db 0   ; Pad remainder of boot sector with 0s
dw 0xAA55

所以输出应该如下所示:

AAAAAAAAAAB

我使用在Windows 10 Ubuntu Bash程序上运行的nasm汇编程序汇编代码.在生成.bin文件之后,我使用十六进制编辑器打开它.我使用相同的十六进制编辑器将该.bin文件的内容复制到闪存驱动器的前512个字节中.将程序写入闪存驱动器后,我将其断开并将其插入装有Intel Core i3-7100的计算机.在启动时,我选择了我的USB闪存驱动器作为启动设备,只是为了得到以下输出:

A

在改变程序中的各种东西之后,我终于感到沮丧,并在另一台计算机上尝试了该程序.另一台电脑是i5-2520m的笔记本电脑.我遵循了前面提到的相同过程.果然,它给了我预期的输出:

AAAAAAAAAAB

我立即在i3的原始计算机上尝试过它,但它仍然没有用.

所以我的问题是:为什么我的程序使用一个x86处理器而不是另一个?它们都支持x86指令集.是什么赋予了?

解:
好的,我已经能够通过一些帮助来追踪真正的解决方案.如果您在下面阅读Michael Petch的答案,您将找到解决我的问题的解决方案,以及BIOS寻找BPB的另一个问题.

这是我的代码的问题:我正在将程序写入我的闪存驱动器的第一个字节.这些字节被加载到内存中,但是一些BIOS中断正在使用这些字节.所以我的程序被BIOS覆盖了.为防止这种情况,您可以添加BPB描述,如下所示.如果你的BIOS工作方式与我的相同,它只会覆盖内存中的BPB,而不会覆盖你的程序.或者,您可以将以下代码添加到程序的顶部:

jmp start
resb 0x50

start: 
;enter code here

此代码(由Ross Ridge提供)将您的程序推送到内存位置0x50(偏离0x7c00),以防止它在执行期间被BIOS覆盖.

还要记住,每当调用任何子例程时,您使用的寄存器的值都可能被覆盖.确保在调用子例程之前使用push,pop或将值保存到内存中.请看Martin Rosenau在下面的回答,了解更多相关信息.

感谢所有回复我问题的人.我现在对这种低级别的东西如何运作有了更好的理解.

最佳答案
这可能会成为关于这个主题的规范答案.

真正的硬件/ USB /笔记本电脑问题

如果您尝试使用USB在真实硬件上启动,那么即使您在BOCHS和QEMU中工作,也可能会遇到另一个问题.如果您的BIOS设置为进行USB FDD仿真(而不是USB HDD或其他),则可能需要在引导加载程序的开头添加BIOS Parameter Block(BPB).你可以像这样创建一个假的:

org 0x7c00
bits 16

boot:
    jmp main
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector:    dw    512
    sectPerCluster:    db    1
    reservedSectors:   dw    1
    numFAT:            db    2
    numRootDirEntries: dw    224
    numSectors:        dw    2880
    mediaType:         db    0xf0
    numFATsectors:     dw    9
    sectorsPerTrack:   dw    18
    numHeads:          dw    2
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0x29
    volumeID:          dd    0x2d7e5a1a
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

main:
    [insert your code here]

如果您只需要默认的0x0000,请将ORG指令调整为您需要的值或省略它.

如果您要修改代码以使Unix / Linux文件命令上方的布局可能能够转储它认为构成磁盘映像中MBR的BPB数据.运行命令文件disk.img,您可能会得到以下输出:

disk.img: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID “mkfs.fat”, root entries 224, sectors 2880 (volumes <=32 MB) , sectors/FAT 9, sectors/track 18, serial number 0x2d7e5a1a, unlabeled, FAT (12 bit)

如何修改本课程中的守则

在这个OP原始代码的情况下,它可能已被修改为如下所示:

bits 16

boot:
    jmp main
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector:    dw    512
    sectPerCluster:    db    1
    reservedSectors:   dw    1
    numFAT:            db    2
    numRootDirEntries: dw    224
    numSectors:        dw    2880
    mediaType:         db    0xf0
    numFATsectors:     dw    9
    sectorsPerTrack:   dw    18
    numHeads:          dw    2
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0x29
    volumeID:          dd    0x2d7e5a1a
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

main:
    mov ax, 07C0h       ; Set up 4K stack space after this bootloader
    add ax, 288     ; (4096 + 512) / 16 bytes per paragraph
    mov ss, ax
    mov sp, 4096

    mov ax, 07C0h       ; Set data segment to where we're loaded
    mov ds, ax

    mov cl, 10          ; Use this register as our loop counter
    mov ah, 0Eh         ; This register holds our BIOS instruction

.repeat:
    mov al, 41h         ; Put ASCII 'A' into this register
    int 10h             ; Execute our BIOS print instruction
    cmp cl, 0           ; Find out if we've reached the end of our loop
    dec cl              ; Decrement our loop counter
    jnz .repeat         ; Jump back to the beginning of our loop
    jmp .done           ; Finish the program when our loop is done

.done:
    mov al, 42h         ; Put ASCII 'B' into this register
    int 10h             ; Execute BIOS print instruction
    ret

times 510-($-$$) db 0   ; Pad remainder of boot sector with 0s
dw 0xAA55

其他建议

正如已经指出的那样 – 你不能回到结束引导程序.您可以将其置于无限循环中或使用cli后跟hlt暂停处理器.

如果您在堆栈上分配大量数据或开始写入引导加载程序的512字节以外的数据,则应将自己的堆栈指针(SS:SP)设置为不会干扰您自己的代码的内存区域.此问题中的原始代码确实设置了堆栈指针.这是对阅读此Q / A的其他人的一般观察.我在包含General Bootloader Tips的Stackoverflow答案中有更多相关信息.

测试代码以查看您的BIOS是否覆盖了BPB

如果您想知道BIOS是否可能覆盖BPB中的数据并确定它写入了什么值,您可以使用此引导加载程序代码来转储BPB,因为引导程序在控制转移到它之后会看到它.在正常情况下,前3个字节应该是EB 3C 90,然后是一系列AA.任何非AA的值都可能被BIOS覆盖.此代码在NASM中,可以使用nasm -f bin boot.asm -o boot.bin组装到引导加载程序中

; Simple bootloader that dumps the bytes in the BIOS Parameter
; Block BPB. First 3 bytes should be EB 3C 90. The rest should be 0xAA
; unless you have a BIOS that wrote drive geometry information
; into what it thinks is a BPB.

; Macro to print a character out with char in BX
%macro print_char 1
    mov al, %1
    call bios_print_char
%endmacro

org 0x7c00
bits 16

boot:
    jmp main
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    ; Fake BPB filed with 0xAA
    TIMES 59 DB 0xAA

main:
    xor ax, ax
    mov ds, ax
    mov ss, ax              ; Set stack just below bootloader at 0x0000:0x7c00
    mov sp, boot
    cld                     ; Forward direction for string instructions

    mov si, sp              ; Print bytes from start of bootloader
    mov cx, main-boot       ; Number of bytes in BPB
    mov dx, 8               ; Initialize column counter to 8
                            ;     So first iteration prints address
.tblloop:
    cmp dx, 8               ; Every 8 hex value print CRLF/address/Colon/Space
    jne .procbyte
    print_char 0x0d         ; Print CRLF
    print_char 0x0a
    mov bx, si              ; Print current address
    call print_word_hex
    print_char ':'          ; Print ': '
    print_char ' '
    xor dx, dx              ; Reset column counter to 0
.procbyte:
    lodsb                   ; Get byte to print in AL
    call print_byte_hex     ; Print the byte (in BL) in HEX
    print_char ' '
    inc dx                  ; Increment the column count
    dec cx                  ; Decrement number of  bytes to process
    jnz .tblloop

    cli                     ; Halt processor indefinitely
.end:
    hlt
    jmp .end

; Print the character passed in AL
bios_print_char:
    push bx
    xor bx, bx              ; Attribute=0/Current Video Page=0
    mov ah, 0x0e
    int 0x10                ; Display character
    pop bx
    ret

; Print the 16-bit value in AX as HEX
print_word_hex:
    xchg al, ah             ; Print the high byte first
    call print_byte_hex
    xchg al, ah             ; Print the low byte second
    call print_byte_hex
    ret

; Print lower 8 bits of AL as HEX
print_byte_hex:
    push bx
    push cx
    push ax

    lea bx, [.table]        ; Get translation table address

    ; Translate each nibble to its ASCII equivalent
    mov ah, al              ; Make copy of byte to print
    and al, 0x0f            ;     Isolate lower nibble in AL
    mov cl, 4
    shr ah, cl              ; Isolate the upper nibble in AH
    xlat                    ; Translate lower nibble to ASCII
    xchg ah, al
    xlat                    ; Translate upper nibble to ASCII

    xor bx, bx              ; Attribute=0/Current Video Page=0
    mov ch, ah              ; Make copy of lower nibble
    mov ah, 0x0e
    int 0x10                ; Print the high nibble
    mov al, ch
    int 0x10                ; Print the low nibble

    pop ax
    pop cx
    pop bx
    ret
.table: db "0123456789ABCDEF", 0

; boot signature
TIMES 510-($-$$) db 0
dw 0xAA55

对于在将控制权转移到引导加载程序代码之前未更新BPB的任何BIOS,输出应如下所示:

06003

转载注明原文:程序集 – 通过USB驱动器引导的自定义引导加载程序在某些计算机上产生错误的输出 - 代码日志