gpt4 book ai didi

assembly - 通过 USB 驱动器启动的自定义引导加载程序在某些计算机上产生不正确的输出

转载 作者:行者123 更新时间:2023-12-04 13:56:58 26 4
gpt4 key购买 nike

我对组装相当陌生,但我正试图深入了解低级计算的世界。我正在尝试学习如何编写作为引导加载程序代码运行的汇编代码;如此独立于任何其他操作系统,如 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]

ORG 指令调整为您需要的内容,或者如果您只需要默认的 0x0000,则省略它。

如果您要修改代码以具有 Unix/Linux 上的布局, file 命令可能能够转储它认为构成磁盘镜像中 VBR 的 BPB 数据。运行命令 file 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)



如何修改此问题中的代码

在这个 OPs 原始代码的情况下,它可以被修改为如下所示:
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

其他建议

正如已经指出的那样 - 你不能 ret 来结束引导加载程序。您可以将其放入无限循环或使用 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 ax, 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,输出应该如下所示:

7C00: EB 3C 90 AA AA AA AA AA
7C08: AA AA AA AA AA AA AA AA
7C10: AA AA AA AA AA AA AA AA
7C18: AA AA AA AA AA AA AA AA
7C20: AA AA AA AA AA AA AA AA
7C28: AA AA AA AA AA AA AA AA
7C30: AA AA AA AA AA AA AA AA
7C38: AA AA AA AA AA AA

关于assembly - 通过 USB 驱动器启动的自定义引导加载程序在某些计算机上产生不正确的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47277702/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com