Hello World引导程序挂在第一个字符后

时间:2012-04-15 23:15:38

标签: assembly operating-system x86 bootloader

我将此代码用于Hello world引导程序。而不是打印出“Hello world \ n”,它只打印出一个'H'并挂起。我已经成功使用了loadb打印出来的消息,但是我无法理解为什么这种方法不会起作用,因为它似乎是等效的。

[ORG 0x7c00]
[BITS 16]

    xor ax, ax  ;make it zero
    mov ds, ax

    mov ecx, msg
bios_print:
    mov al, [ecx]
    add ecx,1
    cmp al, 0  ;zero=end of str
    je hang    ;get out
    cmp al,100
    jge hang
    mov ah, 0x0E
    int 0x10
    jmp bios_print


hang:
    jmp hang

msg   db 'Hello World', 13, 10, 0






   times 510-($-$$) db 0
   db 0x55
   db 0xAA

编辑: 我将[BITS 64]更改为[BITS 16]

1 个答案:

答案 0 :(得分:4)

您的程序完全以您编码的方式工作。 ASCII中的小写e表示为65h,十进制等于101。因此,如果cmp al, 100 / jge hang(101)位于e,则执行al会导致跳转到标签hang。一切安好。 :)

你的问题的解决方案就是删除那一行,因为我真的没有看到它的任何目的 - 看看你的字符串是如何被\0终止的,循环将在它结束时结束到了最后。

但有四个额外提示:

  • 请记住,标志是在(几乎)每个算术和逻辑运算之后设置的。因此,实际上不需要在cmp之后执行add,特别是如果跳转的唯一条件可以由其中一个标志表示。在这种情况下,add ecx, 1 / cmp al, 0 / je hang可以替换为非常简单的add ecx, 1 / jz hang。这样可以节省两个字节,这些在bootsector条件下非常有用。
  • 确实确实希望在调用BIOS例程时保留您在应用程序中使用的寄存器。 BIOS来自不同的供应商,其中一些可能不会很好地破坏您在寄存器中的内容。指令pusha就是这样做的。在调用您自己未编写的任何代码时,保留所有运行时信息通常是一个经验法则(尽管在更“文明”的环境中这可能听起来极端 - 但BIOS和一般实模式代码不是一个)。
  • 当计算机启动时,它处于实模式。此模式下的默认操作数和地址大小为16位(地址通过分段人工扩展为20位)。当然,您可以使用寄存器的32位部分,但必须在指令之前生成操作数大小前缀。这是另一个字节,现在你只有510个。 :)因此,mov ecx, msg只是多余的 - 地址的偏移部分永远不会超过16位。同样适用于add ecx, 1。在这两种情况下,只需将ecx替换为cx
  • 使用hlt指令。它使你的CPU血压降得更低,并且在整个地方都没有任何用处。只需在hlt标签之后和跳转之前添加hang