微机原理与接口技术 第三章: 汇编语言程序设计
DESCRIPTION
微机原理与接口技术 第三章: 汇编语言程序设计. 主讲人:鞠 雷 山东大学 计算机科学与技术学院. 内容提要. 汇编语言源程序格式. 文件类型. 扩展名. 汇编语言文件. . s. C 语言源文件. . c. C++ 源文件. . cpp. 引入文件. . INC. 头文件. . h. ARM 源程序文件. .h 文件给出函数和变量的声明 . .c 文件给出函数的定义. 使用简单的文本 编辑器或者其他 的编程开发环境 进行编辑. 引入其他程序中的变量或函数 , 在本程序使用. Arm 源程序汇编连接过程:. - PowerPoint PPT PresentationTRANSCRIPT
微机原理与接口技术 第三章:汇编语言程序设计微机原理与接口技术
第三章:汇编语言程序设计
主讲人:鞠 雷
山东大学 计算机科学与技术学院
2
内容提要 内容提要
汇编语言源程序格式
3
ARM 源程序文件 ARM 源程序文件
使用简单的文本 编辑器或者其他 的编程开发环境 进行编辑 .
文件类型 扩展名
汇编语言文件 .s
C 语言源文件 .c
C++ 源文件 .cpp
引入文件 .INC
头文件 .h
引入其他程序中的变量或函数 ,在本程序使用 .
.h 文件给出函数和变量的声明 ..c 文件给出函数的定义 .
Arm 源程序汇编连接过程:
例 armasm hello.s
armlink hello.o -o hello.axf
armsd -exec hello.axf ;
armcc -g hello.s -o hello.axf
汇编语言源程序格式汇编语言源程序格式
汇编语言程序的结构1
汇编语言的行构成2
伪指令3
1. 汇编语言程序的结构1. 汇编语言程序的结构汇编源程序示例第一部分 (test0 源程序 )
CODE32 ;32 位的 ARM 指令段 AREA codesec, CODE, READONLY ; 代码段,名称 codesec, 属性为 ; 只读main PROC ; 函数 main
STMFD sp!,{lr} ; 保存返回地址到数据栈 ADR r0,strhello ; 取标签 strhello 的地址 BL _printf ; 调用 C 运行时库的 _printf 函数 ; 打印“ Hello world!” 字符串 BL welcomefun ; 调用子函数 welcomfun
LDMFD sp!,{pc} ; 返回调用函数strhello ;strhello 代表本地字符串的地址 DCB "Hello world!\n\0" ; 定义一段字节空间 ENDP ; 函数 main 结束
ARM 的汇编语言程序 一般由几个段组成, 每个段均由 AREA 伪操 作定义。
段可以分为多种,如代码段、数据段、通用段,每个段又有不同的属性,代码段的默认属性为 READONLY ,数据段的默认属性为 READWRITE 。
本程序定义了两个段,第一个段为代码段 codesec ; 多个代码段的情况,一个源文件只能含一个代码段并单独编译,多
个代码段分别编译最后连接成映象文件。可执行映象文件的构成: 一个或多个代码段,代码段的属性为只读。 零个或多个包含初始化数据的数据段,数据段的属性为可读写。 零个或多个不包含初始化数据的数据段,数据段的属性为可读写。
在 ARM ( Thumb )汇编语言程序中,以程序段为单位组织代码。 用 AREA 伪指令定义一个段,并说明所定义段的相关属性 .
段是相对独立的指令或数据序列,具有特定的名称。 段分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。 一个汇编程序至少应该有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。
7
1. 汇编语言程序的结构1. 汇编语言程序的结构汇编源程序示例第二部分
welcomefun ; 子函数 welcomfun
STMFD sp!,{lr} ; 保存返回地址到数据栈
ADR r0,adrstrarm ; 取 adrstrarm 的地址放到寄存器 r0 中 LDR r0,[r0,#0] ; 将 strarm 的值放到 r0 中 BL _printf ; 调用 C 运行时库的 _printf 函数打印 ; “Welcom to ARM world!” 字符串 LDMFD sp!,{pc} ; 返回调用函数adrstrarm ;adrstrarm 标签
DCD strarm ; 保存 strarm 的地址
AREA constdatasec, DATA, READONLY,ALIGN=0
;
本程序定义了两个段,第二个段为数据段 constdatasec
8
1. 汇编语言程序的结构1. 汇编语言程序的结构汇编源程序示例第三部分
strarm
DCB "Welcome to ARM world!\n\0" ; 存放“ Welcome to ARM
; world!” 字符串
EXPORT main ; 导出 main 函数供外部调用 ; 引入三个 C 运行时库函数和 ARM 库 IMPORT _main
IMPORT __main
IMPORT _printf
IMPORT ||Lib$$Request$$armlib||, WEAK
END ; 源程序结束
导入 main PROC ENDP, 在本程序使用 .
导入 ARM 库。WEAK ,若导入符号不存在不给出错误信息 .
导出 main 函数供其他程序使用 .
9
2. 汇编语言的行构成2. 汇编语言的行构成 格式:
[ 标签 ] 指令 / 伪操作 操作数 [; 语句的注释 ]
所有的标签必须在一行的开头顶格写,前面不能留空格,后面不加“ :” ;
ARM 汇编器对标识符的大小写敏感 , 书写标号及指令时字母的大小写要一致;
注释使用“ ;” 符号,注释的内容从“ ;” 开始到该行的结尾结束。
例: Labeladd add r0,r0,r1 ; 加法指令 Str1 SETS “This is a string.” ; 给字符串 Str1 赋值
10
2. 汇编语言的行构成2. 汇编语言的行构成1. 标签:
标签是一个符号,可以代表指令的地址、变量、数据的地址和常数。
一般以字母开头,由字母、数字、下划线组成。
标签不能采用关键字。 标签 ( 标号 ) 代表可执行语句地址。 标号按其作用域可分为段内标号和和段外标号: 段内标号的地址值在汇编时确定。 例 welcomefun 段外标号的地址值在链接时确定。 例 EXPORT main 段外调用: BL main 局部标号,宏中使用的标号称之为局部标号。
11
2. 汇编语言的行构成2. 汇编语言的行构成
2. 指令/伪操作: 指令 / 伪操作的助记符或者定义符,通知 ARM 的处理器应该
执行什么样的操作或者通知汇编程序伪操作的功能。3. 操作数
由寄存器名、变量名和常量组成的表达式。例 add r0 , r1, r2 x dcd 0x12
12
2. 汇编语言的行构成2. 汇编语言的行构成伪操作使用的常量:
数字常量(伪操作不加 # ) 十进制数,如 1 、 2 、 123 十六进制数,如 0x123,0xabc 2_1010
字符常量 由单引号及中间的字符组成,包括 C 语言中的转义字符,
如‘ \a’,‘\n’ ( 响铃,换行 ) 字符串常量
由一对双引号及中间的字符串表示,中间也可以使用 C 语言中的转义字符,比如:“ abcdef\a\r\n”
逻辑常量 {TRUE},{FALSE}, 注意带大括号
13
3. 伪操作3. 伪操作Ø 没有相对应的操作码或者机器码,通常称为伪指令。Ø 作用是为完成汇编程序作各种准备工作的,由汇编程序在源程
序的汇编期间进行处理,仅在汇编过程中起作用 ( 取地址伪指令与其他伪指令有较大区别 ) 。
Ø 有如下几种伪操作:1. 符号定义伪操作 定义一个内存变量。2. 数据定义伪操作 分配连续内存并初始化。定义多个连续内存变
量。 3. 汇编控制伪操作 控制汇编过程。
14
3. 伪操作3. 伪操作
1. 符号定义伪操作 ( 定义一个内存变量 )
用于定义 ARM 汇编程序中的变量、对变量赋值等。 符号定义有如下几种伪操作:
用于定义局部变量的 LCLA 、 LCLL 、 LCLS (在宏中使用,不讲) 用于定义全局变量的 GBLA 、 GBLL 、 GBLS ; 用于对变量赋值的 SETA 、 SETL 、 SETS ; 局部变量在宏中定义使用!
15
3. 伪操作3. 伪操作 GBLA 、 GBLL 、 GBLS 伪操作 定义一个汇编程序中的全局变量。 格式: GBLA/ GBLL/ GBLS 变量名
定义一个全局的数字变量,并初始化为 0
定义一个全局的逻辑变量,并初始化为 F
定 义 一 个 全局 字 符 串 变量 , 并 初 始化为空串
全局变量在整个程序范围内变量名必须唯一。
16
3. 伪操作3. 伪操作
例: GBLA num1 ; 定义一个全局的数字变量 num1 ,初值 0 num1 SETA 0xabcd GBLL l2 ; 定义一个全局的逻辑变量,变 ; 量名为 l2 ,初值为 F l2 SETL {FALSE} ; 将该变量赋值为假 GBLS str3 ; 定义一个全局的字符串变量 str3 ,初值空串。 str3 SETS “Hello!”
17
3. 伪操作3. 伪操作
III. SETA 、 SETL 、 SETS格式:变量名 SETA/SETL/SETS 表达式
给 一 个 数字 变 量 赋值
给 一 个 逻辑 变 量 赋值
给 一 个 字符 串 变 量赋值
格式中的变量名必须为已经定义过的全局或局部变量,表达式为将要赋给变量的值。
18
3. 伪操作3. 伪操作
例: GBLA num1 ; 定义全局数字变量 num1 SETA 0x1234 ; 赋值为 0x1234 GBLS str3 ; 定义全局字符串变量 str3 SETS “Hello!” ; 赋值为“ Hello!”
19
3. 伪操作3. 伪操作2. 数据定义伪操作
分配存储单元,并初始化。 数据定义有如下几种伪指令:
DCB 分配字节DCW/DCWU 分配半字DCD/DCDU 分配字SPACE MAPFIELD
定义多个连续内存变量 .
定义连续内存空间 .
定义内存表 .
20
3. 伪操作3. 伪操作
I. DCB DCB 用于分配一块字节单元并用伪指令中指定的
表达式进行初始化。 格式:标号 / 变量 DCB 表达式
DCB 可 用“ =” 代替
表达式可以为使用双引号的字 符 串 或 0-255 的数字
例: Array1 DCB 1,2,3,4,5 ; 数组 str1 DCB “Your are welcome !” ; 构造字符串 ; 并分配空间
21
3. 伪操作3. 伪操作
II. DCW/DCWU 格式:标号 / 变量 DCW/DCWU 表达式
DCW 分配若干个半字存储单元并用表达式值初始化,存储空间半字对齐。
DCWU 功 能 跟DCW 类似,只是分配的字存储单元不严格半字对齐
例:Arrayw1 DCW 0xa,-0xb,0xc,-0xd ; 构造固定数组并分 ; 配半字存储单元
22
3. 伪操作3. 伪操作
III. DCD/DCDU 格式:标号 / 变量 DCD/DCDU 表达式
分配若干个字存储单元并用表达式初始化,存储空间字对齐。也可用“ &” 代替
DCDU 只是分配的存储单元不严格字对齐
例:Arrayd1 DCD 1334 , 234 , 345435 ; 构造固定数组并分配 ; 字为单元的存储单元Label DCD str1 ; 该字单元存放 str1 的地址
23
3. 伪操作3. 伪操作
IV. SPACE 格式: 标号 SPACE 表达式
SPACE 用 于 分 配 一 片连续的存储区域并初始化为 0 ,也可用“%”代替
表达式为要分配的字节数
例:
Freespace SPACE 1000 ; 分配 1000 字节的存储空间
24
3. 伪操作3. 伪操作V. MAP 格式: MAP 表达式 [, 基址寄存器 ]
MAP 定 义 一个 结 构 化 的内 存 表 的 首地址。
表 达 式 可以 为 程 序中 的 标 号或 数 学 表达式
内存表首地址 =
( 基址寄存器 )+ 表达式
MAP 可以与 FIELD 伪操作配合使用来定义结构化的内存表。
例: MAP 0x130 , R2 ; 内存表首地址为 0x130 +R2
25
3. 伪操作3. 伪操作VI. FILED 格式:标号 FIELD 字节数
定义一个结构化内存表中数据域的的字节数。 可用“ #” 来代替 FILED
FIELD 常与 MAP 配合使用:MAP 定义内存表的首地址FIELD 定义内存表中的各个数据域的字节数并分配存储单元。
26
3. 伪操作3. 伪操作
例:MAP 0xF10000 ; 定义结构化内存表首地址为 ;0xF10000 count FIELD 4 ; 定义 count 的长度为 4 字
节, ; 位置为 0xF10000 x FIELD 4 ; 定义 x 的长度为 4 字节,位
置 ; 为 0xF10004 y FIELD 4 ; 定义 y 的长度为 4 字节,位
置 ; 为 0xF10008
27
其他伪操作其他伪操作
1. AREA 格式: AREA 段名 属性,……
AREA 用于定义一个代码段、数据段或者特定属性的段
属性部分表示该代码段 / 数据段的相关属性,多个属性可以用“,”分隔。
常见属性如下:DATA :定义数据段。 CODE :定义代码段。READONLY :表示本段为只读。READWRITE :表示本段可读写。
其他伪操作其他伪操作
一个汇编程序至少应该包含一个代码段,当程序太长时,也可以将程序分为多个代码段和数据段。
例: AREA test , CODE , READONLY
AREA ||.text||, CODE, READONLY
28
出现非法字符” .”,以” ||” 使其合法化 .
29
其他伪操作其他伪操作2. CODE16 、 CODE32
格式: CODE16/CODE32
CODE16 伪操作指示编译器后面的代码为16 位的 Thumb 指令
CODE32 伪操作指示编译器后面的代码为32 位的 ARM 指令
在汇编源代码中同时包含 Thumb 和 ARM 指令时,须用 “ CODE32” 和“ CODE16” 伪指令通知编译器其后的指令序列的类型。
“CODE32” 和“ CODE16” 不能对处理器进行状态的切换。
30
其他伪操作其他伪操作3. ENTRY
格式: ENTRY
ENTRY 用于指定汇编程序的入口。在一个完整的汇编程序中至少要有一个 ENTRY ,也可以有多个。
下面的代码使用了 ENTRY : AREA subrout, CODE, READONLY
ENTRY ; 指定程序入口 start
MOV r0, #10 ; 设置参数 MOV r1, #3
BL doadd ; 调用子函数
其他伪操作其他伪操作stop MOV r0, #0x18 ;设置软中断输入参数 LDR r1, =0x20026 SWI 0x123456 ; 调用 ARM 软中断返回 ; cpu 控制权交给调试器doadd ADD r0, r0, r1 ;子函数代码 MOV pc, lr ;子函数返回 END ; 源文件结束
31
32
其他伪操作其他伪操作
4. END 格式: END
END 告诉编译器已经到了源程序的结尾
例:AREA constdata , DATA , READONLY
……
END ; 结尾
33
其他伪操作其他伪操作
5. EQU 格式:名称 EQU 表达式 [ ,类型 ]
将程序中的数字常量、标号赋予一个等效的名称。
例:num1 EQU 1234 ; 定义 num1 为 1234
addr5 EQU str1+0x50
d1 EQU 0x2400 , CODE32
; 定义 d1 的值为 0x2400,且 ; 该处为 32 位的 ARM 指令
若表达式为 32 位的常量或 32 位地址常量,可以指定表达式的数据类型。有以下三种类型: DATA 表明该地址处为数据区 CODE16 表明该地址处为thumb 指令 CODE32 表明该地址处为 arm指令
34
其他伪操作其他伪操作6. EXPORT
格式: EXPORT 标号 [,WEAK]
EXPORT 在程序中声明一个全局标号,其他文件中的代码可以引用该标号。用户也可以用GLOBAL 代替EXPORT例:
AREA ||.text|| , CODE , READONLY
main PROC
……
ENDP
EXPORT main ;声明一个可全局引用的函数 main
END
[,WEAK] 可选项若其他文件有同名的标号,则其他文件同名标号优先被引用。
35
其他伪操作其他伪操作7. IMPORT
格式: IMPORT 标号 [ , WEAK]
通知编译器此标号在当前源文件中使用,但标号是在其他的源文件中定义的。 不管当前源文件是否使用过该标号,这个标号都被加入到当前源文件的符号表中例:
AREA mycode , CODE , READONLY
IMPORT _printf ; 通知编译器当前文件要引用函 ; 数 _printf
END
[ , WEAK]选项表示如果所有的源文件都没有找到这个标号的定义,编译器也不会提示错误信息。 出错时该标号置为 0 。如果这个标号被 B 或 BL 指令引用,则将 B 或 BL 指令替换为NOP 操作。
36
其他伪操作其他伪操作8. EXTERN
格式: EXTERN 标号 [,WEAK]
通知编译器此标号在当前源文件中使用,但标号是在其他的源文件中定义的。 与 IMPORT 不同的是,如果该标号未被引用,则该标号不被加入到当前文件的符号表中。
例:AREA ||.text|| , CODE , READONLY
EXTERN _printf ,WEAK ; 告诉编译器当前文件要引用标 ; 号 , 如果找不到,则不提示错误END
[ , WEAK]选项表示如果所有的源文件都没有找到这个标号的定义,编译器也不会提示错误信息。 出错时该标号置为 0 。如果这个标号被 B 或 BL 指令引用,则将 B 或 BL 指令替换为NOP 操作。
37
其他伪操作其他伪操作9. INCBIN
格式: INCBIN 文件名
INCBIN 将一个数据文件或者目标文件包含到当前的源文件中,编译时被包含的文件不作任何变动的存放在当前文件中,编译器从后面开始继续处理。例:
AREA constdata , DATA , READONLY
INCBIN data1.dat ; 源文件包含文件data1.dat
INCBIN E : \DATA\data2.bin ; 源文件包含文件 ;E : \DATA\data2.bin
END
ARM Procedure Call Standard (APCS)ARM Procedure Call Standard (APCS)
ARM 过程调用规范 一套函数调用者与被调用之间的协议
对寄存器使用的限制。 使用栈的惯例。 在函数调用之间传递 /返回参数。 可以被‘回溯’的基于栈的结构的格式,用来提供
从失败点到程序入口的函数 ( 和给予的参数 ) 的列表。
APCS 不一个单一的给定标准,而是一系列类似但在特定条件下有所区别的标准
牺牲部分的系统性能,提升程序的通用性
APCSAPCS
子程序间通过寄存器 R0-R3来传递参数,这时,寄存器 R0-R3可以记作 A1-A4 。被调用的子程序在返回前无需恢复寄存器R0-R3 的内容。
在子程序中,使用寄存器 R4-R11来保存局部变量.这时,寄存器 R4-R11 可以记作 V1-V8 。如果在子程序中使用到了寄存器 V1-V8 中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在 Thumb 程序中,通常只能使用寄存器 R4-R7来保存局部变量。
寄存器 R12 用作子程序间 scratch 寄存器(用于保存 SP ,在函数返回时使用该寄存器出栈),记作 ip 。在子程序间的连接代码段中常有这种使用规则。
APCSAPCS
寄存器 R13 用作数据栈指针,记作 sp 。在子程序中寄存器R13 不能用作其他用途。寄存器 sp 在进入子程序时的值和退出子程序时的值必须相等。
寄存器 R14 称为连接寄存器,记作 lr 。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器 R14则可以用作其他用途。
寄存器 R15 是程序计数器,记作 pc 。它不能用作其他用途。
补充习题补充习题
1 、利用全局变量和局部变量声明伪操作及其赋值伪操作,分别定义算术变量、逻辑变量和串变量并为其赋值。
2 、读懂下面一段程序,子程序 dststr 执行过程中寄存器R0 、 R1 、 R2 中的内容如何变化 ?试分析并给出子程序执行后的结果 (dststr 所存内容 ) 。 AREA StrCopy, CODE, READONLY
ENTRY ; mark the first instruction to call
start
LDR r1, =srcstr ; pointer to first string
LDR r0, =dststr ; pointer to second string
BL strcopy ; call subroutine to do copy
stop
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
strcopy
LDRB r2, [r1],#1
STRB r2, [r0],#1
CMP r2, #0
BNE strcopy
MOV pc,lr ; Return
AREA Strings, DATA, READWRITE
srcstr DCB "First string - source",0
dststr DCB "Second string - destination",0
END
44
AREA max_min, CODE, READONLY
ENTRY
ldr r0,=BUFF
LDR R1,[R0];R1=MAX 最大值 MOV R2,R1 ;R2=MIN 最小值 MOV R4,#0 ;counter
BEGIN
LDR R3,[R0]
CMP R3,R1
BLT LESS
MOV R1,R3
LESS
CMP R3,R2
BGT GREAT
MOV R2,R3
45
GREAT
ADD R0,R0,#4 ;
ADD R4,R4,#1
CMP R4,#9
BNE BEGIN
str r1,max
str r2,min
MOV r0, #0x18
LDR r1, =0x20026
SWI 0x123456
AREA data, DATA, READWRITE
BUFF DCD 0,-1,2,-3,4,5,6,7,9
max dcd 0
min dcd 0
END