我是操作系统开发的新手,我很好奇我在开发自己的引导加载程序时遇到的问题。我的操作系统将以汇编语言编写,并将以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!
_
函数中尝试保存和恢复寄存器时,为什么我的内核会挂起?我应该在哪里放置堆栈以及我应该在哪里加载内核?
答案 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字节的内存被认为是保留的。