目录
- 1. 汇编
-
- 1.1 例题
-
- 1.1.1 在某工程中,要求设置一绝对地址为0x987a的整型变量的值为0x3434。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
- 1.1.2 假设某系统的一个绝对地址为0x9527的整型变量的值为0x1633。请编写代码实现这一操作。
- 1.1.3 请写出对应C代码的ARM 指令。
- 1.1.4 请写出下列ARM 指令的功能。
- 1.1.5 嵌入式系统中经常要用到无限循环,怎么样用C语言编写死循环呢?
- 1.6 下段代码是一段简单的C循环函数,在循环中含有数组指针调用。
- 1.7 假设目标机ARM开发板的IP地址为192.168.1.166,主机IP地址为192.168.1.10,请首先在主机上编写程序实现对10个整数由大到小进行排序(请写出完整源码)。然后简述该程序编译、下载至目标机、修改文件权限以及执行该程序的过程。
- 2. 冒泡排序
- 3. 命令功能、结果
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) *~