【问题标题】:Real Mode Simple OS difficulty实模式简单操作系统难度
【发布时间】:2013-09-08 14:33:36
【问题描述】:

我正在用实模式 ASM 编写一个简单的操作系统,以获得乐趣。我最近决定转向文件系统。我用

组装了代码
nasm -f bin -o boot.o boot.asm
nasm -f bin -o kernel.o kernel.asm
nasm -f bin -o fs.o fs.asm

dd if=boot.o bs=512 of=os.img
dd if=kernel.o bs=512 of=os.img seek=1
dd if=fs.o bs=512 of=os.img seek=2

在我的引导加载程序中,我将文件系统加载到地址 0x1000:0000,将内核加载到 0x2000:0000。每个都是 512 字节(到目前为止)相当小。因此,为了测试我的新文件系统,我编写了内核来打印表中第一个文件名的第一个字母。它将值 0x2000 放入 si 中,将 si 中地址处的字节移动到 al 中。然后它将 0x0e 传递给 ah 并调用 int 0x10。然后它停止。但是,当我将操作系统启动到 qemu 时,它只是向我显示 BIOS 信息,说从软盘启动,然后什么也不做。没有信。没有。以下是相关代码:

相关引导加载程序代码

    ;;the part that loads the file system.
    ;;The part for the kernel is identical, except that cl is 02 and bx is 0x2000
    mov ah, 02
    mov al, 01
    mov ch, 00
    mov cl, 03
    mov dh, 00
    mov dl, 00
    mov bx, 0x1000
    mov es, bx
    xor bx, bx
    int 0x13
    ;;jumps to address 0x2000 (where the kernel is)
    mov ax, 0x2000
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    xor ax, ax
    jmp 0x2000
    ;;halts
    hlt

相关内核代码

    ;;gets address
    mov si, 0x1000
    ;;loads al
    mov al, [si]
    ;;prints
    mov ah, 0x0e
    int 0x10
    ;;halts
    hlt

相关文件系统代码

    ;;declares first file (it is hard coded for testing purposes)
    ;;format: [name], [sector number], [number of sectors to load]
    db 'file.bin', 4, 1

如果我在发布此内容时做错了什么,请原谅我,因为这是我的第一篇帖子。

【问题讨论】:

    标签: assembly x86 nasm real-mode


    【解决方案1】:

    你的代码有几个问题。

    1. 您正在将文件系统加载到地址0x1000:0000。转换为线性地址(这是最重要的形式)是0x10000,或者简称为 64KiB。
    2. 您正在将内核加载到地址 0x2000:0000 (0x20000, 128KiB)。 你正在做的跳跃

      jmp 0x2000
      

      是相对于 IP 的跳转,因此您无法到达您想要的位置。这个跳转的执行是通过将作为指令参数传递的立即值添加到 IP 寄存器的值来执行的,IP 寄存器(在那个时候)保存下一条指令的地址(你的hlt)。你需要做的是跳远。它跳转到绝对地址。远跳形式如下:

      jmp segment:offset
      

      用这个替换你的跳跃:

      jmp 0x2000:0000
      
    3. 应该打印字符的内核代码使用SI 寄存器使用段相对寻址,因为您正在实模式下执行代码。与 IP 相关跳转不同,您不能将有符号值作为偏移量传递(尽管它很酷,有时很有用)。更改DS 段的值,或使用段覆盖使一切更快。在您的情况下,我建议对所有文件系统操作使用fs 段,因此无需保存和恢复ds 的值。

      mov ax, 0x1000
      mov fs, ax
      xor si, si
      mov al, byte [fs:si] ;or simply [fs:si] without byte
      

      请记住,ds 是除BP 使用之外的所有寻址的默认段。您应该将它用于内核的主要目的(它会节省一些字节,而且它肯定会更快)。 esfsgs 在这里为您提供其他内存操作。您可以将es 用于处理目标,fs 用于文件系统,gs 用于视频内存指向。一切都取决于你。

    请注意,某些指令(movscmpsscas...)具有预先分配的寄存器和段组合(有时只能覆盖其中一个),因此在选择它们时要明智,并在使用时细心。

    【讨论】:

    • 糟糕!我完全错过了他对内核的跳转不是“远”(不同的部分)。好收获!
    • 为这个标签的答案投入的精力通常并不重要; OP以某种方式获得解决方案并在这里忘记它。顺便说一句,我注意到我们在汇编/osdev 问题上反复见面。
    • 啊,我知道这很老了,但我只想说我从来没有找到自己的答案,我用了你们的。我有点讨厌这个。感谢您的努力。
    • @Kpuonyer Nah,好吧...现在我们已经弄清楚了。
    【解决方案2】:

    您已将 fs 加载到 1000h:0。在跳转到你的内核之前,你用 2000h 加载 segregs。到目前为止,一切都很好。但是mov al, si 得到 2000h:1000h。尝试类似:

    push ds ; save kernel ds
    push 1000h
    pop ds ; set ds to filesystem's segment
    mov si, 0
    mov al, [si]
    pop ds ; get kernel's segment back
    ; now print it...
    

    我认为这应该有效 - 未经测试!啊,分段记忆的乐趣!祝你开机愉快!

    【讨论】:

      猜你喜欢
      • 2022-06-20
      • 2016-10-21
      • 1970-01-01
      • 1970-01-01
      • 2013-05-22
      • 2012-01-22
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      相关资源
      最近更新 更多