1、首先通过Keil5,在stm32H743非中断模式下,使用主堆栈指针下的汇编语句查看。
2、然后理解这个汇编逻辑后,将有利于更自由的进行程序调试,查找BUG。
3、最后即可在特权模式下用arm汇编。
#include< reg51h>
#include< stdioh>
#include< stringh>
#define INBUF_LEN 4 //数据长度
unsigned char inbuf1[INBUF_LEN];
unsigned char checksum,count3 , flag,temp,ch;
bit read_flag=0;
sbit cp=P1^1;
sbit DIR=P1^2;
int i;
unsigned int xdata RAMDATA; /定义RAM地址指针/
unsigned char a[6] ={0x11,0x22,0x33,0x44,0x55,0x66} ;
void init_serialcomm(void)
{
SCON=0x50; //在110592MHz下,设置串行口波特率为9600,方式1,并允许接收
PCON=0x00;
ES=1;
TMOD=0x21; //定时器工作于方式2,自动装载方式
TH0=(65536-1000)%256;
TL0=(65536-1000)/256;
TL1=0xfd;
TH1=0xfd;
ET0=1;
TR0=1;
TR1=1;
// TI=0;
EA=1;
// TI=1;
RAMDATA=0x1F45;
}
void serial () interrupt 4 using 3
{
if(RI)
{ RI=0;
ch=SBUF;
TI=1; //置SBUF空
switch(ch)
{
case 0x01 :printf("A"); TI=0;break;
case 0x02 :printf("B"); TI=0;break;
case 0x03 :printf("C"); TI=0;break;
case 0x04 :printf("D"); TI=0;break;
default :printf("fg"); TI=0;break;
}
}
}
//向串口发送一个字符
void timer0() interrupt 1 using 3{
// char i;
flag++;
TH0=0x00;
TL0=0x00;
if(flag==10)
{// cp=!cp;
// for(i=0;i<6;i++)
P2=0x25;
TI=1;
temp=RAMDATA;
printf("%c",temp);
TI=0;
// RAMDATA--;
flag=0;
}
}
//主程序
main()
{
init_serialcomm(); //初始化串口
//向6264中送数据
{
RAMDATA=0x33;
}
while(1)
{
RAMDATA=0x33;;
}
}
汇编主要是要了解CPU指令及用法,常说的是PC机的x86汇编,指令是x86的复杂指令集。
arm汇编是arm的精简指令集,比x86容易学,程序格式倒是和x86汇编差不多。
C语言ARM的和x86的差不多,除了对硬件寄存器 *** 作不同,其它语法和流程都一样。
arm汇编程序每一行是指定arm core执行一条指令,每条指令都是硬件相关。
如:LDR R3, #1 ;用LDR指令将数值1放入R3寄存器准备参与运算
C语言与arm指令无关,只与逻辑运算有关,指定硬件地址的 *** 作才与硬件相关;
如果用arm编译器来编译,每行可能编译出1到多条arm指令。
如:i++; //变量 i 递增1等效于LDR R3,#1 ;
用LDR指令将数值1放入R3寄存器准备参与运算ADD R2, R2, R3 ;
用ADD指令将R2、R3寄存器里的数值相加后放回R2寄存器以上等效汇编的R2、R3寄存器只是为了举例,C语言不像汇编,不需要由程序员指定用哪个寄存器参与运算,编译器编译时会根据程序结构自动判断选择。
无论是c语言还是汇编语言,编译器编译后的结果是机器执行码,很多人因为汇编语言比较难懂及指令相关,所以以为它就是机器语言,其实它仍是人类设计的编写程序的语言,仍需要编译器编译成机器码才能执行,它只是比C语言更接近硬件而已。
ADD proc
arg_C equ 0xC
arg_8 equ 8
arg_4 equ 4
arg_0 equ 0
MOV R12, SP
STMFD SP!, {R0-R3} ;四个参数
STMFD SP!, {R4-R10,R12,LR}
SUB SP, SP, #0x10
LDR R0, [SP,#0x34+arg_0]
LDR R1, [SP,#0x34+arg_4]
LDR R2, [SP,#0x34+arg_8]
ADD R0, R0, R1
ADD R0, R0, R2
ADD SP, SP, #0x10
LDMFD SP, {R4-R10,SP,PC}
ENDP
如果参数传递大于 4 个,那么你在 BL 的地方应使用:
STR R0, [SP,#0x8] ;第七个
STR R0, [SP,#0x4] ;第六个
STR R0, [SP,#0x0] ;第五个
============================================
我理解你的意思了
LDR r0,=0x1
LDR r1,=0x2
LDR r2,=0x3
BL add
改为:
LDR r0,=0x1
STR R0, [SP,#0x0]
LDR r1,=0x2
STR R1, [SP,#0x4]
LDR r2,=0x3
STR R2, [SP,#0x8]
BL ADD
你是这个意思吧
------------
int a = R0
int b = R1
int c = R2
在linux源代码中会有一部分c语言与汇编语言相交融的部分。
其中linux中汇编语言采用的不是我们通用的intel的汇编语言,而是采用的是AT&T格式的汇
编语言,它们之间有一些差别:
1
目标与源的方向不大一样
mov
ax,
bx
mov
%bx,
%ax
2
AT&T寄存器前要加入%
ax
%ax
3
AT&T立即数前面要加上$
add
ax,
4
add
%ax,$4
4
对于访问指令的 *** 作数大小
intel的格式是在 *** 作数前加上BYTE
PTR、DWORD
PTR等等
AT&T格式:在 *** 作数后面加上b、l、w等
MOV
AL,
BYTE
PTR
FOO(intel)
movb
FOO,
%al
(AT&T)
5
间接寻址:
SECTION:[BASE+INDEXSCALE+DISP]
Section:disp(base,
index,
scale)
C语言中插入汇编代码比纯粹的汇编要难,因为要设计到“如何分配使用寄存器、怎样与C语
言中变量相结合”
下面“=”代表只读,“+”代表读写
每个输出部分均以=开始
比如我想定义一个char型的变量,放入ax中
register
char
_temp
asm
("ax");
register
char
_temp
__asm__
("ax");
以上两种方式均正确
在这里我们通常看到普通的寄存器前面有两个%,代表下面的意思:第一寄存器前要有一个%
第二对于一个模板前要加入一个%
下面以一个在内核中常见的目的为了实现原子 *** 作的一个函数为例子atomic_add来介绍
static
__inline__
void
atomic_add(int
i,
atomic_t
v)
{
__asm__
__volatile__(
LOCK
"addl
%1,
%0"
:"=m"(v->counter)
:"ir"(i),
"m"(v->counter)
);
}
ir代表一个寄存器中的直接 *** 作数
首先一个头部:__asm__
__volatile__();
asm();
__asm__();告诉编译器里面是汇编语言
:第一个冒号是输出部分:第二个冒号是输入部分
其实还有第三个冒号:代表着损坏部分
asm("汇编语句"
:输出部分
:输入部分
:损坏部分)
%0、%1
等等代表着一种模板 *** 作数,其中数字到几取决于cpu寄存器数量
"m",
"v",
"o"
--内存单元
"r"
任意寄存器
"q"
表示eax、ebx、ecx、edx之一
"i",
"h"
表示立即数
"a",
"b",
"c",
"d"表示eax、ebx、ecx、edx
关键字LOCK表示在执行的时候把系统总线锁住,不让其他
cpu干扰。
dseg segment
tab db '0123456789ABCDEF'
str db 4 dup(),'$'
dseg ends
cseg segment
assume cs:cseg,ds:dseg
begin: mov ax,dseg
mov ds,ax
mov ax,1
mov bx,2
mov cx,99
next: add ax,bx
inc bx
loop next
call disp
mov ah,4ch
int 21h
disp proc
lea si,str
mov cl,4
mov di,4
abc: rol ax,cl
mov dx,ax
and ax,0fh
lea bx,tab
xlat
mov [si],al
inc si
mov ax,dx
dec di
jnz abc
lea dx,str
mov ah,9
int 21h
ret
disp endp
cseg ends
end begin
stack 100h
data
code
start:
mov ax, @data
mov ds, ax
mov cx, 50 ;循环50次
xor ax, ax
label:
mov bx, cx
shl bx, 1 ;相当于将当前循环计数乘2,用来做加法
add ax, bx
loop label ;循环完成后,结果放在AX中
mov ax, 4c00h
int 21h
在ARM汇编语言程序里,有一些特殊指令助记符,这些助记符与指令系统的助记符不同,没有相对应的 *** 作码,通常称这些特殊指令助记符为伪指令,他们所完成的 *** 作称为伪 *** 作。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,这些伪指令仅在汇编过程中起作用,一旦汇编结束,伪指令的使命就完成。
在ARM的汇编程序中,有如下几种伪指令:符号定义伪指令、数据定义伪指令、汇编控制伪指令、宏指令以及其他伪指令。
411 符号定义(Symbol Definition)伪指令
符号定义伪指令用于定义ARM汇编程序中的变量、对变量赋值以及定义寄存器的别名等 *** 作。常见的符号定义伪指令有如下几种:
— 用于定义全局变量的GBLA、GBLL和GBLS。
— 用于定义局部变量的LCLA、LCLL和LCLS。
— 用于对变量赋值的SETA、SETL、SETS。
— 为通用寄存器列表定义名称的RLIST。
1、 GBLA、GBLL和GBLS
语法格式:
GBLA(GBLL或GBLS) 全局变量名
GBLA、GBLL和GBLS伪指令用于定义一个ARM程序中的全局变量,并将其初始化。其中:
GBLA伪指令用于定义一个全局的数字变量,并初始化为0;
GBLL伪指令用于定义一个全局的逻辑变量,并初始化为F(假);
GBLS伪指令用于定义一个全局的字符串变量,并初始化为空;
由于以上三条伪指令用于定义全局变量,因此在整个程序范围内变量名必须唯一。
使用示例:
GBLA Test1 ;定义一个全局的数字变量,变量名为Test1
Test1 SETA 0xaa ;将该变量赋值为0xaa
GBLL Test2 ;定义一个全局的逻辑变量,变量名为Test2
Test2 SETL {TRUE} ;将该变量赋值为真
GBLS Test3 ;定义一个全局的字符串变量,变量名为Test3
Test3 SETS “Testing” ;将该变量赋值为“Testing”
2、 LCLA、LCLL和LCLS
语法格式:
LCLA(LCLL或LCLS) 局部变量名
LCLA、LCLL和LCLS伪指令用于定义一个ARM程序中的局部变量,并将其初始化。其中:
LCLA伪指令用于定义一个局部的数字变量,并初始化为0;
LCLL伪指令用于定义一个局部的逻辑变量,并初始化为F(假);
LCLS伪指令用于定义一个局部的字符串变量,并初始化为空;
以上三条伪指令用于声明局部变量,在其作用范围内变量名必须唯一。
使用示例:
LCLA Test4 ;声明一个局部的数字变量,变量名为Test4
Test3 SETA 0xaa ;将该变量赋值为0xaa
LCLL Test5 ;声明一个局部的逻辑变量,变量名为Test5
Test4 SETL {TRUE} ;将该变量赋值为真
LCLS Test6 ;定义一个局部的字符串变量,变量名为Test6
Test6 SETS “Testing” ;将该变量赋值为“Testing”
3、 SETA、SETL和SETS
语法格式:
变量名 SETA(SETL或SETS) 表达式
伪指令SETA、SETL、SETS用于给一个已经定义的全局变量或局部变量赋值。
SETA伪指令用于给一个数学变量赋值;
SETL伪指令用于给一个逻辑变量赋值;
SETS伪指令用于给一个字符串变量赋值;
其中,变量名为已经定义过的全局变量或局部变量,表达式为将要赋给变量的值。
使用示例:
LCLA Test3 ;声明一个局部的数字变量,变量名为Test3
Test3 SETA 0xaa ;将该变量赋值为0xaa
LCLL Test4 ;声明一个局部的逻辑变量,变量名为Test4
Test4 SETL {TRUE} ;将该变量赋值为真
4、 RLIST
语法格式:
名称 RLIST {寄存器列表}
RLIST伪指令可用于对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在ARM指令LDM/STM中使用。在LDM/STM指令中,列表中的寄存器访问次序为根据寄存器的编号由低到高,而与列表中的寄存器排列次序无关。
使用示例:
RegList R >
以上就是关于如何在特权模式下用arm汇编全部的内容,包括:如何在特权模式下用arm汇编、求一汇编小程序!用ARM汇编语言完成一个C语言嵌入式汇编方式实现字符串(数组)拷贝的功能、arm编程与C语言的编程区别和方法等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)