STM8汇编代码分析

发布者:钱老李最新更新时间:2018-09-08 来源: eefocus关键字:STM8  汇编代码 手机看文章 扫描二维码
随时随地手机看文章

使用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 4000-43FF 'eeprom'


    WORDS           ; The following addresses are 16 bits long

    segment byte at 8080-FFFF 'rom'


    WORDS           ; The following addresses are 16 bits long

    segment byte at 8000-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/


    #include "mapping.inc"


    segment 'rom'

main.l

    ; initialize SP

    ldw X,#stack_end

    ldw SP,X


    #ifdef RAM0 

    ; clear RAM0

ram0_start.b EQU $ram0_segment_start

ram0_end.b EQU $ram0_segment_end

    ldw X,#ram0_start

clear_ram0.l

    clr (X)

    incw X

    cpw X,#ram0_end 

    jrule clear_ram0

    #endif


    #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


    ; 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

    jra infinite_loop


    interrupt NonHandledInterrupt

NonHandledInterrupt.l

    iret


    segment 'vectit'

    dc.l {$82000000+main}                                  ; reset

    dc.l {$82000000+NonHandledInterrupt}   ; trap

    dc.l {$82000000+NonHandledInterrupt}   ; irq0

    dc.l {$82000000+NonHandledInterrupt}   ; irq1

    dc.l {$82000000+NonHandledInterrupt}   ; irq2

    dc.l {$82000000+NonHandledInterrupt}   ; irq3

    dc.l {$82000000+NonHandledInterrupt}   ; irq4

    dc.l {$82000000+NonHandledInterrupt}   ; irq5

    dc.l {$82000000+NonHandledInterrupt}   ; irq6

    dc.l {$82000000+NonHandledInterrupt}   ; irq7

    dc.l {$82000000+NonHandledInterrupt}   ; irq8

    dc.l {$82000000+NonHandledInterrupt}   ; irq9

    dc.l {$82000000+NonHandledInterrupt}   ; irq10

    dc.l {$82000000+NonHandledInterrupt}   ; irq11

    dc.l {$82000000+NonHandledInterrupt}   ; irq12

    dc.l {$82000000+NonHandledInterrupt}   ; irq13

    dc.l {$82000000+NonHandledInterrupt}   ; irq14

    dc.l {$82000000+NonHandledInterrupt}   ; irq15

    dc.l {$82000000+NonHandledInterrupt}   ; irq16

    dc.l {$82000000+NonHandledInterrupt}   ; irq17

    dc.l {$82000000+NonHandledInterrupt}   ; irq18

    dc.l {$82000000+NonHandledInterrupt}   ; irq19

    dc.l {$82000000+NonHandledInterrupt}   ; irq20

    dc.l {$82000000+NonHandledInterrupt}   ; irq21

    dc.l {$82000000+NonHandledInterrupt}   ; irq22

    dc.l {$82000000+NonHandledInterrupt}   ; irq23

    dc.l {$82000000+NonHandledInterrupt}   ; irq24

    dc.l {$82000000+NonHandledInterrupt}   ; irq25

    dc.l {$82000000+NonHandledInterrupt}   ; irq26

    dc.l {$82000000+NonHandledInterrupt}   ; irq27

    dc.l {$82000000+NonHandledInterrupt}   ; irq28

    dc.l {$82000000+NonHandledInterrupt}   ; irq29


    end

上面的代码看得明白吗?哈哈。首先先给一个内存图,有用到内存的地方可以回来看这个图比较直观。 
这里写图片描述

下面采用一行一行注释的方式给大家讲解(发表完之后发现注释跑到屏幕外面了,把代码栏下面的横条向右拉就可以看到了。)。

;就如之前所说的,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{$82000000+main}                                   ; reset

    dc.l {$82000000+NonHandledInterrupt}   ; trap

    dc.l {$82000000+NonHandledInterrupt}   ; irq0

    dc.l {$82000000+NonHandledInterrupt}   ; irq1

    dc.l {$82000000+NonHandledInterrupt}   ; irq2

    dc.l {$82000000+NonHandledInterrupt}   ; irq3

    dc.l {$82000000+NonHandledInterrupt}   ; irq4

    dc.l {$82000000+NonHandledInterrupt}   ; irq5

    dc.l {$82000000+NonHandledInterrupt}   ; irq6

    dc.l {$82000000+NonHandledInterrupt}   ; irq7

    dc.l {$82000000+NonHandledInterrupt}   ; irq8

    dc.l {$82000000+NonHandledInterrupt}   ; irq9

    dc.l {$82000000+NonHandledInterrupt}   ; irq10

    dc.l {$82000000+NonHandledInterrupt}   ; irq11

    dc.l {$82000000+NonHandledInterrupt}   ; irq12

    dc.l {$82000000+NonHandledInterrupt}   ; irq13

    dc.l {$82000000+NonHandledInterrupt}   ; irq14

    dc.l {$82000000+NonHandledInterrupt}   ; irq15

    dc.l {$82000000+NonHandledInterrupt}   ; irq16

    dc.l {$82000000+NonHandledInterrupt}   ; irq17

    dc.l {$82000000+NonHandledInterrupt}   ; irq18

    dc.l {$82000000+NonHandledInterrupt}   ; irq19

    dc.l {$82000000+NonHandledInterrupt}   ; irq20

    dc.l {$82000000+NonHandledInterrupt}   ; irq21

    dc.l {$82000000+NonHandledInterrupt}   ; irq22

    dc.l {$82000000+NonHandledInterrupt}   ; irq23

    dc.l {$82000000+NonHandledInterrupt}   ; irq24

    dc.l {$82000000+NonHandledInterrupt}   ; irq25

    dc.l {$82000000+NonHandledInterrupt}   ; irq26

    dc.l {$82000000+NonHandledInterrupt}   ; irq27

    dc.l {$82000000+NonHandledInterrupt}   ; irq28

    dc.l {$82000000+NonHandledInterrupt}   ; irq29


    end


好了,本文就讲完了,其实想尽量讲得明白一些,但发现涉及到的方面太多了,展开来讲的话,篇幅会很大,也会显得很啰嗦了。比如说汇编指令我就没有介绍过,汇编连接器所支持的伪指令有哪些我也没有讲到,想讲,但可能花的篇幅比本文还长。。后续有时间,觉得有必要再进行补充吧。最近也比较忙。当然最重要的一点是,没人看。没人看。没人看啊。



关键字:STM8  汇编代码 引用地址:STM8汇编代码分析

上一篇:学习STM8 关于数据类型的定义心得
下一篇:stm8单片机内部存储EEPROM字节读写实例解析

推荐阅读最新更新时间:2024-03-16 16:13

STM8 UART中断发,中断收
STM8 UART 初始化 配置STM8 UART的几个常用寄存器分别为: UART1_CR1:控制寄存器1 UART1_CR3:控制寄存器3 UART1_BRR2:波特率寄存器2 UART1_BRR1:波特率寄存器1 利用控制寄存器1,2,3可以配置UART数据传输的具体帧格式,这里将UART配置为1个起始位,8个数据位,1个停止位,无校验位。具体操作代码如下: UART1_CR1=0x00; UART1_CR3=0x00; 在UART1_CR1中第4位,定义了数据字的长度,该位写0将设置UART为一个起始位,8个数据位,n个停止位,停止位n的数量可在UART1_CR3中设置。 UART1_CR
[单片机]
stm8 窗口看门狗应用
手册说明: 窗口看门狗WWDG的主要功能 ●可编程的自由运行递减计数器 ●有条件的复位 ─如果开启了看门狗,当递减计数器的数值小于0x40时产生复位 ─如果开启了看门狗,当在指定的时间窗口之外重加载递减计数器的数值(见图27)时产生复位 ●硬件或软件启动看门狗(由选择字节指定) ●可在HALT指令时产生复位(由选择字节配置) WWDG功能说明 如果开启了看门狗(设置了WDGA=1),当7位的递减计数器(T 位)从0x40变为0x3F时(即T6变为0),看门狗产生一个复位信号并把复位引脚拉低。如果软件刷新计数器时,计数器的数值大于窗口寄存器中的数值,同样会产生复位。 (图25:STM8窗口看门狗框图) 在正常的操作期间
[单片机]
<font color='red'>stm8</font> 窗口看门狗应用
STM8仿真调试快速入门
● ST Visual Develop的安装   到 ST 官方网站: http://www.st.com/stonline/products/support/micro/files/sttoolset.exe 下载安装。 ● ST Visual Develop之设置软件与建立、打开相关调试文件   ----使用Cosmic C语言,软件仿真   要用STVD的IDE下使用COSMIC C语言开发的话,首先要在STVD中对COSMIC STM8编译器进行设置。 如下图所示,运行ST Visual Develop 集成开发环境,选择菜单 Tools - Options ,在出现的对话框中选择 Toolset 选项卡。在 Tools
[单片机]
<font color='red'>STM8</font>仿真调试快速入门
STM8开发记录二:UART RX空闲中断和DMA操作
一、用STM8L的时候,没能在同时读取Rx中断和IDLE中断标志,最后用DMA取数据,见 (三、DMA实现数据拷贝): 1.1 uart配置 void UsartConfig(void) { // USART_DeInit(USART1); /* Enable USART clock */ CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE); /* USART pin remap */ SYSCFG_REMAPPinConfig(REMAP_Pin_USART1TxRxPortA, ENABLE); /* Configure USART
[单片机]
stm8用ST-Link下载时出现Connection error
错误信息:Connection error (usb://usb): gdi-error : can't access configuration database 解决办法: win7解决方法:重新安装Program Files (x86)/STMicroelectronics/st_toolset/stvd/dao/ST Toolset.msi 在管理员权限下安装 在百度知道上找到的解决方案:http://zhidao.baidu.com/link?url=_ft7AOwaWeKLK2bQNO76lP9g9L7PzLeIItpb-aJ_kkZ132KMvtKdhXQLPHQSNsNFxlpMDUNjGeMA9a
[单片机]
STM8使用STVD开发环境问题
1、编译时出现.ubsct size overflow --http://blog.sina.com.cn/s/blog_817a5eb601018186.html ----------- Project roewe - STM8 Cosmic - Configuration Debug ------------- Running Linker clnk -l C:Program FilesCOSMICCXSTM8Lib -o Debugroewe.sm8 -mDebugroewe.map Debugroewe.lkf #error clnk Debugroewe.lkf:1 segment .ubsct s
[单片机]
STM8单片机关于rtc部分代码分享
STM8 rtc 时钟可以使用内部低频时钟源,或者外部低速 32768Hz 时钟源,关于 rtc 部分代码如下: #include #include voidinit_rtc(void) { RTC_InitTypeDefRTC_InitStr; RTC_TimeTypeDefRTC_TImeStr; RTC_DateTypeDefRTC_DateStr; #if0 /*LSI*/ CLK_RTCClockConfig(CLK_RTCCLKSource_LSI,CLK_RTCCLKDiv_1); CLK_PeripheralClockConfig(CLK_Peripheral_RTC,ENABLE); RTC_I
[单片机]
<font color='red'>STM8</font>单片机关于rtc部分<font color='red'>代码</font>分享
STM8学习笔记——定时器定时功能和中断相关
定时器都差不多,无非就是计数,溢出了标志置位,如果中断允许的话产生一个中断。说到中断,STM8S105的中断向量表如下 时基单元: 在选用内部时钟的情况下,CK_PSC是由fmaster提供的,经过定时器预分频器分频产生CK_CNT时钟,然后作为一次计数的时基,举个例子,16M的内部RC,经过16分频,则CK_CNT为1M,那么定时1ms只需计数1000次。 计数模式: 共有三种:向上,向下,中央对齐 以向上计数为例:计数器从0计数到用户定义的比较值(TIMx_ARR寄存器的值),然后重新从0开始计数并产生一个计数器溢出事件,同时,如果TIM1_CR1寄存器的UDIS位是0,将会产生一个更新事件(U
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved