欢迎光临散文网 会员登陆 & 注册

ARM裸机开发篇2:ARM微处理器指令系统(一)

2021-10-25 11:14 作者:华清远见研发中心  | 我要投稿

写在前面:

本文章为《ARM Cortex-A7裸机开发篇》系列中的一篇,全系列总计11篇。笔者使用的开发平台为华清远见FS-MP1A开发板(STM32MP157开发板)。

针对FS-MP1A开发板,除了Cortex-A7裸机开发篇外,还包括其他多系列教程,包括Cortex-M4开发篇、FreeRTOS篇、Linux基础及应用开发篇、Linux系统移植篇、Linux驱动开发篇、硬件设计篇、人工智能机器视觉篇、Qt应用编程篇、Qt综合项目实战篇等。欢迎关注,更多stm32mp157开发教程及视频,可加技术交流Q群483143191,感谢关注。

FS-MP1A开发板详情介绍:item.taobao.com/item.ht

ARM微处理器指令系统

本章主要介绍ARM汇编语言。主要内容如下:

  • ARM处理器的寻址方式。

  • ARM处理器的指令集。

ARM处理器寻址方式

ARM指令的寻址方式分为数据处理指令寻址方式和内存访问指令寻址方式。

数据处理指令寻址方式

数据处理指令的基本语法格式如下:

<opcode> {<cond>} {S} <Rd>,<Rn>,<shifter_operand>

其中,<shifter_operand>有11种形式,如表所示。

表 <shifter_operand>的寻址方式

数据处理指令寻址方式可以分为以下几种。

    1. 立即数寻址方式。

    2. 寄存器寻址方式。

    3. 寄存器移位寻址方式。




    1. 立即数寻址方式

    指令中的立即数是由一个8bit的常数移动4bit偶数位(0,2,4,…,26,28,30)得到的。所以,每一条指令都包含一个8bit的常数X和移位值Y,得到的立即数 = X循环右移(2×Y),如图所示。

    下面列举了一些有效的立即数:

    0xFF、0x104、0xFF0、0xFF00、0xFF000、0xFF000000、0xF000000F

    下面是一些无效的立即数:

    0x101、0x102、0xFF1、0xFF04、0xFF003、0xFFFFFFFF、0xF000001F

    下面是一些应用立即数的指令:

    MOV R0,#0 ;送0到R0

    ADD R3,R3,#1 ;R3的值加1

    CMP R7,#1000 ;将R7的值和1000比较

    BIC R9,R8,#0xFF00 ;将R8中8~15位清零,结果保存在R9中

    1. 寄存器寻址方式

    寄存器的值可以被直接用于数据操作指令,这种寻址方式是各类处理器经常采用的一种方式,也是一种执行效率较高的寻址方式,如:

    MOV R2,R0 ;R0的值送R2

    ADD R4,R3,R2 ;R2加R3,结果送R4

    CMP R7,R8 ;比较R7和R8的值

    1. 寄存器移位寻址方式

    寄存器的值在被送到ALU之前,可以事先经过桶形移位寄存器的处理。预处理和移位发生在同一周期内,所以有效地使用移位寄存器,可以增加代码的执行效率。

    下面是一些在指令中使用了移位操作的例子:

    ADD R2,R0,R1,LSR #5

    MOV R1,R0,LSL #2

    RSB R9,R5,R5,LSL #1

    SUB R1,R2,R0,LSR #4

    MOV R2,R4,ROR R0

    内存访问指令寻址方式

    内存访问指令的寻址方式可以分为以下几种。

    1. 字及无符号字节的Load/Store指令的寻址方式。

    2. 杂类Load/Store指令的寻址方式。

    3. 批量Load/Store指令的寻址方式。

    4. 协处理器Load/Store指令的寻址方式。

    5. 字及无符号字节的Load/Store指令的寻址方式

    字及无符号字节的Load/Store指令语法格式如下:

    LDR|STR{<cond>}{B}{T} <Rd>,<addressing_mode>

    其中,<addressing_mode>共有9种寻址方式,如表所示。

    上表中,“!”表示完成数据传输后要更新基址寄存器。

    1. 杂类Load/Store指令的寻址方式

    使用该类寻址方式的指令的语法格式如下:

    LDR|STR{<cond>}H|SH|SB|D <Rd>,<addressing_mode>

    使用该类寻址方式的指令包括(有符号/无符号)半字Load/Store指令、有符号字节Load/Store指令和双字Load/Store指令。

    该类寻址方式分为6种类型,如表所示。

    格 式模 式1[Rn,#±<offset_8>]立即数偏移寻址(Immediate offset)2[Rn,±Rm]寄存器偏移寻址(Register offset)3[Rn,#±< offset_8>]!立即数前索引寻址(Immediate pre-indexed)4[Rn,±Rm]!寄存器前索引寻址(Register post-indexed)5[Rn],#±< offset_8>立即数后索引寻址(Immediate post-indexed)6[Rn],±<Rm>寄存器后索引寻址(Register post-indexed)

    1. 批量Load/Store指令寻址方式

    批量Load/Store指令将一片连续内存单元的数据加载到通用寄存器组中或将一组通用寄存器的数据存储到内存单元中。

    批量Load/Store指令的寻址模式产生一个内存单元的地址范围,指令寄存器和内存单元的对应关系满足这样的规则,即编号低的寄存器对应于内存中低地址单元,编号高的寄存器对应于内存中的高地址单元。

    该类指令的语法格式如下:

    LDM|STM{<cond>}<addressing_mode> <Rn>{!},<registers><^>

    该类指令的寻址方式如表所示。

    格 式模 式1IA(Increment After)后递增方式2IB(Increment Before)先递增方式3DA(Decrement After)后递减方式4DB(Decrement Before)先递减方式

    1. 堆栈操作寻址方式

    堆栈操作寻址方式和批量Load/Store指令寻址方式十分类似。但对于堆栈的操作,数据写入内存和从内存中读出要使用不同的寻址模式,因为进栈操作(pop)和出栈操作(push)要在不同的方向上调整堆栈。

    下面详细讨论如何使用合适的寻址方式实现数据的堆栈操作。

    根据不同的寻址方式,将堆栈分为以下4种。

      1. Full栈:堆栈指针指向栈顶元素(last used location)。

      2. Empty栈:堆栈指针指向第一个可用元素(the first unused location)。

      3. 递减栈:堆栈向内存地址减小的方向生长。

      4. 递增栈:堆栈向内存地址增加的方向生长。




      根据堆栈的不同种类,将其寻址方式分为以下4种。

        1. 满递减FD(Full Descending)。

        2. 空递减ED(Empty Descending)。

        3. 满递增FA(Full Ascending)。

        4. 空递增EA(Empty Ascending)。

        如表所示列出了堆栈的寻址方式和批量Load/Store指令寻址方式的对应关系。

        批量数据寻址方式堆栈寻址方式L位P位U位LDMDALDMFA100LDMIALDMFD101LDMDBLDMEA110LDMIBLDMED111STMDASTMED000STMIASTMEA001STMDBSTMFD010STMIBSTMFA011

        1. 协处理器Load/Store寻址方式

        协处理器Load/Store指令的语法格式如下:

        <opcode>{<cond>}{L} <coproc>,<CRd>,<addressing_mode>


        ARM处理器指令集


        数据操作指令

        数据操作指令是指对存放在寄存器中的数据进行操作的指令。主要包括数据传送指令、算术指令、逻辑指令、比较与测试指令及乘法指令。

        如果在数据处理指令前使用S前缀,指令的执行结果将会影响CPSR中的标志位。数据处理指令如表所示。

        助 记 符操 作行 为MOV数据传送MVN数据取反传送AND逻辑与Rd:=Rn AND op2EOR逻辑异或Rd:=Rn EOR op2SUB减Rd:=Rn − op2RSB翻转减Rd:=op2 − RnADD加Rd:=Rn + op2ADC带进位的加Rd:=Rn + op2 + CSBC带进位的减Rd:=Rn− op2 + C − 1RSC带进位的翻转减Rd:=op2 − Rn + C − 1TST测试Rn AND op2并更新标志位TEQ测试相等Rn EOR op2并更新标志位CMP比较Rn−op2并更新标志位CMN负数比较Rn+op2并更新标志位ORR逻辑或Rd:=Rn OR op2BIC位清0Rd:=Rn AND NOT(op2)


        1. MOV指令

        MOV指令是最简单的ARM指令,执行的结果就是把一个数N送到目标寄存器Rd,其中N可以是寄存器,也可以是立即数。

        MOV指令多用于设置初始值或者在寄存器间传送数据。

        MOV指令将移位码(shifter_operand)表示的数据传送到目的寄存器Rd,并根据操作的结果更新CPSR中相应的条件标志位。

        1. 指令的语法格式:

        MOV{<cond>}{S} <Rd>,<shifter_operand>

        1. 指令举例:

        MOV R0, R0 ; R0 = R0… NOP 指令

        MOV R0, R0, LSL#3 ; R0 = R0 * 8

        如果R15是目的寄存器,将修改程序计数器或标志。这用于被调用的子函数结束后返回到调用代码,方法是把连接寄存器的内容传送到R15。

        MOV PC, R14 ; 退出到调用者,用于普通函数返回,PC即是R15

        MOVS PC, R14 ; 退出到调用者并恢复标志位,用于异常函数返回

        1. 指令的使用如下。

        MOV指令主要完成以下功能。

        将数据从一个寄存器传送到另一个寄存器。

        将一个常数值传送到寄存器中。

        实现无算术和逻辑运算的单纯移位操作,操作数乘以2n可以用左移n位来实现。

        当PC(R15)用做目的寄存器时,可以实现程序跳转。如“MOV PC,LR”,所以这种跳转可以实现子程序调用及从子程序返回,代替指令“B,BL”。

        当PC作为目标寄存器且指令中S位被设置时,指令在执行跳转操作的同时,将当前处理器模式的SPSR寄存器的内容复制到CPSR中。这种指令“MOVS PC LR”可以实现从某些异常中断中返回。

        1. MVN指令

        MVN是反相传送(Move Negative)指令。它将操作数的反码传送到目的寄存器。

        MVN指令多用于向寄存器传送一个负数或生成位掩码。

        MVN指令将shifter_operand表示的数据的反码传送到目的寄存器Rd,并根据操作结果更新CPSR中相应的条件标志位。

        1. 指令的语法格式:

        MNV{<cond>}{S} <Rd>,<shifter_operand>

        指令举例如下

        1. MVN指令和MOV指令相同,也可以把一个数N送到目标寄存器Rd,其中N可以是立即数,也可以是寄存器。这是逻辑非操作而不是算术操作,这个取反的值加1才是它的取负的值。

        MVN R0, #4 ; R0 = -5

        MVN R0, #0 ; R0 = -1

        1. 指令的使用如下。

        MVN指令主要完成以下功能:

        向寄存器中传送一个负数。

        生成位掩码(Bit Mask)。

        求一个数的反码。

        1. AND指令

        AND指令将shifter_operand表示的数值与寄存器Rn的值按位(bitwise)做逻辑与操作,并将结果保存到目标寄存器Rd中,同时根据操作的结果更新CPSR寄存器。

          1. 指令的语法格式:




          AND{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

            1. 指令举例如下。


            2. 保留R0中的0位和1位,丢弃其余的位。

            AND R0, R0, #3

            1. R2 = R1&R3。

            AND R2,R1,R3

            1. R0 = R0&0x01,取出最低位数据。

            ANDS R0,R0,#0x01

            1. EOR指令

            EOR(Exclusive OR)指令将寄存器Rn中的值和shifter_operand的值执行按位“异或”操作,并将执行结果存储到目的寄存器Rd中,同时根据指令的执行结果更新CPSR中相应的条件标志位。

            1. 指令的语法格式:

            EOR{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

            1. 指令举例:

            2. 反转R0中的位0和1。

            EOR R0, R0, #3

            1. 将R1的低4位取反。

            EOR R1,R1,#0x0F

            1. R2 = R1∧R0。

            EOR R2,R1,R0

            1. 将R5和0x01进行逻辑异或,结果保存到R0,并根据执行结果设置标志位。

            EORS R0,R5,#0x01

            1. SUB指令

            SUB(Subtract)指令从寄存器Rn中减去shifter_operand表示的数值,并将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

              1. 指令的语法格式:




              SUB{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                1. SUB指令举例:

                2. R0 = R1 − R2。

                SUB R0, R1, R2

                1. R0 = R1 − 256。

                SUB R0, R1, #256

                1. R0 = R2− (R3<<1)。

                SUB R0, R2, R3,LSL#1

                1. RSB指令

                RSB(Reverse Subtract)指令从寄存器shifter_operand中减去Rn表示的数值,并将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

                  1. 指令的语法格式:




                  RSB{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                    1. RSB指令举例:




                    下面的指令序列可以求一个64位数值的负数。64位数放在寄存器R0与R1中,其负数放在R2和R3中。其中R0与R2中放低32位值。

                    RSBS R2,R0,#0

                    RSC R3,R1,#0

                    1. ADD指令

                    ADD指令将寄存器shifter_operand的值加上Rn表示的数值,并将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

                      1. 指令的语法格式:




                      ADD{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                        1. ADD指令举例:




                        ADD R0, R1, R2 ; R0 = R1 + R2

                        ADD R0, R1, #256 ; R0 = R1 + 256

                        ADD R0, R2, R3,LSL#1 ; R0 = R2 + (R3 << 1)

                        1. ADC指令

                        ADC指令将寄存器shifter_operand的值加上Rn表示的数值,再加上CPSR中的C条件标志位的值,将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

                          1. 指令的语法格式:




                          ADC{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                            1. ADC指令举例:




                            ADC指令将把两个操作数加起来,并把结果放置到目的寄存器中。它使用一个进位标志位,这样就可以做比32位大的加法。下面的例子将加两个128位的数。

                            128位结果:寄存器R0、R1、R2和R3。

                            第一个128位数:寄存器R4、R5、R6和R7。

                            第二个128位数:寄存器R8、R9、R10和R11。

                            ADDS R0, R4, R8 ;加低端的字

                            ADCS R1, R5, R9 ;加下一个字,带进位

                            ADCS R2, R6, R10 ;加第三个字,带进位

                            ADCS R3, R7, R11 ;加高端的字,带进位

                            1. SBC指令

                            SBC(Subtract with Carry)指令用于执行操作数大于32位时的减法操作。该指令从寄存器Rn中减去shifter_operand表示的数值,再减去寄存器CPSR中C条件标志位的反码[NOT(Carry flag)],并将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

                              1. 指令的语法格式:




                              SBC{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                                1. SBC指令举例:




                                下面的程序使用SBC实现64位减法,(R1,R0)−(R3,R2),结果存放到(R1,R0)。

                                SUBS R0,R0,R2

                                SBCS R1,R1,R3

                                1. RSC指令

                                RSC(Reverse Subtract with Carry)指令从寄存器shifter_operand中减去Rn表示的数值,再减去寄存器CPSR中C条件标志位的反码[NOT(Carry Flag)],并将结果保存到目标寄存器Rd中,并根据指令的执行结果设置CPSR中相应的标志位。

                                  1. 指令的语法格式:




                                  RSC{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                                    1. RSC指令举例:




                                    下面的程序使用RSC指令实现求64位数值的负数。

                                    RSBS R2,R0,#0

                                    RSC R3,R1,#0

                                    1. TST测试指令

                                    TST(Test)测试指令用于将一个寄存器的值和一个算术值进行比较。条件标志位根据两个操作数做“逻辑与”后的结果设置。

                                      1. 指令的语法格式:




                                      TST{<cond>} <Rn>,<shifter_operand>

                                        1. TST指令举例:




                                        TST指令类似于CMP指令,不产生放置到目的寄存器中的结果。而是在给出的两个操作数上进行操作并把结果反映到状态标志上。使用TST指令来检查是否设置了特定的位。操作数1是要测试的数据字而操作数2是一个位掩码。经过测试后,如果匹配则设置Zero标志,否则清除它。与CMP指令一样,该指令不需要指定S后缀。

                                        下面的指令测试在R0中是否设置了位0。

                                        TST R0, #1

                                        1. TEQ指令

                                        TEQ(Test Equivalence)指令用于将一个寄存器的值和一个算术值做比较。条件标志位根据两个操作数做“逻辑异或”后的结果设置。以便后面的指令根据相应的条件标志来判断是否执行。

                                          1. 指令的语法格式:




                                          TEQ{<cond>} <Rn>,<shifter_operand>

                                            1. TEQ指令举例:




                                            下面的指令是比较R0和R1是否相等,该指令不影响CPSR中的V位和C位。

                                            TEQ R0,R1

                                            TST指令与EORS指令的区别在于TST指令不保存运算结果。使用TEQ进行相等测试,常与EQ和NE条件码配合使用,当两个数据相等时,条件码EQ有效;否则条件码NE有效。

                                            1. CMP指令

                                            CMP(Compare)指令使用寄存器Rn的值减去operand2的值,根据操作的结果更新CPSR中相应的条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。

                                              1. 指令的语法格式:




                                              CMP{<cond>} <Rn>,<shifter_operand>

                                                1. CMP指令举例:




                                                CMP指令允许把一个寄存器的内容与另一个寄存器的内容或立即值进行比较,更改状态标志来允许进行条件执行。它进行一次减法,但不存储结果,而是正确地更改标志位。标志位表示的是操作数1与操作数2比较的结果(其值可能为大于、小于、相等)。如果操作数1大于操作数2,则此后的有GT后缀的指令将可以执行。

                                                显然,CMP不需要显式地指定S后缀来更改状态标志。

                                                1. 下面的指令是比较R1和立即数10并设置相关的标志位。

                                                CMP R1,#10

                                                1. 下面的指令是比较寄存器R1和R2中的值并设置相关的标志位。

                                                CMP R1,R2

                                                通过上面的例子可以看出,CMP指令与SUBS指令的区别在于CMP指令不保存运算结果,在进行两个数据大小判断时,常用CMP指令及相应的条件码来进行操作。

                                                1. CMN指令

                                                CMN(Compare Negative)指令使用寄存器Rn的值减去operand2的负数值(加上operand2),根据操作的结果更新CPSR中相应的条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。

                                                  1. 指令的语法格式:




                                                  CMN{<cond>} <Rn>,<shifter_operand>

                                                    1. CMN指令举例:




                                                    CMN指令将寄存器Rn中的值加上shifter_operand表示的数值,根据加法的结果设置CPSR中相应的条件标志位。寄存器Rn中的值加上shifter_operand的操作结果对CPSR中条件标志位的影响,与寄存器Rn中的值减去shifter_operand的操作结果的相反数对CPSR中条件标志位的影响有细微差别。当第2个操作数为0或者为0x80000000时两者结果不同。比如下面两条指令。

                                                    CMP Rn,#0

                                                    CMN Rn,#0

                                                    第1条指令使标志位C值为1,第2条指令使标志位C值为0。

                                                    下面的指令使R0值加1,判断R0是否为1的补码,若是,则Z置位。

                                                    CMN R0,#1

                                                    1. ORR指令

                                                    ORR(Logical OR)为逻辑或操作指令,它将第2个源操作数shifter_operand的值与寄存器Rn的值按位做“逻辑或”操作,结果保存到Rd中。

                                                      1. 指令的语法格式:




                                                      ORR{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                                                        1. ORR指令举例:




                                                        1. 设置R0中位0和1。

                                                        ORR R0, R0, #3

                                                        1. 将R0的低4位置1。

                                                        ORR R0,R0,#0x0F

                                                        1. 使用ORR指令将R2的高8位数据移入到R3的低8位中。

                                                        MOV R1,R2,LSR #4

                                                        ORR R3,R1,R3,LSL #8

                                                        1. BIC位清零指令

                                                        BIC(Bit Clear)位清零指令,将寄存器Rn的值与第2个源操作数shifter_operand的值的反码按位做“逻辑与”操作,结果保存到Rd中。

                                                          1. 指令的语法格式:




                                                          BIC{<cond>}{S} <Rd>,<Rn>,<shifter_operand>

                                                            1. BIC指令举例:




                                                            1. 清除R0中的位0、1和3,保持其余的不变。

                                                            BIC R0, R0, #0x1011

                                                            1. 将R3的反码和R2做“逻辑与”操作,结果保存到R1中。

                                                            BIC R1,R2,R3

                                                            乘法指令

                                                            ARM乘法指令完成两个数据的乘法。两个32位二进制数相乘的结果是64位的积。在有些ARM的处理器版本中,将乘积的结果保存到两个独立的寄存器中。另外一些版本只将最低有效32位存放到一个寄存器中。无论是哪种版本的处理器,都有乘—累加的变型指令,将乘积连续累加得到总和。而且有符号数和无符号数都能使用。对于有符号数和无符号数,结果的最低有效位是一样的。因此,对于只保留32位结果的乘法指令,不需要区分有符号数和无符号数这两种情况。

                                                            如表所示为各种形式乘法指令的功能。


                                                            操作码[23∶21]助 记 符意 义操 作000MUL乘(保留32位结果)Rd:=(Rm×Rs)[31∶0]001MLA乘—累加(保留32位结果)Rd:=(Rm×Rs+Rn)[31∶0]100UMULL无符号数长乘RdHi:RdLo:=Rm×Rs101UMLAL无符号长乘—累加RdHi:RdLo:+=Rm×Rs110SMULL有符号数长乘RdHi:RdLo:=Rm×Rs111SMLAL有符号数长乘—累加RdHi:RdLo:+=Rm×Rs

                                                            其中:

                                                              1. “RdHi:RdLo”是由RdHi(最高有效32位)和RdLo(最低有效32位)连接形成的64位数,“[31:0]”只选取结果的最低有效32位。

                                                              2. 简单的赋值由“:=”表示。

                                                              3. 累加(将右边加到左边)是由“+ =”表示。




                                                              各个乘法指令中的位S(参考下文具体指令的语法格式)控制条件码的设置会产生以下结果。

                                                              1. 对于产生32位结果的指令形式,将标志位N设置为Rd的第31位的值;对于产生长结果的指令形式,将其设置为RdHi的第31位的值。

                                                              2. 对于产生32位结果的指令形式,如果Rd等于零,则标志位Z置位;对于产生长结果的指令形式,RdHi和RdLo同时为零时,标志位Z置位。

                                                              3. 将标志位C设置成无意义的值。

                                                              4. 标志位V不变。

                                                              5. MUL指令

                                                              MUL(Multiply)32位乘法指令将Rm和Rs中的值相乘,结果的最低32位保存到Rd中。

                                                                1. 指令的语法格式:




                                                                MUL{<cond>}{S} <Rd>,<Rm>,<Rs>

                                                                  1. 指令举例:




                                                                  1. R1 = R2 × R3。

                                                                  MUL R1, R2, R3

                                                                  1. R0 = R3 × R7,同时设置CPSR中的N位和Z位。

                                                                  MULS R0, R3, R7

                                                                  1. MLA指令

                                                                  MLA(Multiply Accumulate)32位乘—累加指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的最低32位保存到Rd中。

                                                                    1. 指令的语法格式:




                                                                    MLA{<cond>}{S} <Rd>,<Rm>,<Rs>,<Rn>

                                                                      1. 指令举例:




                                                                      下面的指令完成R1 = R2×R3 + 10的操作。

                                                                      MOV R0, #0x0A

                                                                      MLA R1, R2, R3, R0

                                                                      1. UMULL指令

                                                                      UMULL(Unsigned Multiply Long)为64位无符号乘法指令。它将Rm和Rs中的值做无符号数相乘,结果的低32位保存到RsLo中,高32位保存到RdHi中。

                                                                        1. 指令的语法格式:




                                                                        UMULL{<cond>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>

                                                                          1. 指令举例:




                                                                          下面的指令完成(R1,R0) = R5 × R8操作。

                                                                          UMULL R0, R1, R5, R8;

                                                                          1. UMLAL指令

                                                                          UMLAL(Unsigned Multiply Accumulate Long)为64位无符号长乘—累加指令。指令将Rm和Rs中的值做无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RsLo中,高32位保存到RdHi中。

                                                                            1. 指令的语法格式:




                                                                            UMALL{<cond>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>

                                                                              1. 指令举例:




                                                                              下面的指令完成(R1,R0) = R5 × R8+(R1,R0)操作。

                                                                              UMLAL R0, R1, R5,R8;

                                                                              1. SMULL指令

                                                                              SMULL(Signed Multiply Long)为64位有符号长乘法指令。指令将Rm和Rs中的值做有符号数相乘,结果的低32位保存到RsLo中,高32位保存到RdHi中。

                                                                                1. 指令的语法格式:




                                                                                SMULL{<cond>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>

                                                                                  1. 指令举例:




                                                                                  下面的指令完成(R3,R2) = R7 × R6操作。

                                                                                  SMULL R2, R3, R7,R6;

                                                                                  1. SMLAL指令

                                                                                  SMLAL(Signed Multiply Accumulate Long)为64位有符号长乘—累加指令。指令将Rm和Rs中的值做有符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RsLo中,高32位保存到RdHi中。

                                                                                    1. 指令的语法格式:




                                                                                    SMLAL{<cond>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>

                                                                                      1. 指令举例:




                                                                                      下面的指令完成(R3,R2) = R7 × R6 +(R3,R2)操作。

                                                                                      SMLAL R2, R3, R7,R6;

                                                                                      Load/Store指令

                                                                                      Load/Store内存访问指令在ARM寄存器和存储器之间传送数据。ARM指令中有3种基本的数据传送指令。

                                                                                        1. 单寄存器Load/Store指令(Single Register),这些指令在ARM寄存器和存储器之间提供更灵活的单数据项传送方式。数据项可以是字节、16位半字或32位字。

                                                                                        2. 多寄存器Load/Store内存访问指令。这些指令的灵活性比单寄存器传送指令差,但可以使大量的数据更有效地传送。它们用于进程的进入和退出、保存和恢复工作寄存器及复制存储器中的一块数据。

                                                                                        3. 单寄存器交换指令(Single Register Swap)。这些指令允许寄存器和存储器中的数值进行交换,在一条指令中有效地完成Load/Store操作。它们在用户级编程中很少用到。它的主要用途是在多处理器系统中实现信号量(Semaphores)的操作,以保证不会同时访问公用的数据结构。

                                                                                        4. 单寄存器的Load/Store指令,这种指令用于把单一的数据传入或者传出一个寄存器。支持的数据类型有字节(8位)、半字(16位)和字(32位)。




                                                                                        1. 单寄存器的Load/Store指令

                                                                                        如表所示列出了所有单寄存器的Load/Store指令。

                                                                                        指 令作 用操 作LDR把存储器中的一个字装入一个寄存器Rd←mem32[address]STR将寄存器中的字保存到存储器Rd→mem32[address]LDRB把一个字节装入一个寄存器Rd←mem8[address]STRB将寄存器中的低8位字节保存到存储器Rd→mem8[address]LDRH把一个半字装入一个寄存器Rd←mem16[address]STRH将寄存器中的低16位半字保存到存储器Rd→mem16[address]LDRBT用户模式下将一个字节装入寄存器Rd←mem8[address] under user modeSTRBT用户模式下将寄存器中的低8位字节保存到存储器Rd→mem8[address] under user modeLDRT用户模式下把一个字装入一个寄存器Rd←mem32[address]under user modeSTRT用户模式下将存储器中的字保存到寄存器Rd←mem32[address] ]under user modeLDRSB把一个有符号字节装入一个寄存器Rd←sign{mem8[address]}LDRSH把一个有符号半字装入一个寄存器Rd←sign{mem16[address]}

                                                                                        1. LDR指令

                                                                                        LDR指令用于从内存中将一个32位的字读取到目标寄存器。

                                                                                          1. 指令的语法格式:




                                                                                          LDR{<cond>} <Rd>,<addr_mode>

                                                                                            1. 指令举例:




                                                                                            LDR R1,[R0,#0x12] ;将R0+12地址处的数据读出,保存到R1中(R0的值不变)

                                                                                            LDR R1,[R0] ;将R0地址处的数据读出,保存到R1中(零偏移)

                                                                                            LDR R1,[R0,R2] ;将R0+R2地址的数据读出,保存到R1中(R0的值不变)

                                                                                            LDR R1,[R0,R2,LSL #2] ;将R0+R2×4地址处的数据读出,保存到R1中(R0、R2的值不变)

                                                                                            LDR Rd,label ;label为程序标号,label必须是当前指令的-4~4KB范围内

                                                                                            LDR Rd,[Rn],#0x04 ;Rn的值用做传输数据的存储地址。在数据传送后,将偏移量0x04与Rn相加,结果写回到Rn中。Rn不允许是R15

                                                                                            1. STR指令

                                                                                            STR指令用于将一个32位的字数据写入到指令中指定的内存单元。

                                                                                              1. 指令的语法格式:




                                                                                              STR{<cond>} <Rd>,<addr_mode>

                                                                                                1. 指令举例:LDR/STR指令用于对内存变量的访问、内存缓冲区数据的访问、查表、外围部件的控制操作等,若使用LDR指令加载数据到PC寄存器,则实现程序跳转功能,这样也就实现了程序散转。




                                                                                                1. 变量访问。

                                                                                                NumCount .equ 0x40003000 ;定义变量NumCount

                                                                                                LDR R0,=NumCount ;使用LDR伪指令装载NumCount的地址到R0

                                                                                                LDR R1,[R0] ;取出变量值

                                                                                                ADD R1,R1,#1 ;NumCount=NumCount+1

                                                                                                STR R1,[R0] ;保存变量

                                                                                                1. GPIO设置。

                                                                                                GPIO—BASE .equ 0xe0028000 ;定义GPIO寄存器的基地址

                                                                                                LDR R0,=GPIO—BASE

                                                                                                LDR R1,=0x00ffff00 ;将设置值放入寄存器

                                                                                                STR R1,[R0,#0x0C] ;IODIR=0x00ffff00,IOSET的地址为0xE0028004

                                                                                                1. 程序散转。

                                                                                                MOV R2,R2,LSL #2 ;功能号乘以4,以便查表

                                                                                                LDR PC,[PC,R2] ;查表取得对应功能子程序地址并跳转

                                                                                                NOP

                                                                                                FUN—TAB .woRd FUN—SUB0

                                                                                                .woRd FUN—SUB1

                                                                                                .woRd FUN—SUB2

                                                                                                1. LDRB指令

                                                                                                LDRB指令根据addr_mode所确定的地址模式将一个8位字节读取到指令中的目标寄存器Rd。

                                                                                                指令的语法格式:

                                                                                                LDR{<cond>}B <Rd>, <addr_mode>

                                                                                                1. STRB指令

                                                                                                STRB指令从寄存器中取出指定的8位字节放入寄存器的低8位,并将寄存器的高位补0。指令的语法格式:

                                                                                                STR{<cond>}B <Rd>,<addr_mode>

                                                                                                1. LDRH指令

                                                                                                LDRH指令用于从内存中将一个16位的半字读取到目标寄存器。

                                                                                                如果指令的内存地址不是半字节对齐的,指令的执行结果不可预知。

                                                                                                指令的语法格式:

                                                                                                LDR{<cond>}H <Rd>,<addr_mode>

                                                                                                1. STRH指令

                                                                                                STRH指令从寄存器中取出指定的16位半字放入寄存器的低16位,并将寄存器的高位补0。

                                                                                                指令的语法格式:

                                                                                                STR{<cond>}H <Rd>,<addr_mode>


                                                                                                1. 多寄存器的Load/Store内存访问指令

                                                                                                多寄存器的Load/Store内存访问指令也叫批量加载/存储指令,它可以实现在一组寄存器和一块连续的内存单元之间传送数据。LDM用于加载多个寄存器,STM用于存储多个寄存器。多寄存器的Load/Store内存访问指令允许一条指令传送16个寄存器的任何子集或所有寄存器。多寄存器的Load/Store内存访问指令主要用于现场保护、数据复制和参数传递等。如表所示列出了多寄存器的Load/Store内存访问指令。

                                                                                                表 多寄存器的Load/Store内存访问指令

                                                                                                指 令作 用操 作LDM装载多个寄存器{Rd}*N←mem32[start address+4*N]STM保存多个寄存器{Rd}*N→mem32[start address+4*N]

                                                                                                1. LDM指令

                                                                                                LDM指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。当PC包含在LDM指令的寄存器列表中时,指令从内存中读取的字数据将被作为目标地址值,指令执行后程序将从目标地址处开始执行,从而实现了指令的跳转。

                                                                                                指令的语法格式:

                                                                                                LDM{<cond>}<addressing_mode> <Rn>{!}, <registers>

                                                                                                寄存器R0~R15分别对应于指令编码中bit[0]~bit[15]位。如果Ri存在于寄存器列表中,则相应的位等于1,否则为0。LDM指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。

                                                                                                指令的语法格式:

                                                                                                LDM{<cond>}<addressing_mode><Rn>,<registers_without_pc>

                                                                                                1. STM指令

                                                                                                STM指令将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中。主要用于块数据的写入、数据栈操作及进入子程序时保存相关寄存器的操作。

                                                                                                指令的语法格式:

                                                                                                STM{<cond>}<addressing_mode> <Rn>{!}, <registers>

                                                                                                1. 数据传送指令应用

                                                                                                LDM/STM批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器,STM为存储多个寄存器。允许一条指令传送16个寄存器的任何子集或所有寄存器。指令格式如下:

                                                                                                LDM{cond}<模式> Rn{!},regist{ˆ}

                                                                                                STM{cond}<模式> Rn{!},regist{ˆ}

                                                                                                LDM/STM的主要用途有现场保护、数据复制和参数传递等。其模式有8种,其中前面4种用于数据块的传输,后面4种是堆栈操作,如下所示。

                                                                                                (1)IA:每次传送后地址加4。

                                                                                                (2)IB:每次传送前地址加4。

                                                                                                (3)DA:每次传送后地址减4。

                                                                                                (4)DB:每次传送前地址减4。

                                                                                                (5)FD:满递减堆栈。

                                                                                                (6)ED:空递增堆栈。

                                                                                                (7)FA:满递增堆栈。

                                                                                                (8)EA:空递增堆栈。

                                                                                                其中,寄存器Rn为基址寄存器,装有传送数据的初始地址,Rn不允许为R15;后缀“!”表示最后的地址写回到Rn中;寄存器列表reglist可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6~R9},寄存器排列由小到大排列;“ˆ”后缀不允许在用户模式下使用,只能在系统模式下使用。若在LDM指令用寄存器列表中包含有PC时使用,那么除了正常的多寄存器传送外,将SPSR复制到CPSR中,这可用于异常处理返回;使用“ˆ”后缀进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式寄存器,而不是当前模式寄存器。

                                                                                                LDMIA R0!,{R3~R9} ;加载R0指向的地址上的多字数据,保存到R3~R9中,R0值更新

                                                                                                STMIA R1!,{R3~R9} ;将R3~R9的数据存储到R1指向的地址上,R1值更新

                                                                                                STMFD SP!,{R0~R7,LR} ;现场保存,将R0~R7、LR入栈

                                                                                                LDMFD SP!,{R0~R7,PC}ˆ ;恢复现场,异常处理返回

                                                                                                在进行数据复制时,先设置好源数据指针,然后使用块复制寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用SP然后使用堆栈寻址指令STMFD/LDMFD、STMED/LDMED、STMEA/LDMEA实现堆栈操作。数据是存储在基址寄存器的地址之上还是之下,地址是存储第一个值之前还是之后、增加还是减少,如表所示。

                                                                                                向 上 生 长向 下 生 长满空满空增加之前STMIBLDMIBSTMFALDMED之后STMIALDMIASTMEALDMFD增加之前LDMDBSTMDBLDMEASTMFD之后LDMDASTMDALDMFASTMED

                                                                                                【举例】 使用LDM/STM进行数据复制。

                                                                                                LDR R0,=SrcData ;设置源数据地址

                                                                                                LDR R1,=DstData ;设置目标地址

                                                                                                LDMIA R0,{R2~R9} ;加载8字数据到寄存器R2~R9

                                                                                                STMIA R1,{R2~R9} ;存储寄存器R2~R9到目标地址

                                                                                                【举例】 使用LDM/STM进行现场寄存器保护,常在子程序或异常处理使用。

                                                                                                SENDBYTE:

                                                                                                STMFD SP!,{R0~R7,LR} ;寄存器压栈保护

                                                                                                BL DELAY ;调用DELAY子程序

                                                                                                LDMFD SP!,{R0~R7,PC} ;恢复寄存器,并返回


                                                                                                1. 单数据交换指令

                                                                                                交换指令是Load/Store指令的一种特例,它把一个寄存器单元的内容与寄存器内容交换。交换指令是一个原子操作(Atomic Operation),也就是说,在连续的总线操作中读/写一个存储单元,在操作期间阻止其他任何指令对该存储单元的读/写。交换指令如表所示。

                                                                                                指 令作 用操 作SWP字交换tmp=men32[Rn]mem32[Rn]=RmRd=tmpSWPB字节交换tmp=men8[Rn]mem8[Rn]=RmRd=tmp

                                                                                                1. SWP字交换指令

                                                                                                SWP指令用于将内存中的一个字单元和一个指定寄存器的值相交换。操作过程如下:假设内存单元地址存放在寄存器<Rn>中,指令将<Rn>中的数据读取到目的寄存器Rd中,同时将另一个寄存器<Rm>的内容写入到该内存单元中。

                                                                                                当<Rd>和<Rm>为同一个寄存器时,指令交换该寄存器和内存单元的内容。

                                                                                                指令的语法格式:

                                                                                                SWP{<cond>} <Rd>,<Rm>,[<Rn>]

                                                                                                1. SWPB字节交换指令

                                                                                                SWPB指令用于将内存中的一个字节单元和一个指定寄存器的低8位值相交换,操作过程如下:假设内存单元地址存放在寄存器<Rn>中,指令将<Rn>中的数据读取到目的寄存器Rd中,寄存器Rd的高24位设为0,同时将另一个寄存器<Rm>的低8位内容写入到该内存字节单元中。当<Rd>和<Rm>为同一个寄存器时,指令交换该寄存器低8位内容和内存字节单元的内容。

                                                                                                指令的语法格式:

                                                                                                SWP{<cond>}B <Rd>,<Rm>,[<Rn>]

                                                                                                1. 交换指令SWP应用

                                                                                                SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写到该内存单元中,使用SWP可实现信号量操作。

                                                                                                指令的语法格式:

                                                                                                SWP{cond}B Rd,Rm,[Rn]

                                                                                                其中,B为可选后缀,若有B,则交换字节;否则交换32位字。Rd为目的寄存器,存储从存储器中加载的数据,同时,Rm中的数据将会被存储到存储器中。若Rm与Rn相同,则为寄存器与存储器内容进行交换。Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同。

                                                                                                SWP指令举例:

                                                                                                SWP R1,R1,[R0] ;将R1的内容与R0指向的存储单元内容进行交换

                                                                                                SWPB R1,R2,[R0] ;将R0指向的存储单元内容读取一字节数据到R1中(高24位清零), 并将R2的内容 写入到该内存单元中(最低字节有效),使用SWP指令可以方便地进行信号量操作

                                                                                                12C_SEM .equ 0x40003000

                                                                                                12C_SEM_WAIT:

                                                                                                MOV R0,#0

                                                                                                LDR R0,=12C_SEM

                                                                                                SWP R1,R1,[R0] ;取出信号量,并将其设为0

                                                                                                CMP R1,#0 ;判断是否有信号

                                                                                                BEQ 12C_SEM_WAIT ;若没有信号则等待

                                                                                                跳转指令

                                                                                                跳转(B)和跳转连接(BL)指令是改变指令执行顺序的标准方式。ARM一般按照字地址顺序执行指令,需要时使用条件执行跳过某段指令。只要程序必须偏离顺序执行,就要使用控制流指令来修改程序计数器。尽管在特定情况下还有其他几种方式实现这个目的,但转移和转移连接指令是标准的方式。跳转指令改变程序的执行流程或者调用子程序。这种指令使得一个程序可以使用子程序、if-then-else结构及循环。执行流程的改变迫使程序计数器(PC)指向一个新的地址,ARMv5架构指令集包含的跳转指令如表所示。

                                                                                                助 记 符说 明操 作B跳转指令pc←labelBL带返回的连接跳转pc←label(lr←BL后面的第一条指令)BX跳转并切换状态pc←Rm&0xfffffffe, T←Rm&1BLX带返回的跳转并切换状态pc←lable, T←1pc←Rm&0xfffffffe, T←Rm&1lr←BL后面的第一条指令

                                                                                                另一种实现指令跳转的方式是通过直接向PC寄存器中写入目标地址值,实现在4GB地址空间中任意跳转,这种跳转指令又称为长跳转。如果在长跳转指令之前使用“MOV LR”或“MOV PC”等指令,可以保存将来返回的地址值,也就实现了在4GB的地址空间中的子程序调用。

                                                                                                1. 跳转指令B及带连接的跳转指令BL

                                                                                                跳转指令B使程序跳转到指定的地址执行程序。带连接的跳转指令BL将下一条指令的地址复制到R14(即返回地址连接寄存器LR)寄存器中,然后跳转到指定地址运行程序。需要注意的是,这两条指令和目标地址处的指令都要属于ARM指令集。两条指令都可以根据CPSR中的条件标志位的值决定指令是否执行。

                                                                                                1. 指令的语法格式:

                                                                                                B{L}{<cond>} <target_address>

                                                                                                BL指令用于实现子程序调用。子程序的返回可以通过将LR寄存器的值复制到PC寄存器来实现。下面3种指令可以实现子程序返回。

                                                                                                1. BX R14(如果体系结构支持BX指令)。

                                                                                                2. MOV PC,R14。

                                                                                                3. 当子程序在入口处使用了压栈指令:

                                                                                                STMFD R13!,{<registers>,R14}

                                                                                                可以使用指令:

                                                                                                LDMFD R13!,{<registers>,PC}

                                                                                                将子程序返回地址放入PC中。

                                                                                                ARM汇编器通过以下步骤计算指令编码中的signed_immed_24。

                                                                                                1. 将PC寄存器的值作为本跳转指令的基地址值。

                                                                                                2. 从跳转的目标地址中减去上面所说的跳转的基地址,生成字节偏移量。由于ARM指令是字对齐的,该字节偏移量为4的倍数。

                                                                                                3. 当上面生成的字节偏移量超过−33 554 432~+33 554 430时,不同的汇编器使用不同的代码产生策略。否则,将指令编码字中的signed_immed_24设置成上述字节偏移量的bits[25:2]。

                                                                                                4. 程序举例:

                                                                                                5. 程序跳转到LABLE标号处。

                                                                                                B LABLE ;

                                                                                                ADD R1,R2,#4

                                                                                                ADD R3,R2,#8

                                                                                                SUB R3,R3,R1

                                                                                                LABLE:

                                                                                                SUB R1,R2,#8

                                                                                                1. 跳转到绝对地址0x1234处。

                                                                                                B 0x1234

                                                                                                1. 跳转到子程序func处执行,同时将当前PC值保存到LR中。

                                                                                                BL func

                                                                                                1. 条件跳转:当CPSR寄存器中的C条件标志位为1时,程序跳转到标号LABLE处执行。

                                                                                                BCC LABLE

                                                                                                1. 通过跳转指令建立一个无限循环。

                                                                                                LOOP:

                                                                                                ADD R1,R2,#4

                                                                                                ADD R3,R2,#8

                                                                                                SUB R3,R3,R1

                                                                                                B LOOP

                                                                                                1. 通过使用跳转使程序体循环10次。

                                                                                                MOV R0,#10

                                                                                                LOOP:

                                                                                                SUBS R0,#1

                                                                                                BNE LOOP

                                                                                                1. 条件子程序调用示例。

                                                                                                CMP R0,#5 ;如果R0<5

                                                                                                BLLT SUB1 ;则调用

                                                                                                BLGE SUB2 ;否则调用SUB2

                                                                                                1. 带状态切换的跳转指令BX

                                                                                                带状态切换的跳转指令(BX)使程序跳转到指令中指定的参数Rm指定的地址执行程序,Rm的第0位复制到CPSR中T位,bit[31∶1]移入PC。若Rm的bit[0]为1,则跳转时自动将CPSR中的标志位T置位,即把目标地址的代码解释为Thumb代码;若Rm的位bit[0]为0,则跳转时自动将CPSR中的标志位T复位,即把目标地址代码解释为ARM代码。

                                                                                                  1. 指令的语法格式:




                                                                                                  BX{<cond>} <Rm>

                                                                                                  1. 当Rm[1∶0]=0b10时,指令的执行结果不可预知。因为在ARM状态下,指令是4字节对齐的。

                                                                                                  2. PC可以作为Rm寄存器使用,但这种用法不推荐使用。当PC作为<Rm>使用时,指令“BX PC”将程序跳转到当前指令下面第二条指令处执行。虽然这样跳转可以实现,但最好使用下面的指令完成这种跳转。

                                                                                                  MOV PC, PC

                                                                                                  ADD PC, PC, #0

                                                                                                    1. 指令举例:




                                                                                                    1. 转移到R0中的地址,如果R0[0]=1,则进入Thumb状态。

                                                                                                    BX R0;

                                                                                                    1. 跳转到R0指定的地址,并根据R0的最低位来切换处理器状态。

                                                                                                    ADRL R0,ThumbFun+1 ;

                                                                                                    BX R0;

                                                                                                    1. 带连接和状态切换的连接跳转指令BLX

                                                                                                    带连接和状态切换的跳转指令(Branch with Link Exchange,BLX)使用标号,用于使程序跳转到Thumb状态或从Thumb状态返回。该指令为无条件执行指令,并用分支寄存器的最低位来更新CPSR中的T位,将返回地址写入到连接寄存器LR中。

                                                                                                      1. 语法格式:




                                                                                                      BLX <target_add>

                                                                                                      其中,<target_add>为指令的跳转目标地址。该地址根据以下规则计算。

                                                                                                      1. 将指令中指定的24位偏移量进行符号扩展,形成32位立即数。

                                                                                                      2. 将结果左移两位。

                                                                                                      3. 位H(bit[24])加到结果地址的第一位(bit[1])。

                                                                                                      4. 将结果累加进程序计数器(PC)中。

                                                                                                      计算偏移量的工作一般由ARM汇编器来完成。这种形式的跳转指令只能实现−32~32MB空间的跳转。左移两位形成字偏移量,然后将其累加进程序计数器(PC)中。这时,程序计数器的内容为BX指令地址加8字节。位H(bit[24])也加到结果地址的第一位(bit[1]),使目标地址成为半字地址,以执行接下来的Thumb指令。计算偏移量的工作一般由ARM汇编器来完成。这种形式的跳转指令只能实现−32~32MB空间的跳转。

                                                                                                        1. 指令的使用




                                                                                                        1. 从Thumb状态返回到ARM状态,使用BX指令。

                                                                                                        BX R14

                                                                                                        1. 可以在子程序的入口和出口增加栈操作指令。

                                                                                                        PUSH {<registers>,R14}

                                                                                                        POP {<registers>,PC}



                                                                                                        硬件平台:华清远见FS-MP1A开发板(STM32MP157)

                                                                                                        部分开发教程下载:加QQ群483143191,群文件里有。

                                                                                                        部分视频课程收看:华清远见研发中心的个人空间_哔哩哔哩_Bilibili

                                                                                                        淘宝购买链接:item.taobao.com/item.ht

                                                                                                        手机淘宝分享码:复制本行文字打开手淘₤T4FPXn3YYJ2₤


                                                                                                        ARM裸机开发篇2:ARM微处理器指令系统(一)的评论 (共 条)

                                                                                                        分享到微博请遵守国家法律