STM8 汇编学习笔记2: 开发环境

写在前面

  就我所知,STM8的开发平台有两种:意法官方的STVD和IAR for STM8. STVD没尝试过。反正IAR用的感觉还凑合,各种该有的功能都有,但界面不怎么友好。估计是低版本的缘故,由于我长时间盯着屏幕眼睛酸痛,所以没办法只能把编辑界面底色改成黑色,可TM怎么行号编程白色的了?根本看不清。看不清就不看了呗,反正也没啥用。但是就是感觉超不爽。而且字体显示不知道怎么搞的,在缩放的时候就变得特别奇怪,跟手写的一样。

相关资料

  如之前所述,官方的永远是最好的,没有谁比创造者更了解他的产品了。
在这里插入图片描述

环境设置

  俗话说,站在巨人的肩膀上能看得更远。学习汇编也是一样,先看代码。但是由于汇编对于不同的芯片差异很大,网络上也很难找到资源。怎么办呢?最好的办法是去看看编译环境是怎么把c语言转换成汇编的。但是默认情况下,编译结果是不会自动跑到workspace的。所以需要作如下设置:
1. workspace右键选’options…'或单击Project菜单选择’options…'打开工程选项
2. 选择 ‘C/C++ Compiler’,在 ‘List’ 选项卡中选择 ‘Output assembler file’,并选择其子选项 'Include source’
  ‘Output assembler file’ 是为了让编译器输出汇编的编译结果,‘Include source’ 可以把对应的C语言标在每段汇编前,这样有助于理解每段汇编的含义。如果想看到全部汇编信息,请将 ‘Include all call frame information’ 勾选。
3. 还是在 ‘Options…’ => ‘C/C++ Compiler’ 中,选择 ‘Optimization’ 选项卡,优化等级选择 ‘Low’ 或 'None’
  选择优化等级的原因是,在高等级的优化中,C语言和汇编的对应关系不是特别明显,有的时候会把十几行代码一起优化掉,虽然结果相同,但是你很难去用自己写的时候的思路去理解生成的代码,而且有的时候会把你的代码逻辑彻底变掉。之前就遇到过这种情况,初始化的时候本来想用查表法去对参数赋初值,结果发现改Flash里面的数据根本不会影响运行结果。把汇编打开之后,发现居然把从Flash取值的指令直接优化成立即数寻址。唉,不得不感慨编译器不懂我啊。当然还有其他更好的办法解决,之后会讲到。
  完成以上设置之后,去编译你的工程,你会发现在每个c文件下多了个Output文件夹,点开后,里面有一个 ‘.s’ 文件,这个里面就是编译得到的汇编结果。 在这里插入图片描述
在这里插入图片描述

汇编语言结构

  做完上述设置之后,编译完打开 ‘.s’ 文件,你会看到如下格式的代码:

        RTMODEL "__SystemLibrary", "DLib"
        RTMODEL "__code_model", "small"
        RTMODEL "__core", "stm8"
        RTMODEL "__data_model", "medium"
        RTMODEL "__rt_version", "4"

        EXTERN ?b0
        EXTERN ?b1
        EXTERN ?w0
        EXTERN ?w1

        PUBLIC array
        PUBLIC main
        PUBLIC val
        
          CFI Names cfiNames0
          CFI StackFrame CFA SP DATA
          CFI Resource A:8, XL:8, XH:8, YL:8, YH:8, SP:16, CC:8, PC:24, PCL:8
          CFI Resource PCH:8, PCE:8, ?b0:8, ?b1:8, ?b2:8, ?b3:8, ?b4:8, ?b5:8
          CFI Resource ?b6:8, ?b7:8, ?b8:8, ?b9:8, ?b10:8, ?b11:8, ?b12:8, ?b13:8
          CFI Resource ?b14:8, ?b15:8
          CFI ResourceParts PC PCE, PCH, PCL
          CFI EndNames cfiNames0
        
          CFI Common cfiCommon0 Using cfiNames0
          CFI CodeAlign 1
          CFI DataAlign 1
          CFI ReturnAddress PC CODE
          CFI CFA SP+2
          CFI A Undefined
          CFI XL Undefined
          CFI XH Undefined
          CFI YL Undefined
          CFI YH Undefined
          CFI CC Undefined
          CFI PC Concat
          CFI PCL Frame(CFA, 0)
          CFI PCH Frame(CFA, -1)
          CFI PCE SameValue
          CFI ?b0 Undefined
          CFI ?b1 Undefined
          CFI ?b2 Undefined
          CFI ?b3 Undefined
          CFI ?b4 Undefined
          CFI ?b5 Undefined
          CFI ?b6 Undefined
          CFI ?b7 Undefined
          CFI ?b8 SameValue
          CFI ?b9 SameValue
          CFI ?b10 SameValue
          CFI ?b11 SameValue
          CFI ?b12 SameValue
          CFI ?b13 SameValue
          CFI ?b14 SameValue
          CFI ?b15 SameValue
          CFI EndCommon cfiCommon0
        
// E:\soc\example_empty\main.c
//    1 #include "Untitled1.h"
//    2 typedef enum {FALSE = 0, TRUE = !FALSE} bool;

        SECTION `.near.bss`:DATA:REORDER:NOROOT(0)
//    3 int val;
val:
        DS8 2

        SECTION `.near.data`:DATA:REORDER:NOROOT(0)
//    4 int array[10] = {1,33,2,6,8,9,4,45,8,0};
array:
        DC16 1, 33, 2, 6, 8, 9, 4, 45, 8, 0

        SECTION `.near_func.text`:CODE:REORDER:NOROOT(0)
          CFI Block cfiBlock0 Using cfiCommon0
          CFI Function main
        CODE
//    5 int main( void )
//    6 {
//    7   int i = 0;
main:
        CLR       S:?b1
        CLR       S:?b0
//    8   int buf;
//    9   bool flag = TRUE;
        LD        A, #0x1
        JRA       L:??main_0
//   10   while(1)
//   11   {
//   12     if(array[i] > array[i+1])
//   13     {
//   14       buf = array[i];
//   15       array[i] = array[i+1];
//   16       array[i+1] = buf;
//   17       flag = FALSE;
//   18     }
//   19     if(i == 9)
//   20     {
//   21       if(flag)
//   22       {
//   23         break;
//   24       }
//   25       i = 0;
??main_1:
        CLR       S:?b1
        CLR       S:?b0
??main_0:
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array + 2
        LDW       X, (X)
        LDW       S:?w1, X
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array
        LDW       Y, X
        LDW       X, S:?w1
        CPW       X, (Y)
        JRSGE     L:??main_2
        LDW       X, S:?w0
        SLLW      X
        LDW       X, (L:array,X)
        LDW       S:?w1, X
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array + 2
        LDW       X, (X)
        LDW       Y, X
        LDW       X, S:?w0
        SLLW      X
        LDW       (L:array,X), Y
        LDW       X, S:?w0
        SLLW      X
        LDW       Y, S:?w1
        ADDW      X, #array + 2
        LDW       (X), Y
        SUBW      X, #array + 2
        CLR       A
??main_2:
        LDW       X, S:?w0
        CPW       X, #0x9
        JRNE      L:??main_0
        TNZ       A
        JREQ      L:??main_1
//   26     }
//   27 
//   28   }
//   29   return 0;
        CLRW      X
        RET
//   30 }
          CFI EndBlock cfiBlock0

        SECTION VREGS:DATA:REORDER:NOROOT(0)

        END
// 
//  2 bytes in section .near.bss
// 20 bytes in section .near.data
// 84 bytes in section .near_func.text
// 

  具体含义简单介绍一下:

1. 运行模型结构(Runtime model attributes)

  这类东西基本找不到官方的翻译,那就按字面上的意思咯。它主要是对程序整体的一些基础设置,一般要保持代码中所有汇编文件保持一致。在自己写汇编时可加可不加。如果要加的话一定要从同工程下别处原样复制过来,一个符号都不要改

        RTMODEL "__SystemLibrary", "DLib"		//基础库,包括C和C++所必需的头文件
        RTMODEL "__code_model", "small"			//代码量,代表寻址空间
        										//"small"  ->最大16KB寻址空间
        										//"medium" ->最大16MB寻址空间,但函数代码地址不允许超过64KB边界
        										//"large"  ->最大16MB寻址空间,函数代码地址允许超过64KB边界
        RTMODEL "__core", "stm8"				//芯片型号,只能是"stm8"
        RTMODEL "__data_model", "medium"		//详见帮助文档
        RTMODEL "__rt_version", "4"				//同个程序RTMODEL改变时,版本号相应改变

2. 标号声明(Symbol control)

  对一些本文件中涉及到的变量名或函数名(地址标号)进行声明,并表示该变量的作用域。

        EXTERN ?b0
        EXTERN ?b1
        EXTERN ?w0
        EXTERN ?w1

        PUBLIC array
        PUBLIC main
        PUBLIC val
指令描述
EXTERN, IMPORT声明该标号为外部标号
OVERLAY可以识别该标号但是忽略它(不大理解)
PUBLIC声明一个标号,可以被其他文件调用
PUBWEAK标号可以被其他文件调用,可以被重定义
PEQUIRE声明一个参考标号,如果一段代码对于某段程序的加载必不可少,但除此以外没什么用

  这部分很重要。如果你想要定义全局变量或引用外部变量,必须在此处声明。
  (a) 引用外部变量或函数,请在此处将相应标号声明为:EXTERN;
  (b) 定义全局变量或函数,请在此处将相应标号声明为:PUBLIC;

3. 标号定义(Call frame information directives)

  框架信息,内容较多.

          CFI Names cfiNames0
          //命名块定义(Define a names block)
          CFI StackFrame CFA SP DATA
          //将堆栈指针指向数据。
          //格式:CFI STACKFRAME cfa resource type
          //CFA     ->(Canonical Frame Address)堆栈指针基地址
          //resource->堆栈指针资源的名称
          //type    ->三类,CODE, CONST和DATA. 具体地址由ILINK编译器决定
          CFI Resource A:8, XL:8, XH:8, YL:8, YH:8, SP:16, CC:8, PC:24, PCL:8
          CFI Resource PCH:8, PCE:8, ?b0:8, ?b1:8, ?b2:8, ?b3:8, ?b4:8, ?b5:8
          CFI Resource ?b6:8, ?b7:8, ?b8:8, ?b9:8, ?b10:8, ?b11:8, ?b12:8, ?b13:8
          CFI Resource ?b14:8, ?b15:8
          //定义系统资源,其中?b0-?b15为虚拟通用寄存器
          CFI ResourceParts PC PCE, PCH, PCL
          //PC由三字节构成:(MSB->)PCE->PCH->PAL(->LSB)
          CFI EndNames cfiNames0
		  //结束命名块定义

          CFI Common cfiCommon0 Using cfiNames0
          //使用之前定义的cfiNames0资源定义共有资源cfiCommon0
          CFI CodeAlign 1
          //代码域数据对齐为8位,即字节对齐
          CFI DataAlign 1
          //数据域数据对齐为8位,即字节对齐
          CFI ReturnAddress PC CODE
          //返回值赋给PC,指向代码域
          CFI CFA SP+2
          //Canonical Frame Address,才疏学浅,找不到合适的汉语,反正就是一个地址,
          //这个地址是个相对于堆栈地址向上偏移2字节的地址,

          CFI A Undefined
          CFI XL Undefined
          CFI XH Undefined
          CFI YL Undefined
          CFI YH Undefined
          CFI CC Undefined
          //Undefined 将变量声明为临时变量,不需要重载
          CFI PC Concat
          //声明PC为拼接变量,上文定义Resource时,PC被定义为24位字长,则其包含之后的字长和为24的变量,
          //即PCL,PCH,PCE
          CFI PCL Frame(CFA, 0)
          CFI PCH Frame(CFA, -1)
          //由CFA指向的地址偏移x值后的地址内的值赋给操作数
          //如果SP指向0x0700,则CFA指向SP+2,即0x0702;由于STM8堆栈方式为向下生长,并指向空数据,
          //所以在该命名块(Name block)中,PCL初始化值为地址0x0702中的值,PCH为地址0x0703的值
          //可见,正好与函数调用返回(RET指令)后的效果一致
          //该语法在带有形参和返回值的汇编函数中很有用
          CFI PCE SameValue
          //不需要初始化,能够保证它的值是正确的(小段程序PCE用不到)
          CFI ?b0 Undefined
          CFI ?b1 Undefined
          //同上
		  ...
          CFI ?b15 SameValue
          //同上
          CFI EndCommon cfiCommon0
          //结束共有命名块的定义

4. 内存块分配及控制(Section control directives)

// E:\soc\example_empty\main.c
//    1 #include "Untitled1.h"
//    2 typedef enum {FALSE = 0, TRUE = !FALSE} bool;

        SECTION `.near.bss`:DATA:REORDER:NOROOT(0)
        //下方的数据块地址采用如上配置

内存块配置标记(Section configuration):

标记描述存储位置
,near.bss初始值为0的静态或__near全局变量RAM起始处
.near.data含有初始值的静态或全局__near变量(初始值在.o中)RAM起始处
.near.data_init为.near.data赋初值的__near常量可在16MB寻址空间的任意位置,但所有量必须集中在64KB范围内
near.noinit不含有初始值的__near静态或全局变量RAM起始处
.near.rodata__near常量,可用于查表(数码管/LCD/CRC等等)64KB ROM起始处

  REORDER表示用SECTION后的名称申请一个内存块,于此相对有NOREORDER,只是申请一个内存片段。
  ROOT防止编译器将其优化,NOROOT表示不用的时候此块区域可供给其他变量使用
  后面的参数表示字节对齐,对齐字节数为2n(n为括号内的值)一般不需要对齐,破八位机。
  上述涉及.icf文件的一些内容,之后可能会讲到…

//    3 int val;
val:
        DS8 2

  这里涉及一些数据类型的定义(Data Defination),和上面内存块(Section)定义相关联:

标号别名描述
DC8DB8位常量,可以是字符串(ROM)
DC16DW16位常量(ROM)
DC24/
DC64/
DF32/32位浮点数常量(ROM)
DF64/64位浮点数常量(ROM)
DQ15/16位小数常量(ROM)
DQ31/32位小数常量(ROM)
DS16/16位整形变量(RAM)
DS24/16位整形变量(RAM)
DS32/
DS64/

        SECTION `.near.data`:DATA:REORDER:NOROOT(0)
//    4 int array[10] = {1,33,2,6,8,9,4,45,8,0};
array:
        DC16 1, 33, 2, 6, 8, 9, 4, 45, 8, 0

  同上


        SECTION `.near_func.text`:CODE:REORDER:NOROOT(0)
          CFI Block cfiBlock0 Using cfiCommon0
          CFI Function main

  分配代码空间

特性关键字标号内存范围指针大小默认代码量(RTMODEL中定义)描述
__near_func0x0-0xFF FF2字节Small前64KB空间,在Midium和Large中不允许使用
__far_func0x0-0xFF FF FF3字节Medium代码运行范围不可超过64KB,不允许在Small下定义
__huge_func0x0-0xFF FF FF3字节Large可以越过64KB空间,不允许在Small code下定义

5. 函数主体(Main body of function)

        CODE								//代码区声明
//    5 int main( void )
//    6 {
//    7   int i = 0;
main:										//函数入口声明,可以看做一个标号
        CLR       S:?b1
        CLR       S:?b0
//    8   int buf;
//    9   bool flag = TRUE;
        LD        A, #0x1
        JRA       L:??main_0
//   10   while(1)
//   11   {
//   12     if(array[i] > array[i+1])
//   13     {
//   14       buf = array[i];
//   15       array[i] = array[i+1];
//   16       array[i+1] = buf;
//   17       flag = FALSE;
//   18     }
//   19     if(i == 9)
//   20     {
//   21       if(flag)
//   22       {
//   23         break;
//   24       }
//   25       i = 0;
??main_1:
        CLR       S:?b1
        CLR       S:?b0
??main_0:
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array + 2
        LDW       X, (X)
        LDW       S:?w1, X
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array
        LDW       Y, X
        LDW       X, S:?w1
        CPW       X, (Y)
        JRSGE     L:??main_2
        LDW       X, S:?w0
        SLLW      X
        LDW       X, (L:array,X)
        LDW       S:?w1, X
        LDW       X, S:?w0
        SLLW      X
        ADDW      X, #array + 2
        LDW       X, (X)
        LDW       Y, X
        LDW       X, S:?w0
        SLLW      X
        LDW       (L:array,X), Y
        LDW       X, S:?w0
        SLLW      X
        LDW       Y, S:?w1
        ADDW      X, #array + 2
        LDW       (X), Y
        SUBW      X, #array + 2
        CLR       A
??main_2:
        LDW       X, S:?w0
        CPW       X, #0x9
        JRNE      L:??main_0
        TNZ       A
        JREQ      L:??main_1
//   26     }
//   27 
//   28   }
//   29   return 0;
        CLRW      X
        RET
//   30 }
          CFI EndBlock cfiBlock0

        SECTION VREGS:DATA:REORDER:NOROOT(0)

        END

链接器配置文件(linker configuration file)

  下图为.icf中涉及到的几个概念之间的关系。存储区域(Region)把整个存储地址空间划分为若干个部分:Tinydata, NearFuncCode… 这些部分中存储着各种各样的数据,这些数据可以为Section或Block.其中两者的区别,我觉得,Section是表明一些用户自定义的变量存放区域,而Section是系统资源的数据块,比如说向量、堆栈区域。

Section
Section
block
Region
Memory
.far.rodata
FarFuncCode
.far_func.text
.huge.rodata
HugeFuncCode
.huge_func.text
.eeprom.noinit
Eeprom
.eeprom.data
.eeprom.rodata
.vregs
TinyData
.tiny.bss
.tiny.data
.tiny.noinit
.tiny.rodata
.far.data_init
NearFuncCode
.far_func.textrw_init
INTVEC
CSTACK
NearData
HEAP
Address
BootROM

  如果我们想自定义一些数据,并将数据存放到指定地址,那么我们就需要修改.icf文件。对于新建的一个工程来说,它的.icf文件地址默认在IAR安装文件中,那么我们如果想要修改,不要去在默认文件修改,因为这玩意就这一份,改错了就不好恢复原样了。所以我们可以先设置一下cfi文件路径,然后把默认icf文件复制到设置的路径下。设置方法如下:
 (1) 在workspace中右击选Options…
 (2) 依次选择Linker->Config,在其中勾选Override Default,然后选择路径。我设置的是:
$PROJ_DIR$\config\lnkstm8l152c6.icf. 其中$PROJ_DIR$的含义是工程目录下的路径。
 (3) 将默认目录下的.icf文件复制到选择的文件夹下,把文件的权限修改一下,使其可以编辑,然后编译工程,出现在Output->project.out下的.icf就是了在这里插入图片描述
  下面介绍几种设置的语法:
(1) 设置Region
  define region TinyData = [from 0x00 to 0xFF];
  定义TinyData为0x00-0xFF地址范围内的Region名称。
(2) 设置Section
  place at start of TinyData { rw section .vregs };
  在区域TinyData的起始位置放置名为.vreg的Section。
2019-09-15

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值