寄存器_怎么看寄存器通用还是外设-程序员宅基地

技术标签: 所学所思所想  

1 寄存器的分类

1.1 CPU寄存器

CPU寄存器(通用寄存器):

  • 通用寄存器(ARM中有37个)是CPU的组成部分,CPU的很多活动都需要通用寄存器的支持和参与。
  • 专用指令执行、数据运算、变量处理、参数传递

1.2 外设寄存器

外设寄存器(SFR):

  • SFR(special function register,特殊功能寄存器)不在CPU中,而存在于CPU的外设中,我们通过访问外设的SFR来编程操控这个外设,这就是硬件编程控制的方法。
  • 用于控制外设的行为和工作方式。
  • 寄存器值的配置需要根据芯片手册完成。

对于外设寄存器来说:
寄存器属于CPU外设的硬件组成部分。 CPU可以像访问内存一样访问寄存器。 寄存器是CPU的硬件设计者制定的,目的是留作外设被编程控制的“活动开关”。 正如汇编指令集是CPU的编程接口API一样,寄存器是外设硬件的软件编程接口API。使用软件编程控制某一硬件,其实就是编程读写该硬件的寄存器。 编程操作寄存器类似于访问内存。 寄存器中每个bit位都有特定含义,因此编程操作时需要位操作。 单个寄存器的位宽一般和CPU的位宽一样,以实现最佳访问效率。

编程访问寄存器的方法:
汇编方式:

ldr r1, =0xE0200280 
str r0, [r1] 
mov r0, #0 

C语言方式:

int p = (int )0x30008000; 
*p = 16;

2 处理器中的关键寄存器

2.1 PC和SP寄存器

PC - 程序计数器(指令指针 IP):

  • 每执行一条指令,PC中的值就会发生变化
  • PC时钟保存下一条CPU要执行的指令地址

SP - 栈指针(Stack Pointer):

  • 始终指向栈空间的顶端,实现LIFO特性
  • 保存中断断点、保存函数调用返回点、保存CPU现场数据等

2.2 PC 和 SP的使用案例 - 函数调用

函数调用相关的寄存器:

在这里插入图片描述

函数调用栈信息:
在这里插入图片描述

如上为linux系统的栈帧,window系统下的栈帧与linux系统下的栈帧略有差异:被调用保存的寄存器信息linux系统下位于old ebp下面,windows系统下则位于局部变量下。如下举例为windows系统。

函数调用相关汇编代码(intel格式):

// C代码
#include <stdio.h>

void first(int a);
void second(int a);

int main(void)
{
    
    first(10);

    return 0;
}

void first(int a)
{
    
    printf("a = %d\n", a);

    a = 100;
    second(a);
}

void second(int a)
{
    
    printf("a = %d\n", a);
}

// C代码对应的汇编代码
void first(int a)
{
    
00A613D0  push        ebp  
00A613D1  mov         ebp,esp  
00A613D3  sub         esp,0C0h  
00A613D9  push        ebx  
00A613DA  push        esi  
00A613DB  push        edi  
00A613DC  lea         edi,[ebp-0C0h]  
00A613E2  mov         ecx,30h  
00A613E7  mov         eax,0CCCCCCCCh  
00A613EC  rep stos    dword ptr es:[edi]  
    printf("a = %d\n", a);
00A613EE  mov         esi,esp  
00A613F0  mov         eax,dword ptr [a]  
00A613F3  push        eax  
00A613F4  push        0A65858h  
00A613F9  call        dword ptr ds:[0A69114h]  
00A613FF  add         esp,8  
00A61402  cmp         esi,esp  
00A61404  call        __RTC_CheckEsp (0A61140h)  

    a = 100;
00A61409  mov         dword ptr [a],64h  
    second(a);
00A61410  mov         eax,dword ptr [a]  
00A61413  push        eax  
00A61414  call        _second (0A61127h)  
00A61419  add         esp,4  
}
00A6141C  pop         edi  
00A6141D  pop         esi  
00A6141E  pop         ebx  
00A6141F  add         esp,0C0h  
00A61425  cmp         ebp,esp  
00A61427  call        __RTC_CheckEsp (0A61140h)  
00A6142C  mov         esp,ebp  
00A6142E  pop         ebp  
00A6142F  ret  

#include <stdio.h>

void first(int a);
void second(int a);

int main(void)
{
    
00A61450  push        ebp  
00A61451  mov         ebp,esp  
00A61453  sub         esp,0C0h  
00A61459  push        ebx  
00A6145A  push        esi  
00A6145B  push        edi  
00A6145C  lea         edi,[ebp-0C0h]  
00A61462  mov         ecx,30h  
00A61467  mov         eax,0CCCCCCCCh  
00A6146C  rep stos    dword ptr es:[edi]  
    first(10);
00A6146E  push        0Ah  
00A61470  call        _first (0A610A0h)  
00A61475  add         esp,4  

    return 0;
00A61478  xor         eax,eax  
}
00A6147A  pop         edi  
00A6147B  pop         esi  
00A6147C  pop         ebx  
00A6147D  add         esp,0C0h  
00A61483  cmp         ebp,esp  
00A61485  call        __RTC_CheckEsp (0A61140h)  
00A6148A  mov         esp,ebp  
00A6148C  pop         ebp  
00A6148D  ret  


void second(int a)
{
    
00A614A0  push        ebp  
00A614A1  mov         ebp,esp  
00A614A3  sub         esp,0C0h  
00A614A9  push        ebx  
00A614AA  push        esi  
00A614AB  push        edi  
00A614AC  lea         edi,[ebp-0C0h]  
00A614B2  mov         ecx,30h  
00A614B7  mov         eax,0CCCCCCCCh  

void second(int a)
{
    
00A614BC  rep stos    dword ptr es:[edi]  
    printf("a = %d\n", a);
00A614BE  mov         esi,esp  
00A614C0  mov         eax,dword ptr [a]  
00A614C3  push        eax  
00A614C4  push        0A65858h  
00A614C9  call        dword ptr ds:[0A69114h]  
00A614CF  add         esp,8  
00A614D2  cmp         esi,esp  
00A614D4  call        __RTC_CheckEsp (0A61140h)  
}
00A614D9  pop         edi  
00A614DA  pop         esi  
00A614DB  pop         ebx  
00A614DC  add         esp,0C0h  
00A614E2  cmp         ebp,esp  
00A614E4  call        __RTC_CheckEsp (0A61140h)  
00A614E9  mov         esp,ebp  
00A614EB  pop         ebp  
00A614EC  ret 

// 下面对整个程序的汇编代码进行一一分析:
main:
int main(void)
{
    
    push        ebp                
    mov         ebp,esp  
    sub         esp,0C0h  
    // 保存了EBP旧值,EBP指向了ESP所在位置,之后将ESP向下移动
    push        ebx  
    push        esi  
    push        edi  
    // 保存相关寄存器信息
    lea         edi,[ebp-0C0h]  
    mov         ecx,30h  
    mov         eax,0CCCCCCCCh  
    rep stos    dword ptr es:[edi] 
    // 将增长栈帧中部分内容初始化为0CCCCCCCCh(主要用来存放局部变量)

    first(10);
    push        0Ah 
    // 参数入栈 
    call        _first (0A610A0h)
    // 注意call会做两件事情:返回地址入栈、执行被调函数

first:
    void first(int a)
{
    
    push        ebp  
    mov         ebp,esp  
    sub         esp,0C0h  
    push        ebx  
    push        esi  
    push        edi  
    lea         edi,[ebp-0C0h]  
    mov         ecx,30h  
    mov         eax,0CCCCCCCCh  
    rep stos    dword ptr es:[edi]  
    // 如上完成保存EBP旧值、栈帧增长、保存寄存器信息、初始化部分栈内存值

    printf("a = %d\n", a);
    mov         esi,esp 
    // 留着后面做ESP校验的时候用

    mov         eax,dword ptr [a]  
    push        eax    
    push        0A65858h 
    // 参数入栈,共两个参数,分别为第一个字符串参数和第二个整形参数

    call        dword ptr ds:[0A69114h]  
    add         esp,8  
    // 平衡栈:函数调用前后栈指针应该相同

    cmp         esi,esp  
    call        __RTC_CheckEsp (0A61140h)    
    // ESP校验

    a = 100;
    mov         dword ptr [a],64h  
    second(a);
    mov         eax,dword ptr [a]  
    push        eax  
    call        _second (0A61127h)  

second:
void second(int a)
{
    
    push        ebp  
    mov         ebp,esp  
    sub         esp,0C0h  
    push        ebx  
    push        esi  
    push        edi  
    lea         edi,[ebp-0C0h]  
    mov         ecx,30h  
    mov         eax,0CCCCCCCCh 
void second(int a)
{
    
    rep stos    dword ptr es:[edi]  
    printf("a = %d\n", a);
    mov         esi,esp  
    mov         eax,dword ptr [a]  
    push        eax  
    push        0A65858h  
    call        dword ptr ds:[0A69114h]  
    add         esp,8  
    cmp         esi,esp  
    call        __RTC_CheckEsp (0A61140h)  
}
    pop         edi  
    pop         esi  
    pop         ebx 
    // 弹出之前保存的相关寄存器信息 
    add         esp,0C0h  
    cmp         ebp,esp  
    call        __RTC_CheckEsp (0A61140h)  
    mov         esp,ebp  
    // ESP移动到EBP所指的位置
    pop         ebp  
    ret
    // second函数返回
    add         esp,4  
    // 平衡栈,first函数中调用second函数前后的栈指针相同
}
    pop         edi  
    pop         esi  
    pop         ebx  
    add         esp,0C0h  
    cmp         ebp,esp  
    call        __RTC_CheckEsp (0A61140h)  
    mov         esp,ebp  
    pop         ebp  
    ret  
    // first函数返回
    add         esp,4  
    // 平衡栈,first函数中调用second函数前后的栈指针相同

    return 0;
    xor         eax,eax  
}
    pop         edi  
    pop         esi  
    pop         ebx  
    add         esp,0C0h  
    cmp         ebp,esp  
    call        __RTC_CheckEsp (0A61140h)  
    mov         esp,ebp  
    pop         ebp  
    ret 
    // main函数返回

函数调用总结:

  1. 参数入栈
  2. 返回地址入栈
  3. 转去执行被调函数
    1. 保存EBP旧值
    2. 栈帧增长(ESP发生移动)
    3. 保存相关寄存器信息(被调用者保存寄存器)
    4. 初始化局部变量内存空间
    5. 执行相关代码
    6. 之前保存的相关寄存器信息出栈
    7. ESP移动到EBP所指向位置
    8. EBP旧值出栈
    9. ret
  4. 平衡栈(此为外平栈,即由调用者平衡栈;另外还有内平栈)

参考资料:

  1. 嵌入式操作系统原理课
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/SlowIsFastLemon/article/details/103195712

智能推荐

Kali下安装网易云音乐-程序员宅基地

文章浏览阅读433次。很多人在使用Linux时总喜欢听点音乐,Windows下的音乐平台那么多,酷狗、酷我、虾米、网易云音乐等等一大堆,但是到了Linux下就很难找到一个合适的平台来听歌,更多的都只是一个播放器,需要自己下载音乐到本地来播放,这并不能让咱从心理上接受。不过好消息是网易云音乐在Linux下能完..._neteasagamas

win10+deepin双系统安装防踩坑_mbrsotool deepin-程序员宅基地

文章浏览阅读1.2k次。win10+deepin双系统安装防踩坑笔记本:小米游戏本2018款系统:win10专业版加装deepin 20社区版1、第一次安装踩坑过程参考官方U盘安装教程对D盘_mbrsotool deepin

Xilinx FPGA单端时钟设计方法-程序员宅基地

文章浏览阅读501次。1.1Xilinx FPGA单端时钟设计方法1.1.1 本节目录1)本节目录;2)本节引言;3)FPGA简介;4)Xilinx FPGA单端时钟设计方法;5)结束语。1.1.2 本节引言“不积跬步,无以至千里;不积小流,无以成江海。就是说:不积累一步半步的行程,就没有办法达到千里之远;不积累细小的流水,就没有办法汇成江河大海。1.1.3 FPGA简介FPGA(Field Programmable Gate Array)是在PAL、GAL等可编程器件的基础上进一步发展._单端时钟设计

s5原生android 5.0,三星Galaxy S5/S4吃上原生安卓5.0棒棒糖-程序员宅基地

文章浏览阅读321次。IT之家讯,不能否认,三星旗舰机一直都是最热门的安卓系列手机,背后也有着为数众多的开发者支持,这显然是一件好事,这样用户在官方升级出来之前就会提前用上新的系统。那些一直眼馋原生安卓5.0系统的三星S5和三星S4用户听到这个消息应该很高兴,目前针对两款设备的CM12安卓5.0 ROM已经出炉,提供接近原生的安卓5.0体验,支持的机型包括SM-G900I, SM-G0900F和SM-G900M版三星S..._三星s5手机 模拟安卓系统

Android按钮单击事件_android单击事件-程序员宅基地

文章浏览阅读296次。这篇文章主要介绍了Android按钮单击事件的四种常用写法总结; 第四种写法是自己刚开始接触安卓时总用的,后来点击事件多了就用第三种,结果今天看到第四种写法时发现自己忘了,因此记录下来,有时间看看。XML文件代码如下:<Button android:id="@+id/button2" android:layout_width="match_parent" and..._android单击事件

拿到一个待检测的站或给你一个网站,你觉得应该先做什么?-程序员宅基地

文章浏览阅读2.1k次,点赞3次,收藏14次。拿到一个待检测的站或给你一个网站,你觉得应该先做什么?一、信息收集1.获取域名的whois信息,获取注册者邮箱姓名电话等。2.通过站长之家、明小子、k8等查询服务器旁站以及子域名站点,因为主站一般比较难,所以先看看旁站有没有通用性的cms或者其他漏洞。3、通过DNS域传送漏洞、备份号查询、SSl证书、APP、微信公众号、暴力破解、DNS历史记录、K8 C段查询、Jsfinder、360或华为威胁情报、证书序列号获取企业域名与ip。4.通过Nmap、Wappalyzer、御剑等查看服务器操作系统版本

随便推点

谈谈软件架构详解_mary shaw 软件-程序员宅基地

文章浏览阅读2k次,点赞2次,收藏12次。软件架构(software architecture)软件架构(software architecture)是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。软件架构是一个系统的草图。软件架构描述的对象是直接构成系统的抽象组件。各个组件之间的连接则明确和相对细致地描述组件之间的通讯。在实现阶段,这些抽象组件被细化为实际的组件,比如具体某个类或者对象。在面向对象领域中,组件之间的连接通..._mary shaw 软件

CARLA传感器详细文档介绍+python实例(持续更新ing)_carla 语义分割相机-程序员宅基地

文章浏览阅读2.1w次,点赞36次,收藏153次。作为模拟真实驾驶环境的模拟器,CARLA提供了丰富的传感器接口,不同传感器的特点不同,应该针对不同场景选择合适的传感器或将多种传感器结合使用。 _carla 语义分割相机

Nginx root 与 alias_nginx root asian-程序员宅基地

文章浏览阅读1.8k次。叙述nginx指定文件路径有两种方式root和aliasroot语法: root path;默认值:root html;配置段:http、server、location、ifalias语法: alias path;配置段:locationroot与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。root的处理结果是:root路径+location路径alias的处理结果是:使用alia_nginx root asian

重装系统的三种方法和出现的问题_系统重装有几个方法-程序员宅基地

文章浏览阅读551次。 重装系统大概有三种方法:Dell官网下载USB制作小程序,这个程序会自动下载win10家庭版,并且把插入的u盘制作成启动盘,然后插入电脑从优盘启动,选择疑难解答-&gt;从驱动器安装,这种方法会删除所有个人文件,唯一一个好处就是操作系统是正版的。还可以去微软的官网,有一个win10启动盘制作工具下载页面,同样是下载个小程序一键操作,安装的时候可以选择安装在哪个盘,C盘安过之后可以在E盘再..._系统重装有几个方法

最少换乘(最短路+恶心的输入)acm寒假集训日记22/1/3 or 22/1/4_最少换乘课程日志-程序员宅基地

文章浏览阅读430次。题目如下:AC代码如下:#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int inf = 0x3f3f3f3f;const int N = 2009;int d[N][N];int dis[N];int vis[N];char s[N];int jl[N];int main()//目标1->n; { int t;._最少换乘课程日志

【ffmpeg】ffmpeg avformat_open_input返回失败的解决办法_avformat_open_input返回值-5-程序员宅基地

文章浏览阅读2.5k次。很多朋友在使用新版本的ffmpeg时,都遇到了avformat_open_input返回失败的问题。在下也遇到了此问题。在stackoverflow上搜了一下,解决方法如下。在调用avformat_open_input之前,先调用如下接口初始化一下即可。av_register_all();这算是新版本ffmpeg代码流程的一个变化了。老版本的ffmpeg,代码流程如下:avcodec_..._avformat_open_input返回值-5