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位,并且还支持级联,可以把很多指令都连在一起。
未完待续