在linux中,通用r0寄存器器包括R0-R5可以分为具体哪三类?

ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可编程访问的,取决微处理器的工作状态及具体的运行模式。但在任何时候,通用寄存器R14~R0、程序计数器PC、一个或两个状态寄存器都是可访问的。
一、ARM工作状态下的寄存器组织
通用寄存器:
通用寄存器包括R0~R15,可以分为三类:
&&&&&&& ─ 未分组寄存器R0~R7,<span style="color:#个
&&&&&&& ─ 分组寄存器R8~R14,<span style="color:#个
&&&&&&& ─ 程序计数器PC(R15),<span style="color:#个
未分组寄存器R0~R7:
&&&&&&&在所有的运行模式下,未分组寄存器都指向同一个物理寄存器,他们未被系统用作特殊的用途,因此,在中断或异常处理进行运行模式转换时,由于不同的处理器运行模式均使用相同的物理寄存器,可能会造成寄存器中数据的破坏,这一点在进行程序设计时应引起注意。
分组寄存器R8~R14
&&&&&& 对于分组寄存器,他们每一次所访问的物理寄存器与处理器当前的运行模式有关。
&&&&&& 对于R8~R12来说,每个寄存器对应两个不同的物理寄存器,当使用fiq模式时,访问寄存器R8_fiq~R12_fiq;当使用除fiq模式以外的其他模式时,访问寄存器R8_usr~R12_usr。
&&&&&& 对于R13、R14来说,每个寄存器对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外<span style="color:#个物理寄存器对应于其他<span style="color:#种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:
R13_&mode&
R14_&mode&
其中,mode为以下几种模式之一:usr、fiq、irq、svc、abt、und。
&&&&&&&寄存器R13在ARM指令中常用作堆栈指针,但这只是一种习惯用法,用户也可使用其他的寄存器作为堆栈指针。而在Thumb指令集中,某些指令强制性的要求使用R13作为堆栈指针。
&&&&&& 由于处理器的每种运行模式均有自己独立的物理寄存器R13,在用户应用程序的初始化部分,一般都要初始化每种模式下的R13,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入R13所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行。
&&&&&&&R14也称作子程序连接寄存器(Subroutine Link Register)或连接寄存器LR。当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份。其他情况下,R14用作通用寄存器。与之类&#20284;,当发生中断或异常时,对应的分组寄存器R14_svc、R14_irq、R14_fiq、R14_abt和R14_und用来保存R15的返回&#20540;。
寄存器R14常用在如下的情况:
&&&&&& 在每一种运行模式下,都可用R14保存子程序的返回地址,当用BL或BLX指令调用子程序时,将PC的当前&#20540;拷贝给R14,执行完子程序后,又将R14的&#20540;拷贝回PC,即可完成子程序的调用返回。以上的描述可用指令完成:
1、执行以下任意一条指令:
MOV PC,LR
2、在子程序入口处使用以下指令将R14存入堆栈:
STMFD SP!,{&Regs&,LR}
对应的,使用以下指令可以完成子程序返回:
LDMFD SP!,{&Regs&,PC}
R14也可作为通用寄存器。
程序计数器PC(R15)
&&&&&& 寄存器R15用作程序计数器(PC)。在ARM状态下,位[1:0]为<span style="color:#,位[31:2]用于保存PC;在Thumb状态下,位[0]为<span style="color:#,位
[31:1]用于保存PC;虽然可以用作通用寄存器,但是有一些指令在使用R15时有一些特殊限制,若不注意,执行的结果将是不可预料的。
&&&&&& R15虽然也可用作通用寄存器,但一般不这么使用,因为对R15的使用有一些特殊的限制,当违反了这些限制时,程序的执行结果是未知的。
&&&&&& 由于ARM体系结构采用了多级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的&#20540;为当前指令的地址&#20540;加<span style="color:#个字节。需要注意的是,当使用指令STR/STM保存R15时,保存的可能是当前指令地址&#20540;加8字节,也可能保存的是当前指令地址加12字节。到底是哪种方式,取决于芯片具体设计方式。无论如何,在同一芯片中,要么采用当前指令地址加8,要么采用当前指令地址加12,不能有些指令采用当前指令地址加8,另一些指令采用当前指令地址加12。因此对于用户来说,尽量避免使用STR/STM指令来保存R15的&#20540;。当不可避免这种使用方式时,可以先通过一些代码来确定所用的芯片使用的是哪种实现方式。&&
假设R0指向可用的一个内存字,下面代码可以在R0指向的内存字中返回该芯片所采用的地址偏移量。&&&&&&&&
SUB & & & R1, PC, #4&&&&& R1中存放下面STR指令的地址&
STR & & & PC, [R0]&&&&&&& 将PC=STR地址&#43;offset保存到R0中&
LDR&&&&&& R0, [R0]&&&&&&&&
SUB & & &R0, R0, R1&&&&& offset=PC-STR地址
&&&&&&在ARM状态下,任一时刻可以访问以上所讨论的16个通用寄存器和一到两个状态寄存器。在非用户模式(特权模式)下,则可访问到特定模式分组寄存器,图<span style="color:#.3说明在每一种运行模式下,哪一些寄存器是可以访问的。
寄存器R16:
&&&&&& 寄存器R16用作CPSR(Current Program Status Register,当前程序状态寄存器),CPSR可在任何运行模式下被访问,它包括条件标志位、中断禁止位、当前处理器模式标志位,以及其他一些相关的控制和状态位。
&&&&&& 每一种运行模式下又都有一个专用的物理状态寄存器,称为SPSR(Saved Program Status Register,备份的程序状态寄存器),当异常发生时,SPSR用于保存CPSR的当前&#20540;,从异常退出时则可由SPSR来恢复CPSR。
&&&&&& 由于用户模式和系统模式不属于异常模式,他们没有SPSR,当在这两种模式下访问SPSR,结果是未知的。
二、Thumb工作状态下的寄存器组织
&&&&&&Thumb状态下的寄存器集是ARM状态下寄存器集的一个子集,程序可以直接访问<span style="color:#个通用寄存器(R7~R0)、程序计数器(PC)、堆栈指针(SP)、连接寄存器(LR)和CPSR。同时,在每一种特权模式下都有一组SP、LR和SPSR。图<span style="color:#.4表明Thumb状态下的寄存器组织。
Thumb状态下的寄存器组织与ARM状态下的寄存器组织的关系:
&&&&&& ─ Thumb状态下和ARM状态下的R0~R7是相同的。
&&&&&& ─ Thumb状态下和ARM状态下的CPSR和所有的SPSR是相同的。
&&&&&& ─ Thumb状态下的SP对应于ARM状态下的R13。
&&&&&& ─ Thumb状态下的LR对应于ARM状态下的R14。
&&&&&& ─ Thumb状态下的程序计数器对应于ARM状态下R15
以上的对应关系如图<span style="color:#.5所示:
访问THUMB状态下的高位寄存器(Hi-registers):
&&&&&& 在Thumb状态下,高位寄存器R8~R15并不是标准寄存器集的一部分,但可使用汇编语言程序受限制的访问这些寄存器,将其用作快速的暂存器。使用带特殊变量的MOV指令,数据可以在低位寄存器和高位寄存器之间进行传送;高位寄存器的&#20540;可以使用CMP和ADD指令进行比较或加上低位寄存器中的&#20540;。
程序状态寄存器
&&&&&&&ARM体系结构包含一个当前程序状态寄存器(CPSR)和五个备份的程序状态寄存器(SPSRs)。备份的程序状态寄存器用来进行异常处理,其功能包括:
&&&&& ─ 保存ALU中的当前操作信息
&&&&& ─ 控制允许和禁止中断
&&&&& ─ 设置处理器的运行模式
程序状态寄存器的每一位的安排如图<span style="color:#.6所示:
条件码标志(Condition Code Flags)
&&&&&& N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。
&&&&&& 在ARM状态下,绝大多数的指令都是有条件执行的。
&&&&&& 在Thumb状态下,仅有分支指令是有条件执行的。
N&Negative&&&&&&& 如果结果是负数则置位
& Z&Zero&&&&&&&&&&& 如果结果是零则置位
& C&Carry&&&&&&&&&& 如果发生进位则置位
& O&Overflow&&&&&&& 如果发生溢出则置位
& I&IRQ&&&&&&& &&&&&中断禁用
& F&FIQ&&&&&&&&&&&& 快速中断禁用
M[4:0] &处理器模式 &&可访问的寄存器
0b10000 User &&&&&&&&PC,R14~R0,CPSR&
0b10001 FIQ& &&&&&&&&PC,R14_fiq~R8_fiq,R7~R0,CPSR,SPSR_fiq
0b10010 IRQ &&&&&&&&&PC,R14_irq~R13_irq,R12~R0,CPSR,SPSR_irq&
0b10011 Supervisor &&PC,R14_svc~R13_svc,R12~R0,CPSR,SPSR_svc&
0b10111 Abort &&&&&&&PC,R14_abt~R13_abt,R12~R0,CPSR,SPSR_abt&
0b11011 Undefined &&&PC,R14_und~R13_und,R12~R0,CPSR,SPSR_und&
0b11111 System &&&&&&PC,R14~R0,CPSR(ARM v4及更高版本)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:250136次
积分:3210
积分:3210
排名:第8806名
原创:49篇
转载:122篇
评论:27条
(3)(9)(8)(2)(1)(3)(2)(1)(3)(1)(13)(20)(1)(1)(2)(4)(7)(4)(3)(12)(11)(3)(9)(1)(2)(5)(7)(2)(2)(3)(2)(8)(17)(1)(1)ARM Linux启动过程分析是本人要介绍的内容,嵌入式 Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。对于不同体系结构的处理器来说Linux的启动过程也有所不同。本文以S3C2410 ARM处理器为例,详细分析了系统上电后 bootloader的执行流程及 ARM Linux的启动过程。
Linux 最初是由瑞典赫尔辛基大学的学生 Linus Torvalds在1991 年开发出来的,之后在 GNU的支持下,Linux 获得了巨大的发展。虽然 Linux 在桌面 PC 机上的普及程度远不及微软的 Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。而近些年来 Linux 在嵌入式领域的迅猛发展,更是给 Linux 注入了新的活力。
一个嵌入式 Linux 系统从软件角度看可以分为四个部分[1]:引导加载程序(bootloader), Linux 内核,文件系统,应用程序。
其中 bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用 Linux 内核。Linux 内核在完成系统的初始化之后需要挂载某个文件系统做为根文件系统(Root Filesystem)。根文件系统是 Linux 系统的核心组成部分,它可以做为Linux 系统中文件和数据的存储区域,通常它还包括系统配置文件和运行应用软件所需要的库。
应用程序可以说是嵌入式系统的“灵魂”,它所实现的功能通常就是设计该嵌入式系统所要达到的目标。如果没有应用程序的支持,任何硬件上设计精良的嵌入式系统都没有实用意义。
从以上分析我们可以看出 bootloader 和 Linux 内核在嵌入式系统中的关系和作用。Bootloader在运行过程中虽然具有初始化系统和执行用户输入的命令等作用,但它最根本的功能就是为了启动 Linux 内核。在嵌入式系统开发的过程中,很大一部分精力都是花在bootloader 和 Linux 内核的开发或移植上。如果能清楚的了解 bootloader 执行流程和 Linux的启动过程,将有助于明确开发过程中所需的工作,从而加速嵌入式系统的开发过程。而这正是本文的所要研究的内容。
2、Bootloader
(1)Bootloader的概念和作用
Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于 PC 机上的 BIOS。在完成对系统的初始化任务之后,它会将非易失性存储器(通常是 Flash或 DOC 等)中的Linux 内核拷贝到 RAM 中去,然后跳转到内核的第一条指令处继续执行,从而启动 Linux 内核。由此可见,bootloader 和 Linux 内核有着密不可分的联系,要想清楚的了解 Linux内核的启动过程,我们必须先得认识 bootloader的执行过程,这样才能对嵌入式系统的整个启过程有清晰的掌握。
(2)Bootloader的执行过程
不同的处理器上电或复位后执行的第一条指令地址并不相同,对于
ARM 处理器来说,该地址为 0x。对于一般的嵌入式系统,通常把 Flash 等非易失性存储器映射到这个地址处,而
bootloader就位于该存储器的最前端,所以系统上电或复位后执行的第一段程序便是 bootloader。而因为存储
bootloader的存储器不同,bootloader的执行过程也并不相同,下面将具体分析。
嵌入式系统中广泛采用的非易失性存储器通常是
Flash,而 Flash 又分为 Nor Flash 和Nand Flash 两种。 它们之间的不同在于: Nor Flash
支持芯片内执行(XIP, eXecute In Place),这样代码可以在Flash上直接执行而不必拷贝到RAM中去执行。而Nand
Flash并不支持XIP,所以要想执行 Nand Flash 上的代码,必须先将其拷贝到 RAM中去,然后跳到 RAM 中去执行。
实际应用中的
bootloader根据所需功能的不同可以设计得很复杂,除完成基本的初始化系统和调用 Linux
内核等基本任务外,还可以执行很多用户输入的命令,比如设置 Linux 启动参数,给 Flash
分区等;也可以设计得很简单,只完成最基本的功能。但为了能达到启动Linux 内核的目的,所有的 bootloader都必须具备以下功能[2] :
初始化 RAM
Linux 内核一般都会在 RAM 中运行,所以在调用 Linux 内核之前 bootloader 必须设置和初始化 RAM,为调用
Linux内核做好准备。初始化 RAM 的任务包括设置 CPU 的控制寄存器参数,以便能正常使用 RAM 以及检测RAM 大小等。
初始化串口
Linux 的启动过程中有着非常重要的作用,它是 Linux内核和用户交互的方式之一。Linux
在启动过程中可以将信息通过串口输出,这样便可清楚的了解 Linux 的启动过程。虽然它并不是 bootloader
必须要完成的工作,但是通过串口输出信息是调试 bootloader 和Linux 内核的强有力的工具,所以一般的 bootloader
都会在执行过程中初始化一个串口做为调试端口。
检测处理器类型
Bootloader在调用 Linux内核前必须检测系统的处理器类型,并将其保存到某个常量中提供给 Linux 内核。Linux 内核在启动过程中会根据该处理器类型调用相应的初始化程序。
设置 Linux启动参数
Bootloader在执行过程中必须设置和初始化
Linux 的内核启动参数。目前传递启动参数主要采用两种方式:即通过 struct param_struct 和struct
tag(标记列表,tagged list)两种结构传递。struct param_struct 是一种比较老的参数传递方式,在 2.4
版本以前的内核中使用较多。从 2.4 版本以后 Linux 内核基本上采用标记列表的方式。但为了保持和以前版本的兼容性,它仍支持 struct
param_struct 参数传递方式,只不过在内核启动过程中它将被转换成标记列表方式。标记列表方式是种比较新的参数传递方式,它必须以
ATAG_CORE 开始,并以ATAG_NONE
结尾。中间可以根据需要加入其他列表。Linux内核在启动过程中会根据该启动参数进行相应的初始化工作。
调用 Linux内核映像
Bootloader完成的最后一项工作便是调用
Linux内核。如果 Linux 内核存放在 Flash 中,并且可直接在上面运行(这里的 Flash 指 Nor
Flash),那么可直接跳转到内核中去执行。但由于在 Flash 中执行代码会有种种限制,而且速度也远不及 RAM
快,所以一般的嵌入式系统都是将 Linux内核拷贝到 RAM 中,然后跳转到 RAM 中去执行。不论哪种情况,在跳到 Linux 内核执行之前
CUP的寄存器必须满足以下条件:r0=0,r1=处理器类型,r2=标记列表在 RAM中的地址。
3、Linux内核的启动过程
在 bootloader将 Linux 内核映像拷贝到 RAM 以后,可以通过下例代码启动 Linux 内核:call_linux(0, machine_type, kernel_params_base)。
其中,machine_tpye
是 bootloader检测出来的处理器类型, kernel_params_base 是启动参数在 RAM 的地址。通过这种方式将 Linux
启动需要的参数从 bootloader传递到内核。Linux 内核有两种映像:一种是非压缩内核,叫
Image,另一种是它的压缩版本,叫zImage。根据内核映像的不同,Linux 内核的启动在开始阶段也有所不同。zImage 是
Image经过压缩形成的,所以它的大小比 Image 小。但为了能使用 zImage,必须在它的开头加上解压缩的代码,将 zImage
解压缩之后才能执行,因此它的执行速度比 Image 要慢。但考虑到嵌入式系统的存储空容量一般比较小,采用 zImage
可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。所以一般的嵌入式系统均采用压缩内核的方式。
ARM 系列处理器来说,zImage 的入口程序即为 arch/arm/boot/compressed/head.S。它依次完成以下工作:开启
MMU 和 Cache,调用 decompress_kernel()解压内核,最后通过调用 call_kernel()进入非压缩内核
Image 的启动。下面将具体分析在此之后 Linux 内核的启动过程。
(1)Linux内核入口
非压缩内核的入口位于文件/arch/arm/kernel/head-armv.S 中的 stext
段。该段的基地址就是压缩内核解压后的跳转地址。如果系统中加载的内核是非压缩的 Image,那么bootloader将内核从 Flash中拷贝到
RAM 后将直接跳到该地址处,从而启动 Linux 内核。不同体系结构的 Linux
系统的入口文件是不同的,而且因为该文件与具体体系结构有关,所以一般均用汇编语言编写[3]。对基于 ARM 处理的 Linux
系统来说,该文件就是head-armv.S。该程序通过查找处理器内核类型和处理器类型调用相应的初始化函数,再建立页表,最后跳转到
start_kernel()函数开始内核的初始化工作。
检测处理器内核类型是在汇编子函数__lookup_processor_type中完成的。通过以下代码可实现对它的调用:bl
__lookup_processor_type。__lookup_processor_type调用结束返回原程序时,会将返回结果保存到寄存器中。其中r8
保存了页表的标志位,r9 保存了处理器的 ID 号,r10 保存了与处理器相关的 struproc_info_list 结构地址。
检测处理器类型是在汇编子函数
__lookup_architecture_type 中完成的。与__lookup_processor_type类似,它通过代码:“bl
__lookup_processor_type”来实现对它的调用。该函数返回时,会将返回结构保存在 r5、r6 和 r7 三个寄存器中。其中
r5 保存了 RAM 的起始基地址,r6 保存了 I/O基地址,r7 保存了
I/O的页表偏移地址。当检测处理器内核和处理器类型结束后,将调用__create_page_tables 子函数来建立页表,它所要做的工作就是将
RAM 基地址开始的 4M 空间的物理地址映射到 0xC0000000 开始的虚拟地址处。对笔者的 S3C2410 开发板而言,RAM
连接到物理地址 0x 处,当调用 __create_page_tables 结束后 0x ~
0x 物理地址将映射到0xCxC0400000 虚拟地址处。
当所有的初始化结束之后,使用如下代码来跳到 C 程序的入口函数 start_kernel()处,开始之后的内核初始化工作:
b SYMBOL_NAME(start_kernel)
(2)start_kernel函数
start_kernel是所有
平台进入系统内核初始化后的入口函数,它主要完成剩余的与硬件平台相关的初始化工作,在进行一系列与内核相关的初始化后,调用第一个用户进程-init
进程并等待用户进程的执行,这样整个 Linux 内核便启动完毕。该函数所做的具体工作有[4][5]:
调用 setup_arch()函数进行与体系结构相关的第一个初始化工作;
对不同的体系结构来说该函数有不同的定义。对于
ARM 平台而言,该函数定义在arch/arm/kernel/Setup.c。它首先通过检测出来的处理器类型进行处理器内核的初始化,然后通过
bootmem_init()函数根据系统定义的 meminfo 结构进行内存结构的初始化,最后调用paging_init()开启
MMU,创建内核页表,映射所有的物理内存和 IO空间。
a、创建异常向量表和初始化中断处理函数;
b、初始化系统核心进程调度器和时钟中断处理机制;
c、初始化串口控制台(serial-console);
d、ARM-Linux 在初始化过程中一般都会初始化一个串口做为内核的控制台,这样内核在启动过程中就可以通过串口输出信息以便开发者或用户了解系统的启动进程。
e、创建和初始化系统 cache,为各种内存调用机制提供缓存,包括;动态内存分配,虚拟文件系统(VirtualFile System)及页缓存。
f、初始化内存管理,检测内存大小及被内核占用的内存情况;
g、初始化系统的进程间通信机制(IPC);
当以上所有的初始化工作结束后,start_kernel()函数会调用
rest_init()函数来进行最后的初始化,包括创建系统的第一个进程-init 进程来结束内核的启动。Init
进程首先进行一系列的硬件初始化,然后通过命令行传递过来的参数挂载根文件系统。最后 init 进程会执行用
户传递过来的“init=”启动参数执行用户指定的命令,或者执行以下几个进程之一:
1 execve("/sbin/init",argv_init,envp_init);& &
2 execve("/etc/init",argv_init,envp_init);& &
3 execve("/bin/init",argv_init,envp_init);& &
4 execve("/bin/sh",argv_init,envp_init)。&&
当所有的初始化工作结束后,cpu_idle()函数会被调用来使系统处于闲置(idle)状态并等待用户程序的执行。至此,整个 Linux 内核启动完毕。
内核是一个非常庞大的工程,经过十多年的发展,它已从从最初的几百 KB
大小发展到现在的几百兆。清晰的了解它执行的每一个过程是件非常困难的事。但是在嵌入式开发过程中,我们并不需要十分清楚 linux
的内部工作机制,只要适当修改 linux 内核中那些与硬件相关的部分,就可以将 linux 移植到其它目标平台上。通过对 linux
的启动过程的分 析,我们可以看出哪些是和硬件相关的,哪些是 linux 内核内部已实现的功能,这样在移植linux 的过程中便有所针对。而 linux内核的分层设计将使 linux 的移植变得更加容易。
阅读(...) 评论()
DON'T FORGET TO HAVE FUN3234人阅读
hardware(24)
最近在做kernel从ARM到一款PPC处理器的移植,需要学下下PPC的通用寄存器,找到的文档整理如下:
Classification
commonly used to hold the old link register when building the stack frame
stack pointer
table of contents pointer
commonly used as the return value of a function, and also the first argument in
commonly used to send in arguments 2 through 8 into a function
cannot be used as a general register. Use&mflr&(move from link register) or&mtlr&(move to link register) to get at, e.g.,&mtlr r0
condition register
r0 - r31都是32位长度的寄存器,其中使用上各有区别:
r0&&&&&&&&&&&&&连接寄存器?
r1&&&&&&&&&&&&&堆栈指针--通俗点叫sp,程序在内存中运行时当前的地址指针
r2&&&&&&&&&&&&&目录表的指针?
r3 - r10&&&&&函数参数(1-8),r3是函数第一个参数,r4是函数第二个参数,依次类推...,r3也用在函数返回&#20540;时。
PowerPC简介
PowerPC 体系结构规范(PowerPC Architecture Specification)发布于 1993 年,它是一个 64 位规范 ( 也包含 32 位子集 )。几乎所有常规可用的 PowerPC(除了新型号 IBM RS/6000 和所有 IBM pSeries 高端服务器)都是 32 位的。
PowerPC 处理器有 32 个(32 位或 64 位)GPR(通用寄存器)以及诸如 PC(程序计数器,也称为 IAR/指令地址寄存器或 NIP/下一指令指针)、LR(链接寄存器)、CR(条件寄存器)等各种其它寄存器。有些 PowerPC CPU 还有 32 个 64 位 FPR(浮点寄存器)。MPC555使用的PowerPC CPU是带有FPR的。一些常用寄存器介绍如下:
通用寄存器的用途:
r0   在函数开始(function prologs)时使用。
r1   堆栈指针,相当于ia32架构中的esp寄存器,idapro把这个寄存器反汇编标识为sp。
r2   内容表(toc)指针,idapro把这个寄存器反汇编标识为rtoc。系统调用时,它包含系统调用号(这个好像跟系统有关吧)。
r3   作为第一个参数和返回&#20540;。
r4-r10 函数或系统调用开始的参数。
r11   用在指针的调用和当作一些语言的环境指针。
r12   它用在异常处理和glink(动态连接器)代码。
r13   保留作为系统线程ID。
r14-r31 作为本地变量,非易失性。
专用寄存器的用途:
lr   链接寄存器,它用来存放函数调用结束处的返回地址。
ctr   计数寄存器,它用来当作循环计数器,会随特定转移操作而递减。
xer   定点异常寄存器,存放整数运算操作的进位以及溢出信息。
msr   机器状态寄存器,用来配置微处理器的设定。
cr   条件寄存器,它分成8个4位字段,cr0-cr7,它反映了某个算法操作的结果并且提供条件分支的机制。
寄存器r1、r14-r31是非易失性的,这意味着它们的&#20540;在函数调用过程保持不变。寄存器r2也算非易失性,但是只有在调用函数在调用后必须恢复它的&#20540;时才被处理。
寄存器r0、r3-r12和特殊寄存器lr、ctr、xer、fpscr是易失性的,它们的&#20540;在函数调用过程中会发生变化。此外寄存器r0、r2、r11和r12可能会被交叉模块调用改变,所以函数在调用的时候不能采用它们的&#20540;。
条件代码寄存器字段cr0、cr1、cr5、cr6和cr7是易失性的。cr2、cr3和cr4是非易失性的,函数如果要改变它们必须保存并恢复这些字段。
在AIX上,svca指令(sc是PowerPC的助记符)用来表示系统调用,r2寄存器指定系统调用号,r3-r10寄存器是给该系统调用的参数。在执行系统调用指令之前有两个额外的先决条件:LR寄存器必须保存返回系统调用地址的&#20540;并且在系统调用前执行crorc cr6, cr6, cr6指令(?)。
从技术而言,开发人员可以将任一 GPR 用于任何操作。例如,由于不存在:“堆栈指针寄存器”,为此程序员就可以使用任何寄存器。实际上,定义一组约定很有用,这样二进制对象就可以与不同的编译器和预先编写好的汇编代码进行互操作。
调用约定是由使用的 ABI(应用程序二进制接口)决定的。ppc32 Linux 和 NetBSD 实现使用 SVR4(System V R4)ABI,而 ppc64 Linux 仿效了 AIX,使用 PowerOpen ABI。ABI 还指定当调用子例程时哪些寄存器被认为是易失型的(调用者保存(caller-save))以及哪些被认为是非易失型的(被调用者保存(callee-save)),以及许多其它内容。
SVR4 ABI 指定了一些行为的具体示例:
-由于 PowerPC 拥有如此多的 GPR(32 个,而相比之下 IA32 只有 8 个),所以传递参数的寄存器从 gpr3 开始。
-寄存器 gpr3 到 gpr12 是易失型的(调用者保存)寄存器,如果需要的话,在调用子例程之前必须先保存它们并在返回之后恢复它们。
&&&&&&&-寄存器 gpr1 用来作为栈帧指针。
指令&#26684;式
指令包括操作码和操作数两部分,PowerPC支持三操作数的指令&#26684;式。如算术指令:
add rD,rA,rB
表示把(rA)+(rB)的和存放到rD寄存器中。
指令中的点号“.”表示:指令将更新条件寄存器CR0。如add. rD,rA,rB。
指令中的字母“c”表示:指令显示说明结果影响XER寄存器中的进位位[CA],如addc rD,rA,rB。
指令中的字母“e”表示:在指令中把XER[CA]中的数据作为一个操作数,并在XER[CA]位记录进位位,如adde rD,rA,rB
指令中的字母“o”表示:溢出标志。对于整数,在XER[OA]位记录溢出和在CR0[SO]记录溢出位,如addo rD,rA,rB
条件寄存器
条件寄存器CR包括8个4bit的字段,即CR0~CR7。每个字段可以表示整数运算或比较的结果。每个条件字段可以记录比较结果,即大于、小于、等于和总体溢出等。条件寄存器&#26684;式如图1所示。
异常处理器
整数异常寄存器XER是一个特殊功能寄存器,它包括一些对增加计算精度有用的信息和出错信息。XER的&#26684;式如下:
SO为总体溢出标志:一旦有溢出位OV置位,SO就会置位。
OV为溢出标志:当发生溢出时置位,否则清零;在作乘法或除法运算时,如果结果超过寄存器的表达范围,则溢出置位。
CA为进位标志:当最高位产生进位时,置位,否则清零;扩展精度指令(后述)可以用CA作为操作符参与运算。
存储/加载指令
1 整数存储指令
整数存储指令如表2所示。
表2 整数存储指令
语法&#26684;式
字节存储(偏移地址寻址)
字节存储(寄存器寻址)
rS, rA, rB
记录有效地址的字节存储(偏移地址寻址)
记录有效地址的字节存储(寄存器寻址)
rS, rA, rB
半字存储(偏移地址寻址)
半字存储(寄存器寻址)
rS, rA, rB
记录有效地址的半字存储(偏移地址寻址)
记录有效地址的半字存储(寄存器寻址)
rS, rA, rB
字存储(偏移地址寻址)
字存储(寄存器寻址)
rS, rA, rB
记录有效地址的字存储(偏移地址寻址)
记录有效地址的字存储(寄存器寻址)
rS, rA, rB
(1)&&&&字节存储指令stb(偏移地址寻址)
stb rS,d(rA)&&
有效地址为rA的内容加d,rS的低8位内容存储到有效地址为EA的存储器中。
(2)&&&&字节存储指令stbx(寄存器寻址)
stbx rS,rA,rB
有效地址为rA的内容加上rB的内容,rS的低8位内容存储到有效地址为EA的存储器中。
(3)&&&&记录有效地址的字节存储指令stbu(偏移地址寻址)
stub rS,d(rA)
有效地址EA=(rA)+d,rS的低8位内容存储到有效地址为EA的存储器中。rA=EA,如果rA=0,则指令无效。
(4)&&&&记录有效地址的字节存储指令stbux(寄存器寻址)
stbux rS,rA,rB
有效地址EA=(rA)+(rB),rS的低8位内容存储到有效地址为EA的存储器中,rA=EA,如果rA=0,则指令无效。
(5)&&&&半字存储指令sth(偏移地址寻址)
sth rS,d(rA)
有效地址EA=(rA)+d,rS的低16位内容存储到有效地址为EA的存储器中。
(6)&&&&记录有效地址的半字存储指令sthu(偏移地址寻址)
sthu rS,d(rA)
有效地址EA=(rA)+d,rS的低16位内容存储到有效地址为EA的存储器中。rA=EA,如果rA=0,则指令无效。
(7)&&&&字存储指令stw(偏移地址寻址)
stw rS,d(rA)
有效地址EA=(rA)+d,rS的32位内容存储到有效地址为EA的存储器中。
(8)&&&&记录有效地址的字存储指令stwu(偏移地址寻址)
stwu rS,d(rA)
有效地址EA=(rA)+d,rS的32位内容存储到有效地址为EA的存储器中,rA=EA,如果rA=0,则指令无效。
(9)&&&&记录有效地址的字存储指令stwux(寄存器寻址)
stwux rS,rA,rB
有效地址EA=(rA)+(rB),rS的32位内容存储到有效地址为EA的存储器中。rA=EA,如果rA=0,则指令无效。
(10)字存储指令stwx(寄存器寻址)
stwx rS,rA,rB
有效地址EA=(rA)+(rB),rS的32位内容存储到有效地址为EA的存储器中。
2、整数加载指令
整数加载指令如表3所示。
语法&#26684;式
高位清零加载字节指令(偏移地址寻址)
高位清零的加载字节指令(寄存器寻址)
rD, rA, rB
高位清零的加载字节并记录有效地址指令(偏移地址寻址)
高位清零的加载字节并记录有效地址指令(寄存器寻址)
rD, rA, rB
高位清零的加载半字指令(偏移地址寻址)
高位清零的加载半字指令(寄存器寻址)
rD, rA, rB
高位清零的加载半字并记录有效地址指令(偏移地址寻址)
高位清零的加载半字并记录有效地址指令(寄存器寻址)
rD, rA, rB
加载半字指令(偏移地址寻址)
加载半字指令(寄存器寻址)
rD, rA, rB
加载半字并记录有效地址指令(偏移地址寻址)
加载半字并记录有效地址指令(寄存器寻址)
rD, rA, rB
加载字指令(偏移地址寻址)
加载字指令(寄存器寻址)
rD, rA, rB
加载字并记录有效地址指令(偏移地址寻址)
加载字并记录有效地址指令(寄存器寻址)
rD, rA, rB
(1)&&&&lbz rD, d(rA) ;EA=(rA|0)+d。从存储器读取EA地址的内容,并加载低8位到rD,rD的其他位清0。不影响其他寄存器。
(2)&&&&lbzu rD, d(rA) ;EA=(rA)+d。从存储器读取EA地址一个字节的内容,并加载低8位到rD,rD的其他各位清零,有效地址EA存放在rA中。
(3)&&&&lbzux rD,rA,rB ;EA=(rA)+(rB)。从存储器读取EA地址一个字节的内容,并加载低8位到rD,rD的其他各位清零,EA存放在rA中。如果rA=0或者rA=rD,则指令无效。
(4)&&&&lbzx rD,rA,rB&&&;EA=(rA|0)+(rB)。从存储器读取EA地址一个字节的内容,并加载低8位到rD,rD的其他各位清0。
(5)&&&&lha rD, d(rA) ;EA=(rA|0)+d。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD的其他位填充最高位的&#20540;。
(6)&&&&lhax rD,rA,rB ;EA=(rA)+(rB)。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD的其他位填充最高位的&#20540;。
(7)&&&&lhau rD, d(rA) ;EA=(rA)+d。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD的其他位填充最高位的&#20540;。EA存放在rA中,如果rA=0或者rA=rD,则指令&#26684;式无效。
(8)&&&&lhaux rD,rA,rB ;EA=(rA)+(rB)。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD的其他位填充最高位的&#20540;。EA存放在rA中,如果rA=0或者rA=rD,则指令&#26684;式无效。
(9)&&&&lhz rD, d(rA) ;EA=(rA|0)+d。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD的其他位清零。
(10)lhzu rD, d(rA) ;EA=(rA|0)+d。从存储器EA处读取两个字节的数,并加载到rD的低16位。rD其他位清零。EA存入rA,如果rA=0或者rA=rD,则指令&#26684;式无效。
(11)lhzux rD,rA,rB ;EA=(rA)+(rB)。从存储器EA处读取两个字节的数,加载到rD的低16位,rD其他位清零。EA存入rA,如果rA=0或者rA=rD,则指令&#26684;式无效。
(12)lhzx rD,rA,rB ;EA=(rA|0)+(rB),从EA处读取两个字节的数,并加载到rD的低16位,将rD的其他位清零。
(13)lwz rD,d(rA) ;EA=(rA|0)+d,从EA处读取4个字节的数,并加载到rD。
(14)lwzu rD,d(rA) ;EA=(rA)+d,从EA处读取4个字节的数,并加载到rD。rA=EA,如果rA=0或rA=rD,则指令&#26684;式无效。
(15)lwzux rD,rA,rB ;EA=(rA)+(rB),从EA处读取4个字节的数,并加载到rD。rA=EA,如果rA=0或rA=rD,则指令&#26684;式无效。
(16)lwzx rD,rA,rB ;EA=(rA|0)+(rB),从EA处读取4个字节的数,并加载到rD。
整数多字存储/加载指令
表3 整数多字存储/加载指令
语法&#26684;式
rD,d(rA)
rS,d(rA)
(1)&&&&lmw rD,d(rA) ;EA=rA+d。以EA起始的n个连续的字加载到通用寄存器GPRs rD到r31处,n=32-rD。EA必须为4的倍数,如果rA=0,则指令&#26684;式无效。指令执行时间长。
(2)&&&&stmw rS,d(rA) ;EA=rA+d。把通用寄存器从GPRs rS到GPRs r31,存储到以EA起始的n个连续的字存储器,EA必须是4的倍数。指令执行时间长。
表4 分支控制指令
语法&#26684;式
无条件转移
b( ba bl bla)
target_addr
bc( bca bcl bcla)
BO,BI,target_addr
条件转移(转移目标地址由LR指出)
bclr(bclrl)
条件转移(转移目标地址由CTR指出)
bcctr(bcctrl)
(1)&&&&无条件转移指令bx(b ba bl bla)
指令的编码&#26684;式:
指令的语法&#26684;式:
b target_addr(AA=0 LK=0)
ba target_addr(AA=1 LK=0)
bl target_addr(AA=0 LK=1)
bla target_addr(AA=1 LK=1)
如果AA=0,则转移目标地址为LI||0b00的&#20540;经符号位扩展后加上指令地址。
如果AA=1,则转移目标地址为LI||0b00的&#20540;经符号扩展后的&#20540;。
如果LK=1,则转移指令下一条指令的有效地址存放到连接寄存器。
(1)&&&&条件转移指令bcx
指令编码&#26684;式:
指令语法&#26684;式:
bc BO, BI, target_addr(AA=0 LK=0)
bca BO, BI, target_addr(AA=1 LK=0)
bcl BO, BI, target_addr(AA=0 LK=1)
bcla BO, BI, target_addr(AA=1 LK=1)
BI字段表示条件寄存器CR中的位用于转移条件。BO字段操作码定义见表5。
表5 BO字段操作码定义
计数器CTR减量,如果条件不成立则转移
计数器CTR减量,如果条件不成立则转移
如果条件不成立,则转移
计数器CTR减量,如果条件成立则转移
计数器CTR减量,如果条件成立则转移
如果条件成立则转移
计数器CTR减量,如果CTR!=0,则发生转移
计数器CTR减量,如果CTR=0,则发生转移
注:位z表示该位可以被忽略,位y表示是不是条件转移
(2)&&&&条件转移指令bclx(转移目标地址由LR指出)
指令的编码&#26684;式:
指令的语法&#26684;式:
bclr BO, BI(LK=0)
bclrl BO, BI(LK=1)
BI字段表示条件寄存器CR中的位用于转移条件。
BO字段操作码定义如表5所示。
转移目标地址为LR[0-29]||0b00。
如果LK=1,则转移指令下一条有效地址存放到连接寄存器。
(3)&&&&条件转移指令bcctrx(转移目标地址由CTR指出)
指令的编码&#26684;式:
指令的语法&#26684;式:
bcctr BO, BI(LK=0)
bcctrl BO, BI(LK=1)
转移目标地址是CTR||0b00。
如果LK=1,则转移指令下一条指令的有效地址存放到连接寄存器。
如果减量计数器(BO[2]=0),指令&#26684;式无效,则转移到目标地址。
特殊寄存器传送指令
特殊寄存器传送指令如表6所示。
表6 特殊寄存器传送指令
语法&#26684;式
读取机器状态寄存器
写入机器状态寄存器
读取特殊功能寄存器
写入特殊功能寄存器
读取段寄存器
写入段寄存器
间接读取段寄存器
间接写入段寄存器
读取时基寄存器
(1)&&&&读取机器状态寄存器指令mfmsr
&&&指令的编码&#26684;式:
&&&指令的语法&#26684;式:
读取MSR的内容放入rD中,这是超级用户层指令,不影响其他寄存器。
(2)写入机器状态寄存器指令mtmsr
指令的编码&#26684;式:
指令的语法&#26684;式:
把rS的内容存入MSR中,这是超级用户指令。
(1)&&&&读取特殊功能寄存器指令mfspr
指令的编码&#26684;式:
指令的语法&#26684;式:
mfspr rD,SPR
指令操作:
n&—spr[5-9]||spr[0-4]
rD&—spr(n)
特殊功能寄存器(SPR)的编码如表7所示,将SPR的内容存入rD中。
表7 Power PC UISA SPR编码
(2)&&&&写入特殊功能寄存器指令mtspr
指令的编码&#26684;式:
指令的语法&#26684;式:
mtspr spr,rS
把rS的内容存入到指定的特殊功能寄存器中。
(3)&&&&读取段寄存器指令mfsr
指令的编码&#26684;式:
&&&&指令的语法&#26684;式:
&&&&mfsr rD,SR
指令操作:
rD&—SEGREG(SR)
将段寄存器SR的内容读入rD中,这是一个超级用户层指令。
(1)&&&&写入段寄存器指令mtsr
指令的编码&#26684;式:
指令的语法&#26684;式:
mtsr SR,rS
将rS中的内容读入SR,这是一个超级用户层指令。
(2)&&&&间接读取段寄存器指令mfsrin
指令的编码&#26684;式:
指令的语法&#26684;式:
mfsrin rD,rB
指令操作:
rD&—SEGREG(rB[0-3])
由rB寄存器的0~3位选取的段寄存器的内容,复制到rDzhong。这是一个超级用户层指令。
(3)&&&&间接写入段寄存器指令mtsrin
指令的编码&#26684;式:
指令的语法&#26684;式:
mtsrin rS,rB
指令操作:
SEGREG(rB[0-3])&—(rS)
将rS中的内容复制到由rB的0~3位所指定的寄存器中。这是一个超级用户层指令。
(4)&&&&读取时基寄存器指令mftb
指令的编码&#26684;式:
指令的语法&#26684;式:
mftb rD,TBR
指令操作:
n&—tbr[5-9]||tbr[0-4]
if n=268 then
else if n=269 then
该指令的TBR编码如表8所示。
表8 指令mftb的TBR编码
系统调用指令
(1)&&&&系统调用指令sc
指令的编码&#26684;式:
指令的使用:
sc指令调用操作系统去执行服务程序。当控制返回到一个执行系统调用的程序时,寄存器的内容依赖于程序提供的系统所使用的寄存器的约定。
跟在sc指令后面的有效指令地址被放在SRR0中。MSR中的位0、5~9和16~31被放在SRR1中对应的位置,SRR1中位1~4和10~15被设 置为未定义&#20540;。当sc异常产生,异常处理程序更改MSR寄存器。异常处理程序到MSR[IP]形成基址加0xC00偏移量形成的地址去取下一条指令。
受影响的寄存器有:
依赖于系统服务、SRR0、SRR1及MSR。
(2)&&&&中断返回指令rfi
指令的编码&#26684;式:
指令操作:
MSR[16-23,25-27,30-31] &—SRR1[16-23,25-27,30-31]
NIA&—iea SRR0[0-29]||0b00
SRR1中的位0、5~9和16~31被放在MSR中对应的位置。如果新的MSR&#20540;没有使能任何未完的操作,则在MSR的控制下,从地址SRR0[0-29]||0b00取下一条指令。
指令的使用中受影响的寄存器为MSR。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:876544次
积分:8721
积分:8721
排名:第1768名
原创:131篇
转载:95篇
评论:276条
阅读:16215
阅读:40740
阅读:83653
(1)(1)(1)(1)(3)(2)(1)(1)(1)(2)(2)(3)(2)(1)(1)(4)(2)(1)(1)(4)(5)(2)(6)(2)(7)(7)(3)(10)(1)(2)(7)(3)(1)(7)(13)(13)(6)(7)(9)(10)(13)(11)(12)(7)(8)(17)(2)

我要回帖

更多关于 r0寄存器 的文章

 

随机推荐