历史上的今天

今天是:2024年09月06日(星期五)

正在发生

2021年09月06日 | 徒手编写了一个STM8的反汇编工具

发布者:石头12345 来源: 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的中断系统以及外部中断详解

推荐阅读

8月30-31日,由中国高科技行业门户OFweek维科网、高科会主办,OFweek人工智能网、OFweek医疗科技网承办的2018中国(上海)国际人工智能展览会暨OFweek(第二届)人工智能产业大会在上海跨国采购会展中心成功举办。本次大会共汇集了人工智能领域的国际知名企业高层、行业资深专家、专家分析机构等数千位精英,用全新视角透析行业动态,解读人工智能领域本...
一、定时器STM32F1系列的产品,除了互联型产品外,工位8个定时器 TIM6、TIM7是基本定时器,TIM2、3、4、5是通用定时器,TIM1、TIM8是高级定时器基本定时器1.时钟源定时器时钟TIMxCLK,即内部时钟CK_INT,经APB1预分频提供。在STM32L4:Tim1是高级时钟可做PWM输出Tim15、Tim16是通用时钟,他们的时钟来源是APB2总线Tim2、Tim3通用时钟,Tim6,Tim7基本时钟的时...
据 Windowslatest 报道,尽管 Surface Duo 目前仍无法直接购买,但这款手机的续作已在研发之中了。 根据多个微软 Android 操作系统工作清单显示,微软备受期待的 Surface Duo 现在可以预订,同时 Surface Duo 2 的研发工作已经开始。  IT之家了解到,下一代 Surface Duo 内部代号为 Zeta,除此以外目前尚无更多有关 ...
近日,晶盛机电在接受机构调研时表示,截至2021年6月30日,公司未完成晶体生长设备及智能化加工设备合同总计114.5亿元,其中未完成半导体设备合同6.44亿元。(以上合同金额均含增值税)。公司产品种类较多,不同产品交货周期不同,主要在3-6个月左右。在设备验收时确认收入。其称,上半年,受下游需求拉动及贸易政策影响,国内半导体产业呈现快速发展势头...

史海拾趣

问答坊 | AI 解惑

Sealevel Systems公司8口串行PC/104模块面向串行密集应用

Sealevel Systems公司面向串行密集应用的SIO-104+8.485型8端口PC/104 RS-485模块完全遵循PC-104技术规范,还包括数个带128B FIFO的UART器件,以满足高达921.6kbps的通信速率要求。该模块可以自动管理RS-485驱动程序,允许操作系统将其RS-485端口作 ...…

查看全部问答∨

直流电源模块的设计难点在什么地方

作者: 快意江湖:           本人认为: 1、热处理。这应该是电源模块设计的最大局限性,也是最容易被用户所忽略的地方。市场的现状是客户没几个真正懂电源,因而,给很多电源厂家玩文字游戏的空间。 ...…

查看全部问答∨

高清监控选购与安装大评论

目前时常上的几款高清网络摄像机,分别采用CCD、CMOS传感器,分别支持1280x720、1600x1200高清编码,可实现高达5倍D1效果显示,码率在2M-8Mbps之间可调。在后端上,国内首推的DS-9000系列混合型DVR最多可接入8路高清网络摄像机,录像与解码分辨率支 ...…

查看全部问答∨

请问一下高手:evc的程序在PDA出现这个对话框怎么回事?谢谢

D:\\ 对话框里面写着: CE Platform POCKET PC 2003 don\'t match remote OS version 502.continue?…

查看全部问答∨

USB设备开发中,既然遵循协议2.0的usb接口部件数据传输速率比较高,那么51单片机作为其微控制器,单片机本身的速度会不会成为整个usb速度或性能的瓶颈??

菜鸟初学usb开发,看过一些例子,发现基本都是以51作为usb芯片的微控制器,51的工作频率最高好像才33Mz,而usb高速传输理论速率可达480Mb/s,搞不太清它们会怎么相互影响。想充分发挥usb接口部件的高速性能,那微控制器的处理速度会带来什么样的影 ...…

查看全部问答∨

什么叫X86开放式构架?

什么叫X86开放式构架? X86指什么?除了X86还有X什么吗? 做软件开发需要考虑X86吗?…

查看全部问答∨

用.net SDK1.1开发的应用程序能有办法在WinCE.NET中使用吗?

如题:用.net SDK1.1开发的应用程序能有办法在WinCE.NET中使用吗? 即我的应用程序是用Visual Studio .NET 2003开发的,现在想做成嵌入式应用,这个程序能在WinCE.NET中使用吗?还是我必须另外写程序 希望高手指点!…

查看全部问答∨

wince6.0中,IE6.0无法自动弹出软键盘

在wince6.0中,IE6.0和在wince6.0中的一样,当鼠标点在地址栏内,无法自动弹出软件盘。各位大侠有没有比较好的办法解决呢? 谢谢了! 另外,请问有谁做过将intel的PSM从5.0移植到6.0上呢?我现在一直过去了,但是PSM似乎都没有工作,看不到flash ...…

查看全部问答∨

求一块LM3S学习板

本帖最后由 ddllxxrr 于 2016-1-7 17:12 编辑 求一块LM3S板子学习,我有LM3S8962 LM3S6XXX 各两个,也有STM32F103的板子,可以交换。也可以购买你的板子。 …

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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