放置堆栈的位置并加载内核

时间:2016-07-05 05:13:15

标签: assembly stack 16-bit osdev real-mode

我是操作系统开发的新手,我很好奇我在开发自己的引导加载程序时遇到的问题。我的操作系统将以汇编语言编写,并将以16位实模式运行。

我知道堆栈是什么,我觉得它会向下扩展到内存中。如果我错了,请纠正我。我知道如何从软盘中将基本内核加载到内存中,我不相信这是问题所在。

我遇到的问题是,我不确定将堆栈放在何处并将内核加载到内存中。我试过像这样创建我的堆栈,但我遇到了问题:

mov ax, 0x0000
mov ss, ax
mov sp, 0xFFFF

我正在0x1000:0x0000加载我的内核。当我 PUSH 以后 POP 我的print函数中的易失性寄存器时,我的内核只挂起第二时间{{ 1}}。这是我的call print函数:

print

这些是我想要显示的行:

print:
    push ax
    push bx
    push cx

    mov al, [si]
    cmp al, 0
    je p_Done

    cmp al, 9
    je p_Tab

    mov ah, 0xE
    int 0x10

    cmp al, 10
    je p_NewLine
   p_Return:
    inc si
    jmp print
  p_Tab:
    xor cx, cx
   p_Tab_Repeat:
    cmp cx, 8
    je p_Return
    mov ah, 0xE
    mov al, " "
    int 0x10
    inc cx
    jmp p_Tab_Repeat
  p_NewLine:
    xor bx, bx
    mov ah, 0x3
    int 0x10

    mov dl, 0x00
    mov ah, 0x2
    int 0x10
    jmp p_Return
  p_Done:
    pop cx
    pop bx
    pop ax
    ret

这是我运行内核时得到的输出(db "Kernel successfully loaded!", 10, 0 db 9, "Lmao, just a tab test!", 10, 0 是光标):

_

它成功打印第一行,但在打印第二行时挂起。如果我删除了 PUSH POP 语句,它就可以了。当我在Kernel successfully loaded! _ 函数中尝试保存和恢复寄存器时,为什么我的内核会挂起?我应该在哪里放置堆栈以及我应该在哪里加载内核?

1 个答案:

答案 0 :(得分:6)

这不是一个Minimal Complete Verifiable Example没有帮助,但你的问题提出了可能需要寻找的东西。通常,如果代码通过在函数序言和结尾中删除 PUSH es和 POP 来工作,通常意味着在执行函数体时堆栈变得不平衡。不平衡堆栈将导致 RET 指令返回到堆栈顶部的任何半随机位置。这可能会导致明显的挂起和/或重新启动。行为将是未定义的。

我没有遵循代码中的逻辑,但这很突出:

print:
    push ax
    push bx
    push cx

    ... snip out code for brevity  

    jmp print

在某些时候,您的print函数可能会在所有推送之前的某个时刻重新启动。这将导致更多 PUSH 进入堆栈,而不会在末尾添加相应的 POP 。我想你可能一直试图得到这样的行为:

print:
    push ax
    push bx
    push cx

.prloop:
    ... snip out code for brevity  

    jmp .prloop

.prloop标签出现在功能的顶部,但是在推动之后。这可以防止将多余的值放在堆栈上。 .prloop可以是您选择的任何有效标签。

堆栈可以放在内存中未被系统使用的任何位置,并且不会干扰引导加载程序和/或内核代码。正如@RossRidge指出的那样,使用0xFFFF的 SP 会使堆栈错位,因为它是一个奇数地址(0xFFFF = -1)。 x86不会抱怨,但它可能会损害某些x86架构上的堆栈性能。

注意:将 SS SP 设置为0x1000:0x0000将使堆栈从0x1000:0xFFFF运行到0x1000:0x0000。推送的第一个16位值将为0x1000:0xFFFE。

您的内核和堆栈通常在物理地址0x00520和0x90000之间的任何位置都是安全的,只要它们不会相互冲突即可。在某些系统上,0x90000和0xA0000之间的存储区域的上半部分可能不可用。如果你想使用这个存储区,我会避免0x9C000和0xA0000之间的区域。 BIOS可以将此区域用作Extended BIOS Data Area(EBDA)的一部分。不应使用0x00000和0x00520之间的区域,因为它包含实模式中断向量表,BIOS Data Area(BDA)和32字节的内存被认为是保留的。