【问题标题】:Int 10, ah 0eh gives wrong output in a bootloaderInt 10, ah 0eh 在引导加载程序中给出错误的输出
【发布时间】:2018-03-21 12:33:17
【问题描述】:

我正在尝试编写自己的操作系统,我需要一些 print_string 函数来打印 sI 中的字符串 就像这样

print_string:
    Pusha
    mov ah,0Eh

    .repeat:
    lodsb
    cmp al,0
    je .done
    int 10h
    Jmp .repeat

    .done:
    Popa
    Ret

但是输出在

mov si,Msg
call print_string

就像 Wn=Wn=

当消息是“你好”时

【问题讨论】:

  • 对不起,我忘了写剩下的
  • 这不是minimal reproducible example。无论如何,你可能设置了DS 错误,如果有的话。
  • @Jester 无论如何我如何设置 ds
  • 你刚刚设置的?如果你想创建 x86 操作系统,你应该知道什么是段:x86 CPU 的 16 位实模式下的偏移寻址,你应该知道哪些寄存器可用,为什么lodsb 确实使用ds 以及如何甚至覆盖它使用不同的段寄存器,以及x86机器开机后内存映射的外观,即您可以将数据放在哪里,以及您想要将段寄存器设置为哪些值等等......以及所有这些你应该已经知道 x86 汇编基础知识,即有 MOV Sreg,r/m16 变体 mov 指令。使用一些书籍/教程?
  • 一旦您学习了一些 x86 汇编基础知识,就会有合理的资源用于 OS 开发(或者更像是 OS 开发 basics):wiki.osdev.org/Main_Page ... 如果您真的喜欢操作系统开发,在学习了这些基础知识(可能需要几个月或几年)之后,您可能想要检查一些真实的操作系统,如 linux 或 BSD,源代码,以及他们的开发人员正在做什么,以及他们试图解决什么样的问题.在这样的社区工作几年后,您可能能够做出有意义的贡献。

标签: assembly x86 nasm x86-16 bootloader


【解决方案1】:

这不是一个最小的完整示例,但问题很常见。即使没有看到其余代码,我猜你在启动时处理器(或模拟处理器)使用的段和汇编代码使用的偏移量不匹配。

对于 16 位 segment:offset addressing,存在许多指向同一物理内存地址的段和偏移量组合。 BIOS 最常用的是 0x07c0:0x0000 和 0x0000:0x0000。 (0x07c0 DS。 NASM 中缺少 ORG 指令(使用 -f bin 构建时)默认为 org 0x0000。

我有通用引导加载程序提示是这样说的:

  • 当 BIOS 跳转到您的代码时,您不能依赖 CS、DS、ES、SS,SP 寄存器具有有效或预期值。当您的引导加载程序启动时,它们应该被适当地设置。您只能保证您的引导加载程序将从物理地址 0x00007c00 加载和运行,并且引导驱动器号加载到 DL 寄存器中。 [剪辑]
  • lodsb、movsb 等使用的方向标志可以设置或清除。如果方向标志设置不当SI/DI 寄存器可能会在错误的方向上调整。使用STD/CLD 将其设置为您希望的方向(CLD=forward/STD=backwards)。在这种情况下,代码假定向前移动,因此应该使用CLD。更多信息请参见instruction set reference

当您的引导加载程序启动时,您不能依赖方向 DF 标志(LODSB 需要)和段寄存器(如 DS)作为特定值(或正确的值)。您需要在代码开头明确设置它。如果不这样做,您希望打印的字符串将从 RAM 的错误部分读取,结果将是乱码。您似乎两次打印相同的乱码这一事实表明了另一个问题,那就是您没有某种无限循环来结束引导加载程序。我还有另一个 Stackoverflow Answer 我认为是相关的。

我将填写一个缺少的引导加载程序,它调用您提供的使用 0x0000 作为段的 print_string 函数,我们使用 org 0x7c00 作为起始偏移量:

; Create bootloader with: nasm -f bin boot.asm -o boot.bin
; To test in QEMU use:    qemu-system-i386 -fda boot.bin 
org 0x7c00
main:
    xor ax, ax          ; XOR register with itself zeroes the register
    mov ds, ax          ; We want DS:ORG to be 0x0000:0x7c00
    cld                 ; Forward direction for string instructions like LODSB

    mov si,Msg
    call print_string   ; Print the message

    ; This is a preferred way to do an infinite loop but
    ; you could also do: JMP $. This prevents falling through
    ; the code below and off to memory we don't intend to execute
    cli
.endloop:
    hlt
    loop .endloop

print_string:
    Pusha
    mov ah,0Eh
    xor bx,bx          ; Ensure we are writing to page 0
                       ; XOR register with itself zeroes the register
    .repeat:
    lodsb
    cmp al,0
    je .done
    int 10h
    Jmp .repeat

    .done:
    Popa
    Ret

; Place data after the code but before the boot signature.
Msg db "hello",0            ; Nul terminated string

times 510 - ($ - $$) db 0   ; padding with 0 at the end
dw 0xAA55                   ; PC boot signature

或者,我们可以使用 0x07c0 的段并使用 org 0x0000(或省略它),它应该仍然可以工作。代码的开头可能如下所示:

org 0x0000
main:
    mov ax, 0x07c0
    mov ds, ax          ; We want DS:ORG to be 0x07c0:0x0000
    cld                 ; Forward direction for string instructions like LODSB

这是我在两个版本中使用 QEMU 得到的输出:

【讨论】:

  • 我对其进行了测试,它工作得很好,但我发现了另一种方法,所以无论如何我将其标记为已批准,工作答案,以便其他人有这个问题可以解决它,我会小心设置ds
猜你喜欢
  • 2015-07-16
  • 1970-01-01
  • 2016-03-09
  • 2014-05-27
  • 2021-05-26
  • 2016-03-18
  • 1970-01-01
  • 2012-04-12
  • 2017-01-08
相关资源
最近更新 更多