Assembly 为什么我的引导程序在添加这一行后崩溃?
我正在学习Nick Blundell的引导扇区编程和编程教程。我的代码在qemu模拟器中运行良好,但是当我在物理机器上运行它时,每当我开始引用段寄存器时,它就会崩溃。 我在学校的老师不熟悉低级编程,不能帮助我。这是我的引导加载程序,我用字符串crash注释了导致它崩溃的行:当我说crash时,它实际上只是从下一个磁盘加载我的操作系统。我正在从外部硬盘加载此代码:Assembly 为什么我的引导程序在添加这一行后崩溃?,assembly,x86,bootloader,osdev,Assembly,X86,Bootloader,Osdev,我正在学习Nick Blundell的引导扇区编程和编程教程。我的代码在qemu模拟器中运行良好,但是当我在物理机器上运行它时,每当我开始引用段寄存器时,它就会崩溃。 我在学校的老师不熟悉低级编程,不能帮助我。这是我的引导加载程序,我用字符串crash注释了导致它崩溃的行:当我说crash时,它实际上只是从下一个磁盘加载我的操作系统。我正在从外部硬盘加载此代码: [bits 16] [org 0x7c00] mov bp, 0xffff mov sp, bp mov ax, 0
[bits 16]
[org 0x7c00]
mov bp, 0xffff
mov sp, bp
mov ax, 0x0000
mov ds, ax
;; mov es, ax ;; CRASH
;; mov ss, ax ;; CRASH
mov si, BOOT_MSG
call print_string
call print_newline
mov si, INIT_SEG_MSG
call print_string
call print_newline
;; mov dx, ds ;; CRASH
;; call print_hex
;; call print_newline
;;mov dx, cs ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, es ;; CRASH
;;call print_hex
;;call print_newline
;;mov dx, ss ;; CRASH
;;call print_hex
;;call print_newline
;; mov dl, 0x80 ;; disk where kernel is
;; mov cl, 3 ;; start sect
;; mov al, 1 ;; num sect
;; mov bx, 0x7ef0 ;; RAM addr
;; call load_kernel
;; mov si, KERN_MSG
;; call print_string
;; call print_newline
;; call switch_to_pm
jmp $
%include "print.asm"
%include "print_hex.asm"
%include "disk.asm"
%include "pm.asm"
[bits 32]
pm :
mov esi, PM_MSG
call print_string_pm
jmp 0x7ef0
jmp $
[bits 16]
BOOT_MSG : db 'booted 16-bit to 0x7c00',0
KERN_MSG : db 'loaded kernel to es 0x7ef0',0
PM_MSG : db 'switched to 32-bit mode',0
INIT_SEG_MSG : db 'init segment registers',0
times 510-($-$$) db 0
dw 0xaa55 `
我相信我有一个根本性的误解,任何帮助都将不胜感激。以下是我的打印程序:
print_string :
push ax
_loop :
lodsb
cmp al, 0
je _end
mov ah, 0x0e
int 0x10
jmp _loop
_end :
pop ax
ret
print_hex :
mov si, HEX_TEMPLATE
mov bx, dx
shr bx, 12
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+2], bl
mov bx, dx ;; bx -> 0x1234
shr bx, 8 ;; bx -> 0x0012
and bx, 0x000f ;; bx -> 0x0002
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+3], bl
mov bx, dx
shr bx, 4
and bx, 0x00f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+4], bl
mov bx, dx
and bx, 0x0f
mov bx, [bx+HEXABET]
mov [HEX_TEMPLATE+5], bl
call print_string
ret
HEX_TEMPLATE : db '0x???? ',0
HEXABET : db '0123456789abcdef'
print_newline :
pusha
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov al, 0x0a
int 0x10
popa
ret
BIOS的一个特点是CS的状态未知。在Bochs中,可能Qemu CS=0,因此原点为0x7C00的代码将起作用。真正的硬件可能会通过CS=0x7C0,因此,如果在代码开始时没有适当的跳远,对接近绝对值的函数的调用将偏移0x7C00字节,原点设置为0x7C00 解决方案:
org 0x7C00
jmp 0:Begin ; Far jump so CS = 0
Begin:
mov ax, cs
mov ds, ax
mov es, ax
或
这可能就是正在发生的情况,崩溃即将发生@call print_string,它实际上在寻找代码@0xF8???。BIOS的一个特点是CS的状态未知。在Bochs中,可能Qemu CS=0,因此原点为0x7C00的代码将起作用。真正的硬件可能会通过CS=0x7C0,因此,如果在代码开始时没有适当的跳远,对接近绝对值的函数的调用将偏移0x7C00字节,原点设置为0x7C00 解决方案:
org 0x7C00
jmp 0:Begin ; Far jump so CS = 0
Begin:
mov ax, cs
mov ds, ax
mov es, ax
或
这可能就是正在发生的事情,崩溃即将发生@call print\u string,它实际上在寻找代码@0xF8???。所以在多次硬重置之后,我找到了解决方案。问题是我认为是问题,不是问题。所以我决定把我的闪存驱动器从我爸爸的电脑里拿出来,它工作得很好。所以我继续在我自己的电脑上写我的引导程序。我写了进入PM模式的代码,运行它,完美,没有问题。然后我做了另一个改变。使用dd将字节复制到闪存驱动器,并在我爸爸的pc上运行。但是请稍候。找不到零钱。。。所以我又运行了几次dd,将驱动器归零,但仍然没有结果。所以我重新启动了我的电脑,再次运行DD,它工作了。问题似乎是我的操作系统Ubuntu 16发出dd命令的方式,不确定是否相关。似乎/dev/sdb是一种实际上不会写入磁盘的缓冲区,至少在我第一次卸下闪存驱动器之后是这样。也许这是操作系统中的一个bug。也许是dd中的一个bug,也许我遗漏了什么。谁知道呢。评论中提到的问题是,我不知道Linux块设备是如何工作的。请参阅:。显然,我所做的更改是缓存的,而不是编写的。无论哪种方式,从现在起我都将使用模拟器。谢谢大家 经过多次艰难的重置,我找到了解决方案。问题是我认为是问题,不是问题。所以我决定把我的闪存驱动器从我爸爸的电脑里拿出来,它工作得很好。所以我继续在我自己的电脑上写我的引导程序。我写了进入PM模式的代码,运行它,完美,没有问题。然后我做了另一个改变。使用dd将字节复制到闪存驱动器,并在我爸爸的pc上运行。但是请稍候。找不到零钱。。。所以我又运行了几次dd,将驱动器归零,但仍然没有结果。所以我重新启动了我的电脑,再次运行DD,它工作了。问题似乎是我的操作系统Ubuntu 16发出dd命令的方式,不确定是否相关。似乎/dev/sdb是一种实际上不会写入磁盘的缓冲区,至少在我第一次卸下闪存驱动器之后是这样。也许这是操作系统中的一个bug。也许是dd中的一个bug,也许我遗漏了什么。谁知道呢。评论中提到的问题是,我不知道Linux块设备是如何工作的。请参阅:。显然,我所做的更改是缓存的,而不是编写的。无论哪种方式,从现在起我都将使用模拟器。谢谢大家 mov bp,0xffff mov sp,bp可能存在故障。堆栈由SS:SP指向。你不知道SS设置在哪里。您应该同时设置SS和SP。PS:出于性能原因,堆栈指针不应该是奇数地址。要在引导加载程序下方设置堆栈,可以使用xor ax、ax mov ss、ax mov sp、0x7c00。xor ax,ax将调零ax您可以使用较长的指令mov ax,0x000实际的mov dx,ds类型指令不会自行崩溃。这必须是在mov之后做的其他事情。你真正的硬件上是什么类型的处理器?8086? 80286? 80386?处理器是Intel Atom 32位…我假设仿真器会自动设置我的段寄存器。此外,此代码:xor ax、ax mov ss、ax mov ds、ax mov sp、0x7c00 mov bp、sp jmp$也将导致“崩溃”,模拟器和真实硬件将设置
n SS:BIOS启动期间自身的SP对。但你不能像现在这样改变一半。如果更改SP,则需要更改SS,因为您不知道BIOS对SS使用了什么值。物理内存由段*16+偏移量计算。据您所知,堆栈可能指向内存中某个潜在的不可写入区域。幸运的是,代码可以正常工作。mov bp,0xffff mov sp,bp可能是坏的。堆栈由SS:SP指向。你不知道SS设置在哪里。您应该同时设置SS和SP。PS:出于性能原因,堆栈指针不应该是奇数地址。要在引导加载程序下方设置堆栈,可以使用xor ax、ax mov ss、ax mov sp、0x7c00。xor ax,ax将调零ax您可以使用较长的指令mov ax,0x000实际的mov dx,ds类型指令不会自行崩溃。这必须是在mov之后做的其他事情。你真正的硬件上是什么类型的处理器?8086? 80286? 80386?处理器是Intel Atom 32位…我假设仿真器会自动设置我的段寄存器。此外,此代码:xor ax、ax mov ss、ax mov ds、ax mov sp、0x7c00 mov bp、sp jmp$也会导致“崩溃”,模拟器和真实硬件会在BIOS启动期间为自己设置ss:sp对。但你不能像现在这样改变一半。如果更改SP,则需要更改SS,因为您不知道BIOS对SS使用了什么值。物理内存由段*16+偏移量计算。据您所知,堆栈可能指向内存中某个潜在的不可写入区域。你可能会很幸运,代码能正常工作。他确实用mov ax,0x0000 mov DS,ax将DS设置为零。CS只需在接近绝对跳跃或使用跳跃表时正确设置。这两件事他都没有做。只有在需要设置ES时才需要设置ES。显示的代码在崩溃时不需要ES,尽管磁盘读取中断需要ES。因为他已经把所有的电话都注释掉了,所以很难说他用的是什么,当它不起作用的时候。它包括磁盘读取代码吗?我不知道。SS:SP也是另一个问题。如果有两件事很突出的话,那就是SS:SP和ES可能是罪魁祸首。近乎绝对的跳跃也适用于此类呼叫。代码中没有一个使用这种类型。还有一个缺失的地方是对正确设置方向标志的依赖。他应该明确地使用CLD来确定前进的方向,因为OP使用LODSB的假设是这样的。我已经将代码简化为只设置内存段,因为现在已经确认这是崩溃的根源。。。下面的[bits 16][org 0x7c00]jmp 0:begin begin:mov ax,cs mov ss,ax mov ds,ax jmp$乘以510-$-$$db 0 dw 0xaa55仍然会导致计算机从下一个磁盘引导操作系统…这似乎是BIOS问题,因为它无法识别软盘、USB或其他介质。您必须中断引导过程,以便选择设备或更改设置中的设备优先级。他使用mov ax、0x0000 mov DS、ax将DS设置为零。CS只需在接近绝对跳跃或使用跳跃表时正确设置。这两件事他都没有做。只有在需要设置ES时才需要设置ES。显示的代码在崩溃时不需要ES,尽管磁盘读取中断需要ES。因为他已经把所有的电话都注释掉了,所以很难说他用的是什么,当它不起作用的时候。它包括磁盘读取代码吗?我不知道。SS:SP也是另一个问题。如果有两件事很突出的话,那就是SS:SP和ES可能是罪魁祸首。近乎绝对的跳跃也适用于此类呼叫。代码中没有一个使用这种类型。还有一个缺失的地方是对正确设置方向标志的依赖。他应该明确地使用CLD来确定前进的方向,因为OP使用LODSB的假设是这样的。我已经将代码简化为只设置内存段,因为现在已经确认这是崩溃的根源。。。下面的[bits 16][org 0x7c00]jmp 0:begin begin:mov ax,cs mov ss,ax mov ds,ax jmp$乘以510-$-$$db 0 dw 0xaa55仍然会导致计算机从下一个磁盘引导操作系统…这似乎是BIOS问题,因为它无法识别软盘、USB或其他介质。您必须中断启动过程,以便在设置中选择设备或更改设备优先级。在拉出USB驱动器之前,您是否已选择或弹出/dev/sdb?默认情况下,Linux块设备不同步。另外,您确定重新插入所需的驱动器时它在sdb下吗?查看/dev/disk/by-id和/或其他任何目录。是的,只有在真实硬件在模拟硬件上工作后,才能在真实硬件上进行测试。如果你只是为了好玩才这么做的话,那就完全不是了。从理论上讲,最好知道
当BIOS跳转到您的代码时,您对事情状态的假设是什么,以确保您对所有的段和寄存器都有一个可靠的处理。我假设BIOS将设置es、ds和ss fs和gs?去记忆中某个自由的地方。打印十六进制值[0x7c00]将打印我的代码的第一个字节。cs是否设置为0x7c00?由于BIOS IVT是0x0000,我假设尝试在这里写入是非法的……现在除了几十年前的一些设备外,唯一可以指望的是DL将引导驱动器号传递给它。至于其他所有内容,包括所有段寄存器,都没有假设它们包含特定值。我在我的旁注中介绍了这一点,我使用UDisk分离任意类型的USB介质。有一个合理的解释。它也适用于USB硬盘。在拔出USB硬盘之前,您是否已启动或弹出/dev/sdb?默认情况下,Linux块设备不同步。另外,您确定重新插入所需的驱动器时它在sdb下吗?查看/dev/disk/by-id和/或其他任何目录。是的,只有在真实硬件在模拟硬件上工作后,才能在真实硬件上进行测试。如果你只是为了好玩才这么做的话,那就完全不是了。理论上,当BIOS跳转到您的代码时,最好了解您对事物状态的假设,以确保您对所有的段和寄存器都有一个可靠的处理。我假设BIOS将设置es、ds和ss fs和gs?去记忆中某个自由的地方。打印十六进制值[0x7c00]将打印我的代码的第一个字节。cs是否设置为0x7c00?由于BIOS IVT是0x0000,我假设尝试在这里写入是非法的……现在除了几十年前的一些设备外,唯一可以指望的是DL将引导驱动器号传递给它。至于其他所有内容,包括所有段寄存器,都没有假设它们包含特定值。我在我的旁注中介绍了这一点,我使用UDisk分离任意类型的USB介质。有一个合理的解释。它也适用于USB硬盘。