Assembly Hello World引导加载程序在第一个字符后挂起

Assembly Hello World引导加载程序在第一个字符后挂起,assembly,operating-system,x86,bootloader,Assembly,Operating System,X86,Bootloader,我将此代码用于Hello world引导加载程序。它不打印“Hello world\n”,只打印“H”并挂起。我已经使用loadsb成功地打印了消息,但我不明白为什么这种方法不起作用,因为它看起来是等效的 [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 ;

我将此代码用于Hello world引导加载程序。它不打印“Hello world\n”,只打印“H”并挂起。我已经使用loadsb成功地打印了消息,但我不明白为什么这种方法不起作用,因为它看起来是等效的

[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]

您的程序正以您编码的方式工作。ASCII中的小写字母
e
表示为
65h
,在十进制中等于101。因此,如果
e
(101)处于
al
中,则执行
cmp al,100/jge hang
将导致跳转到标签
hang
。一切都很好

您的问题的一个解决方案是简单地删除该行,因为我看不出它有什么用途-看看您是如何让字符串以
\0
结尾的,循环将在到达末尾时结束

不过,还有四个额外的提示:

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

    • 您的程序正以您编写的方式工作。ASCII中的小写字母
      e
      表示为
      65h
      ,在十进制中等于101。因此,如果
      e
      (101)处于
      al
      中,则执行
      cmp al,100/jge hang
      将导致跳转到标签
      hang
      。一切都很好

      您的问题的一个解决方案是简单地删除该行,因为我看不出它有什么用途-看看您是如何让字符串以
      \0
      结尾的,循环将在到达末尾时结束

      不过,还有四个额外的提示:

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

      为什么在计算机启动时使用
      [BITS 64]
      生成64位汇编代码,它处于16位模式?为什么在计算机启动时使用
      [BITS 64]
      生成64位汇编代码,它处于16位模式?谢谢您这样做修复了我的问题。我现在觉得自己真的很笨。谢谢你这么做解决了我的问题。我现在觉得自己很愚蠢。