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