徒手编写了一个STM8的反汇编工具

发布者:石头12345最新更新时间:2021-12-16 来源: eefocus关键字:STM8  反汇编工具 手机看文章 扫描二维码
随时随地手机看文章

最近打算玩一下STM8, 只为了消化一下我的库存,因为我曾经买过几个型号的STM8单片机,但是一直没用来DIY啥。我对STM8熟悉程度远不如STM32,  后者是流行广泛的ARM核,STM8却是ST独家的架构。

STM8 CPU是在ST7基础上增强,有人说是从6502演变来的,我看倒也不像。学习了一下历史,Motorola的6800演变出来的6805/6811/6809三个分支,以及6502这个与6800有渊源的CPU,从寄存器和指令集上看STM8是和它们有相似之处的,不过差异的地方也很大。作为一个8位MCU,STM8的寻址范围居然达到16M byte(我不信ST会给8位机配上1M以上的ROM或RAM),寻址模式就很多了,间接内存访问比x86都复杂,看惯了RISC的CPU更不能忍。好吧,虽然指令集复杂,STM8的执行速度还快,反正不会纯用汇编来开发。

ST并没有提供STM8的C编译器(汇编器是有的),需要用第三方的。Cosmic C编译器有免费License的版本可以用,这也是ST推荐的,我就装了一个来试。ST官方支持的还有Raisonance的编译器,此外IAR也有STM8的开发环境。


试写了个C程序测试,可以用STVP连接ST-Link下载程序,但我觉得还需要个能反汇编看编译结果的东西。Cosmic工具链里面没有反汇编程序,ST的汇编工具里也没有,STVD既然能跟踪调试应该有,但我没能把它用起来。


干脆自己写一个STM8反汇编工具吧,也练下手怎么写。

先研究下STM8的指令集,这是一种典型变长指令集,除了前缀字节,操作码就在一个字节里面。于是我照着手册统计了一张表出来:
 
一个字节能表示的范围除了 0x90, 0x91, 0x92, 0x72 用来做指令前缀,其它几乎都用来作操作码了。当然许多指令都有多种寻址模式的(比如加法是谁和谁相加,需要指定),因此用了不止一个操作码。算上寻址模式,256种指令都不够用的,所以STM8靠前面增加前缀字节来扩展。从手册里面截一个例子如下(这是XOR指令的多种编码):

在指令的操作码后面就是提供数据或地址的字节了,长度由操作码加上前缀来决定。

编写反汇编程序就是写一个根据字节数据流的查表过程。上面我做的那个表只是划分了指令的分布,涉及到寻址模式的细节还是得一边写一边查手册。从表上看,操作码的高半字节大概可以把指令划分为几类,再用低半字节去细分指令,于是我的程序解码第一步就是一个 switch-case 结构来划分任务:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

int decode_instr(unsigned char opcode)

{

    switch(opcode>>4)

    {

        case 1: case 0x0A: case 0x0B: case 0x0C:

        case 0x0D: case 0x0E: case 0x0F:

            return decode_group1(opcode);

        case 0: case 3: case 4: case 6: case 7:

            return decode_group2(opcode);

        case 5:

            if(Prefix==0x72)

                return decode_group2(opcode);

            else

                return decode_5x(opcode);

        case 8:

            return decode_8x(opcode);

        case 2:

            return decode_2x(opcode);

        case 9:

            return decode_9x(opcode);

        default:

            return -1;

    }

}


解码的结果是放到全局变量里面的,返回值只代表了指令是否有效。例如,表格最右边一列的指令我是这样解析的:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

int decode_9x(unsigned char opcode)

{

    AutoXY=1;

    switch(opcode&0x0f)

    {

        case 0: return set_prefix(0x90);

        case 1: return set_prefix(0x91);

        case 2: return set_prefix(0x92);

        case 3: format(0, LDW, regX, regY);

                format(0x90, LDW, regY, regX);

                return 1;

        case 4: format(0, LDW, regSP, regX);

                return 1;

        case 5: format(0, LD, regXH, regA);

                return 1;

        case 6: format(0, LDW, regX, regSP);

                return 1;

        case 7: format(0, LD, regXL, regA);

                return 1;

        case 8: format(0, RCF, 0, 0);

                return 1;

        case 9: format(0, SCF, 0, 0);

                return 1;

        case 0xA: format(0, RIM, 0, 0);

                return 1;

        case 0xB: format(0, SIM, 0, 0);

                return 1;

        case 0xC: format(0, RVF, 0, 0);

                return 1;

        case 0xD: format(0, NOP, 0, 0);

                return 1;

        case 0xE: format(0, LD, regA, regXH);

                return 1;

        case 0xF: format(0, LD, regA, regXL);

                return 1;

        default:

            return -1;

    }

}


主要是靠 format() 函数根据当前的指令前缀来翻译操作码:指令名称,寻址的第一操作数、第二操作数。若一共写 256 个 case 分支就太繁琐了,需要抓住共性,像表格中绿色背景的这一组指令我是这么处理的:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

int decode_group2(unsigned char opcode)

{

    int instr;

    AutoXY=1;

    switch(opcode&0x0f)

    {

        case 1:

            switch(opcode>>4)

            {

                case 0: format(0, RRWA, regX, 0); return 1;

                case 3: format(0, EXG, regA, longmem); return 1;

                case 4: format(0, EXG, regA, regXL); return 1;

                case 6: format(0, EXG, regA, regYL); return 1;

                default: return -1;

            }

            break;

        case 2:

            switch(opcode>>4)

            {

                case 0: format(0, RLWA, regX, 0); return 1;

                case 3: format(0, POP, longmem, 0); return 1;

                case 4: format(0, MUL, regX, regA); return 1;

                case 6: format(0, DIV, regX, regA); return 1;

                case 7: return set_prefix(0x72);

            }

            break;

        case 5:

            switch(opcode>>4)

            {

                case 3: format(0, MOV, longmem, imm8); return 1;

                case 4: format(0, MOV, mem, mem); return 1;

                case 6: format(0, DIVW, regX, regY); return 1;

                default: return -1;

            }

            break;

        case 0xB:

            switch(opcode>>4)

            {

                case 3: format(0, PUSH, longmem, 0); return 1;

                case 4: format(0, PUSH, imm8, 0); return 1;

                case 6: format(0, LD, offSP, regA); return 1;

[1] [2] [3]
关键字:STM8  反汇编工具 引用地址:徒手编写了一个STM8的反汇编工具

上一篇:STM8单片机CAN滤波器的设置
下一篇:STM8的中断系统以及外部中断详解

推荐阅读最新更新时间:2024-11-17 16:07

STM8 单工通信
STM8 单工通信 SPI能够以两种配置工作于单工方式: 1条时钟线和1条双向数据线 1条时钟线和1条数据线(双工或接收方式) 1条时钟线和1条双向数据线 设置SPI_CR2寄存器中的BDM位启用此模式。在这个模式中,SCK用作时钟,主模式中的MOSI或从模式中的MISO用作数据通信。 传输的方向(输入或输出)由SPI_CR2寄存器里的BDOE控制,当这个位是1的时候,数据线是输出,否则是输入。 1条时钟和1条数据线(双工或只接收方式) 为了释放一根I/O脚作为它用,可以通过设置SPI_CR2寄存器中的RXONLY位来禁止SPI输出功能。这样的话,SPI将运行于只接收模式。当RXONLY位置0时,SPI又会恢复到全
[单片机]
STM8 多处理器通信
STM8 多处理器通信 通过UART可以实现多处理器通信(将几个UART连在一个网络里)。例如某个UART设备可以是主设备,它的TX输出和其他UART从设备的RX输入相连接;UART从设备的各自TX输出作逻辑与运算后和主设备的RX输入相连接。 在多处理器配置中,我们通常希望只有被寻址的接收者才被激活,来接收随后的数据,这样就可以减少由未被寻址的接收器的参与带来的多余的UART服务开销。 未被寻址的设备可启用其静默功能置于静默模式。在STM8静默模式里: 任何接收状态位都不会被设置。 所有接收中断被禁止。 UART_CR1寄存器中的RWU位被置1。RWU可以被硬件自动控制或在某个特定条件下由软件写入。 根据UAR
[单片机]
<font color='red'>STM8</font> 多处理器通信
STM8学习笔记---新建IAR工程文件
1.打开IAR工具 2.选择Project—Create New Project 3.选择Empty project,点击OK。 4.选择文件存储路径,输入工程名,点保存按钮。 5单击左上角新建文件按钮 这时候右侧会出现一个新建文件,新建的是文本文件,需要把文本文件保存为*.c格式。 5.点左上角保存按钮 6.将文件名改为main.c,点保存按钮。 这时候界面右侧文件名就会变为main.c,但是新建的工程中还没有文件,需要将main.c文件添加到工程中。 7.在左侧新建工程名上单机鼠标右键,选择Add–Add Files 8.然后再打开的文件框中选中main.c文件,然后点打开。 这时候工程文件
[单片机]
<font color='red'>STM8</font>学习笔记---新建IAR工程文件
STM8 入门学习与实验(一)GPIO与UART
简介:这一章节将描述如何配置GPIO,以及如何配置UART STM8工程模版:http://download.csdn.net/detail/u012388993/9904051 这两则实验将使用到的基本的系统配置初始化函数 CLK_PeripheralClockConfig(CLK_PERIPHERAL_UART1, ENABLE);//配置UART1时钟 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);配置时钟分频为1,则系统时钟频率为16Mhz/1 = 16Mhz CLK_HSICmd(ENABLE);//使能内部振荡器 实验一、点亮LED灯,灯正极接电源负极连接到引
[单片机]
<font color='red'>STM8</font> 入门学习与实验(一)GPIO与UART
STM8的AWU应用体会
AWU——STM8中的精简RTC 今天使用STM8L001进行了类似RTC定时从停止模式唤醒MCU的实验。不知为何,在STM8S系列部分芯片(我没一个个去查看)和STM8L001、STM8L101等个别芯片上是没有RTC模块的,取而代之的是其阉割版 —— AWU模块。 因为只能配置自动唤醒闹钟,没有设置日历的功能。 这个AWU看文档配置起来极其简单,但实际操作起来还是有些坑的。我以实验用的STM8L001为例。 文档说第一步要测量LSI的时钟频率。 后面的步骤在库文件的AWU_Init函数中都已经有了过程。 怎么测量LSI的时钟频率呢? 这写的一堆是干嘛呢…我比较懒,就去库文件中找了找,发现了几个好东西。 在s
[单片机]
<font color='red'>STM8</font>的AWU应用体会
STM8 GPIO输入输出模式
悬浮输入 悬浮输入,也叫浮空输入,顾名思义,即引脚悬空。这种方式的输入阻抗很高。当悬浮输入的引脚上加上信号时,单片机所得到的信号并不确定是高电平或是低电平,是一个不确定的信号。悬浮输入的典型应用就是模数转换,外部的任何一个小信号都要经过A/D采样转换为数字信号。 上拉输入 上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平!电阻同时起限流作用!强弱只是上拉电阻的阻值不同。 上拉输入最典型的应用就是外部按键,当按键未按下时,我们要保证它是高电平,当按键按下时才被拉低。 推挽输出 推挽输出(Push-pull output),也称为互补输出,推拉式输出。推挽输出模式导通损耗小,效率高。在此
[单片机]
<font color='red'>STM8</font> GPIO输入输出模式
STM8与汇编语言(15)--AD转换
现在大部分的单片机也都具备了A/D转换器,有8位的,也有10位的,当然性能好的具备了12位的A/D。在STM8单片机中,提供的是10位的A/D,通道数随芯片不同而不同,少的有4个通道,多的则有16个通道。 下面的实验程序首先对A/D输入进行采样,然后将采样结果的高8位(丢弃最低的2位),作为延时参数去调用延时子程序,然后再去驱动LED控制信号。因此不同的采样值,决定了LED的闪烁频率。当旋转ST三合一开发板上的电位器时,可以看到LED的闪烁频率发生变化。 同样还是利用ST的开发工具,生成一个汇编程序的框架,然后修改其中的main.asm,修改后的代码如下。 stm8/ #include mapping.inc
[单片机]
STM8】IAR 项目新建步骤
一、新建项目目录和子文件夹 新建如下文件夹名: 二、将 STM8 库文件放入到 LIB 里 STM8 库文件如下: 三、新建 IAR 项目 3.1 新建项目 打开 IAR 软件,新建项目:Project - Create New Project ... 将项目文件保存到第一步新建的的 “IAR” 目录中,文件名可以直接按照项目的名称命名。 3.2 在IAR项目增加组 在 IAR 软件的项目中分别增加三个组:DRV、LIB、USR(和第一步新建的目录相对应) 3.3 设置项目参数 ▪ 选择开发的芯片 ▪ 设置预处理的文件夹 文本内容如下: $PROJ_DIR$ $PROJ_DIR$..LI
[单片机]
【<font color='red'>STM8</font>】IAR 项目新建步骤

推荐帖子

关于msp430g2553UART的调试
关于调试2553UART遇到的几个问题请问大家一下。直接用板子的USB转串口调试的话,应该是先将J3上的两个跳线帽按SWUART方式接,下载完成程序后再按HWUART方式接,再打开调试助手调试。为何我直接接的HWUART方式也能将程序下载到单片机中呢?我用的是IAR。另外发现这种用板子USB转串口调试,这个波特率只能达到9600bps,再大就不行了,这是不是和片上仿真的那个TUSB3410芯片有关?如果是用USB转串口模块(我那个模块的芯片是PL2303,波
风的世界 微控制器 MCU
一起围读《RISC-V体系结构编程与实践 》第2篇 搭载硬件环境测一下指令
这两天大概读了笨书的第三章第四章,趁着学习的热度,再回味一下之前的搭建硬件环境测试分析一下学习了本书第三章第四章的内容,下面我们搭建硬件环境,测试下RISC-V指令Windows环境下搭建基于Eclipse+RISC-Vgcc编译器的RISC-V开发环境,配合openocd调试软件,可以实现RISC-V内核程序的编译、下载和调试。硬件选择GD的一款RISC-V芯片创建工程,选用型号为GD32VF103之前第一篇以为RI
常见泽1 嵌入式系统
YLP2440核心板内存能扩到256M吗
YLP2440核心板内存能扩到256M吗.YLP2440核心板带有64MRAM,由于项目需要扩到256M,请问这个好能实现吗,是不是仅添加两根地址线就可以了?2440的BANK为128M,这样需要2个BANK,硬件上需要那些改动?YLP2440核心板内存能扩到256M吗应该可以我觉得不可以。因为这个2440的内存控制器决定了。你看看这个分析,http://blog.eeworld.net/gooogleman/archive/2008/11/12/3283075.aspx如果
dan123456 嵌入式系统
vxworks BSP配置问题
网络加载vxworks镜像后,在命令行中使用reboot命令,总是重启不了,出现了很多错误,这是什么原因,哪位大侠帮忙解决一下?vxworksBSP配置问题
zxp195728 实时操作系统RTOS
升压电路的问题
我想做一个3v升5v的电路,再网上查了查,觉得34063还可以,所以就做了一个电路,后来电压升上去了,但是效率只有80%大哥们谁有升压电路分享一个,谢谢啦升压电路的问题哦回复沙发仙猫的帖子是的,功率不大,第一次做心里没底回复5楼zhjzh72_2004的帖子我是想做可就是不懂,我电路最多300ma比较小,感觉如果做个作品不可能还那交流电供电,应该用电池了这就牵扯到效率和方便实用的问题了回复7楼murray的帖子
zhangkai0215 电源技术
庆科Wi-Fi开发板试用机会重新开启!还剩5~6块!
当时活动链接本次申请方式:1、点击庆科活动页面,详读开发板资料,围绕开发板写创意申请。2、跟帖提交周计划(周计划模板:)庆科&微雪工程师将进行评选。提交申请截止日期(即日起-11月9日)庆科Wi-Fi开发板试用机会重新开启!还剩5~6块!本帖最后由youki12345于2014-10-3116:32编辑 创意:基于WIFI的实验室防盗&环境监控系统目前,我们实验室的管理还处于低级阶段,所有的东西都由人来控制,例如:由人来确定是否关窗,由人来确定是否
soso RF/无线
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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