STM8S汇编代码分析
转载:http://blog.csdn.net/u010093140/article/details/50021897
使用STVD建立完汇编工程项目之后(具本建立方法可以看我的另一篇博文http://blog.csdn.net/u010093140/article/details/49983397),可以看到这个目录结构(以STM8S105C6芯片为例)
其中.asm文件是汇编代码的源文件,.inc文件是包含文件,类似于C语言当在的.c文件和.h文件。接下来让我们来分析一下这三个文件。(分析汇编代码最好也要对STM8单片机的启动流程有所了解,可以看我的另一篇博文http://blog.csdn.net/u010093140/article/details/49982879)
首先是看mapping.inc文件:
;------------------------------------------------------
; SEGMENT MAPPING FILE AUTOMATICALLY GENERATED BY STVD
; SHOULD NOT BE MANUALLY MODIFIED.
; CHANGES WILL BE LOST WHEN FILE IS REGENERATED.
;------------------------------------------------------
#define RAM0 1
#define ram0_segment_start 0
#define ram0_segment_end FF
#define RAM1 1
#define ram1_segment_start 100
#define ram1_segment_end 5FF
#define stack_segment_start 600
#define stack_segment_end 7FF
这一段代码应该不难看懂,就是定义了一些常量。需要注意的是,分号”;”是汇编代码中用于写注释的符号。所以分号后面跟的是注释。
接下来就是看一下mapping.asm文件
stm8/
;------------------------------------------------------
; SEGMENT MAPPING FILE AUTOMATICALLY GENERATED BY STVD
; SHOULD NOT BE MANUALLY MODIFIED.
; CHANGES WILL BE LOST WHEN FILE IS REGENERATED.
;------------------------------------------------------
#include "mapping.inc" BYTES ; The following addresses are 8 bits long
segment byte at ram0_segment_start-ram0_segment_end 'ram0' WORDS ; The following addresses are 16 bits long
segment byte at ram1_segment_start-ram1_segment_end 'ram1' WORDS ; The following addresses are 16 bits long
segment byte at stack_segment_start-stack_segment_end 'stack' WORDS ; The following addresses are 16 bits long
segment byte at -43FF 'eeprom' WORDS ; The following addresses are 16 bits long
segment byte at -FFFF 'rom' WORDS ; The following addresses are 16 bits long
segment byte at -807F 'vectit' END
上面的代码第一行以stm8/开头,很多人不知道为什么要这样子。其实是因为我们所用的汇编连接器Assembler Linker不仅支持STM8汇编代码而且还支持ST公司的另一款芯片ST7的汇编代码,如果你用的是ST7芯片的话,就要以st7/开头了。结论就是使用stm8/开头是为了表明代码的目标芯片是stm8芯片。
分号后面的注释不算入代码里面,剩下来的代码就定义了芯片上的内存段,比如说segment byte at ram0_segment_start-ram0_segment_end ‘ram0’的意思就是,从ram0_segment_start到ram0_segment_end的这一段内存起个名字叫做“ram0”,segment byte at ram1_segment_start-ram1_segment_end ‘ram1’的意思就是,从ram1_segment_start到ram1_segment_end的这一段内存起个名字叫做“ram1”,其它的也是一样的道理。那么,你也会注意到,每一句这样的代码之前都有一句”Bytes”或者”Words”,这是什么意思呢?按代码注释里的意思就是,Bytes代表内存段里内存的地址是8位的,而Words代表内存段里内存的地址是16位的。通过查Assembler Linker PDF,发现Bytes和Words用于指定跟在它下面的的标号的默认长度,什么意思?可以看以下的例子:
Bytes
label1
;下面这条语句是编译通过的。因为A是8位的,label1也是8位的。
LD A,#label1
Words
label2
;下面这条语句是编译不通过的。因为A是8位的,而label2是16位的,通过赋值给A。
LD A,#label2
Words
label3.b
;而下面这条语句是可以编译通过的,因为我显式地指定了label3为byte的长度(.b),是8位的。
我们再看回到mapping.asm那个文件,mapping文件里所有的指令都是伪指令,并不产生实际的可执行代码,那么使用了bytes,words是什么作用呢?从上面bytes和words的作用来看,我个人认为它们在mapping.asm里不起作用,只起到说明的作用,相当于注释。当然如有错误,欢迎大家指出^_^。所以mapping的作用就是给芯片的存储空间划分区域并命名。我们后面我们写的代码可以通过这个名字,指定存到该名字所代表的存储区域下。比如说ram0区,ram1区,rom区等。
接下来再来看main.asm,这个代码有一些长了,先贴出来吧。
;就如之前所说的,stm8指明以下的代码是用于stm8芯片的,而不是st7芯片。
stm8/
;以下代码是把mapping.inc文件包含进来的意思,这样就可以直接用mapping.inc里面定义的常量了。
#include "mapping.inc"
;以下代码是指明往后的代码都是放在rom存储区域的意思,就如mapping.asm里所表明的,rom的地址范围是8080-FFFF。
segment 'rom'
;main.l是一个标号,写在最左边的一行,标号不产生实际的指令。标号的作用时给一个地址进行命名,然后其它指令就可以使用这个名字来使用这个地址了。比如说下面的main.l的地址就跟下面的ldw X,#stack_end所在的地址相等的。而.l的意思是该地址是3个字节24位的。
main.l
; initialize SP
;下面这一句的意思是把stack_end的值加载到X寄存器,#是立即数的意思。ldw的w是word的意思,表明是16位是加载指令。也有8位的加载指令,为ld.
ldw X,#stack_end
;下面这一句的意思是把寄存器X的值赋给SP寄存器的意思,SP是栈指针,上下两句的作用是让SP指向栈顶。(STM8的栈结构是自顶向下的,栈顶的值就是stack_end,栈中地址值最大的那个数)。
ldw SP,X
;伪指令,如果定义了RAM0就编译其后的代码,显然这个判断是为真的,因为在mapping.inc中已经定义了RAM0和RAM1.
#ifdef RAM0
; clear RAM0
;伪指令,定义标号ram0_start.b的值为ram0_segment_start的值,$是16进制数的意思,ram0_end.b同理。这种直接赋值的方式跟前面的main.l标号有所不同,下面这种是赋绝对地址,而main.l是赋相对地址。
ram0_start.b EQU $ram0_segment_start
ram0_end.b EQU $ram0_segment_end
;加载ram0_start的值到X
ldw X,#ram0_start
;定义标号clear_ram0.l
clear_ram0.l
;clr是清除的意思,()是间接寻址的意思,clr(X)就是以X的值为地址,清除该地址上的值的意思。
clr (X)
;X加1,incw有个w是因为X是16位的。
incw X
;cpw是compare的意思,比较X和ram0_end的值,w的意思跟上面讲的意思一样。
cpw X,#ram0_end
;jrule(jump relative unsigned less than)这个意思明白了吧?就是如果小于就跳转到clear_ram0标号地址的意思。
jrule clear_ram0
;跟前面的#ifdef RAM0相对应。
#endif
;这个面RAM1的操作跟以上对RAM0的操作一样。整一段代码的作用就是清零存储区的作用。
#ifdef RAM1
; clear RAM1
ram1_start.w EQU $ram1_segment_start
ram1_end.w EQU $ram1_segment_end
ldw X,#ram1_start
clear_ram1.l
clr (X)
incw X
cpw X,#ram1_end
jrule clear_ram1
#endif
;下面初始化栈区的操作也是跟前面对RAM0的操作一样的。
; clear stack
stack_start.w EQU $stack_segment_start
stack_end.w EQU $stack_segment_end
ldw X,#stack_start
clear_stack.l
clr (X)
incw X
cpw X,#stack_end
jrule clear_stack
;下面定义了infinite_loop.l标号。
infinite_loop.l
;jra是相对跳转的意思,跳转到上面那个标号。所以这是一个无限循环,代码到这里就是不断地执行jra infinite_loop这条语句,相当于C语言中的while(1);
jra infinite_loop
;interrupt是伪指令,把NoHandleInterrupt说明成是用于中断的标号。
interrupt NonHandledInterrupt
;定义NonHandledInterrupt.l标号
NonHandledInterrupt.l
;iret是中断返回的意思。而ret是函数返回的意思。
iret
;segment 'vectic'指令其下面的代码是放在vectit存储区的,即8000-807F所在的区域。
segment 'vectit'
;dc.l的意思是申请一段四个字节的空间,后面加的数字就是赋予这个空间的值。什么?前面的l的用法都是3个字节的,这里dc.l里的l就成4个字节了?没错,就是这样子的,有点乱,这也是有点费解的地方,我也不明白为啥不改另一个说法。{}的用法是在编译时运算里面的语句,而不是在代码里演算。比如说{1+1}会在编译后变成2.
;下面的所有dc.l其实就是定义了一个中断向量表,分别对应于不同的中断,比如第一个就是复位中断,芯片复位后会在这里找到main标号,然后程序跳转到main里去。当然如果你对main不爽,也可以改成其它的,比如说example.但是这个改了之后,最前面的main.l标号也要相应的改成example.l.就相当于这个程序里面“没有”main函数了。是不是很神奇呢?呃。下面有注释了trap,irq0,irq2等这些,其实就是对应了不同的中断,比如说I2C的中断就对应了其中的irq19,所以当你写好I2C的中断服务程序后,需要把它的标号填写到irq19那一句中,可以参考dc.l{$82000000+main}这句,如果你把I2C中断服务程序的标号定义I2C_Interrupt.l则irq19中那一句要改成dc.l{$82000000+I2C_Interrupt}.最后一个问题,中断后单片机会跳到中断标号里去执行这点没问题了,那下面$82000000中的82是什么意思呢?(现在想找之前看到的资料已经找不到了。。。。不过我还记得那个意思)82是STM8指令集中的一个操作码(汇编指令是由操作码和操作数组成的),我想用在中断这里的意思就是表面这个地址标号是中断服务程序地址标号的意思吧,芯片可以识别82这个操作码,从而区别对待。
dc.l{$+main} ; reset
dc.l {$+NonHandledInterrupt} ; trap
dc.l {$+NonHandledInterrupt} ; irq0
dc.l {$+NonHandledInterrupt} ; irq1
dc.l {$+NonHandledInterrupt} ; irq2
dc.l {$+NonHandledInterrupt} ; irq3
dc.l {$+NonHandledInterrupt} ; irq4
dc.l {$+NonHandledInterrupt} ; irq5
dc.l {$+NonHandledInterrupt} ; irq6
dc.l {$+NonHandledInterrupt} ; irq7
dc.l {$+NonHandledInterrupt} ; irq8
dc.l {$+NonHandledInterrupt} ; irq9
dc.l {$+NonHandledInterrupt} ; irq10
dc.l {$+NonHandledInterrupt} ; irq11
dc.l {$+NonHandledInterrupt} ; irq12
dc.l {$+NonHandledInterrupt} ; irq13
dc.l {$+NonHandledInterrupt} ; irq14
dc.l {$+NonHandledInterrupt} ; irq15
dc.l {$+NonHandledInterrupt} ; irq16
dc.l {$+NonHandledInterrupt} ; irq17
dc.l {$+NonHandledInterrupt} ; irq18
dc.l {$+NonHandledInterrupt} ; irq19
dc.l {$+NonHandledInterrupt} ; irq20
dc.l {$+NonHandledInterrupt} ; irq21
dc.l {$+NonHandledInterrupt} ; irq22
dc.l {$+NonHandledInterrupt} ; irq23
dc.l {$+NonHandledInterrupt} ; irq24
dc.l {$+NonHandledInterrupt} ; irq25
dc.l {$+NonHandledInterrupt} ; irq26
dc.l {$+NonHandledInterrupt} ; irq27
dc.l {$+NonHandledInterrupt} ; irq28
dc.l {$+NonHandledInterrupt} ; irq29 end
STM8S汇编代码分析的更多相关文章
- 一个简单C程序的汇编代码分析
几个重要的寄存器 eip - 用于存放当前所执行的指令地址 esp - 栈(顶)指针寄存器 ebp - 基址(栈底)指针寄存器 简单的C程序 int g(int x) { ; } int f(int ...
- start_kernel之前的汇编代码分析
start_kernel之前的汇编代码分析 Boot中执行下面两句话之后,进入uclinux内核. theKernel = (void (*)(int, int, unsigned int))((ui ...
- STM32F103 ucLinux开发之二(内核启动汇编代码分析)
start_kernel之前的汇编代码分析 Boot中执行下面两句话之后,进入uclinux内核. theKernel = (void (*)(int, int, unsigned int))((ui ...
- 简单C程序生成的汇编代码分析
首先给出完整的C代码: int g(int x) { ; } int f(int x) { return g(x); } int main(void) { )+; } 使用命令:gcc –S –o h ...
- 《Linux内核分析》week1作业-分析一个简单c语言的汇编代码
1.C语言源码 #include <stdio.h> int g(int x){ ; } int f(int x){ return g(x); } int main(){ )+; } 2. ...
- 关于rt-thread调度器实现的底层代码分析
本文使用了rt-thread自带的钩子函数和显示函数进行了实验,从rt-thread自带的延时函数rt_thread_delay()函数入手,对rt-thread系统的调度器进行分析.主要参考资料 ...
- linux内核分析作业:以一简单C程序为例,分析汇编代码理解计算机如何工作
一.实验 使用gcc –S –o main.s main.c -m32 命令编译成汇编代码,如下代码中的数字请自行修改以防与他人雷同 int g(int x) { return x + 3; } in ...
- linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...
- 分析一个C语言程序生成的汇编代码-《Linux内核分析》Week1作业
署名信息 郭春阳 原创作品转载请注明出处 :<Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 C源码 这 ...
随机推荐
- Android设计模式——Builder(建造者)模式
1.建造者模式是一步一步创建一个复杂对象的创建模式.该模式是为了将构建复杂对象的过程和他的部件解耦,使得构建过程和部件表示隔离开. 2.Bulider模式的定义是:将一个复杂对象的构建与它的表示分离, ...
- 发现个很变态的css问题,记录下。
注意看Ul:nth-child(){} 本来是一一对应的,因为后来改版在div中加了<h1></h1>标记后,竟然自动后移了.... 不可思议,莫明奇妙
- 利用Java反射机制对实体类的常用操作工具类ObjectUtil
代码: ObjectUtil类: import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.Simpl ...
- 注解实战@Test标签
1.创建一个Maven工程 2.点击java,右键-新建一个类 package com.course.testng; import org.testng.annotations.Test; publi ...
- UVALive-8078 Bracket Sequence 简单dp
题目链接:https://cn.vjudge.net/problem/UVALive-8078 题意 括号序列T是这样定义的: T是个空的 T是(T), {T}, 或者 [T] T是两个T组成的,比如 ...
- [USACO18JAN] Cow at Large G (dfs)
题目大意:有一只狐狸从给定的S点开始逃跑(出发),向叶节点移动以逃离这棵树,叶节点可能出现农民去抓捕狐狸,当农民和狐狸出现在同一个节点的时候,狐狸会被抓住,农民和狐狸移动速度相同,求抓捕狐狸所需要的最 ...
- Matplotlib 绘图与可视化 一些属性和错误
属性 *)调整图像边缘及图像间的空白间隔plt.subplots.adjust(6个参数) 图像外部边缘的调整可以使用plt.tight_layout()进行自动控制,此方法不能够很好的控制图像间的间 ...
- 在join中,on和where的区别
两个表在,join时,首先做一个笛卡尔积,on后面的条件是对这个笛卡尔积做一个过滤形成一张临时表,如果没有where就直接返回结果,如果有where就对上一步的临时表再进行过滤. 在使用left j ...
- HTML5常见的面试题,基础知识点
HTML5常见的面试题 一.HTML 常 ...
- jvm 垃圾回收概念和算法
1.概念 GC 中的垃圾,特指存在于内存中.不会再被使用的对象.垃圾回收有很多种算法,如引用计数法.复制算法.分代.分区的思想. 2.算法 1.引用计数法:对象被其他所引用时计数器加 1,而当引用失效 ...