每个处理器,有时是处理器系列的子集,都有不同的引导方案。通常是以下类别之一:
1) 不同类型的中断/事件(复位、中断、nmi 等)的地址表,通常称为向量表,该表位于硬编码位置,硬件上电时读取复位向量处的地址,然后然后在复位向量中指定的地址开始执行代码。
2) 指令表,这些指令的地址位于已知位置,例如 ARM,这样,每个向量/事件获得一条指令,因此一条指令必须是加载 pc 或分支 (或者后面的向量不被使用,然后您可以使用更多指令退出向量表。
3) 让我想想其他人
一些公司(处理器架构)使用高地址,例如 0xFFFE 是复位向量,0xFFFC 是中断。 msp430和6502我觉得是这样的。正如您在其他答案中看到的那样,8088/86 和系列使用 0xFFFxxxFFFF0 作为入口点/重置地址。
ARM 和 Atmel AVR 等其他人使用地址 0x0000 作为入口点。
如果你在处理器内存接口非常简单的时候进入你的 Wayback 机器,一些地址位、数据位、读写选通脉冲,可能还有芯片选择或输出使能或其他东西。您将使用外部逻辑,例如 74LSxx 部件来解码一些高地址位,解码全零和全一很容易,因此您要么在高地址有 rom(高地址位全为 1),要么在低地址有 ram地址(高地址位全为零),反之亦然。即使在今天的微控制器和处理器中,这也让生活变得轻松。所以你的 ARM 和 AVR 会倾向于假设地址 0 是 rom 而 ram 是其他地方。你的 x86 和 msp430 和其他人会将 rom 放在顶部,然后在下方或中间的某个地方向下运行。
您必须用汇编语言对该地址进行硬编码吗?不一定,通常你为链接器硬编码一些东西,以便它把向量放在正确的位置,但是有些汇编器仍然支持 .org 类型指令,你在这个指令之后声明代码将被放置在这个地址内存空间。
每个处理器都有某种引导加载程序,甚至您的电脑也是如此。这是一个在知道处理器引导规则的情况下编译的单个程序,该规则是在该处理器上运行的代码的第一位。从那里它可能会有所不同,您只能拥有一个程序,那就是您没有计划在启动后更改程序,有时这只是称为固件。现在有时引导加载程序会出现某种提示,也许您使用串行端口和哑终端键入命令或使用 xmodem 等下载程序(uboot、redboot 等执行此类操作)。通常您希望在没有人为干预的情况下启动并启动和运行,因此可能有机会在终端/串行端口上按下一个键或在启动时按下板上的一个按钮等。否则引导加载程序有一组规则对于接下来要做什么,这些规则对于引导加载程序的作者来说是独一无二的,这只是你可以做任何你想做的事情的软件。例如,您的计算机此引导加载程序统称为 BIOS,您可能可以按 F1 或 F2 或 DEL 或其他键或键序列并中断正常的引导过程以执行其他操作(更改 BIOS 设置等)。否则,BIOS 有一组规则(有时/经常可以修改),用于查找下一个要加载和运行的内容。通常试图找到一些媒体,如硬盘、U 盘、软盘或包含引导扇区的 cd/dvd rom。该引导扇区只是更多代码,BIOS强制执行有关该代码必须看起来像什么或必须为什么地址等进行编译的规则,它加载该代码然后成为另一种形式的引导加载程序,该代码可以执行任何操作,启动linux,启动windows,启动dos,问你一些关于你想启动什么的问题,运行内存测试程序等等。
你可能会加载另一个程序,一个操作系统,一个有自己遵循的规则的程序,如果它选择允许加载其他程序,这些程序必须遵守规则。有时当你有一个 mmu 时,你可以让程序认为它在一个地址上运行,而实际上它在另一个地址上运行,但这使得你可以强制所有程序假设它们从特定地址运行,然后编译它们对于那个地址(你为 windows/linux 等编写的大多数程序都是这样的)。但是如果你没有 mmu,那么可能会有不同的规则,比如整个程序必须是位置无关的代码(任何地方都没有硬编码地址),我听说 uclinux 就是这样的。
微控制器是学习这类东西的好地方,因为您可能已经有一个引导加载程序(arduinos 就是这样,lpc 和 st 芯片通常有一个引导加载程序、串行或 USB 或两者)。有时,就像 avr 一样,引导加载程序位于您可以修改的闪存中,将引导加载程序闪存和应用程序闪存分开,有时引导加载程序位于闪存中,但只有芯片制造商知道如何修改/更改它,因此您永远只能使用他们的引导加载程序。在任何一种情况下,您仍然可以制作自己的引导加载程序,在此之上您可以创建一种加载其他程序的方法,这些程序退出回引导加载程序以便其他程序可以运行的规则,等等。我有一些说明设置模拟器(在调试的各个阶段,thumbulator 有一些用处将其清除,就像我从其他人那里分叉的 pcemu 一样),您可以包裹一个哑终端并创建自己的环境而不必实际买任何东西(可以用 qemu 和 gdb 做到这一点,但不是那么容易)。
如果您特别研究这些微控制器,但所有处理器都有问题,通常启动代码(包括向量)在 rom 中,一旦您运行,虽然您并不总是希望这些向量是硬编码的向量rom,您要么必须制定某种方案,使 rom 代码以某种方式分支到 ram 中的某些东西,要么有一个硬件方案,在启动后您可以在某处翻转开关(向寄存器中某处的某个字节写入位)和更改在该地址解码的内存,允许您切换 ram 并用软的东西替换向量表。其中一些微控制器为此有一些过于复杂的方案。在这里,您必须再次查看该处理器和/或芯片的文档以了解其工作原理。有时是板上芯片之外的东西导致地址空间发生变化,所以你必须仔细阅读。
因为这个级别的大多数程序员已经知道这些东西,所以文档通常不会详细说明。他们可能只是说明向量表从这里开始,这里是项目,或者它可能会遍历表中指定地址的每个项目,但可能永远不会说明该表的内容是内存中的地址还是要执行的指令。 ..你必须“只知道”。而你的学习方式是通过询问他人,以查看他人代码的形式。
变量和数据由您和编译器管理。例如,您使用的语言和您使用的编译器将创建 .text、.data、.bss 段,并且通过编译器或链接器,您必须为工具提供特定地址,以便为该二进制文件放置这些内容。在这个深度嵌入的案例中,这意味着要查看系统文档(芯片加电路板)以找出每个部件需要在哪里才能正常启动和运行。而且您还必须以某种方式将这些信息放在那里。例如,如果您编写的代码创建了一个 .data (我通常不这样做)段,并且您正在从闪存启动,您必须有一些方案让编译器进行编译,以便 .data 段位于 ram 中,但是当您启动它时不在 ram 中,您必须在 rom 中的某处拥有 .data 段的副本,然后您必须在进入代码主体之前将其复制到 ram。同样,您必须设置堆栈指针,并在需要时修改向量表等。如果使用 dram,您必须使用可能必须在没有内存的情况下运行的代码初始化内存,才能启动 dram,然后复制 .data,零 .bss,设置堆栈指针然后分支到 main()。