【问题标题】:Hello World bootloader not workingHello World 引导加载程序不工作
【发布时间】:2010-04-13 15:34:56
【问题描述】:

我一直在学习on this webpage 教程,它逐步创建了一个显示 Hello World 的引导加载程序。

第二个教程(我们尝试输出“A”)完美运行,但第一个教程对我根本不起作用! (BIOS 完全忽略软盘并直接引导至 Windows)。这不是一个问题,尽管任何解释都将不胜感激。

真正的问题是我无法让第三个教程工作。在输出“Hello World”时,我在屏幕的左下角看到了一个不寻常的字符(和闪烁的光标)。它看起来有点像圆角矩形内的笑脸。有谁知道如何让 Hello World 正常显示?

【问题讨论】:

    标签: assembly x86 bootstrapping bootloader


    【解决方案1】:

    您说“直接启动到 Windows”,所以我假设您使用的是物理 PC。未来注意事项:始终使用模拟器进行开发!这更容易。我喜欢用于 OSDeving 的 Bochs,因为它具有很好的调试功能。现在,讨论可能的解决方案。

    有很多错误的 BIOS 破坏了 IBM PC 的 0x7C00 加载地址的非正式规范。

    这会给你在汇编时的内存地址等问题带来很多问题。所以让开头看起来像这样:

    [BITS 16] ;tell the assembler that its a 16 bit code
    [ORG 0x7C00] ;this tells the assembler where the code will be loaded at when it runs on your machine. It uses this to compute the absolute addresses of labels and such.
    
    jmp word 0:flush ;#FAR jump so that you set CS to 0. (the first argument is what segment to jump to. The argument(after the `:`) is what offset to jump to)
    ;# Without the far jmp, CS could be `0x7C0` or something similar, which will means that where the assembler thinks the code is loaded and where your computer loaded the code is different. Which in turn messes up the absolute addresses of labels.
    flush: ;#We go to here, but we do it ABSOLUTE. So with this, we can reset the segment and offset of where our code is loaded.
    mov BP,0 ;#use BP as a temp register
    mov DS,BP ;#can not assign segment registers a literal number. You have to assign to a register first.
    mov ES,BP ;#do the same here too
    ;#without setting DS and ES, they could have been loaded with the old 0x7C0, which would mess up absolute address calculations for data. 
    

    看,0x07C0:0000 上的一些负载和0x0000:7C00 上的大多数负载(并且它被认为是合适的)。它是相同的平面地址,但不同的段设置确实会搞砸绝对内存地址。所以让我们去掉汇编器的“魔法”,看看它是什么样子(注意我不保证地址完全正确。我不知道所有操作码的大小)

    jmp word 0:0x7C04 ;# 0x7C04 is the address of the `flush` label 
    ...
    

    所以,我们跳转到一个绝对地址。

    那么现在。如果我们不这样做会发生什么?

    以这个程序为例:

    mov ax,[mydata]
    hlt
    
    mydata: dw 500 ;#just some data
    

    这会反汇编成类似的东西

    mov ax,[0x7C06] 
    

    哦,它使用绝对寻址,那怎么会出错呢?那么,如果 DS 实际上是 0x7C0 呢?然后得到 0x7C0:0x7C06 而不是得到预期的汇编器 0:0x7C06 ,它们 不是 相同的平面地址。

    我希望这可以帮助您理解。不过,这确实是一个复杂的话题,需要一段时间的低级编程才能完全理解。

    【讨论】:

    • 您好,感谢您的回复。不幸的是,您的代码在 NASM 中的语法不正确。它告诉我以“jmp FAR 0x0000 ...”开头的行上的操作数大小 (??) 不匹配。不过,感谢模拟器提示。
    • @Newbie 是的,我的 NASM 语法有点生疏尝试jmp word 0:begin
    • 刚刚安装了 Bochs 并将原始教程代码引导到其中。每个教程的代码现在可以工作了!问题是,我的物理 BIOS 是非标准的,还是代码? (当然,所有 x86 兼容的 BIOS 都必须符合相同的标准???)无论如何,我都会在我的 PC 物理 BIOS 上尝试您的修改。
    • @Newbie 啊,如果它不能在您的 PC 上运行,那么是的,物理 BIOS 很可能有一些错误。如果你做了很多操作系统开发,你很快就会知道大多数制造商对它是否适用于 Windows 感到满意,并且不在乎它是否打破了非正式标准以节省每台计算机的几美分。 (是的,IBM PC“标准”是非正式的。没有委员会来确定它,它不包含在任何 ISO 或 ANSI 中)
    • 哦,我明白了。无论如何,我已经再次测试了你的代码,这次使用“jmp word 0:begin”,它在 Bochs 中工作。它也适用于我的物理 BIOS !!! :D 感谢您的帮助! (您介意更详细地解释一下您的每一行吗?)
    【解决方案2】:

    我认为问题很可能与指定的来源有关。

    [ORG 0x7C00]    ;Origin, tell the assembler that where the code will
    

    根据我们一直在进行的对话,该地址在某些方面似乎与预测的不符。可能只是 DS 数据段寄存器不是您所期望的。您实际上可以通过在调用之前添加推送和弹出 ds 来从网页中获取原始列表以显示这样的字符串,

     push cs
     pop ds
    

    如果不是以下代码有效。

     [ORG 0x000]    ; switched to 0 since we are going to try to correct it ourself
    
     call nextinstruction
     nextinstruction:    ; get the return address of the call into dx
     pop dx              ; which is essentially the start of the code + 3 (3 bytes for the call instruction)
     MOV SI, HelloString ;Store string pointer to SI
     add si, dx          ; add IP from start of program
     sub si, 3           ; subtract the 3 the call instruction probably took
     push cs
     pop ds              ; make ds the same as cs.  
     CALL PrintString   ;Call print string procedure
     JMP $      ;Infinite loop, hang it here.
    

    此代码计算出正在运行的代码在运行时的偏移量,并确保 DS 指向同一段。除非另有说明,涉及 SI 的指令通常也使用 DS 作为其代码段来引用内存。

    DS 是一个段寄存器,您可能需要阅读Art of Assembly 之类的内容以了解更多信息。

    Earlz 也在做同样的事情,只是确保寄存器正确,以便正确引用内存地址。只是他比我更了解引导扇区的细节。

    【讨论】:

    • 就像我说的,教程 2 工作正常,证明 BIOS 设置正确,可以从软盘启动。只有当它被告知什么都不做但挂起(教程 1)时,BIOS 才会忽略它。老实说,我对整个引导加载都是新手——本教程就是我所做的一切。我以为我 通过引导扇区加载的 - 是什么让你认为我不是?此外,正如教程所解释的,程序使用 NASM 编译成原始二进制文件(不是 COM 文件)。不过我还是会试试你的建议。
    • 没错,我误解了你所说的。在那种情况下,我怀疑来源仍然是问题的原因,但找出实际原因可能需要打印出注册 IP 以找出实际来源。我看看能不能帮上忙。
    • 好的,我已经尝试过你的建议。我仍然没有得到“Hello World”,但我也没有得到那个不寻常的面孔字符了。我只是在左上角得到一个闪烁的光标(而不是底角,以前不寻常的字符所在的位置)。
    • 感谢您的帮助。这个“注册IP”是什么?如何获取?
    • IP 是指令指针。您可以以与引用 AL 和 SI 相同的方式引用它,但它并不那么简单。以 IP 为例,对于如何复制它有一些额外的限制。然后你需要把它转换成对你有用的东西。我会试着敲一些东西给你演示。
    猜你喜欢
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2015-10-16
    • 1970-01-01
    • 2012-04-27
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多