AVR汇编(四):数据传送指令
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 |
第一个汇编程序
.equ PINB, 0x03
.equ DDRB, 0x04
.equ PORTB, 0x05
.section .text ; 定义text段
.global _start ; 定义一个全局符号_start
_start:
LDI R16, 0x20 ; R16 = 0x20
OUT DDRB, R16 ; 设置PB5为输出
OUT PORTB, R16 ; PB5初始输出高电平
loop:
OUT PINB, R16 ; 翻转PB5电平
RJMP loop ; 跳转到loop处继续执行
上述程序实现的功能很简单,就是不断翻转PB5的电平状态。
使用下面的命令进行编译,生成elf文件:
avr-gcc -mmcu=atmega328p -x assembler-with-cpp -g -Og -Wall -c -o hello.o hello.s
avr-gcc -mmcu=atmega328p -nostartfiles -o hello.elf hello.o
其中, -x assembler-with-cpp
表示编译汇编程序, -nostartfiles
表示不添加默认启动文件,启动文件的作用是初始化MCU,创建C语言运行环境,由于这里编写的是汇编程序,所以不需要它,否则编译时会提示找不到 main
函数。
为了以后每次重新编译的时候不用都输一遍命令,可以写一个 Makefile
文件:
.PHONY: all clean
TARGET := hello
all: $(TARGET).elf
%.o: %.s
avr-gcc -mmcu=atmega328p -x assembler-with-cpp -g -Og -Wall -c -o $@ $<
$(TARGET).elf: $(TARGET).o
avr-gcc -mmcu=atmega328p -nostartfiles -o $@ $<
clean:
rm -f *.o $(TARGET).elf
调试程序
使用 simavr
对上面生成的elf文件进行仿真:
simavr -f 16000000 -m atmega328p --gdb hello.elf
为了方便,可以在 Makefile
中添加一个 run
伪目标,将上面的命令添加进去:
.PHONY: all clean run
...
run: $(TARGET).elf
simavr -f 16000000 -m atmega328p --gdb $<
之后需要仿真时,直接执行 make run
即可。
使用 avr-gdb
对程序进行调试, simavr
的GDB端口是 1234
:
avr-gdb -ex "target remote localhost:1234" -ex "layout split" -q --tui hello.elf
在GDB窗口中,可以输入 s
进行单步执行。
为了观察 PINB
、 DDRB
、 PORTB
寄存器的值,可以借助 x/<n/f/u> <addr>
命令,其中 n
表示要查看的值的个数; f
指定显示格式,如果要十六进制显示,这里就要指定 x
; u
表示值的单位,如果单位是字节,这里就要指定 b
。这条命令的具体使用方法可以通过 help x
命令查看。
这里我们查看从I/O地址0x03开始的3个字节:
x/3xb 0x03 # 注意,这里的地址是错误的!
结果如下:
发现读取的值并不符合我们的预期,这是因为上面命令中的地址设置错了,有两个因素:
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
中的内容:
x/3xb 0x800023
结果如下:
这样显示的结果与我们的程序逻辑是一致的。
数据传送指令
由于AVR具有多种寻址方式,因此数据传送指令也对应有多种。
空间 | 指令 |
---|---|
寄存器堆 | MOV |
数据空间 | LD / ST |
程序空间 | LPM / SPM |
I/O空间 | IN / OUT |
栈空间 | PUSH / POP |
一般而言,AVR指令如果有两个操作数,则第一个是目的操作数,第二个是源操作数。
MOV
MOV
指令用于寄存器之间的数据传送(一个字节),后缀如果加 W
表示传送一个字的数据。
例如:
LDI R16, 0x10 ; R16 = 0x10
MOV R0, R16 ; R0 = 0x10
LDI R16, 0x20 ; R16 = 0x20
LDI R17, 0x30 ; R17 = 0x30
MOVW R0, R16 ; R0 = 0x20, R1 = 0x30
LD
LD
指令用于将数据从数据空间加载到寄存器中,后缀加 I
表示加载立即数,加 D
表示偏移量寻址,加 S
表示直接寻址。
X
/ Y
/ Z
寄存器可以用于间接寻址,如果前缀加 -
,表示执行操作前寄存器的值自减一,如果后缀加 +
,表示执行操作后寄存器的值自加一。
Y
/ Z
寄存器可以用于偏移量寻址(注意不包括 X
寄存器),后面加 +q
表示偏移量为 q
。
例如:
LDI R16, 0xAA ; R16 = 0xAA
LDI XL, 0x00
LDI XH, 0x01 ; X = 0x0100
ST X, R16 ; (0x0100) = 0xAA
LD R0, X+ ; R0 = 0xAA, X = 0x0101
LDI ZL, 0xF1
LDI ZH, 0x00 ; Z = 0x00F1
LDD R1, Z+0xF ; R1 = 0xAA
LDS R2, 0x0100 ; R2 = 0xAA
ST
ST
指令用于将数据从寄存器写入到数据空间中,后缀加 D
/ S
的意义同 LD
,注意 ST
不支持立即寻址,即没有 STI
这样的指令!
例如:
LDI R16, 0x55 ; R16 = 0x55
LDI XL, 0x80
LDI XH, 0x01 ; X = 0x0180
ST X, R16 ; (0x0180) = 0x55
LD R0, X ; R0 = 0x55
LDI R16, 0xAA ; R16 = 0xAA
LDI ZL, 0x50
LDI ZH, 0x01 ; Z = 0x0150
STD Z+0x30, R16 ; (0x0180) = 0xAA
LD R1, X ; R1 = 0xAA
LDI R16, 0xA5 ; R16 = 0xA5
STS 0x0180, R16 ; (0x0180) = 0xA5
LD R2, X ; R2 = 0xA5
LPM
/ SPM
LPM
/ SPM
指令用于将数据从程序空间加载到寄存器/从寄存器写入到程序空间。
例如:
LDI ZL, lo8(const)
LDI ZH, hi8(const) ; Z = const
LPM R0, Z+ ; R0 = 0xA5, Z = CONST + 1
LPM R1, Z+ ; R1 = 0x5A, Z = CONST + 2
LPM R2, Z+ ; R2 = 0x55, Z = CONST + 3
LPM R3, Z ; R3 = 0xAA
const:
.byte 0xA5
.byte 0x5A
.word 0xAA55
SPM
指令的用法较为特殊,后面有机会再来介绍。
IN
/ OUT
IN
/ OUT
用于从I/O空间读入数据到寄存器/向I/O空间写入寄存器中的数据,注意 P
为I/O空间的地址,此命令不能访问扩展I/O空间。
例如:
OUT DDRB, 0x00 ; DDRB = 0xAA
IN R0, DDRB ; R0 = 0xAA
PUSH
/ POP
PUSH
/ POP
用于将数据压入/弹出栈,使用时需要注意SP的初始值要设置正确(AVR是空减栈),并要避免出现栈溢出的情况。
例如:
LDI R16, 0xA5 ; R16 = 0xA5
PUSH R16 ; (SP) = 0xA5, SP -= 1
POP R0 ; R0 = 0xA5, SP += 1
参考资料
AVR汇编(四):数据传送指令的更多相关文章
- 汇编语言--微机CPU的指令系统(五)(数据传送指令)
五.微机CPU的指令系统 1.汇编语言指令格式 汇编语言的指令格式如下: 指令助忆符 [操作数1 [, 操作数2 [, 操作数3]]] [;注释] 指令助忆符体现该指令的功能,它对应一条二进制编码的机 ...
- 学 Win32 汇编[21] - 传送指令: MOV、LEA、XCHG、XLATB、XLAT、MOVZX、MOVSX
汇编指令的一般性要求: 1.两个操作数的尺寸必须一致; 2.操作数不能同为内存. MOV(Move): 最常用的数据传送指令 ;该指令不影响 EFlags ;指令格式: (其中的 r.m.i 分别表示 ...
- Intel汇编语言程序设计学习-第四章 数据传送、寻址和算术运算-下
4.3 和数据相关的操作符和伪指令 操作符和伪指令并非机器可执行的指令,相反,它们是由汇编器进行解释的.开发者可以使用一系列的MASM操作符或伪指令获取数据的地址以及大小等特征信息: OFFSET操 ...
- 【汇编】字符串处理指令 stosb、lodsb、movsw、scasb、rep
一.字符串处理指令 (1) lodsb.lodsw:把DS:SI指向的存储单元中的数据装入AL或AX,然后根据DF标志增减SI (2) stosb.stosw:把AL或AX中的数据装入ES:DI指向的 ...
- python接口自动化(十)--post请求四种传送正文方式(详解)
简介 post请求我在python接口自动化(八)--发送post请求的接口(详解)已经讲过一部分了,主要是发送一些较长的数据,还有就是数据比较安全等.我们要知道post请求四种传送正文方式首先需要先 ...
- 汇编 MOVSX与MOVZX 指令
知识点: MOVSX符号扩展传送 MOVZX零扩展传送 一.MOVSX与MOVZX格式 MOVSX 操作数A ,操作数B MOVZX 操作数A ,操作数B 相同点:操作数B 空间必须小于 操作 ...
- python3+requests:post请求四种传送正文方式(详解)
前言:post请求我在python接口自动化2-发送post请求详解(二)已经讲过一部分了,主要是发送一些较长的数据,还有就是数据比较安全等,可以参考Get,Post请求方式经典详解进行学习一下. 我 ...
- 8237dma的四种传送方式简介
8237A有四种工作方式:单字节传送.数据块传送.请求传送和多片级联. (1)单字节传送(single mode) 单字节传送方式是每次DMA传送时,仅传送一个字节.传送一个字节之后,当前字节计数器减 ...
- post请求四种传送正文的方式
一.简介 HTTP协议规定post提交的数据必须放在消息主体(entity-body)中,但协议没有规定数据必须使用什么编码方式.HTTP协议是以ASCII码传输,建立再TCP/IP协议之上的应用层规 ...
- python3+requests:post请求四种传送正文方式
https://www.cnblogs.com/insane-Mr-Li/p/9145152.html 前言:post请求我在python接口自动化2-发送post请求详解(二)已经讲过一部分了,主要 ...
随机推荐
- 2022-12-31:以下go语言代码输出什么?A:1 1;B:-1 1;C:-1 -1;D:编译错误。 package main import “fmt“ func main() { a
2022-12-31:以下go语言代码输出什么?A:1 1:B:-1 1:C:-1 -1:D:编译错误. package main import "fmt" func main() ...
- 2021-01-10:linux中,我要看某一个进程的并发,通过什么命令去查?
福哥答案2021-01-10:[答案来自此链接:](https://blog.csdn.net/sinat_31275315/article/details/108239492)方法一:PS在ps命令 ...
- C# 实现 Linux 视频会议(源码,支持信创环境,银河麒麟,统信UOS)
信创是现阶段国家发展的重要战略之一,面对这一趋势,所有的软件应用只有支持信创国产化的基础软硬件设施,在未来才不会被淘汰.那么,如何可以使用C#来实现支持信创环境的视频会议系统吗?答案是肯定的. 本文讲 ...
- Node.js出现‘Cannot find module init’ 解决方法
1. 首先查看当前根目录是否有node_module文件夹,如果有,请删除 2. 输入 npm clean cache 3. 再次输入 node init -y 大功告成
- 谷歌语法Github及利用方式
0x01简介 GoogleHack(谷歌语法)是指使用Google等搜索引擎对某些特定的网络主机漏洞(通常是服务器上的脚本漏洞)进行搜索,以达到快速找到漏洞主机或特定主机的漏洞的目的.比如使用搜索包含 ...
- 基于 Linux 集群环境上 GPFS 的问题诊断
作者:谷珊,帅炜,陈志阳来源:IBM Developer GPFS 的概述 GPFS 是 IBM 公司提供的一个共享文件系统,它允许所有的集群节点可以并行访问整个文件系统.GPFS 允许客户共享文件, ...
- 有懂的没,json对象中 嵌入 json字符串 它规范吗?
json字符串 和 json对象 1.JSONObject中的String json串中data对应的值是String,String字符串中双引号需要使用反斜杠\进行转义, 痛恨这种, 解析时要进行二 ...
- 「学习笔记」Garsia-Wachs 算法
前言 本文的资料和图片均来自 \(\texttt{OI-Wiki}\). 引入 题目描述 在一个操场上摆放着一排 \(N\) 堆石子.现要将石子有次序地合并成一堆.规定每次只能选相邻的 \(2\) 堆 ...
- Greenplum 上手
最近因为一个项目需要,我们准备考虑使用 Greenplum 应用在我们的大数据平台中的数据治理项目中,期待着可以在我们的项目过程中有着更大的价值发现 Greenplum 介绍 Greenplum是一种 ...
- 通用密钥,无需密码,在无密码元年实现Passkeys通用密钥登录(基于Django4.2/Python3.10)
毋庸讳言,密码是极其伟大的发明,但拜病毒和黑客所赐,一旦密码泄露,我们就得绞尽脑汁再想另外一个密码,但记忆力并不是一个靠谱的东西,一旦遗忘密码,也会造成严重的后果,2023年业界巨头Google已经率 ...