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

DrC*_*eal 5 assembly stack real-mode osdev x86-16

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

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

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

mov ax, 0x0000
mov ss, ax
mov sp, 0xFFFF
Run Code Online (Sandbox Code Playgroud)

我正在加载我的内核0x1000:0x0000.当我推送并稍后在我的函数中弹出易失性寄存器时print,我的内核只是第二次挂起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
Run Code Online (Sandbox Code Playgroud)

这些是我想要显示的行:

db "Kernel successfully loaded!", 10, 0
db 9, "Lmao, just a tab test!", 10, 0
Run Code Online (Sandbox Code Playgroud)

这是我内核运行时得到的输出(_是光标):

Kernel successfully loaded!
_
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*tch 8

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

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

print:
    push ax
    push bx
    push cx

    ... snip out code for brevity  

    jmp print
Run Code Online (Sandbox Code Playgroud)

在某些时候,您print可以在所有推送之前的某个时刻重新启动您的功能.这将导致更多的PUSH进入堆栈,而最后没有相应的POP.我想你可能一直试图得到这样的行为:

print:
    push ax
    push bx
    push cx

.prloop:
    ... snip out code for brevity  

    jmp .prloop
Run Code Online (Sandbox Code Playgroud)

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


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

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


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

  • @DrCereal:是的。这是16位代码中段偏移结构的特点。当您以16位执行_PUSH_时,在将数据放入堆栈之前,偏移量(* SP *)将减少2。该段无论如何都不会改变。如果取0x0000减去2,则得到0xFFFE。由于段未更改,因此您最终得到0x1000:0xFFFE。如果要使堆栈的物理地址为0x00000到0x0FFFF,则可以将SS:SP设置为0x0000:0x0000。在这种情况下,第一次按下将使SP减2,然后在其中放置值。那将是内存地址0x0000:0xfffe。 (3认同)