- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
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-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汇编(四):数据传送指令的文章就讲到这里了,如果你想了解更多关于AVR汇编(四):数据传送指令的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
初学者 android 问题。好的,我已经成功写入文件。例如。 //获取文件名 String filename = getResources().getString(R.string.filename
我已经将相同的图像保存到/data/data/mypackage/img/中,现在我想显示这个全屏,我曾尝试使用 ACTION_VIEW 来显示 android 标准程序,但它不是从/data/dat
我正在使用Xcode 9,Swift 4。 我正在尝试使用以下代码从URL在ImageView中显示图像: func getImageFromUrl(sourceUrl: String) -> UII
我的 Ubuntu 安装 genymotion 有问题。主要是我无法调试我的数据库,因为通过 eclipse 中的 DBMS 和 shell 中的 adb 我无法查看/data/文件夹的内容。没有显示
我正在尝试用 PHP 发布一些 JSON 数据。但是出了点问题。 这是我的 html -- {% for x in sets %}
我观察到两种方法的结果不同。为什么是这样?我知道 lm 上发生了什么,但无法弄清楚 tslm 上发生了什么。 > library(forecast) > set.seed(2) > tts lm(t
我不确定为什么会这样!我有一个由 spring data elasticsearch 和 spring data jpa 使用的类,但是当我尝试运行我的应用程序时出现错误。 Error creatin
在 this vega 图表,如果我下载并转换 flare-dependencies.json使用以下 jq 到 csv命令, jq -r '(map(keys) | add | unique) as
我正在提交一个项目,我必须在其中创建一个带有表的 mysql 数据库。一切都在我这边进行,所以我只想检查如何将我所有的压缩文件发送给使用不同计算机的人。基本上,我如何为另一台计算机创建我的数据库文件,
我有一个应用程序可以将文本文件写入内部存储。我想仔细看看我的电脑。 我运行了 Toast.makeText 来显示路径,它说:/数据/数据/我的包 但是当我转到 Android Studio 的 An
我喜欢使用 Genymotion 模拟器以如此出色的速度加载 Android。它有非常好的速度,但仍然有一些不稳定的性能。 如何从 Eclipse 中的文件资源管理器访问 Genymotion 模拟器
我需要更改 Silverlight 中文本框的格式。数据通过 MVVM 绑定(bind)。 例如,有一个 int 属性,我将 1 添加到 setter 中的值并调用 OnPropertyChanged
我想向 Youtube Data API 提出请求,但我不需要访问任何用户信息。我只想浏览公共(public)视频并根据搜索词显示视频。 我可以在未经授权的情况下这样做吗? 最佳答案 YouTube
我已经设置了一个 Twilio 应用程序,我想向人们发送更新,但我不想回复单个文本。我只是想让他们在有问题时打电话。我一切正常,但我想在发送文本时显示传入文本,以确保我不会错过任何问题。我正在使用 p
我有一个带有表单的网站(目前它是纯 HTML,但我们正在切换到 JQuery)。流程是这样的: 接受用户的输入 --- 5 个整数 通过 REST 调用网络服务 在服务器端运行一些计算...并生成一个
假设我们有一个名为 configuration.js 的文件,当我们查看内部时,我们会看到: 'use strict'; var profile = { "project": "%Projec
这部分是对 Previous Question 的扩展我的: 我现在可以从我的 CI Controller 成功返回 JSON 数据,它返回: {"results":[{"id":"1","Sourc
有什么有效的方法可以删除 ios 中 CBL 的所有文档存储?我对此有疑问,或者,如果有人知道如何从本质上使该应用程序像刚刚安装一样,那也会非常有帮助。我们正在努力确保我们的注销实际上将应用程序设置为
我有一个 Rails 应用程序,它与其他 Rails 应用程序通信以进行数据插入。我使用 jQuery $.post 方法进行数据插入。对于插入,我的其他 Rails 应用程序显示 200 OK。但在
我正在为服务于发布请求的 API 调用运行单元测试。我正在传递请求正文,并且必须将响应作为帐户数据返回。但我只收到断言错误 注意:数据是从 Azure 中获取的 spec.js const accou
我是一名优秀的程序员,十分优秀!