「UESTC II」Advanced RISC Machine

本文是我在准备信软学院⚙️ARM处理器体系结构及其应用这门课考试的时候写下的笔记,同时也记录了这门课的实验任务。

课程目标:

CO1:让学生掌握嵌入式系统的基础知识,嵌入式处理器系列,嵌入式系统开发环境,嵌入式操作系统等,培养学生专业知识能力。

CO2:让学生掌握ARM9处理器内核,ARM处理器工作状态,ARM处理器运行模式,ARM寄存器,ARM存储系统,ARM的异常等,培养学生底层硬件的能力。

CO3:让学生掌握ARM寻址方式和指令系统,ARM的伪指令,ARM的简单编程和ARM的综合编程,培养学生底层综合编程能力。

CO4:让学生掌握S3C2440嵌入式处理器,掌握S3C2440处理器的外设及应用。培养学生解决复杂系统的能力,如简单的硬件设计,以及基于软硬件的综合设计、编程能力。(不考)

考核方式:

平时成绩(40%)+期末考试(60%)

  • 平时成绩:15%期中随堂测验 + 25%作业 + 60%实验

总评成绩(100)= 期中(6)+ 作业(10)+ 实验(24)+ 期末(60)

Topics Covered:

  • 嵌入式系统
  • ARM 处理器系统结构
  • ARM 指令集
  • ARM 伪指令及编程

嵌入式系统基础

作业题:

一、什么是嵌入式系统?

通常的定义:嵌入式系统是以应用为中心,以计算机技术为基础,采用可剪裁软硬件,适用于对功能、可靠性、成本、体积、功耗等有严格要求的专用计算机系统。

通俗的定义:嵌入到对象体系中的专用计算机系统。

嵌入性、专用性与计算机系统是嵌入式系统的三个基本要素。

二、嵌入式微处理器的体系结构有哪两种?简单两种体系结构各自的特点。

嵌入式微处理器的体系结构有冯.诺依曼结构和哈佛结构两种。

冯.诺依曼结构也称为普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。单次取指令长度和取数据的长度相同。

哈佛结构:是一种将 程序指令存储和数据存储分开的存储器结构。程序指令存储和数据存储分开,单次取指令长度和取数据的长度可以不相同。

三、简单描述嵌入式实时操作系统中的任务的四个状态。

实时操作系统中的任务有四个状态:运行就绪挂起冬眠

运行:获得 CPU 的控制权。

就绪:进入任务等待队列,等待通过调度转为运行状态。

挂起:任务发生阻塞,移出任务就绪队列,等待系统实时事件的发生而唤醒,从而转为 就绪 或 运行。

冬眠:任务完成 或 错误 等原因被清除的任务,也可以认为是系统中不存在的任务。

四、简述精简指令集 RISC 和复杂指令集 CISC 的主要区别

RISC 和 CISC 的区别

  1. 以下关于 RISC 和 CISC 说法正确的是( A、C )

A、RISC 一个周期执行一条指令 B、RISC 相对于 CISC,其指令集实现的功能更多 × C、RISC 相对于 CISC 具有更多的寄存器

D、RISC 能直接处理存储器中的数据

  1. 嵌入式系统的三个基本要素是( A、B、C )

A:嵌入性 B:专用性 C:计算机系统 D:通用性

  1. 在以下分类中,属于按实时性划分的是( B、D )。

A、8 位嵌入式系统。 B、硬实时系统。 C、前后台系统 D、软实时系统

  1. 属于硬件调试工具是( A、C、D )。

A、实时在线仿真器 B、电路开发板 C、逻辑分析仪 D、ROM 仿真器

  1. 在下列嵌入式处理器类型中,集成度最高的是( D )。

A、嵌入式微处理器 B、嵌入式微控制器 C、嵌入式 DSP 处理器 D、SOC 片上系统

  1. 实时操作系统中的任务的就绪状态是指( B )。

A、获得 CPU 的控制权。 B、进入任务等待队列,等待通过调度转为运行状态。 C、任务发生阻塞,移出任务就绪队列。

D、任务完成或错误 等原因被清除的任务。

ARM 处理器及系统结构

ARM 处理器简介

ARM 处理器系列

ARM9 处理器内核

以上 PPT。

*ARM 处理器工作状态

嵌入式系统在某些应用场合对存储成本或空间要求比较苛刻,为了让用户更好地控制代码量,于是设计了2套指令系统,分别为ARM指令集和Thumb指令集。其中ARM指令集为32位(字)长度,具有最完整的功能;Thumb指令集为16位(半字)长度,能实现ARM指令集的大部分功能。在功能上可以认为Thumb是ARM指令集的子集。

ARM微处理器中支持字节(8位)、半字(16位)、字(32位)三种数据类型。

*ARM 处理器运行模式

ARM体系结构支持 7种 运行模式。(PPT 66 页)

除用户模式外的其它6种处理器模式称为特权模式(Privileged Modes)。在特权模式下,程序可以访问所有的系统资源,也可以任意的进行处理器模式切换。只有在特权模式下才允许对当前程序状态寄存器(CPSR)的所有控制位直接进行读/写访问,而在非特权模式下只允许对CPSR的控制位进行间接访问(SWI方式)。

*ARM 寄存器

ARM处理器有如下37个用户可见寄存器。(PPT 72 页)31个通用寄存器以及6个状态寄存器。

ARM 处理器共有 7 种运行模式,在每一种处理器模式中都有一组相应的寄存器。

在ARM状态下,任一时刻都可以访问到 16个通用寄存器和1~2个状态寄存器。

这些条件标志位会根据程序中的算数指令或逻辑指令的执行结果进行改变,而且这些条件标志位可由大多数指令检测以决定指令是否执行。

(1)N

本位设置成当前指令执行结果的第31位。当两个由补码表示的有符号整数运算时,N=1 表示结果为负数;否则结果为正数或零。

(2)Z

Z=1 表示运算的结果为零,否则结果不为零。

(3)C

分 4 种情况设置 C 的方法:

① 在加法指令中(包括比较指令CMN),当结果产生了进位,则C=1,表示无符号数运算发生上溢出,其它情况下C=0;

② 在减法指令中(包括比较指令CMP),当运算中发生了借位,则C=0,其它情况下C=1;

③ 对于在操作数中包含移位操作的运算指令(非加/减指令),C被设置成被移位寄存器最后移出去的位;

④ 对于其它非加/减法运算指令,C的值通常不受影响。

(4)V

下面分两种情况讨论V的设置方法:

① 对于加/减运算指令,当操作数和运算结果都是以二进制的补码表示的带符号的数时,且运算结果超出了有

符号运算的范围时溢出。V=1 表示符号位溢出;

② 对于非加/减法指令,通常不改变标志位 V 的值。

ARM 存储系统

ARM支持大端模式(big-endian)和小端模式(little-endian)两种内存模式。

在大端模式下,一个字的高地址单元放的是数据的低位;而在小端模式下,数据的低位放在内存中低地址单元中。

中断和异常的基本概念(不考)

*ARM 的异常

ARM处理器可以响应的中断(异常)有:中断、快中断、复位中断、软中断异常、预取指令中止异常、数据中止异常和未定义指令异常 7 种。

作业题:

一、ARM 处理器有几种工作状态?分别是什么?

ARM 处理器有两种工作状态:ARM 状态和 Thumb 状态。

二、ARM 处理器有几种运行模式?分别是什么?

ARM 处理器有 7 种运行模式:用户模式、系统模式、一般中断模式、快速中断模式、管理模式、中止模式和未定义模式。

三、简单描述 ARM 体系结构与纯粹的 RISC 体系结构的不同点。

ARM 内核不是一个纯粹的 RISC 体系结构。ARM 指令集与纯粹的 RISC 的定义有以下几个不同。

(1)一些特定指令的周期数可变,并不是每条 ARM 指令都是单周期的。

(2)内嵌的桶形移位器产生了更为复杂的指令,扩展了指令的功能,因此改善了内核的性能。

(3)支持 16 位的 Thumb 指令集,提高了代码密度。

(4)支持条件执行:每条指令都可以设置一个执行条件,只有条件满足时才执行。

(5)增强指令:一些功能强大的数字信号处理指令被加入到 ARM 指令集中。

四、简单描述 ARM9 5 级流水线

取指:指令从存储器中取出,放入指令流水线;

译码:指令译码;

执行:把一个操作数移位,产生 ALU 的结果。如果指令是 Load 或 Store,在 ALU 中计算存储器的地址;

缓存/数据:如果需要,则访问数据存储器;否则,ALU 的结果只是简单地缓冲一个时钟周期,以便使所有指令具有同样的流水线流程;

回写:将指令产生的结果写回到寄存器堆,包括任何从寄存器读出的数据。

五、简单描述 ARM 处理器进入异常所采取的操作。

(1) 在适当的 LR 中保存断点的地址。

(2) 把当前程序状态寄存器(CPSR)中的内容保存到模式私有寄存器 SPSR 中;

(3) 将寄存器 CPSR 中的 MODE 域设置为中断(异常)应进入的运行模式;

(4) 对 CPSR 的 I 位和 F 位进行相应的设置,以防止再次响应同一个中断请求。

(5) 强制 PC 从相关的异常向量处取指,即到中断向量表中获取中断向量,转向用户所编写的中断(异常)服务程序。

六、ARM 的快中断(FIQ)采用了几种措施来保证更快的响应速度?这些措施是什么?

为减少延时,ARM 在快中断中采取了 两个措施:

(1)专门为快中断配置了较多的私有寄存器,从而可使中断服务程序有足够的寄存器来使用,而不必与被中断服务程序使用同一组寄存器,这样就免去了因寄存器冲突而必需的保护及恢复现场工作。

(2)ARM 把 FIQ 的中断向量放在了中断(异常)向量表末尾 0X0000001C 处,因此它后面没有其它中断向量,允许用户将中断服务程序直接放在这里。

七、多选题

  1. 为减少延时,提高中断处理速度,ARM 在快中断中采取了哪些措施( B、D )

A. 配置了高速缓存。 B. 配置了较多的私有寄存器。 C. 更高的中断优先级。

D. 把 FIQ 的中断向量放在了中断(异常)向量表末尾处。

  1. 下列说法正确的是( A、B、C、D )。

A. 异常实质上也是一种中断,只不过它主要负责处理处理器内部事件

B. 中断控制器用于中断源和处理器之间,主要用于对处理器可以接收中断源的数目进行扩充及对中断进行必要的管理。

C. 处理器在现行指令执行结束后,才能响应中断。

D. 在处理器收到中断请求之后,它们都需要获得中断服务程序首地址——中断向量。

  1. 下列关于 ARM 的说法正确的是( B、C、D )

A. ARM 公司生产的处理器 B. ARM 是一个公司的名称 C. ARM 是一类微处理器的通称 D. ARM 是一种技术的名称

八、判断题

  1. ARM9TDMI 核其工作模式有两种:ARM 模式和 Thumb 模式。( X )
  2. 当异常发生,进入异常运行模式的时候,处理器会自动禁用中断和快中断,以确保异常处理程序安全运行。( X )
  3. 当处理器处于 Thumb 状态时发生了异常,在异常向量地址装入 PC 时,会自动切换到ARM 状态。( √ )
  4. S3C2440 芯片可以通过软件来指定存储器格式,缺省为小端格式。( √ )
  5. 在减法指令中,当运算中发生了借位,则 C 标志位=1。( X )
  6. ARM 处理器在用户模式下,可以通过修改 CPSR 进入系统模式。( X )
  7. 对于计算机系统来说,一个字的长度是 32 位的,半字的长度是 16 位的。( X )
  8. ARM920T 处理器中,对是通过协处理器 P15 来实现对 MMU 的控制的。( √ )
  9. ARM9 的 5 级流水线设计,相对于 ARM7 的 3 级流水线设计,减少了在每个时钟内必须完成的最大工作量,进而允许使用较高的时钟频率。( √ )
  10. ARM处理器采用的是RISC体系结构,因此所有的指令都在一个周期内执行完成。( X )

九、单选题

  1. 如果处理器采用大端存储器格式,假如一个字存放在地址为 A、A+1、A+2、A+3 四个连续的存储单元中,则该字的地址为( A )大端下该字的地址定义为最低地址

A.A B.A+1 C.A+2 D.A+3

  1. ARM7EJ 名称中的 J 后缀,代表( C )

A、支持增强型 DSP 指令 B、支持 Thumb 指令集 C、支持 Jazelle D、支持 Embedded ICE

  1. 在通用的 CPU 上提供 DSP 能力,是从 ARM 指令集构体系结构版本( B )以后开始的。

A、v4 B、v5 C、v6 D、v7

  1. ARM 推出的 Cortex 系列包括三个系列,其中实时操作系统而设计的是( C )

A、Cortex-A B、Cortex-B C、Cortex-R D、Cortex-M

  1. 在 ARM9 的 5 级流水线设计中,从寄存器中读取操作数,是在哪个阶段完成的?( B )

A、取指 B、译码 C、执行 D、缓存/数据 E、回写

  1. 在 ARM9 的 5 级流水线设计中,计算访存指令访问存储器的地址的操作,是在哪个阶段完成的?( C )

A、取指 B、译码 C、执行 D、缓存/数据 E、回写

  1. ARM 处理器的 7 种运行模式中,处理器复位之后进入( C )。

A.用户模式 B.系统模式 C.管理模式 D.终止模式

  1. 下列 ARM 处理器的运行模式中,不属于异常模式的是( B )。

A.未定义模式 B.系统模式 C.管理模式 D.终止模式。

  1. R13 通常用于存储( D )。

A.程序计数器 B.中断返回地址 C.子程序返回地址 D.堆栈指针

  1. 程序计数器的值保存在( D )中。

A.R12 B.R13 C.R14 D.R15

  1. 读程序计数器时,指令读出的 R15 的值是当前指令地址加上( D )字节。

A.1 B.2 C.4 D.8

ARM 指令集

ARM 指令集简介

ARM微处理器的ARM指令集 ,所有的指令长度都是32位 ,并且大多数指令都在一个单独指令周期内执行。

主要特点包括:

  • 指令是条件执行的;
  • ARM 微处理器的指令集是加载/存储型的;
  • 在多寄存器操作指令中一次最多可以完成 16 个寄存器的数据传送。

ARM 指令格式

一、指令的一般格式

1
<opcode> {<cond>} {S} {<Rd>} {, <Rn>} {, <OP2>}

格式中 < > 的内容必不可少,{ } 中的内容可省略。

opcode 表示操作码,如 ADD 表示算术加法;cond 表示指令执行的条件域,如 EQ、NE 等。

{ S } 决定指令的执行结果是否影响 CPSR 的值,使用该后缀则指令执行的结将果影响 CPSR 的值,否则不影响。

Rd 表示目标寄存器存储结果;Rn 为寄存器提供第一个操作数;OP2 表示第二个操作数,可以是立即数、寄存器和寄存器移位操作数。

在指令格式中,操作码(opcode)是必须指定的部分,不能省略,而其他部分(如 cond、S、Rd、Rn、OP2)都是可选的,可以根据需要选择是否包含在指令中。换句话说,除了操作码外,其他域可以根据具体指令的需求省略或使用。

例如:指令 ADDEQS R0, R1, #8; 执行结果 R0 = R1 + 8LDR R3, [R6]:将 R6 寄存器内容所指向的存储单元内容读出,保存在 R3 中,执行条件 AL。BEQ NEXT:分支指令B,加上后缀EQ,变为BEQ,表示“满足条件相等则跳转” 。ADD R0,R1,#2 加法指令ADD,R0=R1+2,不带S,计算结果不影响CPSR寄存器。SUBNES R0,R1,#0X03条件执行减法运算(NE),R0=R1-0X03,影响CPSR寄存器。

二、指令的机器码

从形式上看,ARM指令在机器中的表示格式是用32位的二进制数表示。

ARM指令代码一般可以分为5个域:

  • 第1个域是4位[31:28]的条件码域,4位条件码共有16种组合;
  • 第2个域是指令代码域[27:20],除了指令编码外,还包含几个很重要的指令特征和可选后缀的编码
  • 第3个域是第1个操作数寄存器Rn,是4位[19:16],为R0~R15共16个寄存器编码;
  • 第4个域是目标或源寄存器Rd,是4位[15:12],为R0~R15共16个寄存器编码;
  • 第5个域是第二个操作数[11:0]。

三、指令的可选后缀

1)S后缀

指令中使用S后缀时,指令执行后程序状态寄存器的条件标志位将被刷新,不使用S后缀时,指令执行后程序状态寄存器CPSR的条件标志将不会发生变化。

例:假设R0=0x1,R3=0x3,指令执行之前CPSR部分标志位为nZcv,分别执行如下指令CPSR的值有何变化?

SUB R1,R0,R3; R0的值减去R3的值,结果存入R1

SUBS R1,R0,R3; R0的值减去R3的值,结果存入R1; 影响标志位。

分析:执行第1条指令对于标志寄存器的值没有任何影响,因此CPSR的值不变。执行第2条指令后CPSR=Nzcv

2)!后缀

如果指令地址表达式中不含!后缀,则基址寄存器中的地址值不会发生变化。

指令中的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果如下:

基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量

例 分别执行下面两条指令有何区别?

LDR R3,[R0,#4]

LDR R3,[R0,#4]!

分析:在上述指令中,第1条指令没有后缀!,指令的结果是把R0加4作为地址指针,把这个指针所指向的地址单元所存储的数据读入R3,R0的值不变。第2条指令除了实现以上操作外,还把R0+4的结果送到R0中。

使用!后缀需要注意如下事项:

(1)!后缀必须紧跟在地址表达式后面,而地址表达式要有明确的地址偏移量;

(2)! 后缀不能用于R15(PC)的后面;

(3)当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量,例如“STMIA R7!, {R0 – R3}”此时地址基址寄存器R7的隐性偏移量是16字节。如果R7的初始值为 0X40000000,则该语句结束后为0X40000010

3)B 后缀

B后缀的含义是:指令所涉及的数据是一个字节,不是一个字或半字。

指令的条件码

ARM 指令的 条件码 和 助记符 如下表所示(PPT 19 页)

条件码表1

条件码表2

指令分类

ARM 指令可以分为:分支指令、数据处理指令、存储访问指令、协处理器指令和杂项指令五类。

ARM 指令的寻址方式

立即数寻址

1
2
ADD R0, R0, #1; R0 <- R0+1
MOV R0, #15; R0 <- 15

循环右移,8位位图。需确认立即数是否可以表示为一个 8 位值(0x00 到 0xFF,十进制 0 到 255)。

ARM 架构的立即数编码规则(一种常见的形式):

许多 ARM 指令中的立即数并不是直接使用一个固定的位数(比如 16 位或 32 位)来表示。相反,它们通常由一个 8 位的常数 (immed_8) 和一个 4 位的循环右移值 (rotate_imm) 组成。最终的 32 位立即数是通过将这个 8 位常数(零扩展到 32 位)循环右移 2 * rotate_imm 得到的。

这意味着,只有那些可以通过将一个 0 到 255 之间的 8 位数值循环右移偶数位(0, 2, 4, …, 30 位)得到的 32 位数值,才是“合法”的立即数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdint.h>

uint32_t rot(uint32_t value, unsigned int shift) {
shift &= 31;
if (shift == 0) {
return value;
}
return (value >> shift) | (value << (32 - shift));
}

int get_arm_immediate_params(uint32_t num, uint8_t *immed_8_ptr, uint8_t *rotate_imm_ptr) {
for (int r = 0; r < 16; r++) {
uint32_t rotated = rot(num, r * 2);
if ((rotated & ~0xFFU) == 0) {
*immed_8_ptr = (uint8_t)rotated;
*rotate_imm_ptr = (uint8_t)r;
return 1;
}
}
return 0;
}

int main() {
uint32_t K;
uint8_t immed_8_val;
uint8_t rotate_imm_val;

printf("请输入一个十六进制数: ");

if (scanf("%x", &K) != 1) {
fprintf(stderr, "输入错误,需要一个有效的十六进制数。\n");
return 2;
}

int result = get_arm_immediate_params(K, &immed_8_val, &rotate_imm_val);

if (result == 1) {
printf("输入值 0x%X 是 ARM 立即数。\n", K);
printf(" 8 位立即数 (immed_8) : 0x%02X\n", immed_8_val);
printf(" 4 位循环右移量 (rotate_imm): %u\n", rotate_imm_val);
printf(" (实际右移位数 : %u)\n", rotate_imm_val * 2);
} else {
printf("输入值 0x%X 不是 ARM 立即数。\n", K);
printf("0\n");
}

return 0;
}

MOV R0,#0xF200 ;E3A00CF2. 0xF200 =0xF2 ROR(2*C)

只有能够通过此构造方法得到的才是合法的立即数。

寄存器寻址

1
2
MOV R1, R2; R1 <- R2
ADD R1, R1, R2; R1 <- R1+R2

寄存器移位寻址

1
2
ADD R3, R2, R1, LSR #2; R3 <- R2+(R1右移2位)
ADD R3, R2, R1, LSR R0; R3 <- R2+(R1右移R0位)

移位方式

寄存器间接寻址

1
LDR R0, [R4]; R0 <- [R4]

基址变址寻址

1
LDR R0, [R1, #4]; R0 <- mem32[R1+4]

多寄存器寻址

1
LDMIA R0!,{R1-R4} ;R1←[R0]、R2←[R0+4]、R3←[R0+8]、R4←[R0+12]

注意:对于所有LDM/STM指令而言,寄存器序号低的,在低地址单元,序号大的在高地址单元!与书写顺序无关!

多寄存器寻址

堆栈寻址

向上生长:向高地址方向生长,称为递增堆栈。

STMXX是存入到主存,是入栈操作;

LDMXX是从主存读出到寄存器,是出栈操作。

相对寻址

ARM 指令集

分支指令

写汇编程序时,可以跳转到一个绝对地址,如:

1
B 0x1234

注: B #0x1234是错误的

例:已知寄存器 R0 中存放了数据 a,寄存器 R1 中存放了数据 b,编写一个程序段,求取 a 和 b 的最大公约数并将其存入寄存器 R0。

先写一个 C 语言版本的:

1
2
3
4
5
6
7
8
9
10
11
int gcd(int a, int b) {
while (a != b) {
if (a > b) {
a = a - b;
} else {
b = b - a;
}
}

return a;
}

转换成对应的汇编指令:

1
2
3
4
5
6
gcd
CMP R0, R1
SUBGT R0, R0, R1
SUBLT R1, R1, R0
BNE gcd
MOV PC LR

B和BL的区别在于:BL在跳转之前会把BL指令的下一条指令地址(断点地址)保存到连接寄存器 LR(R14),因此程序在必要的时候可以通过将 LR 的内容加载到 PC 中,使程序返回到跳转点。

BL 指令经常被用来调用一个子程序。

BX 功能:跳转到指令中所指定的目标地址,并实现状态切换。

BLX 指令的功能是:把程序跳转到指令中所指定的目标地址继续执行,并同时将处理器的工作状态从ARM状态切换到Thumb状态,并将下一条的地址保存到寄存器 LR 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    ; 主程序
MOV R0, #48 ; 示例输入:R0 = 48
MOV R1, #18 ; 示例输入:R1 = 18
BL gcd ; 调用 gcd 子程序
; 结果在 R0 中(这里是 6)
B end ; 跳转到程序结束

gcd:
CMP R0, R1 ; 比较 R0 和 R1
SUBGT R0, R0, R1 ; 如果 R0 > R1,则 R0 = R0 - R1
SUBLT R1, R1, R0 ; 如果 R1 > R0,则 R1 = R1 - R0
BNE gcd ; 如果 R0 != R1,继续循环
BX LR ; 返回到调用者(使用 BX LR 替代 MOV PC, LR)

end:
; 程序结束(可以添加停止指令,如死循环)
B end

数据处理指令

主要完成寄存器中数据的算术和逻辑运算操作。

  • 操作数来源:所有的操作数要么来自寄存器,要么来自立即数,不会来自存储器。
  • 操作结果:如果有结果,则结果一定是为32位宽、或64位宽(长乘法指令),并且放在一个或两个寄存器中,不会写入存储器。
  • 有第二个操作数(除了乘法指令) Operand2 :切记其三种形式:立即数、寄存器、寄存器移位。
  • 乘法指令的操作数:全部是寄存器。

数据处理指令

指令中可以选择s后缀,以影响状态标志。但是比较指令(CMP和CMN)和测试指令(TST和TEQ)不需要后缀S,它们总会直接影响CPSR中的状态标志。

ADC——带进位加法指令:ADC指令将operand2的数据与Rn的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器。

例:有 两个128位数,第一个数由高到低存放在寄存器R7—R4 中,第二个数由高到低存放在寄存器 R11—R8 中,

请编写程序把两个数相加,运算结果由高到低存放到寄存器R3~R0中。

1
2
3
4
ADDS R0,R4,R8 ;加低位字,不带进位
ADCS R1,R5,R9 ;加第二个字,带进位
ADCS R2,R6,R10 ;加第三个字,带进位
ADCS R3,R7,R11 ;加第四个字,带进位

SBC——带进位减法指令(81)

CMP

存储器访问指令

基本的加载/存储指令仅有5条,分为3种:

— LDR和STR,单寄存器加载/存储指令

— LDM和STM,多寄存器加载/存储指令

— SWP,寄存器和存储器数据交换指令

PC(即R15)

已知(R0)=0X00000000和(R1)=0X00009000,并已知在存储器中首地址为 0X00009000 的区域中存放了数

据0X01010101,在首地址为 0X00009004 的区域存放了数据 0X02020202。

试写出执行了指令 LDR R0,[R1,#4] 后R0 和 R1 中的数据。

(R0)= 0X02020202 (R1)= 0X00009000

试写出执行了指令 LDR R0,[R1,#4]!后的 R0 和 R1 中的数据。

(R0)= 0X02020202 (R1)=0X00009004

协处理器指令(了解)

杂项指令

例题:

一、算数逻辑运算指令的应用

例2:64位数据运算

假设R0和R1存放一个64位数据,R0中存放数据的低32位;R2和R3中存放另一个64位数据,R2中存放数据的低32位。对这两个64位数进行加、减和比较运算。(R1,R0)、(R3,R2)

①两个64位数据的加法运算,结果保存到R0和R1中。

1
2
ADDS R0, R0, R2  ;低32位相加,设置CPSR的C标志位。
ADC R1, R1, R3 ;高32位的带位相加

②两个64位数据的减法运算,结果保存到R0和R1中。

1
2
SUBS R0, R0, R2 ;低32位相减,设置CPSR的C标志位。
SBC R1, R1, R3 ;高32位的带位相减

③两个64位数据的比较操作,并设置CPSR中的条件标志位。

1
2
CMP R1, R3 ;比较高32位
CMPEQ R0, R2 ;如果高32位相等,比较低32位

例3:转换内存中数据存储方式

将寄存器R0中的数据存储方式转换成另一种存储方式。指令执行前R0中数据存储方式为:R0=A,B,C,D;指令执行后R0中数据存储方式为:R0=D,C,B,A。

二、跳转指令的应用

例3:循环语句

将内存中从0x400800开始的100个字数据相加,其结果存于R3、R2中(R3中为高32位)。

1
2
3
4
5
6
7
8
9
10
	LDR R0, =0x400800
MOV R1, #100 ;循环计数器
MOV R2, #0 ;用于低32位累加
MOV R3, #0 ;用于高32位累加
loop
LDR R4, [R0], #4
ADDS R2, R2, R4
ADC R3, R3, #0
SUBS R1, R1, #1
BNE loop;循环计数器不为0,跳到loop继续执行

例4:链表操作中的条件码应用

在链表中搜索与某一数据相等的元素。

链表的每个元素包括两个字,第1个字中包含一个字节数据;第2个字中包含指向下一个链表元素的指针,当这个指针为0时表示链表结束。

代码执行前R0指向链表的头元素,R1中存放将要搜索的数据;代码执行后R0指向第1个匹配的元素,或者当没有匹配元素时,R0为0。

1
2
3
4
5
6
7
SEARCH
CMP R0,#0 ;R0指针是否为空
LDRNEB R2,[R0] ;读取当前元素中的字节数据
CMPNE R1,R2 ;判断数据是否为搜索的数据
LDRNE R0,[R0,#4] ;如果不是,指针R0指向下一个元素
BNE SEARCH ;跳转到search执行
MOV PC,LR ;搜索完成,程序返回

作业题:

一、ARM 指令分为几类?分别是什么?

ARM 指令有五类:分支指令、数据处理指令、存储访问指令、协处理器指令和杂项指令五类。

二、ARM 指令有几种寻址方式?分别是什么?

ARM 指令有八种寻址方式:立即数寻址、寄存器寻址、寄存器移位寻址、寄存器间接寻址、基址变址寻址、多寄存器寻址、堆栈寻址、相对寻址。

三、单选

  1. 下列指令合法的是( B )8位位图

A.MOV R0,#0x132 B.MOV R0,#0x264

C.MOV R0,#0x266 D.MOV R0,#0x4C8

  1. 汇编指令 MOV R0,#0x4800, 经编译后,所得到的机器码的低 12 位是( A )

A.0xB12 B.0xB24 C.0xA09 D.0xC48

  1. 假设 R1 寄存器中的值为 ADR,则在执行了指令:STMIA R1!, {R3-R5,R8} 指令后,R1 的

内容为( A )。

A、R1=ADR+16 B、R1=ADR+12

C、R1=ADR-16 D、R1=ADR-12

  1. 假设 R1 寄存器中的值为 ADR,则在执行了指令:STMDB R1!, {R3-R5,R8} 指令后,R5

寄存器中的内容将存放在地址为( B )的存储单元中:

A、ADR-4 B、ADR-8 C、ADR-12 D、ADR-16

  1. 假设 SP 寄存器中的值为 ADR,则在执行了指令:STMED SP!, {R3-R5,LR} 指令后,SP 的

内容为( C )。

A、ADR+16 B、ADR+12 C、ADR-16 D、ADR-12

  1. 假设 SP 寄存器中的值为 ADR,则在执行了指令:STMED SP!, {R3-R5,LR} 指令后,LR 寄

存器中的内容将存放在地址为( A )的存储单元中。

A、ADR B、ADR-4 C、ADR-12 D、ADR-16

  1. 假设 SP 寄存器中的值为 ADR,则在执行了指令:LDMEA SP!, {R3-R5,PC} 指令后,地

址为( B )的存储单元的值,将出栈到 PC 寄存器中。

A、ADR B、ADR-4 C、ADR-12 D、ADR-16

ARM 伪指令及编程基础

  1. 什么是伪指令?

人们设计了一些专门用于指导汇编器进行汇编工作的指令,由于这些指令不形成机器码指令,它们只是在汇编器进行汇编工作的过程中起作用,所以被叫做伪指令。

  1. 伪指令有两个特征:①伪指令是一条指令;②伪指令没有指令代码。
  2. 伪指令的作用:①程序定位的作用;②为非指令代码进行定义;③对程序完整性做标注;④有条件的引导程序段。

通用伪指令

在 ARM 汇编程序语言中,有如下几种伪指令:

  • 符号定义
  • 数据定义
  • 汇编控制
  • 其它

为变量定义或赋值的伪指令

一、声明全局变量伪指令 GBLA、GBLL和GBLS

GBLA 定义一个 全局数字变量,其默认初值为 0 ;

GBLL 定义一个 全局逻GBLS 定义一个 全局字符串变量,其默认初值为 空 ;辑变量 ,其默认初值为 FALSE(假);

二、声明局部变量伪指令LCLA、LCLL和LCLS

LCLA、LCLL和LCLS伪指令用于定义一个ARM程序中的局部变量,并将其初始化。

LCLA 定义一个局部数字变量,其默认初值为 0;LCLL 定义一个局部逻辑变量,其默认初值为 FALSE(假);LCLS 定义一个局部字符串变量,其默认初值为 空。

局部变量 的变量名在变量作用范围内必须具有唯一性。在默认情况下,局部变量只在定义该变量的程序段内有效。

三、变量赋值伪指令 SETA、SETL和SETS

伪指令SETA、SETL和SETS 用于给一个已经定义的全局变量或局部变量进行赋值。 注:要顶格写

SETA伪指令用于给一个数字变量赋值;SETL伪指令用于给一个逻辑变量赋值;SETS伪指令用于给一个字符串变量赋值;

1
Test1 SETA 0xAA

四、定义寄存器列表伪指令

指令 LDM/STM 需要使用一个比较长的寄存器列表,使用伪指令 RLIST 可对一个列表定义一个统一的名称。

1
2
3
4
5
6
LoReg RLIST {R0-R7} ;定义寄存器列表{R0-R7}
;的名称为LoReg
STMFD SP!, LoReg ;堆栈操作使用寄存器列表
RegList RLIST {R0-R5,R8,R10} ;将寄存器列表名称定义
;为RegList,可在ARM指令LDM/STM中
;通过该名称访问寄存器列表

数据定义伪指令

1、 LTORG

用于声明一个数据缓冲池(文字池)的开始。

其目的是,防止在程序中使用LDR之类的指令访问时,可能产生的越界。

通常把数据缓冲池放在代码段的最后面,或放在无条件转移指令或子程序返回指令之后,这样处理器就不会错误地将数据缓冲池中的数据当作指令来执行。

2、MAP 和 FIELD

MAP 用于定义一个结构化的内存表的首地址。FIELD 伪指令用于定义一个结构化内存表中的数据域。MAP 通常和 FIELD 伪指令相配合来定义一个结构化的内存表。

3、SPACE

SPACE伪指令用于分配一片连续的存储区域并初始化为 0

4、DCB分配内存单元并初始化

5、DCD和DCDU分配存储单元并初始化

控制程序流向伪指令

其他伪指令

1、定义对齐方式伪指令 ALIGN

2、段定义伪指令AREA

AREA用于定义一个代码段或数据段。常用的属性如下:

常用属性

一个汇编语言程序 至少 要有一个代码段。

3、CODE16 和 CODE32

4、定义程序入口点伪指令 ENTRY

一个程序(可包含多个源文件)中至少要有一个ENTRY(可以有多个ENTRY,当有多个ENTRY入口时,程序的真正入口点由链接器指定),但一个源文件中最多只能有一个ENTRY(可以没有ENTRY)。

5、汇编结束伪指令 END

6、外部可引用符号声明伪指令 EXPORT(或GLOBAL)

7、IMPORT

8、EXTERN

9、等效伪指令 EQU(#define

10、GET(或INCLUDE)

与ARM指令相关的宏指令

1、MACRO 和 MEND

1
2
3
4
5
6
7
8
9
10
MACRO ;宏定义指令
$MDATA MAXNUM $NUM1,$NUM2 ;主标号,宏名,参数
语句段
$MDATD.WAY1 ; 宏内标号,必须写为“主标号.宏内标号”
语句段
$MDATA.WAY2 ; 宏内标号
语句段
MEND ; 宏结束指令
主程序中调用该宏:
Lab1 MAXNUM 0x01, 0x02

2、MEXIT

MEXIT 用于从宏定义中跳转出去。

宏指令

在ARM中,还有一种汇编器内置的无参数和标号的宏——宏指令。

在汇编时,这些宏指令被替换成一条或两条真正的ARM或 Thumb 指令。ARM宏指令有四条,分别是:

• ADR:小范围的地址读取宏指令;

• ADRL:中等范围的地址读取宏指令;

• LDR:大范围的地址读取宏指令;

• NOP:空操作宏指令。

ARM汇编程序设计模板要点

1
2
3
4
5
6
AREA example,CODE,READONLY
;定义代码段,一个ARM汇编程序至少有一个代码段
ENTRY ;定义程序入口点
……
……
END ;汇编语言程序结束

例题:

该程序执行后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	AREA blockcopy,CODE,READONLY 
ENTRY
LDR R1,=ftt ;将符号ftt的地址加载到寄存器R1中。
LDR R2,=ftt2
LDR R3,[R1]
LDR R4,[R2]
LDR R5,[R1, #4]
LDR R6,[R2, #4]
Src DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
StrMem DCB “ABCS”,0
MAP Src
ftt FIELD 8
ftt2 FIELD 8
END

R3= 1 ,R4= 3 ,R5= 2 ,R6= 4

本门课中(包括考试和作业)如果没有特殊说明,存储方式均默认为小端模式

1
2
3
4
5
6
7
8
9
10
11
	AREA blockcopy,CODE,READONLY 
ENTRY
ARM
LDR R0,=ss1
LDR R1,=ss2
LDR R4,[R0]
ALIGN 4
ss1 DCB 1
ALIGN 4,3
ss2 DCB 3
END

该段程序执行完成后,假如R0的值为0x10001000,则R1=0x10001003 ,R4=0x03000001

作业题:

六、在列表中查找指定的数据

要求如下:

(1) 定义一个存储单元首地址名为 Start 的数据列表,该数据列表包含 5 个字存储单元。第一个单元是列表中数据的数量,其值为 4,即列表中包含 4 个数据。后面四个单元存储的是列表中的 4 个数据:0x0138A, 0x0A21DC, 0x1F5376, 0x9018613。

(2) 定义一个名为 NewItem 的字存储单元,该单元中包含要查找的数据,该例要求是0x1F5376

(3) 要求编写一个程序,在 Start 数据列表中查找是否包含 NewItem 单元中的数据。如果包含则把该数据在列表中的位置序号(1、2、3、4)存储到 Index 字存储单元中。否则在Index 字存储单元中存储 0xFFFFFFFF。

(4) 要求 Start 的数据列表和 NewItem 的字存储单元定义在代码段中(只读的 ROM 区),而 Index 字存储单元要定义在一个名为 mydata 的数据段中(可读写的 RAM 区)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
	AREA myprog, CODE, REDFONLY
ENTRY

Main
LDR R0, =Start ; R0 = Start 的地址
LDR R1, NewItem ; R1 = NewItem 的值
LDR R3, Start ; R3 = Start 的第一个值
CMP R3, #0 ; 检查 R3 是否为 0
BEQ Missing ; 如果 R3 = 0,跳转到 Missing

LDR R4, [R0, #4]! ; R4 = Start[1],R0 增加 4
MOV R2, #1 ; R2 = 1(索引)
Loop
CMP R1, R4 ; 比较 R1 和 R4
BEQ Done ; 如果相等,跳转到 Done
SUBS R3, R3, #1 ; R3 -= 1(计数器)
LDR R4, [R0, #4]! ; R4 = 下一个元素,R0 增加 4
ADD R2, #1 ; R2 += 1(索引)
BNE Loop ; 如果 R3 != 0,继续循环

Missing
MOV R2, #0xFFFFFFFF

Done
LDR R5, =Index ; R5 = Index 的地址
STR R2, [R5] ; 存储 R2 到 Index
SWI 0x11 ; 退出程序

Start
DCD 0x4, 0x0138A, 0x0A21DC, 0x1F5376, 0x9018613]
NewItem
DCD 0x1F5376

AREA mydata, NOINIT
MAP 0x40000000
Index
FIELD 4
END

九、试把如下 C 函数改写成 ARM指令函数。

1
2
3
int subxx(int x, int y) {
return x - y;
}

改写为:

1
2
3
4
5
6
	AREA TT, CODE, READONLY
EXPORT subxx
subxx
SUBS R0, R0, R1
MOV PC, LR
END

十、把下面的 ARM 指令函数改写成 C 语言函数。

1
2
3
4
5
6
7
8
9
	AREA tt, CODE, READONLY
EXPORT strcopy
strcopy
LDRB R2, [R1], #1
STRB R2, [R0], #1 ;将 R2 中的字节写入 R0 指向的内存地址。
CMP R2, #0
BNE strcopy
MOV PC, LR
END

等效于

1
2
3
4
5
6
7
8
void strcopy(char *d, char *s) {
while (*s != '\0') {
*d = *s;
d = d + 1;
s = s + 1;
}
*d = *s;
}

期中考试

判断对错题(30分)

  1. 精简指令集RISC相对复杂指今集CISC,具有更少的指令,指令实现的功能更简单,因此实现同样一个复杂功能,采用RISC指令编写的程序比采用C1SC指令偏写的程序具有更少的代码量。(8分) 错误
  2. 实时操作系统中的任务的硅起态是指任务进入任务等待队列,等待通过调度转为运行状态。(5分)错误
  3. ARM体系结构与纯粹的RISC体系结构不完全相同。(12分) 正确
  4. 在 ARM 状态下, 任一时刻都可以访问到 16 个通用寄存器和 2 个状态寄存器。(5 分) 错误

阅读程序并填空题(39分)

(注:假设 RAM 区存储单元上电后缺省值都为 0)

1、阅读程序,并根据要求填空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
AREA myprog, CODE, READONLY

LDR R0, =MyData
LDR R1, =Des_RAM

STRB R4, [R3,#-1]
LDR R4, [R1]
B
LDR R2, =FLD2
LDR R3, =FLD_C
SUB R6, R2, R0
SUB R7, R3, R1
LDR R4, [R2,#4]
STR R4, [R3,#-4]!
SUB R8, R2, R0
SUB R9, R3, R1
LDR R4, [R2],#-4
STR R4, [R3,#-4]!
MOV R12, #4
LDR R4, [R0,R12,LSL #02]
STR R4, [R3,#-4]
MOV R12, #5
LDRB R4, [R0,R12,LSL #02]

MyData DCD 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB,
DCD 0xC,0xD, 0xE, 0xF
MAP MyData
FLD1 FIELD 4
FLD2 FIELD 8
FLD3 FIELD 8
AREA mydata, DATA, READWRITE
Des_RAM SPACE 40
MAP Des_RAM
FLD_A FIELD 4
FLD_B FIELD 8
FLD_C FIELD 8
END

上述程序执行后,下列各寄存器的值分别是多少?R6 = 4,R7 = 12,R8 = 4,R9 = 8。

从 Des_RAM 地址开始的连续 3 个字单元的内容是:0x6000005,2,3。

2、阅读程序,并根据要求填空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
	GBLA Len
GBLS StrA
GBLS StrB
GBLS StrC
GBLL VL
StrA SETS "My String!"
VL SETL {True}
StrB SETS :STR: 0x12345678
StrC SETS StrA :CC: StrB :RIGHT: 3
Len SETA :LEN: StrC
DAdr EQU DataA
AREA mycode, CODE, READONLY
ENTRY
LDR R0, =DAdr
ADRR1, StrS1
LDR R2, =StrS2
LDR R3, =DataB
LDRB R4,[R0]
LDR R5, =Len
MOV R6, #?StrS2
End1 B End1
DataA DCD 0x11223344
StrS1 DCB StrB,0
ALIGN 4
StrS2 DCB "$StrC$VL$Len",0
DataB DCD 0x01
END

假设上述程序执行后,寄存器 R0 的值为:0x20,则下列各寄存器的值分别是多少?

R1 = 36,R2 = 48,R3 = 64,R4 = 68,R5 = 3。

起始地址为 Str52 的连续内存单元中,所存放的字符串为(注:该字符串以 0 为结束标记):678T00000003。

编程题(31分)

假设系统采用S3C2440处理器,要求实现C语言和汇编语言的混合编程。

(1)使用汇编语言实现一个名为 MYFUN 的子程序:

  1. 该函数有4个输入的int类型参数
  2. 在该汇编子程序中调用C函数cg(),以实现下面的功能:int MYFUN(int a, int b, int c, int d) {return -cg(c,d,a,b,a+c,b+d)}
  3. 在C语言的main函数中调用该汇编MYFUN函数。注:汇编所需的启动代码不用编写,只需要编写MYFUN函数实现部分,假设该MYFUN函数在一个名为ARMcode.s的汇编源程序中实现。ARMcode.s需要包含完整汇编程序结构,需要保护和恢复现场。(18分)

(2)编写完整的C语言程序:

  1. 使用嵌入式汇编方式,编写int FunXY(int x, int y),要求执行:
    • 把参数 x 按位取反后,再进行循环右移6位的操作,得到中间结果a,
    • 把参数 y 取绝对值后,再与a进行逻辑异或操作。其结果作为函数的返回值返回。
  2. 在 C 语言的 main 函数中调用汇编函数 MYFUN,输入的参数分别为 14,25,38,49。
  3. 在 C 语言程序的 main 函数中,编写一段代码,用于判断当前 CPU 的工作状态是 ARM 态还是 Thumb 态。如果是 ARM 态,则把变量 Tflag 设置为 1,否则把变量 Tflag 设置为 0。
  4. 在 C 语言程序的 main 函数中调用函数 FunXY,函数的输入参数为:0x12345678 和 -2。

(3)注意:要遵守 ATPCS 规范,程序结构要完整。

1
2
3
4
int cg(int a1, int a2, int a3, inta4, int a5, int a6) {
return (a1+a2+a3+a4)*a5-a6;
}
int Tflag;

实验一 指令系统及寻址方式

  • 搭建实验环境

安装 MDK Keil uVision5 及 MDK79525(MDK5-ARM9 支持包),学会在 Keil 中编程 ARM 汇编代码文件,掌握 rebuild 和 debug 等基本操作。

  • 实验内容

对40多条常用的指令,使用不同的寻址方式进行测试,并通过查看寄存器、存储器、程序状态寄存器的内容,检查是否与期望一致。

实验一

「UESTC II」Advanced RISC Machine

http://aununo.xyz/2025/04/14/ARM-处理器/

Author

Aununo Gan

Posted on

2025-04-14

Updated on

2025-04-30

Licensed under