【嵌入式系统设计与应用总复习】

目录

1. 汇编

1.1 例题

1.1.1 在某工程中,要求设置一绝对地址为0x987a的整型变量的值为0x3434。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

int *ptr;
ptr = (int *)0x987a;
*ptr = 0x3434;

1.1.2 假设某系统的一个绝对地址为0x9527的整型变量的值为0x1633。请编写代码实现这一操作。

int *ptr;
ptr = (int *)0x9527;
*ptr = 0x1633;

1.1.3 请写出对应C代码的ARM 指令。

C代码:
if(a > b)
	a++;
else
	b++;

ARM指令:
CMP R0,R1        ; R0 与R1 比较
ADDHI R0,R0,#1        ; 若R0>R1,则R0=R0+1
ADDLS R1,R1,#1        ;若R0<=R1,则R1=R1+1
C代码为:
        If((a!=10)&&(b!=20))=a+b;
        
 对应的ARM 指令如下,其中R0 为a,R1为b。
        CMP R0,#10            ; 比较R0 是否为10
        CMPNE R1,#20        ; 若R0 不为10,则比较R1 是否20
        ADDNE R0,R0,R1        ; 若R0 不为10 且R1 不为20,指令执行,R0=R0+R1

1.1.4 请写出下列ARM 指令的功能。

MOV R1,#0x10; 				将0x10存入R1
MOV R0,R1; 					将R1内的数据存入R0
MOVS R3,R1,LSL #2; 	将R1内的数据左移两位后存入R3
MOV PC,LR; 					将LR 的值复制到 PC

1.1.5 嵌入式系统中经常要用到无限循环,怎么样用C语言编写死循环呢?

while(1){}
for(;;){}
Loop:
...
goto Loop;

1.6 下段代码是一段简单的C循环函数,在循环中含有数组指针调用。

void increment(int *restrict b,    int *restrict c)
{
	int i;
	for(i = 0; i < 100; i++)
 {
 	c[i] = b[i] + 1;
 }
}

请改写上述代码段,以实现如下功能:
――循环100次变成了循环50次(loop unrolling),减少了跳转次数;
  ――数组变成了指针,减少每次计算数组偏移量的指令;
  ――微调了不同代码操作的执行顺序,减少了流水线stall的情况;
  ――循环从++循环变成了――循环。这样可以使用ARM指令的条件位,为每次循环减少了一条判断指令。

void increment(int *b, int *c)
{
	int i;
 int *pb, *pc;
 int b3, b4;
 pb = b - 1;
 pc = c - 1;
 b3 = pb[1];
 for (i = (100 / 2); i != 0; i--)
 {
 	b4 = *(pb += 2);
 	pc[1] = b3 + 1;
 	b3 = pb[1];
		*(pc += 2) = b4 + 1;
	}
} 

1.7 假设目标机ARM开发板的IP地址为192.168.1.166,主机IP地址为192.168.1.10,请首先在主机上编写程序实现对10个整数由大到小进行排序(请写出完整源码)。然后简述该程序编译、下载至目标机、修改文件权限以及执行该程序的过程。

#include <stdio.h> 
void main() 
{
	void sort(int x[ ],int n);   
  	int *p,i,a[10]; 
  	p=a; 
  	for(i=0;i<10;i++) 
  		scanf(“%d”,p++); 
  	p=a; 
   sort(p,10); 
   for(p=a,i=0;i<10;i++) 
   {
   		printf(“%d ”,*p);
   		 p++;
   	} 
   printf("\n");
} 
void sort(int x[],int n)  
{
	int i,j,k,t; 
  	for(i=0;i<n-1;i++) 
  	{
  		k=i; 
     	for(j=i+1;j<n;j++) 
       		if(x[j]>x[k]) 
       		k=j; 
           if(k!=i) 
			{
				t=x[i];
				x[i]=x[k];
				x[k]=t;
			} 
	}
} 

2. 冒泡排序

2.1 例题

2.1.1 使用ARM汇编语言指令编写一个实现冒泡排序功能的程序段。

	AREA Sort,CODE,READONLY		;declare for code area
	ENTRY					;entry for the whole code
start						;main code flag
	MOV R4,#0				;clear r4
	LDR R6,=src				;r6 point to the begining of numbers
	ADD R6,R6,#len			;r6 point to the end of numbers  
outer					 	;outer loop begining
	LDR R1,=src				;r1 point to the begining of numbers 
inner						;inner loop begining
	LDR R2,[R1]				;get the number in address of r1
	LDR R3,[R1,#4]			;get the number in address next to r1
	CMP R2,R3				;compare two numbers we gotten
	STRGT R3,[R1]			;if the first > the second 
	STRGT R2,[R1,#4]			;exchange the position of two numbers
	ADD R1,R1,#4				;the point of r1 move
	CMP R1,R6				;compare position current  and ending
	BLT inner				;if not meet the ending go on to loop
	ADD R4,R4,#4				;global counter +1
	CMP R4,#len				;compare the current position
	SUBLE R6,R6,#4			;if not meet the ending
	BLE outer				;go on to loop
	AREA Array,DATA,READWRITE ;   decare for data area
src	DCD 2,4,10,8,14,1,20	  	;init the original numbers
len	EQU 7*4					;get the length of numbers
	END						;end of whole code

LDR
L表示LOAD
理解为:Load from memory into register。
LDR R1, [R2]
R1【寄存器】<——[R2]【内存RAM】
将R2的memory内容copy到R1寄存器.

LDR指令:

例1: ldr r0, 0x12345678 // 就是把0x12345678这个地址中的值存放到r0中。而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中。
例2:ldr r0,r1 //表示把r1寄存器中的值放入r0
例3:ldr r0,[r1] // [r1]表示r1中值对应内存的地址,所以是把r1中的数当作一个地址,把这个地址中的值放入r0.

LDR伪指令:

例1(立即数): ldr r0, =0x12345678
这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。
例2(标号): ldr r0, =_start :将指定标号的值赋给r0
这里取得的是标号 _start 的绝对地址,这个绝对地址(链接地址)是在链接的时候确定的。它要占用 2 个 32bit的空间,一条是指令,另一条是文字池中存放_start 的绝对地址。
这里对比一下,adr r0, _start,和 ldr r0, =_start
它们的目的是一样的,想把标签的地址给r0,区别是一个是相对地址,一个是绝对地址。目的一样,但是得到的结果不一定相同。结果是否相同,就要看这个PC的值,是否和链接地址相同了。

STR

S表示STORE
理解为:Store from a register into memory.
STR R1, [R2]
R1【寄存器】——>[R2]内存RAM】
将R1寄存器内容copy到R2的memory.

LDM

L的含义仍然是LOAD(连接寄存器和连续内存的相互copy)
理解为:Load from memory into register。
虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13],同时,LDM指令中寄存器和内存地址的位置相对于前面两条指令改变了,下面的

例子:
LDMFD SP! , {R0, R1, R2}
实际上可以理解为: LDMFD [SP]!, {R0, R1, R2}
意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去(如果这个地方还不懂的话,可以参看我文章开头提到的链接,里面有详细的图解)

STM

S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。

STMFD SP!, {R0}
同样的,该指令也可理解为: STMFD [SP]!, {R0}
意思是:把R0保存到堆栈(sp指向的地址)中。
显然,这两个堆栈操作指令也有个特点,就是寄存器组写在后面(右边)而堆栈指针写在前面(左边),而且实际上使用的是堆栈指针中的内存地址,这一点与前面两条指令是有区别的。

2.1.1 编写一个实现数组排序的C语言程序,调用汇编语言编写的冒泡排序程序段。

int i, j, tmp;
int b[]={18,24,12,59,101,96,34};
for(i = 0; i < 6; i++)
{
	for(j = i + 1; j < 7 ; j++)
	{
		if(b[i] > b[j])
		{
			tmp = b[i];
			b[i] = b[j];
			b[j] = tmp;
		}
	}
}
i = 0;
while(i++ < 7)
	printf("%d ", b[i - 1]);
	
C语言调用汇编
#include<stdio.h>
extern viod Sort(int b[]);
int main()
{
   int b[]={18,24,12,59,101,96,34};
   Sort(int b[]);
}

3. 命令功能、结果

3.1 例题

3.1.1 请按要求写出一个makefile文件,要求包括:采用arm-linux-gcc交叉编译器,源文件为led8.c,目标文件为led8,使用led8.h头文件,使用相应宏变量。

CC		= arm-linux-gcc
INSTALL		= install
TARGET		= led8
all : $(TARGET)
$(TARGET): led8.c led8.h
	$(CC) -static $< -o $@
clean :
	rm -rf *.o $(TARGET) *~

3.1.2 请按要求写出一个Makefile文件,要求包括:采用arm-linux-gcc交叉编译器,源文件为helloworld.c,目标文件为helloworld,使用cortexa8.h头文件,含有clean操作。

CC		= arm-linux-gcc 
INSTALL		= install
TARGET		= helloworld 
all : $(TARGET)
$(TARGET): helloworld.c cortexa8.h 
	$(CC) -static $< -o $@
clean :
	rm -rf *.o $(TARGET) *~ 

猜你喜欢

转载自blog.csdn.net/weixin_51911075/article/details/129161125