如何在i386中正确设置ss和sp寄存器

WOR*_*559 5 assembly operating-system nasm bootloader i386

我目前正在编写一个bootloader并且已经开始耗尽我的512B空间,所以我开始在512B之外编写更多代码并打算使用bootloader将其读入内存.我添加到我的代码的末尾:

stack_start:
    resb 4096
stack_end:
Run Code Online (Sandbox Code Playgroud)

这样我就可以在操作系统代码的末尾为堆栈分配空间.目前在我的引导程序中,我在引导加载程序之后为堆栈分配4KiB,使用以下我从这里获取:

mov ax, 07c0h           ; 4K stack space after the bootloader -- code is running at 0x07c0
add ax, 288             ; (4096 + 512)/16 bytes per paragraph (288 paragraphs)
mov ss, ax
mov sp, 4096            ; moves the stack pointer
Run Code Online (Sandbox Code Playgroud)

但是现在我需要一种方法来将堆栈分配到我的操作系统代码的末尾,这将是一个未知的大小.

我相信我理解这些是如何建立的 - 类似于使用es,我ss用于扩展寻址空间,但我找不到任何能够很好地解释我的知识水平的东西.我也不确定如何正确设置它以使我的堆栈结束.我用过:

mov ax, stack_start
mov ss, ax
mov sp, 4096
Run Code Online (Sandbox Code Playgroud)

并没有遇到过错误; 但是我想知道这是否正确,或者我是否真的只为堆栈分配了一些空间,同时用更高的地址填充内存.

ss实际上如何运作?我如何使用它并sp在代码末尾为堆栈分配内存?

这是在使用nasm的i386中.

编辑:如果可能的话,某些方法来验证堆栈是否在正确的位置也非常有用.

Mar*_*oom 3

我知道这对于初学者来说很可怕,但是只要满足要求并且没有错误,设置堆栈的方法就没有好坏之分。
在拥有一个完全正常工作的内存管理器之前,您(人类)必须成为内存管理器。
您必须了解如何使用内存。
“分配内存”的整个概念还不存在!你没有内存管理器,你只有一堆 RW RAM 地址。

记录你的假设

首先需要引导加载程序可以安全地占用的最小内存量。
由于初始程序加载器(引导加载器)位于 0x7c00,因此有理由相信系统具有 31.5KiB 内存。您还可以假设不存在内存并依赖缓存,但这是高级主题。

当事情出错时,陈述假设至关重要。

注意间隙

然后您必须了解保留和使用的区域,这是通过标准内存映射来实现的。

摘录:

00000 - 003ff   IVT
00400 - 004ff   BDA
00500 - 0052f   Dangerous Zone (The Petch Zone :) )
00530 - 07bff   Free
07c00 - 07dff   IPL
Run Code Online (Sandbox Code Playgroud)

“Petch Zone”是一个内部笑话,也是对Michael Petch 的致敬。

做出完全清醒的决定

通过设置临时堆栈来构建您的最小环境。
在上面的片段中,该区域00530 - 07bff是空闲的,您可以将其用作 ~29KiB 堆栈。
由于堆栈是完全降序的,因此您可以将堆栈指针ss:sp放在07c00
07c00是一个物理地址,将其转换为任何合适的逻辑地址(0000:7c00, 0001:7bf0, 0002:7be0, 0003:7bd0, ..., 07c0:0000,任何都可以选择您最喜欢的地址)并SS:SP以您知道/喜欢的任何原子方式设置它。

编辑当偏移下溢从 0 到Ped7gfffe正确记录的位置时,使用带有小偏移部分的逻辑地址会导致问题。 虽然静态行为检查(起始地址相同),但动态行为失败,因此最好使用上面的段,并且肯定不要使用任何内容。
0000:7c000053

任何其他区域都可以,将堆栈指针设置为a0000(常规内存的末尾)是另一种选择。
没有哪个更好,只要注意你把什么放在哪里就可以了。

编辑:正如迈克尔·佩奇在评论中指出的a0000那样,该地址也很危险。
更安全的地址是9c000.

更新内存映射

使用适合您需要的块更新内存映射。
记下内核的开始和结束位置、动态数据所在位置等等。

例如

00000 - 003ff   IVT
00400 - 004ff   BDA
00500 - 0052f   Dangerous Zone (The Petch Zone :) )
00530 - 07bff   Stack
07c00 - 07dff   IPL
07e00 - 08fff   Kernel
09000 - 10000   Other kernel stuff
Run Code Online (Sandbox Code Playgroud)

到目前为止,您可以在内存映射中使用静态块,但如果您想使用超过 1 MiB 的内存,则需要查询BIOS以获取可用内存的内存映射。
这本质上是动态的,因为每个系统都具有不同的内存量。

该映射只不过是内存管理器的一个非常小的元数据,所以是时候......

实现一个简单的内存管理器

在您目前设置的基本环境中,编写一个简单的内存管理器来保存内存块。

陷阱:内存管理器需要一些“元内存”来保存其分配的内存簿,这需要一个假设

一旦您可以分配和释放内存,您就可以将堆栈移动到更大的区域,从磁盘或等效文件加载其他数据等等。
这个想法是,您现在可以像在 C 中一样动态管理内存malloc,并将mfree您从精神上处理内存映射的负担中解放出来。

更先进的内存管理器

更高级的内存管理器通常是用高级语言编写的,其中数据操作更容易(特别是在处理诸如分页之类的主题时)。

  • 关于可能的`ss:sp`设置...虽然`07c0:0000`与`0000:7c00`是相同的物理地址,但`07c0:fffe`与`0000:7bfe`不同!所以实际上 `07c0:0000` 会导致问题,从而导致 OP 提出这个问题,可能会覆盖第二阶段代码。`ss:sp` 对必须覆盖尽可能低的有效堆栈地址,在这种情况下,可能 `0053:0000` 是最小的(我的意思是最低的 `ss`,它允许在堆栈的第一阶段使用完整的 ~29kB 块)。 (2认同)