徒手编写了一个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-07 19:56

STVD配置
按以下图配置 1,编译器配置 2,调试配置 3, 4, 5, 6, 7, 8, 9,出现如下错误 #error clnk lkfmicrobus_pro.lkf:1 no default placement for segment .fcode #error clnk lkfmicrobus_pro.lkf:1 no default placement for segment .FLASH_CODE #error clnk lkfmicrobus_pro.lkf:1 no default placement for segment .iapconst The command: clnk -l
[单片机]
STVD配置
STM8 端口 PWM(TIM2_CH3)复用功能开启
单片机型号 STM8S103F TSSOP 20脚 void Write_Option_Byte(void) { /*解锁Flash*/ do { FLASH- DUKR = 0xAE; FLASH- DUKR = 0x56; } while(!(FLASH- IAPSR & 0X08)); /*对选项字节进行写操作使能*/ FLASH- CR2 = 0X80; /*互补控制寄存器*/ FLASH- NCR2 = 0X7F; /*写操作,0x02:PD2*/ *((unsigned char *)0x4803) = 0x02; *((unsig
[单片机]
<font color='red'>STM8</font> 端口 PWM(TIM2_CH3)复用功能开启
STM8单片机ADC采样功能通过定时器触发
  在使用STM8单片机的ADC功能时,读取ADC数据时一般有两种方式,一种是通常不断地读取采样标志位,来判断ADC采样是否结束,一种是通过中断的方式来通知系统采样是否结束。   有时候采样ADC数据的时候,需要按照一定的时间间隔,定点的去采样数据。一般使用的方式就是通过定时器定时,然后在定时中断函数中再去读取ADC采样的数据。但是这种方式采样的时间是不固定的,比如进入定时器中断后,ADC采样刚结束,就可以直接使用当前采样到的数据。但是如果运气不好的话,进入定时中断后,ADC采样刚开始,那么此时就需要等到ADC采样结束后,才能使用数据。这样就好导致每次读取ADC数据时还会有随机的一段等到ADC数据的延时。   通常查阅ST
[单片机]
<font color='red'>STM8</font>单片机ADC采样功能通过定时器触发
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硬件I2C事件锁死
前言: 之前我使用STM8的硬件IIC通信卡死在判忙,专门写了一篇注意事项。这个周又使用,对之前的理解注意事项又有了更深刻的理解。 上一篇,IO口状态设置不对,会使IIC不能正常工作。本质上我也没想明白,这一次使用又使用,结果碰到了更难理解的问题,直接把STM8复位都不能恢复正常。倒是更加深了之前为什么IO设置不对会是硬件I2C不能正常工作的理解。 当把I2C 数据脚设置为输出低的时候,I2C总线会认为外部器件把总线占用了,所以不能正常使用。 来说说,最近碰到的IC复位都不能解决的问题,只能通过长时间断电,来解决。最后万能的网友提供了IO操作模拟一个结束信号,可以达到修复总线的效果。更进一步查,STM32也有这样的问题
[单片机]
STM8-按键切换灯的亮灭
目的:熟悉按位读入语句GPIO_ReadInputPin(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin);的使用 #include stm8s.h #define ulong unsigned long void delay(ulong i) { while(i--); }//延时函数 void main(void)////////////主函数 { unsigned char flag=0; GPIO_DeInit(GPIOD); GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST); GPIO_De
[单片机]
STM8内部时钟切换
简介:在STM8内部有时钟切换功能,可以在需要时变换时钟的频率。板子为STM8_Discovery,LED为PD0。 程序如下: //功能:STM8的内部时钟分频切换 #include iostm8s105c6.h // 函数功能:延时函数 // 输入参数:ms -- 要延时的毫秒数,这里假设CPU的主频为2MHZ void DelayMS(unsigned int ms) { unsigned char i; while(ms != 0) { for(i=0;i 250;i++) { } for(i=0;i 75;i++) { } ms--; } } /
[单片机]
STM8单片机STVD环境监视数组变量的多个元素
调试时监视数组变量的多个元素修改如下设置
[单片机]
<font color='red'>STM8</font>单片机STVD环境监视数组变量的多个元素

推荐帖子

在立创做SMT有坑吗?
最近要做一批板子,因为立创商城配货速度挺快的,想直接在立创做板立创贴。之前没在立创贴过,这次板子有些复杂,有FPGA有模拟一大堆器件成本有些高,在立创贴片不知道有没有什么需要注意的地方?有没有经常在他家下SMT订单的大佬分享一些避坑经验?在立创做SMT有坑吗?比本地的价格贵好像不是坑吧在订单提交前,与立创商城的客服或销售人员保持充分沟通,了解清楚各项服务细节和潜在风险除了贵,没别的毛病没用过,太贵了,自己手焊基本上就能解决 贵点没事,现在是比较着急想尽快做出来
littleshrimp 国产芯片交流
编译错误在VS2005,但是OK在VC6
Dearall,recentlyItransformmyprojectfromvc6tovs2005,itoccurtothefollowingerror:d:\\ProgramFiles\\MicrosoftVisualStudio8\\VC\\ce\\include\\xtree(1172):errorC3848:expressionhavingtype\'constCFormatter::ltwsz\'wouldlosesom
wanxd 嵌入式系统
micropython更新:STM32L4支持SD卡了
今天micropython发布了新的更新,在STM32L4上支持SD卡了。https://github.com/micropython/micropython/commit/f3636a7b46942b7141d75214015a00a4c4aa21d6我们将尽快提供Nucelo_L476RG的移植版本,配合免费提供给大家的Nucelo扩展板,就可以在Nucelo_L476RG上使用了。芯(E)币兑换Nucleo扩展板micropython更新:STM32L4支持SD卡了楼
dcexpert MicroPython开源版块
DAC7621并行12DA的程序问题。
用TI的DAC7621.遇到时序的问题,希望大家帮忙解答下,万分感谢,我后来改的程序如下,voidDAC7621(){DAC7621_CMD_OUT;//DAC7621_INIT;P3DIR|=0XFF;P4DIR|=0X0F;_NOP();DAC7621_LD_H;//DAC7621_CLR_H;//DAC7621_RW_H;//_NOP();//DAC7621_CS_H;_NOP();DAC7621_CS_L;//Dela
youluo 模拟电子
PIC单片机产生PWM波波形出问题了
用PIC16F887单片机产生方波,当我把方波频率设为4KHz时波形是正常的,当我改变TIMER2的分频值,从4改为16,此时频率应该变为1KHz,此时频率是对了,可是波形却变得很奇怪。求有经验的大神指导下!这是产生的4KHZ正常波形,占空比为50%这是1KHZ有问题的波形,占空比为60%。这个高电平会突然下降到一个比较低的电平再上升。1KHz占空比80%就正常了PIC单片机产生PWM波波形出问题了主要的程序我也贴上来各位看一下/********端
Sevenfee 单片机
求助:WINCE S3C2440 30万像素摄像头显示出现多张相同的画面平铺是为什么?
具体情况:我用的LCD为800*600的屏,开发板为S3C2440,摄像头驱动原先为130万像素,现在我想弄成30万像素,改可驱动,结果显示在LCD上的画面是多张相同的画面平铺,就是类似于电脑桌面,原先拉伸的图片变为平铺??请问这是怎么回事??我在注册表中已经把分辨率设置为800*600了,怎么还是出现上面的情况?我想是不是因为30万像素的摄像头的分辨率不够,所以造成图象只能平铺??我是新人不懂请教了求助:WINCES3C244030万像素摄像头显示出现多张相同的画面平铺是为什
ywdxll WindowsCE
小广播
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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