剖析鸿蒙的汇编语言和CPU指令

本篇通过拆解一段很简单的汇编代码来快速认识汇编,为读懂鸿蒙汇编打基础.系列篇后续将逐个剖析鸿蒙的汇编文件.
汇编很简单
第一: 要认定汇编语言一定是简单的,没有高深的东西,无非就是数据的搬来搬去,运行时数据主要待在两个地方:内存和寄存器。寄存器是cpu内部存储器,离运算器最近,所以最快.
第二: 运行空间(栈空间)就是cpu打卡上班的地方,内核设计者规定谁请cpu上班由谁提供场地,用户程序提供的场地叫用户栈,敏感工作cpu要带回公司做,公司提供的场地叫内核栈,敏感工作叫系统调用,系统调用的本质理解是cpu要切换工作模式即切换办公场地。
第三:cpu的工作顺序是流水线的,它只认指令,而且只去一个地方(指向代码段的pc寄存器)拿指令运算消化。指令集是告诉外界我cpu能干什么活并提供对话指令,汇编语言是人和cpu能愉快沟通不拧巴的共识语言。一一对应了cpu指令,又能确保记性不好的人类能模块化的设计idea, 先看一段c编译成汇编代码再来说模块化。
square(c -> 汇编)
//编译器: armv7-a clang (trunk)//++++++++++++ square(c -> 汇编)++++++++++++++++++++++++int square(int a,int b){ return a*b;}square(int, int): sub sp, sp, #8 @sp减去8,意思为给square分配栈空间,只用2个栈空间完成计算 str r0, [sp, #4] @第一个参数入栈 str r1, [sp] @第二个参数入栈 ldr r1, [sp, #4] @取出第一个参数给r1 ldr r2, [sp] @取出第二个参数给r2 mul r0, r1, r2 @执行a*b给r0,返回值的工作一直是交给r0的 add sp, sp, #8 @函数执行完了,要释放申请的栈空间 bx lr @子程序返回,等同于mov pc,lr,即跳到调用处 fp(c -> 汇编)
//++++++++++++ fp(c -> 汇编)++++++++++++++++++++++++int fp(int b){ int a = 1; return square(a+b,a+b);}fp(int): push {r11, lr} @r11(fp)/lr入栈,保存调用者main的位置 mov r11, sp @r11用于保存sp值,函数栈开始位置 sub sp, sp, #8 @sp减去8,意思为给fp分配栈空间,只用2个栈空间完成计算 str r0, [sp, #4] @先保存参数值,放在sp+4,此时r0中存放的是参数 mov r0, #1 @r0=1 str r0, [sp] @再把1也保存在sp的位置 ldr r0, [sp] @把sp的值给r0 ldr r1, [sp, #4] @把sp+4的值给r1 add r1, r0, r1 @执行r1=a+b mov r0, r1 @r0=r1,用r0,r1传参 bl square(int, int)@先mov lr, pc 再mov pc square(int, int) mov sp, r11 @函数执行完了,要释放申请的栈空间 pop {r11, lr} @弹出r11和lr,lr是专用标签,弹出就自动复制给lr寄存器 bx lr @子程序返回,等同于mov pc,lr,即跳到调用处 main(c -> 汇编)
//++++++++++++ main(c -> 汇编)++++++++++++++++++++++++int main(){ int sum = 0; for(int a = 0;a < 100; a++){ sum = sum + fp(a); } return sum;}main: push {r11, lr} @r11(fp)/lr入栈,保存调用者的位置 mov r11, sp @r11用于保存sp值,函数栈开始位置 sub sp, sp, #16 @sp减去16,意思为给main分配栈空间,只用4个栈空间完成计算 mov r0, #0 @初始化r0 str r0, [r11, #-4] @执行sum = 0 str r0, [sp, #8] @sum将始终占用sp+8的位置 str r0, [sp, #4] @a将始终占用sp+4的位置 b .lbb1_1 @跳到循环开始位置.lbb1_1: @循环开始位置入口 ldr r0, [sp, #4] @取出a的值给r0 cmp r0, #99 @跟99比较 bgt .lbb1_4 @大于99,跳出循环 mov pc .lbb1_4 b .lbb1_2 @继续循环,直接 mov pc .lbb1_2.lbb1_2: @符合循环条件入口 ldr r0, [sp, #8] @取出sum的值给r0,sp+8用于写sum的值 str r0, [sp] @先保存sum的值,sp的位置用于读sum值 ldr r0, [sp, #4] @r0用于传参,取出a的值给r0作为fp的参数 bl fp(int) @先mov lr, pc再mov pc fp(int) mov r1, r0 @fp的返回值为r0,保存到r1 ldr r0, [sp] @取出sum的值 add r0, r0, r1 @计算新sum的值,由r0保存 str r0, [sp, #8] @将新sum保存到sp+8的位置 b .lbb1_3 @无条件跳转,直接 mov pc .lbb1_3.lbb1_3: @完成a++操作入口 ldr r0, [sp, #4] @sp+4中记录是a的值,赋给r0 add r0, r0, #1 @r0增加1 str r0, [sp, #4] @把新的a值放回sp+4里去 b .lbb1_1 @跳转到比较 a < 100 处.lbb1_4: @循环结束入口 ldr r0, [sp, #8] @最后sum的结果给r0,返回值的工作一直是交给r0的 mov sp, r11 @函数执行完了,要释放申请的栈空间 pop {r11, lr} @弹出r11和lr,lr是专用标签,弹出就自动复制给lr寄存器 bx lr @子程序返回,跳转到lr处等同于 mov pc, lr 代码有点长,都加了注释,如果能直接看懂那么恭喜你,鸿蒙内核的6个汇编文件基于也就懂了。这是以下c文件全貌
文件全貌
#include #include int square(int a,int b){ return a*b;}int fp(int b){ int a = 1; return square(a+b,a+b);}int main(){ int sum = 0; for(int a = 0;a fp, fp要保存main的栈帧范围和指令位置, lr保存的是main函数执行到哪个指令的位置, r11的作用是指向main的栈顶位置,如此fp执行完后return回main的时候,先mov pc,lr, pc寄存器的值一变, 表示执行的代码就变了,又回到了main的指令和栈帧继续未完成的事业.
内存和寄存器数据怎么搬?
数据主要待在两个地方:内存和寄存器. 寄存器寄存器 , 内存寄存器 , 内存内存 搬运指令都不一样.
str r1, [sp] @ 寄存器->内存 ldr r1, [sp, #4] @ 内存->寄存器 这又是一对,用于 内存寄存器之间,熟知的 mov r0, r1 用于 寄存器寄存器


Linux操作的防火墙软件应用程序定义和配置设置及底层源代码
智能手机的创新之路是哪般?
傅里叶变换的应用 FFT分析信号频谱
GSV6201 USB-C Type-C/ DP 1.4 to HDMI 2.1 8K设计方案
关于SMT基本工艺的知识介绍
剖析鸿蒙的汇编语言和CPU指令
plc在流量计量方面中的应用
小米Max2已经有消息了,你会去买吗?
长城汽车,落地“东南亚”
华为p40手机怎么升级鸿蒙系统
Google似乎正在测试Google搜索用户界面中的细微调整
中国已成为数字医疗行业的领军者
耳机用无线还是有线 到底哪一种比较好
5G云游戏和VR集于一体?
研究人员开发半透明太阳能电池 可用于车辆天窗
中科昊芯DSC28034湖人版OLED屏上显示温湿度值
彩电行业即迎来一轮新的巨变?
纯电容补偿为什么会放大谐波
CEVA推出增强型NeuPro-M NPU IP系列,大力推动生成式人工智能(Generative AI)
“电磁炉”也做无线充电器,iPad也可以充!(实物赏析)