ARM学习笔记--day07

指令限制:
    mov r1,#0xe0200080 @这条指令是不可以的,因为mov受限制
    指令限制,由于指令长度是32位等宽,造成了限制,例如:mov r0,#0xffffffff,这条指令就是32位,这32位中包含存储助记符,还有条件,S标志等也需要32位内存,最后留给立即数的内存,就不到32位了,所以使用gcc编译是无法编译的,所以例子中的指令是错误指令,凡是指令里边里边出现立即数的,这种指令都会受到限制,出现寄存器就没有问题,因为装入寄存器中的数据,肯定是数据存储在对应的内存中
    数据处理指令(带立即数的):
        16条指令,add,mov,sub,cmp,and,orr,bic等指令
        指令格式参照ARM公司提供的格式:31~28:cond;27~25:指令类型;24~21:opcode(操作码);20:S;19~16:Rn;15~12:Rd;11~8:rotate_imm(R移位位);7~0:immed_8(I位被移位位)x=I循环右移R*2位,这个X是被展开成一个8位数被循环右移2的倍数的数就是合法立即数,否则就是非法的,例如:0x32C,它的二进制:0011 0010 1100,当R取1100 1011:0xCD,是一个8位数,循环左移了2位就是循环右移了30位
    单寄存器的内存读写指令(带立即数的):
        ldr,str
        这条指令,最终留给立即数的位数是12个位
    跳转指令:
        b label(函数入口地址,这个label也可能超出存储界限)
        跳转指令的编码:31~28:cond;27~25:指令类型;24:L(是否带连接跳转);23~0:immed_24(label,这个值就决定了跳转范围),由于是4字节对齐,所以后两位为0,所以可以表示24+2=26个位,所以2的26次方为64M,所以跳转范围是+/- 32M
    swi指令(软中断触发指令):
        swi 立即数(也是有限制的,最大是0xffffff)
伪指令:
    伪指令不受上述的立即数的限制,因为伪指令是好几条真指令的执行,如果立即数非法,就改成伪指令
    gcc下的伪指令,这个伪指令与硬件平台无关,不论是ARM还是86还是mips都适用
    符号定义伪指令:
        常量定义:.equ(汇编中的宏定义),用法:.equ GPC1_BASE,0xe0200080 //GPC1_BASE=0xe0200080
        全局定义:.globel(把某一个label声明称全局的),用法:.globel label
            注:没有.globel声明过的标签,文件内部引用自己的标签可以,其他文件不可以引用
    数据定义伪指令(定义变量):
        .byte(定义一字节的数)(对应C语言中的char)
        .hword(定义二字节的数)(对应C语言中的short)
        .word(定义二字节的数)(对应C语言中的int)
        .quad(定义8字节的数)(对应C语言中的long)
        .string(定义字符串)(对应C语言中的char*)
        用法一致:.word (可选表达式),.word(有.word就有四字节的内存),a:.word,就是定义变量a是占四字节空间,等价于int a; a:.word 0x5<==>int a=5;
        .fill申请任意空间,三个参数(repeat:重复申请的次数,size:表示每次申请的字节数,value:填充的值)==》大小是repeat*size这么大,然后value是size这么大,就是每次填充value值,用法:.fill 1024@这就是申请1024这么大字节
        .arm/.code32:就是告诉编译器,是采用arm指令
        .section:可以定义自定义段,elf格式本身可以有自己的自定义段,会带来调试问题,gdb调试,gcc -g编译完,就把调试信息加进去了,调试信息就会加载到.section自定义段中,这个是elf格式特有的,a.out文件中没有这种段,也就不具备调试方便的优点,所以被慢慢淘汰掉,用法:.section 段名称 属性 ;.section abc这个意思就是告诉编译器,下边内容是放到abc这个段里
        .text:代码段,具备执行、读的属性,不具备写的属性,用法:.text 就是声明下边是代码段
        .bss:存储未初始化全局变量
        .data:数据段,可写,可读,不能执行,用法:.data 就是声明下边是数据段
        a(加载),w(写),x(执行)
C与汇编的调用关系:
    1.寄存器的使用
        r13是堆栈指针,r12有时候是备份r13
        r0~r3:未备份寄存器,不需要考虑原来的值,向用就用,不用担心原来的值,如果调用这段程序之前,r0~r3的值存储了重要的值,在调用之前要保存他们之中的值,被调用程序中的r0~r3就不必担心之前的值了
        r4~r12:备份寄存器,在使用之前要进行保存,需要将这些寄存器先push到堆栈中,stmfd sp!,{r4}@这条指令就是压栈,不再用r4了就弹栈ldmfd sp!,{r4}@这就是弹栈
        压栈,弹栈操作指令描述fd是条件,意为满递减,!号意为自更新sp,就是sp-4的值跟新到sp中,从而让sp指针指向栈顶,4就是存储r4的地址,这样的减操作的目的是保证弹栈先弹栈顶元素
    2.函数怎么调用,传参数,传返回值
        PC程序靠栈传递参数,ARM传递参数靠栈或者是寄存器,函数参数小于4个参数 使用寄存器(r0~r3)来传递,大于4个的时候,多于4的那部分参数使用堆栈来传递
        如果传递的参数的大小大于32位寄存器的长度,这个时候,就需要两个寄存器存储这个参数,ARM只提供4个寄存器传递参数,要是传递大于32位的参数,寄存器就只能传递少于4个参数,其余寄存器无法传递的就需要使用堆栈来传递
        ARM返回值用两个寄存器r0,r1所以才导致了下述结论:只可以返回基本类型和指针类型
        C语言不能返回结构体普通类型,可以返回结构体指针类型
        AAPCS规则:
    3.栈的使用规则:
        ARM默认使用满减栈
        栈:
            满栈:堆栈指针指向最后压入堆栈的数据
            空栈:堆栈指针指向下一个将要放入数据的空位置
            递增:堆栈由低地址向高地址生长
            递减:堆栈由高地址向低地址生长
        ARM使用满减栈:就有堆栈指针指向最后压入堆栈的数据,且方向是向下,就是压栈一个,sp就减4    
题外话:GCC与内核不匹配的话,就会导致编译不了内核,内核严重依赖编译器
gcc编译器的-e选项,指定入口函数地址,gcc -e start(函数)
ARM默认采用小端存储模式
objdump就是反汇编
例子代码重写led:
start.s
    @初始化,配置con寄存器,上拉电阻寄存器
    @实现延时
    @led_init入口函数
.global@为了让.c看得到    
    led_init:
        @GPC1_CON
        @GPC1_PUD
    loop:
        mov r0,#1    @传递参数1给led_ops,亮灯
        bl led_ops    @bl是调用函数专用的
        mov r0,#0    @传递参数1给led_ops,灭灯
        bl led_ops    @bl是调用函数专用的
        b loop
    delay:
        sub r0,r0,#1
        cmp r0,#0
        bne delay
        mov pc,lr    @纯汇编写空函数,c调汇编都会有这条,lr在调用之前就已经备份了,执行完后将lr给pc相当于return,就可以返回        
        
led.c
    //亮灯延时,灭灯延时的代码
    void led_ops(int a){
        volatile int* p = (int *)0xe0200080; //设备指针地址操作必须加volatile易变的,声明指针是不稳定的,这个指针可能会被优化掉?
        if(a){    //亮灯
            *p |= (1<<3);
        }
        else{    //灭灯
            *p |= (1<<3);
        }
        delay(0xff0000);
    }
编译:将led.c start.s编译成.o,然后连接编译成机器码,先连接start.o再连接led.o,因为入口地址是在start.o中
volatile,易变的含义,其有两个功能:
1.告诉编译器编译的时候不要进行优化,这样就避免了多次对指针变量取*后,编译器优化成只执行最后一次取*
2.每次读取数据都要从内存中读取,而不是从寄存器或是cache中读取,这样volatile    变量内存地址上的值变化,这个值也就会变化,不论是不是局部变量
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值