Assembly 为什么我的引导程序在添加这一行后崩溃?

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

我正在学习Nick Blundell的引导扇区编程和编程教程。我的代码在qemu模拟器中运行良好,但是当我在物理机器上运行它时,每当我开始引用段寄存器时,它就会崩溃。 我在学校的老师不熟悉低级编程,不能帮助我。这是我的引导加载程序,我用字符串crash注释了导致它崩溃的行:当我说crash时,它实际上只是从下一个磁盘加载我的操作系统。我正在从外部硬盘加载此代码:

[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硬盘。