- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试在现有指令的基础上构建一个新指令,但我在我的过程中停止了。加载页面时,我面临以下错误: Multiple directives [directive#1, directive#2] aski
我是 angularjs 世界的新手,我只需要在数字类型的输入中输入从 1 到 10 的数字。不使用 HTML5 的 min 和 max 属性 我在 Jquery 中找到了一个示例,能否帮我将其转换为
我想使用 ionic与 Material 设计。我被困在使用带有自定义 CSS 的 ionic 指令和 angular-material 之间。 我读过使用 ionic 指令我们得到了很多高效的特性,
我创建了以下代码: var node = document.getElementById('TreeList'); var keys = Object.keys(model[0]); var trac
在 AngularJs 中没有提供 ng-enabled 指令。是否有任何适当的理由不在框架中提供该指令,因为当您可以使用 ng- 时,我们同时拥有 ng-show 和 ng-hide隐藏来实现我们的
我最近制作的程序有问题。基本上,它是 John Conway 人生游戏的简单版本,但它运行不正常。问题出在读取单元格及其邻居的状态并决定该单元格的 future 状态的代码中。这是代码的一部分(有点长
Dockerfile reference关于 FROM 指令的内容如下: FROM can appear multiple times within a single Dockerfile in or
我一直在尝试理解指令中孤立作用域和继承作用域之间的区别。这是我准备让自己理解的一个例子: HTML Inside isolated scope directive: {{m
知道如何从指令内部访问属性值吗? angular.module('portal.directives', []) .directive('languageFlag', ['$r
我正在通过将 c 程序与其等价的汇编程序进行比较来学习汇编。 这是代码。 .file "ex3.c" .section .rodata .LC0: .string "I am %d
我正在尝试写一个 Jenkinsfile并行执行一系列步骤。目标是拥有两个 agents (又名。 nodes )。一个应该进行 Windows 构建,另一个应该进行 linux 构建。但是,我不希望
我想知道为什么指令 FYL2XP1在 x86 架构上精确计算数学公式 y · log2(x + 1)。 这个公式有什么特别之处? 最佳答案 y操作数通常是编译时常量,暂时忘记 x + 1 . 自 lo
这个问题已经有答案了: Parameterize an SQL IN clause (41 个回答) 已关闭 8 年前。 第一个声明: Select GroupMember FROM Group 结果
我从 this question fork 并编辑了一个 plunker 我想做的是在数据加载后更新/填充 SELECT 元素(组合框),但有些事情不对劲。我检索数据,它位于 SELECT 元素的范围
我想创建一个简单的 markdown 指令,它接受元素中的一些内容,解析它并用 html 替换它。 所以这样: #Heading 或这个(其中 $scope.heading = '#Heading';
我对 Ansible 还很陌生,对于我对 local_action 指令的理解有一个简单的问题。 这是否意味着该命令完全在本地执行?假设你有这样的东西: local_action: command w
我有以下 HTML: ... ... 以及以下指令: myApp.directive('specialInput', ['$timeout', function($timeout)
如何在 .htaccess 中创建 Apache 指令强制文件 .mp4和 .pdf去下载?目前它们出现在浏览器窗口中。相反,我希望出现一个下载文件对话框。 最佳答案 将以下内容添加到 .htacce
我的问题是关于 C 中的 fork() 指令。我有以下程序: void main(){ int result, status; result = fork(); if(result=
我想要一个类似于 ng-model 的属性指令。我只想另外将一个输入字段值绑定(bind)到一个范围变量(只是在一个方向输入字段 ->范围变量)。所以我刚刚尝试了这个指令,但无论如何我都无法调用该指令
我是一名优秀的程序员,十分优秀!