CEVA-X16自由式编程-3-破译指令编码

note:本文是我的原始文章的中文重写版本,语义及内容有一定出入

DSP指令解码

得到DSP机器码和汇编语言之间的编码规则,对于后期编写汇编器和反汇编器是必须的。但是我们只有在CEVA-X16自由式编程-1-指令穷举中得到的对应关系,因此只能根据它来整理了。

寻找指令操作码

大致看一下 insn_16.txt就会发现这个指令编码是一团乱麻:

0000 SC : 16
0001 A.S : 16?
0010 A.L : 16?
0011 SQ : 16
 0x3DXX sequencer: VLIW slots combining?
0100 LS0 : 16
0101 LS0 : 16
0110 M0 : 16?
0111 M0 : 16?
1000 LS0
1001 LS0
1010 LS1
1011 LS1
1100 LS0 || SC
 0xC800 A.M0 || SC
 0cCA00 A.M1 || SC
 0xCC00 A.S || SC
 0xCE00 A.L || SC
1101 LS0 + (SC?)
1110 LS1 || SC
1111 LS1 || SC

  • 找不到一个确定的操作码编码区:大致看了一下,有的指令操作码有5位,有的只有2位。

但我们可以确定一些信息:

  • 1000(0x8000)开始,编码变得乱七八糟,并且开始出现||符号,也就是VLIW并行的多条指令。大致浏览就可以发现0x8000以前的指令全部是16位的,而0x8000以后都是32位的指令,所以可以基本确定第1位决定了16/32位编码。也就是说,第1位为0,处理器就以16位编码规则解析后面的机器码;第1位为1,处理器就以32位编码规则解析后面的机器码。

因此我们需要将编码解析分成两部分,16位部分和32位部分:

16位指令解码

其实16位指令编码的穷举一共只有65536种,所以完全可以基于反向查表实现一个汇编器。不过那样终究不是一个好看的解决办法,要想足够优雅的解决这个问题(更何况还有32位的要解决),最好还是弄清楚指令的编码规则。

为了找规律不那么头大,简单的进行了一下可视化:

  • 首先我们修改一下第一篇文章里的代码,去掉结果文件里的机器码hex值:
SYStem.CPU CEVA-X1622
SYStem.Up

PRIVATE &filename

AREA.Create REPORT
AREA.CLEAR  REPORT
AREA.view   REPORT

&filename=OS.PPD()+"insn_16_nohex.txt"

AREA.OPEN REPORT &filename

LOCAL &val0
&val0=0
LOOP:
Data.Set P:0x0 %BE %Word &val0
PRINT DISASSEMBLE.ADDRESS(P:0)
&val0=&val0+1
Var.IF &val0>0xFFFF
	GOTO STOP
ELSE
	GOTO LOOP

STOP:
AREA.CLOSE REPORT

ENDDO

结果就会像这样:

SC.cmps{eq} r0,#0x0,prs0
SC.cmps{eq} r1,#0x0,prs0
SC.cmps{eq} r2,#0x0,prs0
SC.cmps{eq} r3,#0x0,prs0
SC.cmps{eq} r4,#0x0,prs0
SC.cmps{eq} r5,#0x0,prs0
...

接下来做一个简单的界面:

#!/usr/bin/env python3
import tkinter as tk

class BitSwitch(tk.Frame):
    def __init__(self, master=None, bit=0, **kwargs):
        super().__init__(master, **kwargs)
        self.bit = bit
        self.var = tk.BooleanVar()
        self.var.set(False)
        self.checkbox = tk.Checkbutton(self, variable=self.var, command=self.update_text)
        self.checkbox.pack(side="right")

    def update_text(self):
        bits = [switch.var.get() for switch in reversed(switches)]
        num = sum(bit << i for i, bit in enumerate(bits))
        with open("insn_16_nohex.txt", "r") as f:
            lines = f.readlines()
            if num < len(lines):
                text.delete("1.0", "end")
                text.insert("end", lines[num])

root = tk.Tk()
root.title("16-bit Binary Switches")

switches = [BitSwitch(root, bit=i) for i in range(16)]
for switch in switches:
    switch.pack(side="left")

text = tk.Text(root, height=3, width=50)
text.pack()

root.mainloop()

这样子,点点鼠标就可以动态改变每一位的值,观察到反汇编的变化:
在这里插入图片描述在这里插入图片描述这样看起来就容易多了

  • 接下来就是漫长的体力劳动环节了

32位指令解码

在已知第1位是开启32位解码之后,发现了下面的规律:

1001 9C800000 SQ.dint
1011 BC800000 SQ.dint
1101 DC8000000000 SQ.dint || SC.cmps{eq} r0,#0x0,prs0
1111 FC8000000000 SQ.dint || SC.cmps{eq} r0,#0x0,prs0

可以看到第2位似乎启用了VLIW指令并行。我们再试一试:

DC8000003CA0 SQ.dint || SQ.nop
DC800000DC8000000000 SQ.dint || SQ.dint || SC.cmps{eq} r0,#0x0,prs0

的确,在设置一条32位指令的第2位之后,处在后面的一条指令就被并到同一行里,成为一条指令了。我们可以看到,下一条指令既可以是32位也可以是16位,并且还支持级联,可以把很多指令都连在一起。

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值