它说m0有一个完整的降序堆栈,但似乎暗示起点在另一端(0x00000000)。如果能用通俗易懂的方式解释向量表,那就太好了。
;@-----------------------
.cpu cortex-m0
.thumb
;@-----------------------
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000041 stmdaeq r0, {r0, r6}
8000008: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800000c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000010: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000014: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000018: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800001c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000020: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000024: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000028: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800002c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000030: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000034: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000038: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800003c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
08000040 <reset>:
8000040: f000 f80a bl 8000058 <notmain>
8000044: e7ff b.n 8000046 <hang>
08000046 <hang>:
8000046: e7fe b.n 8000046 <hang>
现在为什么不将此代码构建为地址0x00000000?这是因为您必须查看芯片供应商文档,而不仅仅是arm文档。arm不制造芯片,他们制造处理器核心和一些支持逻辑,您去st或nxp或ti或其他地方找到故事的其余部分,特别是引导闪存在地址空间中的位置。毫无疑问,在这种情况下,正常启动时地址空间中的flast位于地址0x08000000处,映射到0x00000000,某些芯片将具有多个引导闪存,根据跳线(输入引脚高电平或低电平或各种组合)之一将被映射到地址零永远或一段时间。
cortex m0(和m1)基于armv6m,cortex m3和m4基于armv7m。巨大的区别后者支持thumb指令集的thumb2扩展(以前未定义的指令成为两个半字指令32位指令的第一半,不要与32位arm模式指令混淆),大约有150个左右的新thumb2指令添加到cortex m3,然后cortex m4具有浮点单元的一部分(仅一个浮点大小,可能是单精度),以及所有随之而来的指令(基本上是重新定义的协处理器指令)。这使得cortex-m0更容易,只有16位指令(是的,bl实际上是并且在文档中被定义为两个单独的指令,如果您愿意,可以将它们分别编码)。
目前,armv6m ARM ARM还包含指令集定义
第A5章Thumb指令集编码
看着
A6.7.17 CMP (immediate)
(我的文件中的章节编号可能在未来不会保持相同或匹配,他们的文档通常不会有太多变化,但是你永远不知道).
首先要注意的是编码方式。
Encoding T1 All versions of the Thumb instruction set.
CMP <Rn>,#<imm8>
您可以看到,在这个指令中,上位比特必须是00101比特15:11,这是处理器知道这是一个比较立即数的方法。Rn是寄存器r0到r7中的任何一个(要访问r8到r15,您必须使用其他mov指令,以便为16位指令保留大多数指令以节省指令编码中的位数)。然后,低8位是从0到255的直接常量值(其他arm/thumb立即数编码不是那么简单明了,而thumb vs arm使用不同的编码,因此您必须仔细阅读手册)。
我强烈建议,如果您想看到编码,请编写汇编语言,然后进行汇编和反汇编,并让已经调试好的工具链为您完成工作,然后尝试反向工程所见并将其与手册匹配。头皮屑是奇数地址,但这些都有文档记录(虽然不一定如您所希望的那样),然后任何与PC相关的内容,当您对其执行某些操作时,PC是在两个指令之前。它实际上不是用管道处理的,但为了向后兼容性和设置标准,ARM/Thumb标准是提前两个指令。因此,在计算或反向工程计算PC相关地址时,您的数学始终偏离4个字节。
与大多数处理器一样,您作为程序员或者是您信任并借用其代码的程序员来设置堆栈指针。您可以将其放在任何位置,ARM核本身也不知道芯片供应商将要如何实现,编译器也不知道或者不愿意知道所有可能存在的芯片,因此您作为程序员需要告诉工具链,然后工具链再告诉ARM处理器您想要堆栈在哪里。传统上,对于降序堆栈,您希望从高地址开始。首先,您需要查看弹出和推入指令以及伪代码,了解ARM首先按寄存器数量乘以4(对于推入)递减,然后写入这些地址,然后在退出时调整SP。因此,如果您的RAM结束于0x2001FFFF,您可以安全地将堆栈指针放置在0x20020000处,第一个被推入的内容将位于0x2001FFFC处。(不是第一个被推入的内容,但是堆栈的底部就在那里)。其他非ARM处理器的工作方式不同,并且有不同的规则,有些根本无法访问堆栈指针,有些可以访问,但重置值正确,有些像ARM一样需要担心。完整尺寸的ARM处理器有多个堆栈指针需要管理,此外您可以使堆栈向上或向下移动,尽管我不建议这样做。对于所有Cortex-M,内存映射中的前两个字(分别位于地址0和4)应该是您的初始堆栈指针和您想要开始执行的第一条指令的地址。
通常情况下,您会将堆栈放在可用RAM地址最高的位置,并使用链接器脚本在地址4处插入程序入口点的地址。