嵌入式系统基础学习知识原理及其应用题目要求程序代码.doc
.嵌入式系统原理及应用 作业题目作业1一、调试下面的程序,并回答问题。.global _start.text_start: LDR SP, =src LDMFD SP!,R0-R6 STMFD SP!,R0-R6 LDMFD SP!,R3 LDMFD SP!,R4 LDMFD SP!,R5 LDMFD SP!,R6 LDMFD SP!,R0 LDMFD SP!,R1 LDMFD SP!,R2 stop: b stop.ltorg src: .long 1,2,3,4,5,6,7.end问:该程序完成了什么功能?答:该程序完成的功能:先把数据区堆栈中的17这七个数据送给R0R0寄存器,然后又把寄存器列表中的R0R7存入堆栈,然后又依次把堆栈中的17这七个数送给R3R6,R0R2,然后程序就结束了,在取数和存数的过程中。堆栈指针sp由0x0000变到0x8030再到0x804c,然后到0x8030,然后依次加4,最后到0x804c;程序计数器R15(PC)由0x8000地址依次加4 。二、LDMFD,STMFD伪代码实现的原理。答:指令STMFD和LDMFD分析:根据ATPCS规则,我们一般使用FD(Full Descending)类型的数据栈!所以经常使用的指令就有STMFD和LDMFD,通过ARM对于栈操作和批量Load/Store指令寻址方式,可以知道指令STMFD和LDMFD的地址计算方法:STMFD指令的寻址方式为事后递减方式(DB)而DB寻址方式实际内存地址为:start_address = Rn - (Number_Of_Set_Bits_In(register_list)*4)end_address = Rn - 4STM指令操作的伪代码: if ConditionPassed(cond) then address = start_address for i = 0 to 15 if register_listi = 1 Memoryaddress = Ri address = address + 4有上面两个伪代码可以得出 STMFD SP!,R0-R7,LR 的伪代码如下: SP SP 94; address =SP;for i = 0 to 7 Memoryaddress = Ri; address = address + 4;Memoryaddress = LR;LDMFD指令的寻址方式为事后递增方式(IA)IA内存的实际地址的伪代码start_address = Rnend_address = Rn + (Number_of_set_bits_in(register_list)*4) - 4LDM指令操作的伪代码(未考虑PC寄存器): if ConditionPassed(cond) then address = start_address for i = 0 to 15 if register_listi = 1 Ri Memoryaddress,4 address = address + 4所以LDMFD SP!,R0-R7,PC (;恢复现场,异常处理返回)伪代码是: address = SP; for i = 0 to 7 Ri = Memoryaddress ,4 address = address + 4; SP = address;作业2一、用移位操作完成(R0)*10运算。参考程序:.text.global _start_start:mov R0,#10mov R1,R0,LSL #3mov R2,R0,LSL #1add R3,R1,R2stop: B stop.end二、已知数据缓冲池中有两组数据x和y,每组中有3个数据(例如x: 90,60,30,y: 60,40,20),将x中的数据减去y中的数据,最后将两组数相减得到的结果送回到x中去!参考代码:.text.global _start_start: LDR SP,=x LDMFD SP!,R0-R2 LDMFD SP!,R3-R5 sub R2,R2,R5 Sub R1,R1,R4 Sub R0,R0,R3 STMFD SP!,R0-R2 stop: b stop.ltorg x: .long 80,90,100 y: .long 10,20,30.end作业3已知R0和R1的值,要求保留R0的低16位,保留R1的高16位状态,最后将这两个值组成一个新的数送给R3.参考代码:.text.global _start_start:LDR R0,=0x12345678LDR R1,=0x87654321ldr R2,=0xffffLDR R4,=0xffff0000AND R0,R0,R2AND R1,R1,R4ORR R3,R0,R1stop: B stop.end作业4在ARM GNU环境下用ARM汇编语言编程序实现参考代码:.text.global _start_start: LDR R0,=x LDR R1,=y LDR R2,R0 CMP R2,#0 BEQ ZERO BGT ZHENG BLT FU ZERO: MOV R3,#0 STR R3,R1 B stopZHENG: mov R3,R2 STR R3,R1 B stop FU: mvn R3,R2 mov R4,#0x1 add R3,R3,R4 STR R3,R1 B stop stop: B stop.ltorg x: .long-10 y:.long 0作业5求20的阶乘:64位结果放在【R9:R8】中,R9放高32位,R放低32位思路:每轮乘法操作中,低32位(R8)乘以X(R0)后,结果的低32位放在R8中,高32位放在R9中;高32位R1乘以X(R0)后再加上R9,得到64位结果的高32位,存入R9参考程序:.global _start.text_start:MOVR8 , #20低32位初始化为20MOV R9 , #0高位初始化为0SUBR0,R8,#1初始化计数器Loop:MOVR1 , R9 暂存高位值UMULLR8 , R9 , R0 , R8R9:R8=R0*R8MLAR9 , R1 , R0 , R9R9=R1*R0+R9SUBSR0 , R0 , #1 计数器递减BNELoop 计数器不为0继续循环Stop:B stop.end二、已知a=R0,b=R1.while(ab) doif(ab) thena=a-belsea=b-a end ifend whileresult=a根据以上代码写出对应的汇编代码:参考代码:CMP R0,R1CMPNE R0,R1SubGT R0,R0,R1SubLT R0,R1,R0.end作业61.将下面的ADS环境下的程序代码改写成GUN环境下的程序代码。(调试时使用F11 step into,注意和F10 step over的区别)AREA Jump, CODE, READONLY num EQU 4; 函数地址表内容的个数 ENTRY CODE32start LDR R0, =choice; R0指向存储区的choice单元 LDR R0, R0; 设置第一个参数:选择执行哪一个函数 MOV R1, #16; 设置第1个操作数 MOV R2, #2; 设置第2个操作数 BL arithfunc; 调用子程序arithfuncstop MOV R0, #0x18; 程序结束返回编译器调试环境 LDR R1, =0x20026 SWI 0x123456arithfunc CMP R0, #num ; 比较R0的值是否超过函数地址表的个数 MOVHS PC, LR ; 如果大于,那么就返回到标号stop处 ADR R3, JumpTable; 将函数地址表的地址作为基地址 LDR PC, R3, R0, LSL #2; 根据R0参数进入对应的子程序JumpTable; 函数地址表的入口基地址 DCD DoAdd; 加法子程序 DCDDoSub; 减法子程序 DCDDoMul; 乘法子程序 DCDDoDiv; 除法子程序DoAdd ADD R0, R1, R2 ; R0 = R1 + R2 MOV PC, LR ; 返回DoSub SUB R0,R1, R2 ; R0 = R1 - R2 MOV PC,LR ; 返回DoMul MOV R0, R1, LSL R2 ; R0 = R1 R2 MOV PC, LR ; 返回 AREA NUM, DATA, READWRITEchoiceDCD3 ; 0:表示选择加法子程序 1:表示选择减法子程序 ; 2:表示选择乘法子程序 3:表示选择除法子程序 END参考程序:.equ num,4 函数地址表内容的个数.text.global _start_start: LDR R0,=choiceR0指向存储区的choice单元LDR R0,R0设置第一个参数:选择执行哪一个函数 MOV R1,#16设置第1个操作数 MOV R2,#2设置第2个操作数 Bl arithfunc调用子程序arithfunc stop:bstoparithfunc: CMP R0,#num 比较R0的值是否超过函数地址表的个数 MOVHS PC, LR 如果大于,那么就返回到标号stop处 ADR R3, JumpTable 将函数地址表的地址作为基地址 LDR PC, R3, R0, LSL#2 根据R0参数进入对应的子程序 JumpTable: 函数地址表的入口基地址 .long DoAdd 加法子程序 .long DoSub 减法子程序 .long DoMul 乘法子程序 .long DoDiv 除法子程序 DoAdd: ADD R0, R1, R2 R0 = R1 + R2 MOV PC, LR 返回DoSub: SUB R0,R1, R2 R0 = R1 - R2 MOV PC,LR 返回DoMul: MOV R0, R1, LSL R2 R0 = R1 R2 MOV PC, LR 返回.ltorg choice: .long 3 0:表示选择加法子程序 1:表示选择减法子程序 2:表示选择乘法子程序 :表示选择除法子程序.end2. 改写以下程序,实现从ARM状态切换到thumb状态,最后再切换到ARM状态!#*# NAME:ThumbCode.s *# Author: Embest*# Desc:ThumbCode examples*# History:shw.He 2005.02.22*#*/*-*/* constant define */*-*/.global _start/*-*/*unable to locate source file.code */*-*/.text_start:.arm/* Subsequent instructions are ARM */header:adrr0, Tstart + 1/* Processor starts in ARM state, */bxr0 /* so small ARM code header used */* to call Thumb main program. */nop .thumbTstart:movr0, #10/* Set up parameters */movr1, #3bldoadd/* Call subroutine */stop:bstop/*-*/*Subroutine code:R0 = R0 + R1 and return */*-*/doadd:addr0, r0, r1/* Subroutine code */movpc, lr/* Return from subroutine. */.end/* Mark end of file */参考程序:.global _start .text_start:mov r3,#0.armheader:adr r0,Tstart + 1add r3,r3,#1 /设置循环标志,便于从thumb切换到arm后能停止程序cmp r3,#2Beq stopbx r0stop: b stop .thumbTstart: LDR r2,=headermovr0, #10movr1, #3bldoaddbx r2doadd:addr0, r0, r1movpc, lr.end作业7已知,若任意给一个定值,假定为-25,存放在x单元,函数值存放在y单元;要求根据x中的值来确定y的值。 参考程序:.text.global _start_start:ldr r0,=srcldr r3,=dstldr r1,r0ldr r2,=0x0 /*r2储存常数0*/cmp r1,r2addgt r2,r2,#1sublt r2,r2,#1str r2,r3stop:b stop.ltorgsrc:.long -25dst:.long 0.end作业8从x单元开始的5个连续字单元中存放5个无符号数,从中找出最大者送入y单元中。参考程序:.text.global _start.equ num,4_start:ldr r4,=num /*r4内的数值作为计数变量*/ldr r0,=xldr r5,=yldr r1,r0,#4loop: ldr r1,r0,#4ldr r2,r0,#4cmp r2,r1strge r2,r5strlt r1,r5sub r4,r4,#1cmp r4,#0bne loopstop:b stop.ltorgx:.long 1,2,6,3,9y:.long 0.end作业9(冒泡排序法):利用逐次求大数的方法对内存单元ARRAY开始的以字节为单位的无符号数进行从大到小排序。在以BUF为首址的字存储区中存放有10个无符号数 0x0FF,0x00, 0x40, 0x10, 0x90, 0x20, 0x80, 0x30, 0x50, 0x70, 0x60,现需将它们按从小到大的顺序排列在BUF存储区中,试编写其程序。参考程序:分析:采用逐一比较法:l 将第一个存储单元中的数与其后n-1个存储单元中的数逐一比较,每次比较之后,总是把小者放在第一个存储单元之中,经过n-1次比较之后,n个数中最小者存入第一存储单元中;l 接着从第二个存储单元开始,同理,经过n-2次比较之后,得到n-1个数中最小者存入第二存储单元中;如此类推,当最后两个存储单元的数比较完成之后,从小到大的排列顺序就实现了。 “冒泡排序”算法。各寄存器分配功能如下:R0:用来指示缓冲区初始地址R1:外循环计数器R2:内循环计数器R3:外循环地址指针R4:内循环地指针R5:内循环下一个数地址指针R6:存放内循环一轮比较的最小值R7:存放内循环取出的下一个比较值程序代码:.equ num,10.text.global _start_start:LDR R0,=Datas 指向数组的首地址mov R1,#0 外循环计数器mov R2,#0 内循环计数器LOOPI: add R3,R0,R1,lsl #2 外循环首地址存R3 mov R4,R3 内循环首地址存R4 ADD R2,R1,#1 内存换计数器初值 MOV R5,R4 内循环下一地址初值 LDR R6,R4 取内循环下一地址值R4LOOPJ: add r5,r5,#4 内循环下一地址值 ldr r7,r5 取出下一地址值至R7 cmp r6,r7 比较 blt next 小则取下一个 swp r7,r6,r5 大则交换,最小值R6 mov r6,r7next:add r2,r2,#1 内循环计数cmp r2,#num 循环中止条件blt LOOPJ 小于N则继续内循环swp r7,r6,r3 否则,内循环一轮结束,将最小数存入外循环的首地址处add r1,r1,#1 外循环计数cmp r1,#num-1 外循环中止条件blt LOOPI 小于N-1则继续执行外循环BEQ stopb _startstop: b stop .ltorgDatas: .long 0x0FF, 0x00, 0x40 .long 0x10, 0x90, 0x20, .long 0x80, 0x30, 0x50, .long 0x70 .end作业10通过设置的入口参数查找函数地址表,实现选择不同的函数功能。已知两个操作数分别存放于寄存器R0与R1中,函数1实现两数相加R0+R1;函数2实现两数相减R0-R1;函数3实现两数相乘R0*R1; 函数4实现两数相除R0/R1。参考代码:.text.equ num,3.global _start_start:ldr r0,=0x01ldr r1,=0x02ldr r4,=0x02 /*r4用来作为选择函数入口Doadd,Dosub,Domul的选项(默认为1,可修改)*/bl Funcstop:b stopFunc:cmp r4,#num movge pc,lr /*当r4内的值大于等于3时,调回_start函数*/ldr r3,=JTableldr pc,r3,r4,lsl #2 /*因为函数入口是.long类型,所以需要左移(LSL)2位*/JTable:.long Doadd,Dosub,DomulDoadd:add r2,r0,r1mov pc,lrDosub:sub r2,r0,r1mov pc,lrDomul:mul r2,r0,r1mov pc,lr.end作业11编程实现S=1+2*3+4*5+.+N*(N+1),其中N=10。参考答案:分析过程l 算法功能:l 相邻数相乘:MULl 累加: ADDl 循环次数:10 CMP B指令l 初始值 S=1 N l N+1 部分和N(N+1)寄存器分配l R0: S 1 R0+R3得S值l R1:N的初始值 2 循环变量l R2:N+1 由R1=1求得l R3:N(N+1)参考程序:.text.equ N,10.global _start_start:MOV R0, #1 R0用作累加,置初值1,S MOV R1, #2 R1用作第一个乘数, 初值为2, N loop: /*内层循环loop1,做乘法运算*/ADD R2, R1, #1 R2用作第二个乘数,N+1 MUL R3, R2, R1 实现N *(N+1)部分积存于R3 ADD R0, R0, R3 将部分乘积累加至R0 ADD R1, R1, #1 改N的值得到下一轮乘数 CMP R1, #10 循环次数比较 BLE loop 未完成则重复stop:b stop.end作业12编程实现S=1!+2!+3!+N!,要求N!用子程序实现。程序代码:.global _start.text_start:mov R0,#0mov R1,#1loop:mov R2,#1mov R3,#1mov R4,#1loop1:mul R4,R2,R3mov R3,R4add R2,R2,#1cmp R1,R2bge loop1loop2:add R0,R0,R3add R1,R1,#1cmp R1,#10bls loopstop:b stop.end作业13在C程序中内嵌汇编程序实现将源字符串src中的数据拷贝到目的字符串dest中参考程序:其中主程序如下:*/#includevoid strcpyy(const char *src, char *dest);_gccmain(void) char *s= this is a example AsmInC !; char temp64; strcpyy(s,temp); /printf(Original string: %s n , s); /printf(Copyed string: %s, temp); return 0;void strcpyy(const char *src, char *dest)_asm( /*GNU和ADS环境下的区别*/loop:ldrb r5,r0,#1strb r5,r1,#1cmp r5,#0;bne loop);作业14/*在GNU ARM环境下设计程序,实现字符串的逆序拷贝。*/.global _start.text_start:LDR R0, =SrcString 源字符串指针LDR R1, =DstString 目标字符串指针StrCopyDes:MOV R4, #0 字符串长度记录寄存器Strcpydes_L1: 计算字符串的长度LDRB R2, R0, #1ADDR4, R4, #1TSTR2, #0xFFBNEStrcpydes_L1 SUBR4, R4, #1SUBR0, R0, #2 R0 指向源字符串的末尾MOVR3, R1 R3 作为目标串的游标指针Strcpydes_L2:LDRB R2, R0, #-1 逐个拷贝字符串STRB R2, R3, #1SUBS R4, R4, #1BNEStrcpydes_L2Strcpydes_L3:STRB R4, R3向目标串末尾写0,此处R4的值一定为0Stop:BStop.ltorgSrcString:.string Hello World!DstString:.string .end作业151、在GNU ARM编译环境下设计程序,用ARM汇编语言调用C语言实现20的阶乘20的阶乘(20!)操作,并将64位结果保存到寄存器R0、R1中,其中R1中存放高32位结果。程序代码:汇编程序:/* Factorial.s */.text.equ NI,20.global _start.extern Factorial 申明Factorial是外部函数_start:mov r0,#NIBL Factorial调用Factorialstop:B stop.end/*main.c*/ long Factorial(char n)long long ni=1;char i;for(i=1;i=n;i+)ni=ni*i;return ni;2、在GNU ARM编译环境下设计程序,用C语言调用ARM汇编语言实现20的阶乘(20!)操作,并将64位结果保存到0xFFFFFFF0开始的内存地址单元,按照小端格式低位数据存放在低地址单元。/* 首先建立汇编源文件start.s */ .global _start.global _gccmain.extern main 定义外部函数.text_start: BL main 调用Main函数Stop: B Stop _gccmain:mov pc, lr .end/* 第二 建立C语言源文件main.c */ extern void Factorial(char Nx); /声明Factorial是一个外部函数 main() char N =20; Factorial(N); /调用汇编文件实现N!操作 /*注:在此处观察结果*/ while(1); /* Factorial.s */ .global Factorial 声明Factorial为一个全局函数 Factorial:MOV R8 , R0 取参数 MOV R9 , #0 高位初始化 SUB R0,R8,#1 初始化计数器 Loop: MOV R1 , R9 暂存高位值 UMULL R8 , R9 , R0 , R8 R9:R8=R0*R8 MLA R9 , R1 , R0 , R9 R9=R1*R0+R9 SUBS R0 , R0 , #1 计数器递减 BNE Loop 计数器不为0继续循环 LDR R0,=0xFFFFFFF0 STMIA R0, R8,R9 结果保存到0x10000开始的内存单元 MOV PC,LR 子程序返回 .end
收藏
- 资源描述:
-
.\
《嵌入式系统原理及应用》 作业题目
作业1
一、调试下面的程序,并回答问题。
.global _start
.text
_start:
LDR SP, =src
LDMFD SP!,{R0-R6}
STMFD SP!,{R0-R6}
LDMFD SP!,{R3}
LDMFD SP!,{R4}
LDMFD SP!,{R5}
LDMFD SP!,{R6}
LDMFD SP!,{R0}
LDMFD SP!,{R1}
LDMFD SP!,{R2}
stop: b stop
.ltorg
src:
.long 1,2,3,4,5,6,7
.end
问:该程序完成了什么功能?
答:
该程序完成的功能:先把数据区堆栈中的1~7这七个数据送给R0~R0寄存器,然后又把寄存器列表中的R0~R7存入堆栈,然后又依次把堆栈中的1~7这七个数送给R3~R6,R0~R2,然后程序就结束了,在取数和存数的过程中。堆栈指针sp由0x0000变到0x8030再到0x804c,然后到0x8030,然后依次加4,最后到0x804c;程序计数器R15(PC)由0x8000地址依次加4 。
二、LDMFD,STMFD伪代码实现的原理。
答:
指令STMFD和LDMFD分析:
根据ATPCS规则,我们一般使用FD(Full Descending)类型的数据栈!所以经常使用的指令就有STMFD和LDMFD,
通过ARM对于栈操作和批量Load/Store指令寻址方式,可以知道指令STMFD和LDMFD的地址计算方法:
STMFD指令的寻址方式为事后递减方式(DB)
而DB寻址方式实际内存地址为:
start_address = Rn - (Number_Of_Set_Bits_In(register_list)*4)
end_address = Rn - 4
STM指令操作的伪代码:
if ConditionPassed(cond) then
address = start_address
for i = 0 to 15
if register_list[i] == 1
Memory[address] = Ri
address = address + 4
有上面两个伪代码可以得出 STMFD SP!,{R0-R7,LR} 的伪代码如下:
SP = SP - 94;
address =SP;
for i = 0 to 7
Memory[address] = Ri;
address = address + 4;
Memory[address] = LR;
LDMFD指令的寻址方式为事后递增方式(IA)
IA内存的实际地址的伪代码
start_address = Rn
end_address = Rn + (Number_of_set_bits_in(register_list)*4) - 4
LDM指令操作的伪代码(未考虑PC寄存器):
if ConditionPassed(cond) then
address = start_address
for i = 0 to 15
if register_list[i] == 1
Ri = Memory[address,4]
address = address + 4
所以LDMFD SP!,{R0-R7,PC}^ (;恢复现场,异常处理返回)伪代码是:
address = SP;
for i = 0 to 7
Ri = Memory[address ,4]
address = address + 4;
SP = address;
作业2
一、用移位操作完成(R0)*10运算。
参考程序:
.text
.global _start
_start:
mov R0,#10
mov R1,R0,LSL #3
mov R2,R0,LSL #1
add R3,R1,R2
stop:
B stop
.end
二、已知数据缓冲池中有两组数据x和y,每组中有3个数据(例如x: 90,60,30,y: 60,40,20),将x中的数据减去y中的数据,最后将两组数相减得到的结果送回到x中去!
参考代码:
.text
.global _start
_start:
LDR SP,=x
LDMFD SP!,{R0-R2}
LDMFD SP!,{R3-R5}
sub R2,R2,R5
Sub R1,R1,R4
Sub R0,R0,R3
STMFD SP!,{R0-R2}
stop:
b stop
.ltorg
x: .long 80,90,100
y: .long 10,20,30
.end
作业3
已知R0和R1的值,要求保留R0的低16位,保留R1的高16位状态,最后将这两个值组成一个新的数送给R3.
参考代码:
.text
.global _start
_start:
LDR R0,=0x12345678
LDR R1,=0x87654321
ldr R2,=0xffff
LDR R4,=0xffff0000
AND R0,R0,R2
AND R1,R1,R4
ORR R3,R0,R1
stop: B stop
.end
作业4
在ARM GNU环境下用ARM汇编语言编程序实现
参考代码:
.text
.global _start
_start:
LDR R0,=x
LDR R1,=y
LDR R2,[R0]
CMP R2,#0
BEQ ZERO
BGT ZHENG
BLT FU
ZERO:
MOV R3,#0
STR R3,[R1]
B stop
ZHENG:
mov R3,R2
STR R3,[R1]
B stop
FU:
mvn R3,R2
mov R4,#0x1
add R3,R3,R4
STR R3,[R1]
B stop
stop:
B stop
.ltorg
x: .long -10
y: .long 0
作业5
求20的阶乘:64位结果放在【R9:R8】中,R9放高32位,R放低32位
思路:每轮乘法操作中,低32位(R8)乘以X(R0)后,结果的低32位放在R8中,高32位放在R9中;高32位R1乘以X(R0)后再加上R9,得到64位结果的高32位,存入R9
参考程序:
.global _start
.text
_start:
MOV R8 , #20 @低32位初始化为20
MOV R9 , #0 @高位初始化为0
SUB R0,R8,#1 @初始化计数器
Loop:
MOV R1 , R9 @暂存高位值
UMULL R8 , R9 , R0 , R8 @[R9:R8]=R0*R8
MLA R9 , R1 , R0 , R9 @R9=R1*R0+R9
SUBS R0 , R0 , #1 @计数器递减
BNE Loop @计数器不为0继续循环
Stop:
B stop
.end
二、已知a=R0,b=R1.
while(a< >b) do
if(a>b) then
a=a-b
else
a=b-a
end if
end while
result=a
根据以上代码写出对应的汇编代码:
参考代码:
CMP R0,R1
CMPNE R0,R1
SubGT R0,R0,R1
SubLT R0,R1,R0
.end
作业6
1.将下面的ADS环境下的程序代码改写成GUN环境下的程序代码。
(调试时使用F11 step into,注意和F10 step over的区别)
AREA Jump, CODE, READONLY
num EQU 4 ; 函数地址表内容的个数
ENTRY
CODE32
start
LDR R0, =choice ; R0指向存储区的choice单元
LDR R0, [R0] ; 设置第一个参数:选择执行哪一个函数
MOV R1, #16 ; 设置第1个操作数
MOV R2, #2 ; 设置第2个操作数
BL arithfunc ; 调用子程序arithfunc
stop
MOV R0, #0x18 ; 程序结束返回编译器调试环境
LDR R1, =0x20026
SWI 0x123456
arithfunc
CMP R0, #num ; 比较R0的值是否超过函数地址表的个数
MOVHS PC, LR ; 如果大于,那么就返回到标号stop处
ADR R3, JumpTable ; 将函数地址表的地址作为基地址
LDR PC, [R3, R0, LSL #2] ; 根据R0参数进入对应的子程序
JumpTable ; 函数地址表的入口基地址
DCD DoAdd ; 加法子程序
DCD DoSub ; 减法子程序
DCD DoMul ; 乘法子程序
DCD DoDiv ; 除法子程序
DoAdd
ADD R0, R1, R2 ; R0 = R1 + R2
MOV PC, LR ; 返回
DoSub
SUB R0, R1, R2 ; R0 = R1 - R2
MOV PC, LR ; 返回
DoMul
MOV R0, R1, LSL R2 ; R0 = R1 << R2
MOV PC, LR ; 返回
DoDiv
MOV R0, R1, LSR R2 ; R0 = R1 >> R2
MOV PC, LR ; 返回
AREA NUM, DATA, READWRITE
choice DCD 3 ; 0:表示选择加法子程序 1:表示选择减法子程序
; 2:表示选择乘法子程序 3:表示选择除法子程序
END
参考程序:
.equ num,4 @ 函数地址表内容的个数
.text
.global _start
_start:
LDR R0,=choice @R0指向存储区的choice单元
LDR R0,[R0] @设置第一个参数:选择执行哪一个函数
MOV R1,#16 @设置第1个操作数
MOV R2,#2 @设置第2个操作数
Bl arithfunc @调用子程序arithfunc
stop:
b stop
arithfunc:
CMP R0,#num @比较R0的值是否超过函数地址表的个数
MOVHS PC, LR @如果大于,那么就返回到标号stop处
ADR R3, JumpTable @将函数地址表的地址作为基地址
LDR PC, [R3, R0, LSL#2] @根据R0参数进入对应的子程序
JumpTable: @函数地址表的入口基地址
.long DoAdd @加法子程序
.long DoSub @减法子程序
.long DoMul @乘法子程序
.long DoDiv @除法子程序
DoAdd:
ADD R0, R1, R2 @R0 = R1 + R2
MOV PC, LR @返回
DoSub:
SUB R0, R1, R2 @R0 = R1 - R2
MOV PC, LR @返回
DoMul:
MOV R0, R1, LSL R2 @R0 = R1 << R2
MOV PC, LR @返回
DoDiv:
MOV R0, R1, LSR R2 @R0 = R1 >> R2
MOV PC, LR @返回
.ltorg
choice: .long 3 @0:表示选择加法子程序 @1:表示选择减法子程序 @2:表示选择乘法子程序 @:表示选择除法子程序
.end
2. 改写以下程序,实现从ARM状态切换到thumb状态,最后再切换到ARM状态!
#*******************************************************************************************
# NAME: ThumbCode.s *
# Author: Embest *
# Desc: ThumbCode examples *
# History: shw.He 2005.02.22 *
#*********************************************************************************************
/*-----------------------------------------------------------------------------------------------------------*/
/* constant define */
/*-----------------------------------------------------------------------------------------------------------*/
.global _start
/*-----------------------------------------------------------------------------------------------------------*/
/* unable to locate source file. code */
/*-----------------------------------------------------------------------------------------------------------*/
.text
_start:
.arm /* Subsequent instructions are ARM */
header:
adr r0, Tstart + 1 /* Processor starts in ARM state, */
bx r0 /* so small ARM code header used */
/* to call Thumb main program. */
nop
.thumb
Tstart:
mov r0, #10 /* Set up parameters */
mov r1, #3
bl doadd /* Call subroutine */
stop:
b stop
/*------------------------------------------------------------------------------------------*/
/* Subroutine code:R0 = R0 + R1 and return */
/*------------------------------------------------------------------------------------------*/
doadd:
add r0, r0, r1 /* Subroutine code */
mov pc, lr /* Return from subroutine. */
.end /* Mark end of file */
参考程序:
.global _start
.text
_start:
mov r3,#0
.arm
header:
adr r0,Tstart + 1
add r3,r3,#1 //设置循环标志,便于从thumb切换到arm后能停止程序
cmp r3,#2
Beq stop
bx r0
stop:
b stop
.thumb
Tstart:
LDR r2,=header
mov r0, #10
mov r1, #3
bl doadd
bx r2
doadd:
add r0, r0, r1
mov pc, lr
.end
作业7
已知,若任意给一个定值,假定为-25,存放在x单元,函数值存放在y单元;要求根据x中的值来确定y的值。
参考程序:
.text
.global _start
_start:
ldr r0,=src
ldr r3,=dst
ldr r1,[r0]
ldr r2,=0x0 /*r2储存常数0*/
cmp r1,r2
addgt r2,r2,#1
sublt r2,r2,#1
str r2,[r3]
stop:
b stop
.ltorg
src:
.long -25
dst:
.long 0
.end
作业8
从x单元开始的5个连续字单元中存放5个无符号数,从中找出最大者送入y单元中。
参考程序:
.text
.global _start
.equ num,4
_start:
ldr r4,=num /*r4内的数值作为计数变量*/
ldr r0,=x
ldr r5,=y
ldr r1,[r0],#4
loop:
ldr r1,[r0],#4
ldr r2,[r0],#4
cmp r2,r1
strge r2,[r5]
strlt r1,[r5]
sub r4,r4,#1
cmp r4,#0
bne loop
stop:
b stop
.ltorg
x:
.long 1,2,6,3,9
y:
.long 0
.end
作业9
(冒泡排序法):利用逐次求大数的方法对内存单元ARRAY开始的以字节为单位的无符号数进行从大到小排序。在以BUF为首址的字存储区中存放有10个无符号数 0x0FF,0x00, 0x40, 0x10, 0x90, 0x20, 0x80, 0x30, 0x50, 0x70, 0x60,现需将它们按从小到大的顺序排列在BUF存储区中,试编写其程序。
参考程序:
分析:采用逐一比较法:
l 将第一个存储单元中的数与其后n-1个存储单元中的数逐一比较,每次比较之后,总是把小者放在第一个存储单元之中,经过n-1次比较之后,n个数中最小者存入第一存储单元中;
l 接着从第二个存储单元开始,同理,经过n-2次比较之后,得到n-1个数中最小者存入第二存储单元中;如此类推,当最后两个存储单元的数比较完成之后,从小到大的排列顺序就实现了。 “冒泡排序”算法。
各寄存器分配功能如下:
R0:用来指示缓冲区初始地址
R1:外循环计数器
R2:内循环计数器
R3:外循环地址指针
R4:内循环地指针
R5:内循环下一个数地址指针
R6:存放内循环一轮比较的最小值
R7:存放内循环取出的下一个比较值
程序代码:
.equ num,10
.text
.global _start
_start:
LDR R0,=Datas @指向数组的首地址
mov R1,#0 @外循环计数器
mov R2,#0 @内循环计数器
LOOPI:
add R3,R0,R1,lsl #2 @外循环首地址存R3
mov R4,R3 @内循环首地址存R4
ADD R2,R1,#1 @内存换计数器初值
MOV R5,R4 @内循环下一地址初值
LDR R6,[R4] @取内循环下一地址值R4
LOOPJ:
add r5,r5,#4 @内循环下一地址值
ldr r7,[r5] @取出下一地址值至R7
cmp r6,r7 @比较
blt next @小则取下一个
swp r7,r6,[r5] @大则交换,最小值R6
mov r6,r7
next:
add r2,r2,#1 @内循环计数
cmp r2,#num @循环中止条件
blt LOOPJ @小于N则继续内循环
swp r7,r6,[r3] @否则,内循环一轮结束,将最小数存入外循环的首地址处
add r1,r1,#1 @外循环计数
cmp r1,#num-1 @外循环中止条件
blt LOOPI @小于N-1则继续执行外循环
BEQ stop
b _start
stop:
b stop
.ltorg
Datas:
.long 0x0FF, 0x00, 0x40
.long 0x10, 0x90, 0x20,
.long 0x80, 0x30, 0x50,
.long 0x70
.end
作业10
通过设置的入口参数查找函数地址表,实现选择不同的函数功能。已知两个操作数分别存放于寄存器R0与R1中,函数1实现两数相加R0+R1;函数2实现两数相减R0-R1;函数3实现两数相乘R0*R1; 函数4实现两数相除R0/R1。
参考代码:
.text
.equ num,3
.global _start
_start:
ldr r0,=0x01
ldr r1,=0x02
ldr r4,=0x02 /*r4用来作为选择函数入口Doadd,Dosub,Domul的选项(默认为1,可修改)*/
bl Func
stop:
b stop
Func:
cmp r4,#num
movge pc,lr /*当r4内的值大于等于3时,调回_start函数*/
ldr r3,=JTable
ldr pc,[r3,r4,lsl #2] /*因为函数入口是.long类型,所以需要左移(LSL)2位*/
JTable:
.long Doadd,Dosub,Domul
Doadd:
add r2,r0,r1
mov pc,lr
Dosub:
sub r2,r0,r1
mov pc,lr
Domul:
mul r2,r0,r1
mov pc,lr
.end
作业11
编程实现S=1+2*3+4*5+…….+N*(N+1),其中N=10。
参考答案:
分析过程
l 算法功能:
l 相邻数相乘:MUL
l 累加: ADD
l 循环次数:10 CMP B指令
l 初始值 S=1 N
l N+1 部分和N(N+1)
寄存器分配
l R0: S 1 R0+R3得S值
l R1:N的初始值 2 循环变量
l R2:N+1 由R1=1 求得
l R3:N(N+1)
参考程序:
.text
.equ N,10
.global _start
_start:
MOV R0, #1 @R0用作累加,置初值1,S
MOV R1, #2 @R1用作第一个乘数, 初值为2, N
loop: /*内层循环loop1,做乘法运算*/
ADD R2, R1, #1 @R2用作第二个乘数,N+1
MUL R3, R2, R1 @实现N *(N+1)部分积存于R3
ADD R0, R0, R3 @将部分乘积累加至R0
ADD R1, R1, #1 @改N的值得到下一轮乘数
CMP R1, #10 @循环次数比较
BLE loop @未完成则重复
stop:
b stop
.end
作业12
编程实现S=1!+2!+3!……+N!,要求N!用子程序实现。
程序代码:
.global _start
.text
_start:
mov R0,#0
mov R1,#1
loop:
mov R2,#1
mov R3,#1
mov R4,#1
loop1:
mul R4,R2,R3
mov R3,R4
add R2,R2,#1
cmp R1,R2
bge loop1
loop2:
add R0,R0,R3
add R1,R1,#1
cmp R1,#10
bls loop
stop:
b stop
.end
作业13
在C程序中内嵌汇编程序实现将源字符串src中的数据拷贝到目的字符串dest中
参考程序:
其中主程序如下:*/
#include
void strcpyy(const char *src, char *dest);
__gccmain(void)
{
char *s= "this is a example AsmInC !";
char temp[64];
strcpyy(s,temp);
//printf("Original string: %s \n ", s);
//printf("Copyed string: %s", temp);
return 0;
}
void strcpyy(const char *src, char *dest)
{
__asm( /*GNU和ADS环境下的区别*/
"
loop:
ldrb r5,[r0],#1
strb r5,[r1],#1
cmp r5,#0;
bne loop
"
);
}
作业14
/*在GNU ARM环境下设计程序,实现字符串的逆序拷贝。*/
.global _start
.text
_start:
LDR R0, =SrcString @ 源字符串指针
LDR R1, =DstString @ 目标字符串指针
StrCopyDes:
MOV R4, #0 @ 字符串长度记录寄存器
Strcpydes_L1: @ 计算字符串的长度
LDRB R2, [R0], #1
ADD R4, R4, #1
TST R2, #0xFF
BNE Strcpydes_L1
SUB R4, R4, #1
SUB R0, R0, #2 @ R0 指向源字符串的末尾
MOV R3, R1 @ R3 作为目标串的游标指针
Strcpydes_L2:
LDRB R2, [R0], #-1 @ 逐个拷贝字符串
STRB R2, [R3], #1
SUBS R4, R4, #1
BNE Strcpydes_L2
Strcpydes_L3:
STRB R4, [R3] @向目标串末尾写\0,此处R4的值一定为0
Stop:
B Stop
.ltorg
SrcString:
.string "Hello World!"
DstString:
.string ""
.end
作业15
1、在GNU ARM编译环境下设计程序,用ARM汇编语言调用C语言实现20的阶乘20的阶乘(20!)操作,并将64位结果保存到寄存器R0、R1中,其中R1中存放高32位结果。
程序代码:
汇编程序:
/* Factorial.s */
.text
.equ NI,20
.global _start
.extern Factorial @申明Factorial是外部函数
_start:
mov r0,#NI
BL Factorial @调用Factorial
stop:
B stop
.end
/*main.c*/
long Factorial(char n)
{
long long ni=1;
char i;
for(i=1;i<=n;i++)
{
ni=ni*i;
}
return ni;
}
2、在GNU ARM编译环境下设计程序,用C语言调用ARM汇编语言实现20的阶乘(20!)操作,并将64位结果保存到0xFFFFFFF0开始的内存地址单元,按照小端格式低位数据存放在低地址单元。
/* 首先建立汇编源文件start.s */
.global _start
.global __gccmain
.extern main @ 定义外部函数
.text
_start:
BL main @调用Main函数
Stop:
B Stop
__gccmain:
mov pc, lr
.end
/* 第二 建立C语言源文件main.c */
extern void Factorial(char Nx); //声明Factorial是一个外部函数
main()
{
char N =20;
Factorial(N); //调用汇编文件实现N!操作
/*注:在此处观察结果*/
while(1);
}
/* Factorial.s */
.global Factorial @声明Factorial为一个全局函数
Factorial:
MOV R8 , R0 @取参数
MOV R9 , #0 @高位初始化
SUB R0,R8,#1 @初始化计数器
Loop:
MOV R1 , R9 @暂存高位值
UMULL R8 , R9 , R0 , R8 @[R9:R8]=R0*R8
MLA R9 , R1 , R0 , R9 @R9=R1*R0+R9
SUBS R0 , R0 , #1 @计数器递减
BNE Loop @计数器不为0继续循环
LDR R0,=0xFFFFFFF0
STMIA R0, {R8,R9} @结果保存到0x10000开始的内存单元
MOV PC,LR @子程序返回
.end
展开阅读全文