AVR汇编(四):数据传送指令¶
AVR指令主要分为五类:算术和逻辑指令、分支指令、位操作指令、数据传送指令、MCU控制指令,今天我们先来认识其中最常用的数据传送指令。
汇编程序的编写、编译和调试¶
学习任何技术都离不开实践,汇编语言也是如此。在正式学习指令前,我们先来体验一下汇编程序从编写到编译,再到调试的整个过程。
伪指令¶
根据百度百科:伪指令(Pseudo Instruction)是用于对汇编过程进行控制的指令,该类指令并不是可执行指令,没有机器代码,只用于汇编过程中为汇编程序提供汇编信息。
下面是几个常用的伪指令:
伪指令 | 说明 | 举例 |
---|---|---|
.section |
定义一个段 | .section .text .section .data |
.global |
定义一个全局符号 | .global _start |
.byte |
定义一个字节数据 | .byte 0x01 |
.word |
定义一个字数据 | .word 0x3412 |
.ascii |
定义一个字符串数据 | .ascii "hello" |
.align |
设置对齐方式 | .align 4 |
.equ |
定义一个符号常量,类似于C宏定义 | .equ INT8_MAX, 0xFF |
第一个汇编程序¶
hello.s | |
---|---|
上述程序实现的功能很简单,就是不断翻转PB5的电平状态。
使用下面的命令进行编译,生成elf文件:
其中, -x assembler-with-cpp
表示编译汇编程序, -nostartfiles
表示不添加默认启动文件,启动文件的作用是初始化MCU,创建C语言运行环境,由于这里编写的是汇编程序,所以不需要它,否则编译时会提示找不到 main
函数。
为了以后每次重新编译的时候不用都输一遍命令,可以写一个 Makefile
文件:
Makefile | |
---|---|
调试程序¶
使用 simavr
对上面生成的elf文件进行仿真:
为了方便,可以在 Makefile
中添加一个 run
伪目标,将上面的命令添加进去:
Makefile | |
---|---|
之后需要仿真时,直接执行 make run
即可。
使用 avr-gdb
对程序进行调试, simavr
的GDB端口是 1234
:
在GDB窗口中,可以输入 s
进行单步执行。
为了观察 PINB
、 DDRB
、 PORTB
寄存器的值,可以借助 x/<n/f/u> <addr>
命令,其中 n
表示要查看的值的个数; f
指定显示格式,如果要十六进制显示,这里就要指定 x
; u
表示值的单位,如果单位是字节,这里就要指定 b
。这条命令的具体使用方法可以通过 help x
命令查看。
这里我们查看从I/O地址0x03开始的3个字节:
结果如下:
发现读取的值并不符合我们的预期,这是因为上面命令中的地址设置错了,有两个因素:
PINB
、DDRB
、PORTB
在I/O空间的地址是0x03开始,而在数据空间中的地址需要加上0x20;- AVR的程序空间和数据空间是分别独立编址的,因此地址存在重叠情况。通过
avr-readelf -S hello.elf
查看,可以发现.data
段的地址是从0x800100开始的,而实际的SRAM地址是从0x0100
开始的,因此可以知道elf文件中数据空间的地址还需要加上0x800000,如果不加,则代表的是.text
段(Flash)的地址。
通过上面的分析,将命令中的地址改为0x800023即可正确查看 PINB
、 DDRB
、 PORTB
中的内容:
结果如下:
这样显示的结果与我们的程序逻辑是一致的。
数据传送指令¶
由于AVR具有多种寻址方式,因此数据传送指令也对应有多种。
空间 | 指令 |
---|---|
寄存器堆 | MOV |
数据空间 | LD / ST |
程序空间 | LPM / SPM |
I/O空间 | IN / OUT |
栈空间 | PUSH / POP |
一般而言,AVR指令如果有两个操作数,则第一个是目的操作数,第二个是源操作数。
MOV
¶
MOV
指令用于寄存器之间的数据传送(一个字节),后缀如果加 W
表示传送一个字的数据。
例如:
LD
¶
LD
指令用于将数据从数据空间加载到寄存器中,后缀加 I
表示加载立即数,加 D
表示偏移量寻址,加 S
表示直接寻址。
X
/ Y
/ Z
寄存器可以用于间接寻址,如果前缀加 -
,表示执行操作前寄存器的值自减一,如果后缀加 +
,表示执行操作后寄存器的值自加一。
Y
/ Z
寄存器可以用于偏移量寻址(注意不包括 X
寄存器),后面加 +q
表示偏移量为 q
。
例如:
ST
¶
ST
指令用于将数据从寄存器写入到数据空间中,后缀加 D
/ S
的意义同 LD
,注意 ST
不支持立即寻址,即没有 STI
这样的指令!
例如:
LPM
/ SPM
¶
LPM
/ SPM
指令用于将数据从程序空间加载到寄存器/从寄存器写入到程序空间。
例如:
SPM
指令的用法较为特殊,后面有机会再来介绍。
IN
/ OUT
¶
IN
/ OUT
用于从I/O空间读入数据到寄存器/向I/O空间写入寄存器中的数据,注意 P
为I/O空间的地址,此命令不能访问扩展I/O空间。
例如:
PUSH
/ POP
¶
PUSH
/ POP
用于将数据压入/弹出栈,使用时需要注意SP的初始值要设置正确(AVR是空减栈),并要避免出现栈溢出的情况。
例如: